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