Aperture: New camera mode selector UI
Change-Id: I8abe5a8b19bef7dcd46457ad2abb42dc73f9a551
diff --git a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
index b52a709..5032c0e 100644
--- a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
+++ b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
@@ -67,7 +67,6 @@
import androidx.camera.view.video.AudioConfig
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.Group
import androidx.core.animation.addListener
import androidx.core.content.ContextCompat
import androidx.core.location.LocationListenerCompat
@@ -79,7 +78,6 @@
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.children
-import androidx.core.view.doOnLayout
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -90,7 +88,6 @@
import coil.request.ImageRequest
import coil.request.SuccessResult
import coil.size.Scale
-import com.google.android.material.button.MaterialButton
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.sync.Mutex
import org.lineageos.aperture.camera.CameraFacing
@@ -109,6 +106,7 @@
import org.lineageos.aperture.camera.VideoStabilizationMode
import org.lineageos.aperture.ext.*
import org.lineageos.aperture.qr.QrImageAnalyzer
+import org.lineageos.aperture.ui.CameraModeSelectorLayout
import org.lineageos.aperture.ui.CapturePreviewLayout
import org.lineageos.aperture.ui.CountDownView
import org.lineageos.aperture.ui.GridView
@@ -132,7 +130,6 @@
import org.lineageos.aperture.utils.Rotation
import org.lineageos.aperture.utils.ShortcutsUtils
import org.lineageos.aperture.utils.StorageUtils
-import org.lineageos.aperture.utils.TimeUtils
import org.lineageos.aperture.utils.TimerMode
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -148,8 +145,7 @@
open class CameraActivity : AppCompatActivity() {
// Views
private val aspectRatioButton by lazy { findViewById<Button>(R.id.aspectRatioButton) }
- private val cameraModeButtonsGroup by lazy { findViewById<Group>(R.id.cameraModeButtonsGroup) }
- private val cameraModeHighlight by lazy { findViewById<MaterialButton>(R.id.cameraModeHighlight) }
+ private val cameraModeSelectorLayout by lazy { findViewById<CameraModeSelectorLayout>(R.id.cameraModeSelectorLayout) }
private val capturePreviewLayout by lazy { findViewById<CapturePreviewLayout>(R.id.capturePreviewLayout) }
private val countDownView by lazy { findViewById<CountDownView>(R.id.countDownView) }
private val effectButton by lazy { findViewById<Button>(R.id.effectButton) }
@@ -166,20 +162,15 @@
private val levelerView by lazy { findViewById<LevelerView>(R.id.levelerView) }
private val mainLayout by lazy { findViewById<ConstraintLayout>(R.id.mainLayout) }
private val micButton by lazy { findViewById<Button>(R.id.micButton) }
- private val modeSelectorLayout by lazy { findViewById<ConstraintLayout>(R.id.modeSelectorLayout) }
- private val photoModeButton by lazy { findViewById<MaterialButton>(R.id.photoModeButton) }
private val previewBlurView by lazy { findViewById<PreviewBlurView>(R.id.previewBlurView) }
private val primaryBarLayout by lazy { findViewById<ConstraintLayout>(R.id.primaryBarLayout) }
private val proButton by lazy { findViewById<ImageButton>(R.id.proButton) }
- private val qrModeButton by lazy { findViewById<MaterialButton>(R.id.qrModeButton) }
private val secondaryBottomBarLayout by lazy { findViewById<ConstraintLayout>(R.id.secondaryBottomBarLayout) }
private val secondaryTopBarLayout by lazy { findViewById<HorizontalScrollView>(R.id.secondaryTopBarLayout) }
private val settingsButton by lazy { findViewById<Button>(R.id.settingsButton) }
private val shutterButton by lazy { findViewById<ImageButton>(R.id.shutterButton) }
private val timerButton by lazy { findViewById<Button>(R.id.timerButton) }
- private val videoDurationButton by lazy { findViewById<MaterialButton>(R.id.videoDurationButton) }
private val videoFrameRateButton by lazy { findViewById<Button>(R.id.videoFrameRateButton) }
- private val videoModeButton by lazy { findViewById<MaterialButton>(R.id.videoModeButton) }
private val videoQualityButton by lazy { findViewById<Button>(R.id.videoQualityButton) }
private val videoRecordingStateButton by lazy { findViewById<ImageButton>(R.id.videoRecordingStateButton) }
private val viewFinder by lazy { findViewById<PreviewView>(R.id.viewFinder) }
@@ -222,6 +213,7 @@
private var videoFrameRate by nullablePropertyDelegate { model.videoFrameRate }
private var videoMicMode by nonNullablePropertyDelegate { model.videoMicMode }
private var videoRecording by nullablePropertyDelegate { model.videoRecording }
+ private var videoDuration by nonNullablePropertyDelegate { model.videoRecordingDuration }
private lateinit var initialCameraFacing: CameraFacing
@@ -568,6 +560,7 @@
initialCameraFacing = sharedPreferences.lastCameraFacing
// Pass the view model to the views
+ cameraModeSelectorLayout.cameraViewModel = model
capturePreviewLayout.cameraViewModel = model
countDownView.cameraViewModel = model
infoChipView.cameraViewModel = model
@@ -596,20 +589,16 @@
}
}
- if (cameraManager.internalCamerasSupportingVideoRecoding.isEmpty()) {
- // Hide video mode button if no internal camera supports video recoding
- videoModeButton.isVisible = false
- if (cameraMode == CameraMode.VIDEO) {
- // If an app asked for a video we have to bail out
- if (singleCaptureMode) {
- Toast.makeText(
- this, getString(R.string.camcorder_unsupported_toast), Toast.LENGTH_LONG
- ).show()
- finish()
- }
- // Fallback to photo mode
- cameraMode = CameraMode.PHOTO
+ if (cameraMode == CameraMode.VIDEO && !cameraManager.videoRecordingAvailable()) {
+ // If an app asked for a video we have to bail out
+ if (singleCaptureMode) {
+ Toast.makeText(
+ this, getString(R.string.camcorder_unsupported_toast), Toast.LENGTH_LONG
+ ).show()
+ finish()
}
+ // Fallback to photo mode
+ cameraMode = CameraMode.PHOTO
}
// Select a camera
@@ -619,7 +608,7 @@
ViewCompat.setOnApplyWindowInsetsListener(mainLayout) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
- modeSelectorLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
+ cameraModeSelectorLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.bottom
leftMargin = insets.left
rightMargin = insets.right
@@ -658,15 +647,6 @@
}
}
- // Initialize camera mode highlight position
- (cameraModeHighlight.parent as View).doOnLayout {
- cameraModeHighlight.x = when (cameraMode) {
- CameraMode.QR -> qrModeButton.x
- CameraMode.PHOTO -> photoModeButton.x
- CameraMode.VIDEO -> videoModeButton.x
- }
- }
-
// Attach CameraController to PreviewView
viewFinder.controller = cameraController
@@ -787,10 +767,6 @@
}
// Set primary bar button callbacks
- qrModeButton.setOnClickListener { changeCameraMode(CameraMode.QR) }
- photoModeButton.setOnClickListener { changeCameraMode(CameraMode.PHOTO) }
- videoModeButton.setOnClickListener { changeCameraMode(CameraMode.VIDEO) }
-
flipCameraButton.setOnClickListener { flipCamera() }
googleLensButton.setOnClickListener {
dismissKeyguardAndRun {
@@ -866,6 +842,11 @@
}
}
+ // Set mode selector callback
+ cameraModeSelectorLayout.onModeSelectedCallback = {
+ changeCameraMode(it)
+ }
+
// Bind viewfinder and preview blur view
previewBlurView.previewView = viewFinder
@@ -901,26 +882,6 @@
googleLensButton.isVisible = cameraMode == CameraMode.QR && isGoogleLensAvailable
updatePrimaryBarButtons()
-
- // Update camera mode buttons
- qrModeButton.isEnabled = cameraMode != CameraMode.QR
- photoModeButton.isEnabled = cameraMode != CameraMode.PHOTO
- videoModeButton.isEnabled = cameraMode != CameraMode.VIDEO
-
- // Animate camera mode change
- (cameraModeHighlight.parent as View).doOnLayout {
- ValueAnimator.ofFloat(
- cameraModeHighlight.x, when (cameraMode) {
- CameraMode.QR -> qrModeButton.x
- CameraMode.PHOTO -> photoModeButton.x
- CameraMode.VIDEO -> videoModeButton.x
- }
- ).apply {
- addUpdateListener { valueAnimator ->
- cameraModeHighlight.x = valueAnimator.animatedValue as Float
- }
- }.start()
- }
}
// Observe single capture mode
@@ -929,9 +890,6 @@
// Update primary bar buttons
galleryButtonCardView.isInvisible = inSingleCaptureMode
-
- // Update camera mode buttons
- updateCameraModeButtons()
}
// Observe camera state
@@ -953,12 +911,6 @@
videoRecordingStateButton.isVisible = cameraState.isRecordingVideo
updatePrimaryBarButtons()
-
- // Update camera mode buttons
- updateCameraModeButtons()
-
- // Update video duration button
- videoDurationButton.isVisible = cameraState.isRecordingVideo
}
// Observe screen rotation
@@ -1442,7 +1394,7 @@
cameraState = CameraState.PRE_RECORDING_VIDEO
// Update duration text
- videoDurationButton.text = TimeUtils.convertNanosToString(0)
+ videoDuration = 0L
// Create output options object which contains file + metadata
val outputOptions = StorageUtils.getVideoMediaStoreOutputOptions(
@@ -1477,8 +1429,7 @@
}
is VideoRecordEvent.Status -> runOnUiThread {
- videoDurationButton.text =
- TimeUtils.convertNanosToString(it.recordingStats.recordedDurationNanos)
+ videoDuration = it.recordingStats.recordedDurationNanos
}
is VideoRecordEvent.Finalize -> {
@@ -1847,6 +1798,21 @@
}
CameraMode.VIDEO -> {
+ if (!cameraManager.videoRecordingAvailable()) {
+ Snackbar.make(
+ cameraModeSelectorLayout,
+ R.string.camcorder_unsupported_toast,
+ Snackbar.LENGTH_SHORT,
+ ).apply {
+ anchorView = cameraModeSelectorLayout
+ setAction(android.R.string.ok) {
+ // Do nothing
+ }
+ }.show()
+
+ return
+ }
+
if (this.cameraMode == CameraMode.PHOTO) {
startShutterAnimation(ShutterAnimation.PhotoToVideo)
} else {
@@ -1928,19 +1894,6 @@
}
}
- /**
- * Some UI elements requires checking more than one value, this function will be called
- * when one of these values will change.
- */
- private fun updateCameraModeButtons() {
- runOnUiThread {
- val inSingleCaptureMode = model.inSingleCaptureMode.value ?: return@runOnUiThread
- val cameraState = model.cameraState.value ?: return@runOnUiThread
-
- cameraModeButtonsGroup.isInvisible = cameraState.isRecordingVideo || inSingleCaptureMode
- }
- }
-
private fun cycleAspectRatio() {
if (!canRestartCamera()) {
return
diff --git a/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt b/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
index 9772f52..07f4c80 100644
--- a/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
+++ b/app/src/main/java/org/lineageos/aperture/camera/CameraManager.kt
@@ -120,9 +120,6 @@
it.supportsVideoRecording
}
- val internalCamerasSupportingVideoRecoding =
- backCamerasSupportingVideoRecording + frontCamerasSupportingVideoRecording
-
private val externalCameras: List<Camera>
get() = cameras.values.filter {
it.cameraFacing == CameraFacing.EXTERNAL
@@ -219,6 +216,8 @@
}
}
+ fun videoRecordingAvailable() = availableCamerasSupportingVideoRecording.isNotEmpty()
+
fun shutdown() {
cameraExecutor.shutdown()
}
diff --git a/app/src/main/java/org/lineageos/aperture/camera/CameraMode.kt b/app/src/main/java/org/lineageos/aperture/camera/CameraMode.kt
index dddb89b..de5a43f 100644
--- a/app/src/main/java/org/lineageos/aperture/camera/CameraMode.kt
+++ b/app/src/main/java/org/lineageos/aperture/camera/CameraMode.kt
@@ -5,8 +5,11 @@
package org.lineageos.aperture.camera
-enum class CameraMode {
- PHOTO,
- VIDEO,
- QR,
+import androidx.annotation.StringRes
+import org.lineageos.aperture.R
+
+enum class CameraMode(@StringRes val title: Int) {
+ PHOTO(R.string.camera_mode_photo),
+ VIDEO(R.string.camera_mode_video),
+ QR(R.string.camera_mode_qr),
}
diff --git a/app/src/main/java/org/lineageos/aperture/camera/CameraViewModel.kt b/app/src/main/java/org/lineageos/aperture/camera/CameraViewModel.kt
index dfafed6..2e88b2d 100644
--- a/app/src/main/java/org/lineageos/aperture/camera/CameraViewModel.kt
+++ b/app/src/main/java/org/lineageos/aperture/camera/CameraViewModel.kt
@@ -100,4 +100,9 @@
* Video [Recording].
*/
val videoRecording = MutableLiveData<Recording?>()
+
+ /**
+ * Video recording duration.
+ */
+ val videoRecordingDuration = MutableLiveData<Long>()
}
diff --git a/app/src/main/java/org/lineageos/aperture/ui/CameraModeSelectorLayout.kt b/app/src/main/java/org/lineageos/aperture/ui/CameraModeSelectorLayout.kt
new file mode 100644
index 0000000..2cb8121
--- /dev/null
+++ b/app/src/main/java/org/lineageos/aperture/ui/CameraModeSelectorLayout.kt
@@ -0,0 +1,132 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.aperture.ui
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.doOnLayout
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import androidx.lifecycle.Observer
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import com.google.android.material.button.MaterialButton
+import org.lineageos.aperture.R
+import org.lineageos.aperture.camera.CameraMode
+import org.lineageos.aperture.camera.CameraState
+import org.lineageos.aperture.camera.CameraViewModel
+import org.lineageos.aperture.ext.px
+import org.lineageos.aperture.utils.TimeUtils
+import java.lang.Exception
+import kotlin.reflect.cast
+
+class CameraModeSelectorLayout @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null
+) : FrameLayout(context, attrs) {
+ // Views
+ private val cameraModeHighlightButton by lazy { findViewById<MaterialButton>(R.id.cameraModeHighlightButton) }
+ private val cameraModeButtonsLinearLayout by lazy { findViewById<LinearLayout>(R.id.cameraModeButtonsLinearLayout) }
+ private val videoDurationButton by lazy { findViewById<MaterialButton>(R.id.videoDurationButton) }
+
+ // System services
+ private val layoutInflater by lazy { context.getSystemService(LayoutInflater::class.java) }
+
+ private val cameraToButton = mutableMapOf<CameraMode, MaterialButton>()
+
+ private val cameraModeObserver = Observer { cameraMode: CameraMode ->
+ val currentCameraModeButton =
+ cameraToButton[cameraMode] ?: throw Exception("No button for $cameraMode")
+
+ cameraToButton.forEach {
+ it.value.isEnabled = cameraMode != it.key
+ }
+
+ // Animate camera mode change
+ doOnLayout {
+ // Animate position
+ ValueAnimator.ofFloat(
+ cameraModeHighlightButton.x, currentCameraModeButton.x + 16.px
+ ).apply {
+ addUpdateListener { valueAnimator ->
+ cameraModeHighlightButton.x = valueAnimator.animatedValue as Float
+ }
+ }.start()
+
+ // Animate width
+ ValueAnimator.ofInt(
+ cameraModeHighlightButton.width, currentCameraModeButton.width
+ ).apply {
+ addUpdateListener { valueAnimator ->
+ cameraModeHighlightButton.width = valueAnimator.animatedValue as Int
+ }
+ }.start()
+ }
+ }
+
+ private val inSingleCaptureModeObserver = Observer { _: Boolean ->
+ updateButtons()
+ }
+
+ private val cameraStateObserver = Observer { cameraState: CameraState ->
+ updateButtons()
+
+ // Update video duration button
+ videoDurationButton.isVisible = cameraState.isRecordingVideo
+ }
+
+ private val videoDurationObserver = Observer { videoDuration: Long ->
+ videoDurationButton.text = TimeUtils.convertNanosToString(videoDuration)
+ }
+
+ internal var cameraViewModel: CameraViewModel? = null
+ set(value) {
+ // Unregister
+ field?.cameraMode?.removeObserver(cameraModeObserver)
+ field?.inSingleCaptureMode?.removeObserver(inSingleCaptureModeObserver)
+ field?.cameraState?.removeObserver(cameraStateObserver)
+ field?.videoRecordingDuration?.removeObserver(videoDurationObserver)
+
+ field = value
+
+ val lifecycleOwner = findViewTreeLifecycleOwner() ?: return
+
+ value?.cameraMode?.observe(lifecycleOwner, cameraModeObserver)
+ value?.inSingleCaptureMode?.observe(lifecycleOwner, inSingleCaptureModeObserver)
+ value?.cameraState?.observe(lifecycleOwner, cameraStateObserver)
+ value?.videoRecordingDuration?.observe(lifecycleOwner, videoDurationObserver)
+ }
+
+ var onModeSelectedCallback: (cameraMode: CameraMode) -> Unit = {}
+
+ init {
+ inflate(context, R.layout.camera_mode_selector_layout, this)
+
+ for (cameraMode in CameraMode.values()) {
+ cameraToButton[cameraMode] = MaterialButton::class.cast(
+ layoutInflater.inflate(
+ R.layout.camera_mode_button, this, false
+ )
+ ).apply {
+ setText(cameraMode.title)
+ setOnClickListener { onModeSelectedCallback(cameraMode) }
+ }.also {
+ cameraModeButtonsLinearLayout.addView(it)
+ }
+ }
+ }
+
+ private fun updateButtons() {
+ val inSingleCaptureMode = cameraViewModel?.inSingleCaptureMode?.value ?: return
+ val cameraState = cameraViewModel?.cameraState?.value ?: return
+
+ cameraToButton.forEach {
+ it.value.isInvisible = cameraState.isRecordingVideo || inSingleCaptureMode
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_camera.xml b/app/src/main/res/layout/activity_camera.xml
index 139ee23..96783f4 100644
--- a/app/src/main/res/layout/activity_camera.xml
+++ b/app/src/main/res/layout/activity_camera.xml
@@ -274,7 +274,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
- app:layout_constraintBottom_toTopOf="@+id/modeSelectorLayout"
+ app:layout_constraintBottom_toTopOf="@+id/cameraModeSelectorLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
@@ -349,103 +349,16 @@
android:scaleType="center"
android:src="@drawable/ic_google_lens"
android:visibility="gone"
- app:layout_constraintBottom_toTopOf="@+id/modeSelectorLayout"
+ app:layout_constraintBottom_toTopOf="@+id/cameraModeSelectorLayout"
app:layout_constraintStart_toStartOf="parent" />
- <androidx.constraintlayout.widget.ConstraintLayout
- android:id="@+id/modeSelectorLayout"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:padding="16dp"
+ <org.lineageos.aperture.ui.CameraModeSelectorLayout
+ android:id="@+id/cameraModeSelectorLayout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent">
-
- <androidx.constraintlayout.widget.Group
- android:id="@+id/cameraModeButtonsGroup"
- android:layout_width="0dp"
- android:layout_height="0dp"
- app:constraint_referenced_ids="cameraModeHighlight,photoModeButton,videoModeButton,qrModeButton" />
-
- <Button
- android:id="@+id/cameraModeHighlight"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:backgroundTint="?attr/colorSecondaryContainer"
- android:contentDescription="@string/camera_mode_highlight_description"
- android:enabled="false"
- android:padding="0dp"
- app:iconPadding="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/videoModeButton"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <Button
- android:id="@+id/photoModeButton"
- style="@style/Theme.Aperture.Camera.CameraModeSelectorButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/photo_mode_button_description"
- android:enabled="false"
- android:padding="0dp"
- android:text="@string/selector_photo"
- app:iconPadding="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/videoModeButton"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <Button
- android:id="@+id/videoModeButton"
- style="@style/Theme.Aperture.Camera.CameraModeSelectorButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/video_mode_button_description"
- android:padding="0dp"
- android:text="@string/select_video"
- app:iconPadding="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/qrModeButton"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/photoModeButton"
- app:layout_constraintTop_toTopOf="parent" />
-
- <Button
- android:id="@+id/qrModeButton"
- style="@style/Theme.Aperture.Camera.CameraModeSelectorButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/qr_mode_button_description"
- android:padding="0dp"
- android:text="@string/select_scan"
- app:iconPadding="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/videoModeButton"
- app:layout_constraintTop_toTopOf="parent" />
-
- <Button
- android:id="@+id/videoDurationButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:backgroundTint="@color/rec_red"
- android:contentDescription="@string/video_mode_button_description"
- android:enabled="false"
- android:padding="0dp"
- android:textColor="@android:color/white"
- android:visibility="gone"
- app:iconPadding="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/videoModeButton"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toStartOf="@+id/videoModeButton"
- app:layout_constraintTop_toTopOf="parent"
- tools:text="3:13:37" />
- </androidx.constraintlayout.widget.ConstraintLayout>
+ app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/capturePreviewLayout"
diff --git a/app/src/main/res/layout/camera_mode_button.xml b/app/src/main/res/layout/camera_mode_button.xml
new file mode 100644
index 0000000..d6ff34b
--- /dev/null
+++ b/app/src/main/res/layout/camera_mode_button.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ SPDX-FileCopyrightText: 2023 The LineageOS Project
+ SPDX-License-Identifier: Apache-2.0
+-->
+<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:backgroundTint="@android:color/transparent"
+ android:padding="0dp"
+ android:textAllCaps="true"
+ android:textColor="@color/camera_mode_selector_text"
+ android:textStyle="bold"
+ android:typeface="monospace"
+ app:iconPadding="0dp" />
diff --git a/app/src/main/res/layout/camera_mode_selector_layout.xml b/app/src/main/res/layout/camera_mode_selector_layout.xml
new file mode 100644
index 0000000..b99a361
--- /dev/null
+++ b/app/src/main/res/layout/camera_mode_selector_layout.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ SPDX-FileCopyrightText: 2023 The LineageOS Project
+ SPDX-License-Identifier: Apache-2.0
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/cameraModeHighlightButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:backgroundTint="?attr/colorSecondaryContainer"
+ android:enabled="false"
+ android:padding="0dp"
+ app:iconPadding="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/cameraModeButtonsLinearLayout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/videoDurationButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:backgroundTint="@color/rec_red"
+ android:enabled="false"
+ android:padding="0dp"
+ android:textColor="@android:color/white"
+ android:visibility="gone"
+ app:iconPadding="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="3:13:37" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 16e26ad..7b5c5c5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -9,7 +9,6 @@
<string name="video_camera_label">Camcorder</string>
<!-- Content descriptions -->
- <string name="camera_mode_highlight_description">Camera mode highlight</string>
<string name="cancel_button_description">Cancel</string>
<string name="confirm_button_description">Confirm</string>
<string name="flash_button_description">Flash mode</string>
@@ -17,11 +16,8 @@
<string name="gallery_button_description">Gallery</string>
<string name="google_lens_button_description">Open Google Lens</string>
<string name="image_view_description">Image preview</string>
- <string name="photo_mode_button_description">Switch to photo mode</string>
<string name="pro_button_description">Pro settings</string>
- <string name="qr_mode_button_description">Switch to QR scanner mode</string>
<string name="shutter_button_description">Shutter</string>
- <string name="video_mode_button_description">Switch to video mode</string>
<string name="video_recording_state_button_description">Pause/resume video recording</string>
<!-- Secondary button text -->
@@ -57,11 +53,6 @@
<string name="settings">SETTINGS</string>
- <!-- Selector chip -->
- <string name="selector_photo">PHOTO</string>
- <string name="select_video">VIDEO</string>
- <string name="select_scan">SCAN</string>
-
<!-- Toast messages -->
<string name="app_permissions_toast">Permissions not granted by the user.</string>
<string name="camcorder_unsupported_toast">No camera supports video recording.</string>
@@ -189,4 +180,9 @@
<!-- Force torch help -->
<string name="force_torch_help">On photo mode, you can long press the flash button to switch into torch mode.</string>
+
+ <!-- Camera modes -->
+ <string name="camera_mode_photo">Photo</string>
+ <string name="camera_mode_video">Video</string>
+ <string name="camera_mode_qr">Scan</string>
</resources>
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 220d744..c015a64 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -90,14 +90,6 @@
<item name="android:layout_height">40dp</item>
</style>
- <!-- Camera mode bar icons theme -->
- <style name="Theme.Aperture.Camera.CameraModeSelectorButton" parent="@style/Widget.Material3.Button.TonalButton">
- <item name="android:backgroundTint">@android:color/transparent</item>
- <item name="android:textColor">@color/camera_mode_selector_text</item>
- <item name="android:textStyle">bold</item>
- <item name="android:typeface">monospace</item>
- </style>
-
<!-- Collapsing toolbar style -->
<style name="Theme.Aperture.Camera.ToolbarCollapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:fontFamily">sans-serif</item>