• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.jetpackcamera.ui
17 
18 import android.Manifest
19 import android.net.Uri
20 import androidx.compose.animation.AnimatedContentTransitionScope
21 import androidx.compose.animation.core.EaseIn
22 import androidx.compose.animation.core.EaseOut
23 import androidx.compose.animation.core.LinearEasing
24 import androidx.compose.animation.core.tween
25 import androidx.compose.animation.fadeIn
26 import androidx.compose.runtime.Composable
27 import androidx.compose.runtime.LaunchedEffect
28 import androidx.compose.ui.Modifier
29 import androidx.navigation.NavHostController
30 import androidx.navigation.NavType
31 import androidx.navigation.compose.NavHost
32 import androidx.navigation.compose.composable
33 import androidx.navigation.compose.rememberNavController
34 import androidx.navigation.navArgument
35 import com.google.accompanist.permissions.ExperimentalPermissionsApi
36 import com.google.accompanist.permissions.isGranted
37 import com.google.accompanist.permissions.rememberMultiplePermissionsState
38 import com.google.jetpackcamera.BuildConfig
39 import com.google.jetpackcamera.feature.postcapture.PostCaptureScreen
40 import com.google.jetpackcamera.feature.preview.PreviewMode
41 import com.google.jetpackcamera.feature.preview.PreviewScreen
42 import com.google.jetpackcamera.permissions.PermissionsScreen
43 import com.google.jetpackcamera.settings.SettingsScreen
44 import com.google.jetpackcamera.settings.VersionInfoHolder
45 import com.google.jetpackcamera.ui.Routes.PERMISSIONS_ROUTE
46 import com.google.jetpackcamera.ui.Routes.POST_CAPTURE_ROUTE
47 import com.google.jetpackcamera.ui.Routes.PREVIEW_ROUTE
48 import com.google.jetpackcamera.ui.Routes.SETTINGS_ROUTE
49 
50 @Composable
51 fun JcaApp(
52     openAppSettings: () -> Unit,
53     /*TODO(b/306236646): remove after still capture*/
54     previewMode: PreviewMode,
55     modifier: Modifier = Modifier,
56     isDebugMode: Boolean,
57     onRequestWindowColorMode: (Int) -> Unit,
58     onFirstFrameCaptureCompleted: () -> Unit
59 ) {
60     JetpackCameraNavHost(
61         previewMode = previewMode,
62         isDebugMode = isDebugMode,
63         onOpenAppSettings = openAppSettings,
64         onRequestWindowColorMode = onRequestWindowColorMode,
65         onFirstFrameCaptureCompleted = onFirstFrameCaptureCompleted,
66         modifier = modifier
67     )
68 }
69 
70 @OptIn(ExperimentalPermissionsApi::class)
71 @Composable
JetpackCameraNavHostnull72 private fun JetpackCameraNavHost(
73     modifier: Modifier = Modifier,
74     previewMode: PreviewMode,
75     isDebugMode: Boolean,
76     onOpenAppSettings: () -> Unit,
77     onRequestWindowColorMode: (Int) -> Unit,
78     onFirstFrameCaptureCompleted: () -> Unit,
79     navController: NavHostController = rememberNavController()
80 ) {
81     NavHost(
82         navController = navController,
83         startDestination = PERMISSIONS_ROUTE,
84         modifier = modifier
85     ) {
86         composable(PERMISSIONS_ROUTE) {
87             PermissionsScreen(
88                 shouldRequestAudioPermission = previewMode is PreviewMode.StandardMode,
89                 onAllPermissionsGranted = {
90                     // Pop off the permissions screen
91                     navController.navigate(PREVIEW_ROUTE) {
92                         popUpTo(PERMISSIONS_ROUTE) {
93                             inclusive = true
94                         }
95                     }
96                 },
97                 openAppSettings = onOpenAppSettings
98             )
99         }
100 
101         composable(route = PREVIEW_ROUTE, enterTransition = { fadeIn() }) {
102             val permissionStates = rememberMultiplePermissionsState(
103                 permissions = listOf(
104                     Manifest.permission.CAMERA,
105                     Manifest.permission.RECORD_AUDIO
106                 )
107             )
108             // Automatically navigate to permissions screen when camera permission revoked
109             LaunchedEffect(key1 = permissionStates.permissions[0].status) {
110                 if (!permissionStates.permissions[0].status.isGranted) {
111                     // Pop off the preview screen
112                     navController.navigate(PERMISSIONS_ROUTE) {
113                         popUpTo(PREVIEW_ROUTE) {
114                             inclusive = true
115                         }
116                     }
117                 }
118             }
119             PreviewScreen(
120                 onNavigateToSettings = { navController.navigate(SETTINGS_ROUTE) },
121                 onNavigateToPostCapture = { imageUri ->
122                     navController.navigate(
123                         "$POST_CAPTURE_ROUTE?imageUri=${Uri.encode(imageUri.toString())}"
124                     )
125                 },
126                 onRequestWindowColorMode = onRequestWindowColorMode,
127                 onFirstFrameCaptureCompleted = onFirstFrameCaptureCompleted,
128                 previewMode = previewMode,
129                 isDebugMode = isDebugMode
130             )
131         }
132         composable(
133             route = SETTINGS_ROUTE,
134             enterTransition = {
135                 fadeIn(
136                     animationSpec = tween(easing = LinearEasing)
137                 ) + slideIntoContainer(
138                     animationSpec = tween(easing = EaseIn),
139                     towards = AnimatedContentTransitionScope.SlideDirection.Start
140                 )
141             },
142             exitTransition = {
143                 slideOutOfContainer(
144                     animationSpec = tween(easing = EaseOut),
145                     towards = AnimatedContentTransitionScope.SlideDirection.End
146                 )
147             }
148         ) {
149             SettingsScreen(
150                 versionInfo = VersionInfoHolder(
151                     versionName = BuildConfig.VERSION_NAME,
152                     buildType = BuildConfig.BUILD_TYPE
153                 ),
154                 onNavigateBack = { navController.popBackStack() }
155             )
156         }
157 
158         composable(
159             "$POST_CAPTURE_ROUTE?imageUri={imageUri}",
160             arguments = listOf(
161                 navArgument("imageUri") {
162                     type = NavType.StringType
163                     defaultValue = ""
164                 }
165             )
166         ) { backStackEntry ->
167             val imageUriString = backStackEntry.arguments?.getString("imageUri")
168 
169             val imageUri = if (!imageUriString.isNullOrEmpty()) {
170                 Uri.parse(
171                     imageUriString
172                 )
173             } else {
174                 null
175             }
176             PostCaptureScreen(
177                 imageUri = imageUri
178             )
179         }
180     }
181 }
182