Aperture: Import count down view from Camera2
Converted to kotlin with some changes here and
there
Signed-off-by: Luca Stefani <luca.stefani.ge1@gmail.com>
Change-Id: I068daa661de3ea478c719f890506202d2bb2df84
diff --git a/app/src/main/java/org/lineageos/aperture/MainActivity.kt b/app/src/main/java/org/lineageos/aperture/MainActivity.kt
index f7bdba8..e4c8e66 100644
--- a/app/src/main/java/org/lineageos/aperture/MainActivity.kt
+++ b/app/src/main/java/org/lineageos/aperture/MainActivity.kt
@@ -13,6 +13,7 @@
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
+import android.graphics.Rect
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.ColorDrawable
import android.location.Location
@@ -68,12 +69,12 @@
import coil.request.SuccessResult
import coil.size.Scale
import com.google.android.material.button.MaterialButton
-import com.google.android.material.chip.Chip
import com.google.android.material.slider.Slider
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
+import org.lineageos.aperture.ui.CountDownView
import org.lineageos.aperture.ui.GridView
import org.lineageos.aperture.utils.CameraFacing
import org.lineageos.aperture.utils.CameraMode
@@ -92,6 +93,7 @@
private val aspectRatioButton by lazy { findViewById<ToggleButton>(R.id.aspectRatioButton) }
private val bottomButtonsLayout by lazy { findViewById<ConstraintLayout>(R.id.bottomButtonsLayout) }
private val cameraModeHighlight by lazy { findViewById<MaterialButton>(R.id.cameraModeHighlight) }
+ private val countDownView by lazy { findViewById<CountDownView>(R.id.countDownView) }
private val effectButton by lazy { findViewById<ImageButton>(R.id.effectButton) }
private val flashButton by lazy { findViewById<ImageButton>(R.id.flashButton) }
private val flipCameraButton by lazy { findViewById<ImageButton>(R.id.flipCameraButton) }
@@ -104,7 +106,6 @@
private val settingsButton by lazy { findViewById<ImageButton>(R.id.settingsButton) }
private val shutterButton by lazy { findViewById<ImageButton>(R.id.shutterButton) }
private val timerButton by lazy { findViewById<ImageButton>(R.id.timerButton) }
- private val timerChip by lazy { findViewById<Chip>(R.id.timerChip) }
private val torchButton by lazy { findViewById<ImageButton>(R.id.torchButton) }
private val videoDuration by lazy { findViewById<MaterialButton>(R.id.videoDuration) }
private val videoModeButton by lazy { findViewById<MaterialButton>(R.id.videoModeButton) }
@@ -594,9 +595,9 @@
*/
private fun canRestartCamera() = when (cameraMode) {
// Disallow camera restart if we're taking a photo or if timer is running
- CameraMode.PHOTO -> !isTakingPhoto && !timerChip.isVisible
+ CameraMode.PHOTO -> !isTakingPhoto && !countDownView.isVisible
// Disallow camera restart if a recording in progress or if timer is running
- CameraMode.VIDEO -> !cameraController.isRecording && !timerChip.isVisible
+ CameraMode.VIDEO -> !cameraController.isRecording && !countDownView.isVisible
// Otherwise, allow camera restart
else -> true
}
@@ -1206,20 +1207,21 @@
return
}
- lifecycleScope.launch {
- shutterButton.isEnabled = false
- timerChip.isVisible = true
-
- for (i in sharedPreferences.timerMode downTo 1) {
- timerChip.text = "$i"
- delay(1000)
- }
-
- timerChip.isVisible = false
+ countDownView.setCountDownStatusListener {
+ countDownView.isVisible = false
shutterButton.isEnabled = true
runnable()
}
+
+ shutterButton.isEnabled = false
+ countDownView.isVisible = true
+
+ val rect = Rect().apply {
+ viewFinder.getGlobalVisibleRect(this)
+ }
+ countDownView.onPreviewAreaChanged(rect)
+ countDownView.startCountDown(sharedPreferences.timerMode)
}
companion object {
diff --git a/app/src/main/java/org/lineageos/aperture/ui/CountDownView.kt b/app/src/main/java/org/lineageos/aperture/ui/CountDownView.kt
new file mode 100644
index 0000000..3bf3e53
--- /dev/null
+++ b/app/src/main/java/org/lineageos/aperture/ui/CountDownView.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2022 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.aperture.ui
+
+import android.content.Context
+import android.graphics.Rect
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.annotation.IntRange
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import org.lineageos.aperture.R
+
+/**
+ * This class manages the looks of the countdown.
+ */
+class CountDownView(context: Context, attrs: AttributeSet?) : FrameLayout(
+ context, attrs
+) {
+ private val remainingSecondsView by lazy {
+ findViewById<TextView>(R.id.remainingSeconds)
+ }
+ private var remainingSeconds = 0
+ private lateinit var listener: () -> Unit
+ private val handler = MainHandler(Looper.getMainLooper())
+ private val previewArea = Rect()
+
+ /**
+ * Returns whether countdown is on-going.
+ */
+ private val isCountingDown: Boolean
+ get() = remainingSeconds > 0
+
+ /**
+ * Responds to preview area change by centering the countdown UI in the new
+ * preview area.
+ */
+ fun onPreviewAreaChanged(previewArea: Rect) {
+ this.previewArea.set(previewArea)
+ }
+
+ private fun remainingSecondsChanged(seconds: Int) {
+ remainingSeconds = seconds
+ if (seconds == 0) {
+ // Countdown has finished.
+ isInvisible = true
+ listener()
+ } else {
+ remainingSecondsView.text = seconds.toString()
+ // Fade-out animation.
+ startFadeOutAnimation()
+ // Schedule the next remainingSecondsChanged() call in 1 second
+ handler.sendEmptyMessageDelayed(SET_TIMER_TEXT, 1000)
+ }
+ }
+
+ private fun startFadeOutAnimation() {
+ val textWidth = remainingSecondsView.measuredWidth
+ val textHeight = remainingSecondsView.measuredHeight
+ remainingSecondsView.scaleX = 1f
+ remainingSecondsView.scaleY = 1f
+ remainingSecondsView.translationX = previewArea.centerX() - textWidth / 2f
+ remainingSecondsView.translationY = previewArea.centerY() - textHeight / 2f
+ remainingSecondsView.pivotX = textWidth / 2f
+ remainingSecondsView.pivotY = textHeight / 2f
+ remainingSecondsView.alpha = 1f
+ val endScale = 2.5f
+ remainingSecondsView.animate().apply {
+ scaleX(endScale)
+ scaleY(endScale)
+ alpha(0f)
+ duration = ANIMATION_DURATION_MS
+ }.start()
+ }
+
+ /**
+ * Sets a listener that gets notified when the status of countdown has finished.
+ */
+ fun setCountDownStatusListener(listener: () -> Unit) {
+ this.listener = listener
+ }
+
+ /**
+ * Starts showing countdown in the UI.
+ *
+ * @param sec duration of the countdown, in seconds
+ */
+ fun startCountDown(@IntRange(from = 0) sec: Int) {
+ if (isCountingDown) {
+ cancelCountDown()
+ }
+ isVisible = true
+ remainingSecondsChanged(sec)
+ }
+
+ /**
+ * Cancels the on-going countdown in the UI, if any.
+ */
+ private fun cancelCountDown() {
+ if (remainingSeconds > 0) {
+ remainingSeconds = 0
+ handler.removeMessages(SET_TIMER_TEXT)
+ isInvisible = true
+ }
+ }
+
+ private inner class MainHandler(looper: Looper) : Handler(looper) {
+ override fun handleMessage(message: Message) {
+ when (message.what) {
+ SET_TIMER_TEXT -> remainingSecondsChanged(remainingSeconds - 1)
+ else -> {}
+ }
+ }
+ }
+
+ companion object {
+ private const val SET_TIMER_TEXT = 1
+ private const val ANIMATION_DURATION_MS = 800L
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 3dc2d8a..d235c16 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -180,18 +180,22 @@
app:layout_constraintStart_toStartOf="@+id/viewFinder"
app:layout_constraintTop_toTopOf="@+id/viewFinder" />
- <com.google.android.material.chip.Chip
- android:id="@+id/timerChip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="16dp"
- android:textColor="@color/white"
- android:visibility="gone"
- app:chipSurfaceColor="@color/black"
+ <org.lineageos.aperture.ui.CountDownView
+ android:id="@+id/countDownView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@+id/viewFinder"
- app:rippleColor="#00ffffff" />
+ app:layout_constraintTop_toTopOf="parent">
+
+ <TextView
+ android:id="@+id/remainingSeconds"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="125sp" />
+ </org.lineageos.aperture.ui.CountDownView>
<com.google.android.material.slider.Slider
android:id="@+id/zoomLevel"