Merge "Revert "Fix the take picture failure issue with unbind preview"" into androidx-main
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index 57c2ba8..4879ddf 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -90,6 +90,8 @@
     implementation 'androidx.compose.animation:animation:1.1.1'
     implementation 'androidx.compose.ui:ui-tooling:1.1.1'
     implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1'
+    implementation 'androidx.navigation:navigation-compose:2.4.2'
+    implementation 'androidx.compose.material:material-icons-extended:1.1.1'
     androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
 
     // Testing framework
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
index e2795b5..3dcab7a 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
@@ -19,32 +19,13 @@
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
+import androidx.camera.integration.uiwidgets.compose.ui.ComposeCameraApp
 
 class ComposeCameraActivity : ComponentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
-            MyApp()
+            ComposeCameraApp()
         }
     }
 }
-
-@Preview
-@Composable
-fun MyApp() {
-    MaterialTheme {
-        Surface {
-            Greeting()
-        }
-    }
-}
-
-@Composable
-fun Greeting() {
-    Text("Hello")
-}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt
new file mode 100644
index 0000000..59c6979
--- /dev/null
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.uiwidgets.compose.ui
+
+import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraNavHost
+import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraScreen
+import androidx.camera.integration.uiwidgets.compose.ui.screen.components.ComposeCameraScreenTabRow
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Scaffold
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+
+@Composable
+fun ComposeCameraApp() {
+    MaterialTheme {
+        val allScreens = ComposeCameraScreen.values().toList()
+        val navController = rememberNavController()
+        val backstackEntry = navController.currentBackStackEntryAsState()
+        val currentScreen = ComposeCameraScreen.fromRoute(
+            route = backstackEntry.value?.destination?.route,
+            defaultRoute = ComposeCameraScreen.ImageCapture
+        )
+
+        Scaffold(
+            topBar = {
+                ComposeCameraScreenTabRow(
+                    allScreens = allScreens,
+                    onTabSelected = { screen ->
+                        navController.navigate(screen.name)
+                    },
+                    currentScreen = currentScreen
+                )
+            }
+        ) { innerPadding ->
+            ComposeCameraNavHost(
+                navController = navController,
+                modifier = Modifier.padding(innerPadding)
+            )
+        }
+    }
+}
+
+@Composable
+fun Greeting() {
+    Text("Hello")
+}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt
new file mode 100644
index 0000000..7516248
--- /dev/null
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.uiwidgets.compose.ui.navigation
+
+import androidx.camera.integration.uiwidgets.compose.ui.Greeting
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+
+@Composable
+fun ComposeCameraNavHost(
+    navController: NavHostController,
+    modifier: Modifier = Modifier
+) {
+    NavHost(
+        navController = navController,
+        startDestination = ComposeCameraScreen.ImageCapture.name,
+        modifier = modifier
+    ) {
+        composable(ComposeCameraScreen.ImageCapture.name) {
+            Greeting()
+        }
+
+        composable(ComposeCameraScreen.VideoCapture.name) {
+            Greeting()
+        }
+
+        composable(ComposeCameraScreen.Gallery.name) {
+            Greeting()
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraScreen.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraScreen.kt
new file mode 100644
index 0000000..8923506
--- /dev/null
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraScreen.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.uiwidgets.compose.ui.navigation
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.CameraAlt
+import androidx.compose.material.icons.filled.PhotoLibrary
+import androidx.compose.material.icons.filled.Videocam
+import androidx.compose.ui.graphics.vector.ImageVector
+
+// Contains each destination screen as an enum
+// and associates an icon to show in the navigation tabs
+enum class ComposeCameraScreen(
+    val icon: ImageVector
+) {
+    ImageCapture(
+        icon = Icons.Filled.CameraAlt
+    ),
+    VideoCapture(
+        icon = Icons.Filled.Videocam
+    ),
+    Gallery(
+        icon = Icons.Filled.PhotoLibrary
+    );
+
+    companion object {
+        fun fromRoute(route: String?, defaultRoute: ComposeCameraScreen): ComposeCameraScreen {
+            return when (route?.substringBefore("/")) {
+                ImageCapture.name -> ImageCapture
+                VideoCapture.name -> VideoCapture
+                Gallery.name -> Gallery
+                null -> defaultRoute
+                else -> throw IllegalArgumentException("Route $route is not recognized.")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/components/TabRow.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/components/TabRow.kt
new file mode 100644
index 0000000..40f5ed1
--- /dev/null
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/components/TabRow.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.uiwidgets.compose.ui.screen.components
+
+import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraScreen
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import java.util.Locale
+
+private val TabHeight = 56.dp
+private val TabPadding = 16.dp
+private val SpacerWidth = 12.dp
+private const val InactiveTabOpacity = 0.60f
+
+private const val TabFadeInAnimationDuration = 150
+private const val TabFadeInAnimationDelay = 100
+private const val TabFadeOutAnimationDuration = 100
+
+// References: Navigation CodeLab for Android
+
+// Header Tab Row for Navigation
+@Composable
+fun ComposeCameraScreenTabRow(
+    allScreens: List<ComposeCameraScreen>,
+    onTabSelected: (ComposeCameraScreen) -> Unit,
+    currentScreen: ComposeCameraScreen
+) {
+    Surface(
+        Modifier
+            .height(TabHeight)
+            .fillMaxWidth()
+    ) {
+        Row(Modifier.selectableGroup()) {
+            allScreens.forEach { screen ->
+                ComposeCameraTab(
+                    text = screen.name,
+                    icon = screen.icon,
+                    onSelected = { onTabSelected(screen) },
+                    selected = currentScreen == screen
+                )
+            }
+        }
+    }
+}
+
+// Individual Tab items for Tab Row
+@Composable
+private fun ComposeCameraTab(
+    text: String,
+    icon: ImageVector,
+    onSelected: () -> Unit,
+    selected: Boolean
+) {
+    val color = MaterialTheme.colors.onSurface
+    val durationMillis = if (selected) TabFadeInAnimationDuration else TabFadeOutAnimationDuration
+    val animSpec = remember {
+        tween<Color>(
+            durationMillis = durationMillis,
+            easing = LinearEasing,
+            delayMillis = TabFadeInAnimationDelay
+        )
+    }
+
+    val tabTintColor by animateColorAsState(
+        targetValue = if (selected) color else color.copy(alpha = InactiveTabOpacity),
+        animationSpec = animSpec
+    )
+
+    Row(
+        modifier = Modifier
+            .padding(TabPadding)
+            .animateContentSize()
+            .height(TabHeight)
+            .selectable(
+                selected = selected,
+                onClick = onSelected,
+                role = Role.Tab,
+                interactionSource = remember { MutableInteractionSource() },
+                indication = rememberRipple(
+                    bounded = false,
+                    radius = Dp.Unspecified,
+                    color = Color.Unspecified
+                )
+            )
+            .clearAndSetSemantics { contentDescription = text }
+    ) {
+        Icon(imageVector = icon, contentDescription = text, tint = tabTintColor)
+        if (selected) {
+            Spacer(Modifier.width(SpacerWidth))
+            Text(text.uppercase(Locale.getDefault()), color = tabTintColor)
+        }
+    }
+}
\ No newline at end of file