Skip to content

Commit

Permalink
Improve UI and UX flow when using postview and proccess progress
Browse files Browse the repository at this point in the history
- add a hold still text
- add a background for the progress and hold still text
- make sure the spinner animates to 100 before switching out postview for final capture

Test: manual testing on pixel 8
  • Loading branch information
jsaund committed May 23, 2024
1 parent 5ab09ac commit e97a8dd
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.example.android.cameraxextensions

import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
Expand Down Expand Up @@ -80,6 +81,29 @@ class MainActivity : AppCompatActivity() {
// monitors changes in camera permission state
private lateinit var permissionState: MutableStateFlow<PermissionState>

private var captureUri: Uri? = null
private var progressComplete: Boolean = false

private suspend fun showCapture() {
if (captureUri == null || !progressComplete) return

cameraExtensionsViewModel.stopPreview()
captureScreenViewState.emit(
captureScreenViewState.value
.updatePostCaptureScreen {
captureUri?.let {
PostCaptureScreenViewState.PostCaptureScreenVisibleViewState(it)
} ?: PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
}.updateCameraScreen {
it.hideCameraControls()
.hideProcessProgressViewState()
.hidePostview()
}
)
captureUri = null
progressComplete = false
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand Down Expand Up @@ -149,6 +173,10 @@ class MainActivity : AppCompatActivity() {
CameraUiAction.RequestPermissionClick -> {
requestPermissionsLauncher.launch(Manifest.permission.CAMERA)
}
CameraUiAction.ProcessProgressComplete -> {
progressComplete = true
showCapture()
}
is CameraUiAction.Focus -> {
cameraExtensionsViewModel.focus(action.meteringPoint)
}
Expand Down Expand Up @@ -195,24 +223,16 @@ class MainActivity : AppCompatActivity() {
)
}
is CaptureState.CaptureFinished -> {
cameraExtensionsViewModel.stopPreview()
captureScreenViewState.emit(
captureScreenViewState.value
.updatePostCaptureScreen {
val uri = state.outputResults.savedUri
if (uri != null) {
PostCaptureScreenViewState.PostCaptureScreenVisibleViewState(
uri
)
} else {
PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
captureUri = state.outputResults.savedUri
if (!progressComplete) {
captureScreenViewState.emit(
captureScreenViewState.value
.updateCameraScreen {
it.showProcessProgressViewState(100)
}
}.updateCameraScreen {
it.hideCameraControls()
.hideProcessProgressViewState()
.hidePostview()
}
)
)
}
showCapture()
}
is CaptureState.CaptureFailed -> {
cameraExtensionsScreen.showCaptureError("Couldn't take photo")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import androidx.camera.extensions.ExtensionMode
* User initiated actions related to camera operations.
*/
sealed class CameraUiAction {
object RequestPermissionClick : CameraUiAction()
object SwitchCameraClick : CameraUiAction()
object ShutterButtonClick : CameraUiAction()
object ClosePhotoPreviewClick : CameraUiAction()
data object RequestPermissionClick : CameraUiAction()
data object SwitchCameraClick : CameraUiAction()
data object ShutterButtonClick : CameraUiAction()
data object ClosePhotoPreviewClick : CameraUiAction()
data object ProcessProgressComplete : CameraUiAction()
data class SelectCameraExtension(@ExtensionMode.Mode val extension: Int) : CameraUiAction()
data class Focus(val meteringPoint: MeteringPoint) : CameraUiAction()
data class Scale(val scaleFactor: Float) : CameraUiAction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.camera.view.PreviewView
import androidx.core.animation.doOnEnd
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
Expand Down Expand Up @@ -85,6 +86,8 @@ class CameraExtensionsScreen(private val root: View) {
private val permissionsRequestButton: TextView =
root.findViewById(R.id.permissionsRequestButton)
private val photoPostview: ImageView = root.findViewById(R.id.photoPostview)
private val processProgressContainer: View =
root.findViewById(R.id.processProgressContainer)
private val processProgressIndicator: CircularProgressIndicator =
root.findViewById(R.id.processProgressIndicator)

Expand Down Expand Up @@ -235,17 +238,24 @@ class CameraExtensionsScreen(private val root: View) {
}

private fun showProcessProgressIndicator(progress: Int) {
processProgressIndicator.isVisible = true
processProgressContainer.isVisible = true
ObjectAnimator.ofInt(processProgressIndicator, "progress", progress).apply {
val currentProgress = processProgressIndicator.progress
val progressStep = max(0, progress - currentProgress)
duration = (progressStep / 100f * MAX_PROGRESS_ANIM_DURATION_MS).toLong()
doOnEnd {
if (animatedValue == 100) {
root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
_action.emit(CameraUiAction.ProcessProgressComplete)
}
}
}
start()
}
}

private fun hideProcessProgressIndicator() {
processProgressIndicator.isVisible = false
processProgressContainer.isVisible = false
}

private fun showPhoto(uri: Uri?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ package com.example.android.cameraxextensions.viewmodel

import android.app.Application
import android.graphics.Bitmap
import androidx.camera.core.*
import androidx.camera.core.AspectRatio
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraSelector.LensFacing
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.MeteringPoint
import androidx.camera.core.Preview
import androidx.camera.core.UseCaseGroup
import androidx.camera.extensions.ExtensionMode
import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider
Expand Down
43 changes: 32 additions & 11 deletions CameraXExtensions/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,41 @@
app:srcCompat="@drawable/ic_close"
app:tint="@color/button" />

<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/processProgressIndicator"
android:layout_width="80dp"
android:layout_height="80dp"
android:visibility="gone"
app:indicatorSize="80dp"
app:indicatorColor="#FFBE0B"
app:trackColor="#272727"
app:trackThickness="4dp"
app:trackCornerRadius="2dp"
<LinearLayout
android:id="@+id/processProgressContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/selector_background"
android:gravity="center"
android:visibility="visible"
android:paddingHorizontal="84dp"
android:paddingVertical="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent" >

<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="#FFBE0B"
android:textSize="16sp"
android:text="@string/hold_still"
android:layout_marginBottom="8dp"
/>

<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/processProgressIndicator"
android:layout_width="64dp"
android:layout_height="64dp"
app:indicatorSize="64dp"
app:indicatorColor="#FFBE0B"
app:trackColor="#272727"
app:trackThickness="6dp"
app:trackCornerRadius="3dp" />
</LinearLayout>

<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/permissionsRationaleContainer"
Expand Down
2 changes: 2 additions & 0 deletions CameraXExtensions/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@
<string name="camera_permissions_request_with_rationale">You can\'t use camera extensions unless CameraX Extensions has access to your camera.</string>
<string name="camera_permissions_request">Allow CameraX Extensions access to your camera to try camera extensions.</string>
<string name="camera_permissions_action">Turn On</string>

<string name="hold_still">Hold Still</string>
</resources>

0 comments on commit e97a8dd

Please sign in to comment.