Enable video recording pause/resume in A/V sync test app

Bug: 257240878
Test: manual test and ./gradlew bOS
Change-Id: I2d7bd689cc9ab58c5c707dd3520236e15a3c1398
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
index ff72ca2..2acc74e 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -63,10 +62,13 @@
         isSignalActive = viewModel.isActivePeriod,
         isSignalStarted = viewModel.isSignalGenerating,
         isRecording = viewModel.isRecording,
+        isPaused = viewModel.isPaused,
         onSignalStartClick = { viewModel.startSignalGeneration() },
         onSignalStopClick = { viewModel.stopSignalGeneration() },
         onRecordingStartClick = { viewModel.startRecording(context) },
         onRecordingStopClick = { viewModel.stopRecording() },
+        onRecordingPauseClick = { viewModel.pauseRecording() },
+        onRecordingResumeClick = { viewModel.resumeRecording() },
     )
 }
 
@@ -77,10 +79,13 @@
     isSignalActive: Boolean = false,
     isSignalStarted: Boolean = false,
     isRecording: Boolean = false,
+    isPaused: Boolean = false,
     onSignalStartClick: () -> Unit = {},
     onSignalStopClick: () -> Unit = {},
     onRecordingStartClick: () -> Unit = {},
     onRecordingStopClick: () -> Unit = {},
+    onRecordingPauseClick: () -> Unit = {},
+    onRecordingResumeClick: () -> Unit = {},
 ) {
     Box(modifier = Modifier.fillMaxSize()) {
         LightingScreen(isOn = isSignalActive)
@@ -93,8 +98,11 @@
         RecordingControl(
             enabled = isRecorderReady,
             isStarted = isRecording,
+            isPaused = isPaused,
             onStartClick = onRecordingStartClick,
             onStopClick = onRecordingStopClick,
+            onPauseClick = onRecordingPauseClick,
+            onResumeClick = onRecordingResumeClick,
         )
     }
 }
@@ -133,10 +141,14 @@
     modifier: Modifier = Modifier,
     enabled: Boolean,
     isStarted: Boolean,
+    isPaused: Boolean = false,
     onStartClick: () -> Unit = {},
     onStopClick: () -> Unit = {},
+    onPauseClick: () -> Unit = {},
+    onResumeClick: () -> Unit = {},
 ) {
-    val icon = painterResource(if (isStarted) R.drawable.ic_stop else R.drawable.ic_record)
+    val startStopIconRes = if (isStarted) R.drawable.ic_stop else R.drawable.ic_record
+    val pauseResumeIconRes = if (isPaused) R.drawable.ic_record else R.drawable.ic_pause
 
     Row(modifier = modifier.fillMaxSize()) {
         Box(
@@ -148,10 +160,31 @@
                 onClick = if (isStarted) onStopClick else onStartClick,
                 backgroundColor = Color.Cyan
             ) {
-                Icon(icon, stringResource(R.string.desc_recording_control), modifier.size(16.dp))
+                Icon(
+                    painterResource(startStopIconRes),
+                    stringResource(R.string.desc_recording_control),
+                    modifier.size(16.dp)
+                )
             }
         }
-        Spacer(modifier = modifier.weight(1f).fillMaxHeight())
+        Box(
+            modifier = modifier.weight(1f).fillMaxHeight(),
+            contentAlignment = Alignment.Center,
+        ) {
+            if (enabled && isStarted) {
+                AdvancedFloatingActionButton(
+                    enabled = true,
+                    onClick = if (isPaused) onResumeClick else onPauseClick,
+                    backgroundColor = Color.Cyan
+                ) {
+                    Icon(
+                        painterResource(pauseResumeIconRes),
+                        stringResource(R.string.desc_pause_control),
+                        modifier.size(16.dp)
+                    )
+                }
+            }
+        }
     }
 }
 
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
index a8cad0d..9931e68 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
@@ -66,6 +66,8 @@
         private set
     var isRecording: Boolean by mutableStateOf(false)
         private set
+    var isPaused: Boolean by mutableStateOf(false)
+        private set
 
     suspend fun initialRecorder(context: Context, lifecycleOwner: LifecycleOwner) {
         withContext(Dispatchers.Main) {
@@ -145,6 +147,23 @@
 
         cameraHelper.stopRecording()
         isRecording = false
+        isPaused = false
+    }
+
+    fun pauseRecording() {
+        Logger.d(TAG, "Pause recording.")
+        Preconditions.checkState(isRecorderReady)
+
+        cameraHelper.pauseRecording()
+        isPaused = true
+    }
+
+    fun resumeRecording() {
+        Logger.d(TAG, "Resume recording.")
+        Preconditions.checkState(isRecorderReady)
+
+        cameraHelper.resumeRecording()
+        isPaused = false
     }
 
     private fun saveOriginalVolume() {
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
index daca14e..161095e 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
@@ -104,6 +104,14 @@
         activeRecording = null
     }
 
+    fun pauseRecording() {
+        activeRecording!!.pause()
+    }
+
+    fun resumeRecording() {
+        activeRecording!!.resume()
+    }
+
     private fun generateVideoFileOutputOptions(): FileOutputOptions {
         val videoFileName = "${generateVideoFileName()}.mp4"
         val videoFolder = Environment.getExternalStoragePublicDirectory(
diff --git a/camera/integration-tests/avsynctestapp/src/main/res/drawable/ic_pause.xml b/camera/integration-tests/avsynctestapp/src/main/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..f701d6f
--- /dev/null
+++ b/camera/integration-tests/avsynctestapp/src/main/res/drawable/ic_pause.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+</vector>
diff --git a/camera/integration-tests/avsynctestapp/src/main/res/values/strings.xml b/camera/integration-tests/avsynctestapp/src/main/res/values/strings.xml
index dca7f0e..def9e0f 100644
--- a/camera/integration-tests/avsynctestapp/src/main/res/values/strings.xml
+++ b/camera/integration-tests/avsynctestapp/src/main/res/values/strings.xml
@@ -18,4 +18,5 @@
     <string name="app_name">AV sync test app</string>
     <string name="desc_signal_control">Signal Control</string>
     <string name="desc_recording_control">Recording Control</string>
+    <string name="desc_pause_control">Pause Control</string>
 </resources>
\ No newline at end of file