11// StoneCameraApp.kt
2+ @file:kotlin.OptIn (ExperimentalMaterial3Api ::class )
3+
24package co.stonephone.stonecamera
35
46import android.annotation.SuppressLint
@@ -8,7 +10,6 @@ import androidx.annotation.OptIn
810import androidx.camera.camera2.interop.ExperimentalCamera2Interop
911import androidx.camera.lifecycle.ProcessCameraProvider
1012import androidx.compose.foundation.background
11- import androidx.compose.foundation.border
1213import androidx.compose.foundation.clickable
1314import androidx.compose.foundation.layout.Arrangement
1415import androidx.compose.foundation.layout.Box
@@ -27,6 +28,7 @@ import androidx.compose.foundation.layout.windowInsetsPadding
2728import androidx.compose.foundation.shape.CircleShape
2829import androidx.compose.material.icons.Icons
2930import androidx.compose.material.icons.filled.FlipCameraAndroid
31+ import androidx.compose.material3.ExperimentalMaterial3Api
3032import androidx.compose.material3.Icon
3133import androidx.compose.material3.IconButton
3234import androidx.compose.material3.MaterialTheme
@@ -48,22 +50,24 @@ import androidx.lifecycle.viewmodel.compose.viewModel
4850import co.stonephone.stonecamera.plugins.AspectRatioPlugin
4951import co.stonephone.stonecamera.plugins.FlashPlugin
5052import co.stonephone.stonecamera.plugins.FocusBasePlugin
53+ import co.stonephone.stonecamera.plugins.PhotoModePlugin
5154import co.stonephone.stonecamera.plugins.PinchToZoomPlugin
5255import co.stonephone.stonecamera.plugins.PortraitModePlugin
5356import co.stonephone.stonecamera.plugins.QRScannerPlugin
5457import co.stonephone.stonecamera.plugins.SettingLocation
58+ import co.stonephone.stonecamera.plugins.SettingsTrayPlugin
5559import co.stonephone.stonecamera.plugins.ShutterFlashPlugin
5660import co.stonephone.stonecamera.plugins.TapToFocusPlugin
5761import co.stonephone.stonecamera.plugins.VolumeControlsPlugin
62+ import co.stonephone.stonecamera.plugins.VideoModePlugin
5863import co.stonephone.stonecamera.plugins.ZoomBarPlugin
5964import co.stonephone.stonecamera.plugins.ZoomBasePlugin
6065import co.stonephone.stonecamera.ui.RenderPluginSetting
66+ import co.stonephone.stonecamera.ui.ResponsiveOrientation
6167import co.stonephone.stonecamera.ui.StoneCameraPreview
6268import co.stonephone.stonecamera.utils.calculateImageCoverageRegion
6369import co.stonephone.stonecamera.utils.getAllCamerasInfo
6470
65- val shootModes = arrayOf(" Photo" , " Video" )
66-
6771// Order here is important, they are loaded and initialised in the order they are listed
6872// ZoomBar depends on ZoomBase, etc.
6973val PLUGINS = listOf (
@@ -78,10 +82,15 @@ val PLUGINS = listOf(
7882 AspectRatioPlugin (),
7983 ShutterFlashPlugin (),
8084 VolumeControlsPlugin (),
85+ PhotoModePlugin (),
86+ VideoModePlugin (),
87+ SettingsTrayPlugin ()
8188// DebugPlugin()
8289)
8390
84- @OptIn(ExperimentalCamera2Interop ::class )
91+ @OptIn(
92+ ExperimentalCamera2Interop ::class
93+ )
8594@SuppressLint(" ClickableViewAccessibility" )
8695@Composable
8796fun StoneCameraApp (
@@ -94,13 +103,15 @@ fun StoneCameraApp(
94103 factory = StoneCameraViewModelFactory (context, lifecycleOwner, PLUGINS )
95104 )
96105
97- val isRecording = stoneCameraViewModel.isRecording
98106 val selectedMode = stoneCameraViewModel.selectedMode
99107
100108 val plugins by remember { stoneCameraViewModel::plugins }
109+ val modePlugins = plugins.filter { it.modeLabel != null }
101110 val previewView = stoneCameraViewModel.previewView
102111 val imageCapture = stoneCameraViewModel.imageCapture
103112
113+ val activeModePlugin = modePlugins.find { it.modeLabel == selectedMode }
114+
104115 var viewfinderDimensions by remember { mutableStateOf<Rect ?>(null ) }
105116
106117 LaunchedEffect (cameraProvider, lifecycleOwner) {
@@ -138,9 +149,11 @@ fun StoneCameraApp(
138149 ) {
139150 stoneCameraViewModel.pluginSettings.filter { it.renderLocation == SettingLocation .TOP }
140151 .map { setting ->
141- RenderPluginSetting (
142- setting, stoneCameraViewModel, modifier = Modifier .padding(4 .dp)
143- )
152+ ResponsiveOrientation {
153+ RenderPluginSetting (
154+ setting, stoneCameraViewModel, modifier = Modifier .padding(4 .dp)
155+ )
156+ }
144157 }
145158 }
146159
@@ -164,8 +177,13 @@ fun StoneCameraApp(
164177 .align(Alignment .BottomCenter ),
165178 verticalArrangement = Arrangement .SpaceBetween
166179 ) {
167- plugins.map {
168- it.renderTray(stoneCameraViewModel, it)
180+
181+ Box (
182+ modifier = Modifier .fillMaxWidth(),
183+ ) {
184+ plugins.map {
185+ it.renderTray(stoneCameraViewModel, it)
186+ }
169187 }
170188
171189 // Translucent overlay for mode switch & shutter
@@ -186,104 +204,22 @@ fun StoneCameraApp(
186204 .padding(bottom = 8 .dp),
187205 horizontalArrangement = Arrangement .SpaceEvenly
188206 ) {
189- shootModes.forEach { mode ->
190- Text (text = mode.uppercase(),
191- color = if (mode == selectedMode) Color (0xFFFFCC00 ) else Color .White ,
207+ modePlugins.forEach { modePlugin ->
208+ val modeLabel = modePlugin.modeLabel!!
209+ Text (text = modeLabel.uppercase(),
210+ color = if (modeLabel == selectedMode) Color (0xFFFFCC00 ) else Color .White ,
192211 style = MaterialTheme .typography.bodyLarge,
193212 fontWeight = FontWeight .Bold ,
194213 modifier = Modifier .clickable {
195- stoneCameraViewModel.selectMode(mode )
214+ stoneCameraViewModel.selectMode(modeLabel )
196215 })
197216 }
198217 }
199218
200- // Bottom row (Flip + Shutter)
201- Row (
202- modifier = Modifier .fillMaxWidth(),
203- horizontalArrangement = Arrangement .SpaceEvenly ,
204- verticalAlignment = Alignment .CenterVertically
205- ) {
206- // Camera Switcher Button
207- Box (
208- modifier = Modifier
209- .size(48 .dp)
210- .background(Color .Transparent , shape = CircleShape )
211- .clickable {
212- stoneCameraViewModel.toggleCameraFacing()
213- }, contentAlignment = Alignment .Center
214- ) {
215-
216- }
217-
218- // Shutter button
219- Box (
220- modifier = Modifier
221- .size(60 .dp)
222- .border(1 .dp, Color .White , CircleShape )
223- .padding(4 .dp),
224-
225- contentAlignment = Alignment .Center
226- ) {
227- Box (
228- modifier = if (isRecording) {
229- Modifier
230- .background(Color .Red )
231- .fillMaxSize(0.5f )
232- } else {
233- Modifier
234- .fillMaxSize()
235- .background(
236- if (selectedMode == " Video" ) Color (0xFFFF3B30 ) else Color .White ,
237- shape = CircleShape
238- )
239-
240-
241- }.clickable {
242- when (selectedMode) {
243- " Photo" -> {
244- // Then capture the photo
245- stoneCameraViewModel.capturePhoto()
246- }
247-
248- " Video" -> {
249- if (! isRecording) {
250- // Start recording
251- stoneCameraViewModel.startRecording(
252- stoneCameraViewModel.videoCapture
253- ) { uri ->
254- Log .d(
255- " StoneCameraApp" , " Video saved to: $uri "
256- )
257- }
258- } else {
259- // Stop recording
260- stoneCameraViewModel.stopRecording()
261- }
262- }
263- }
264-
265- }, contentAlignment = Alignment .Center
266- ) {}
267- }
268-
269- // Camera Switcher Button
270- IconButton (
271- onClick = { stoneCameraViewModel.toggleCameraFacing() },
272- modifier = Modifier
273- .size(48 .dp)
274- .padding(8 .dp)
275- .background(Color .White .copy(alpha = 0.1f ), shape = CircleShape )
276- ) {
277- Icon (
278- imageVector = Icons .Filled .FlipCameraAndroid , // Use FlipCameraAndroid if preferred
279- contentDescription = " Flip Camera" ,
280- tint = Color .White , // Customize the color if needed
281- modifier = Modifier .fillMaxSize()
282- )
283- }
284- }
219+ activeModePlugin?.renderModeControl?.invoke()
285220 }
286221 }
287222 }
223+
288224 }
289225}
0 commit comments