handle some camera errors
- add CameraErrorHandler to handle - errors during camera lifecycle - when capturing images - when recording videos
This commit is contained in:
parent
b10d8639fd
commit
f43cd4f939
|
@ -0,0 +1,41 @@
|
|||
package com.simplemobiletools.camera.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.camera.core.CameraState
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.video.VideoRecordEvent
|
||||
import com.simplemobiletools.camera.R
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
|
||||
class CameraErrorHandler(
|
||||
private val context: Context,
|
||||
) {
|
||||
|
||||
fun handleCameraError(error: CameraState.StateError?) {
|
||||
when (error?.code) {
|
||||
CameraState.ERROR_MAX_CAMERAS_IN_USE,
|
||||
CameraState.ERROR_CAMERA_IN_USE -> context.toast(R.string.camera_in_use_error, Toast.LENGTH_LONG)
|
||||
CameraState.ERROR_CAMERA_FATAL_ERROR -> context.toast(R.string.camera_unavailable)
|
||||
CameraState.ERROR_STREAM_CONFIG -> context.toast(R.string.camera_configure_error)
|
||||
CameraState.ERROR_CAMERA_DISABLED -> context.toast(R.string.camera_disabled_by_admin_error)
|
||||
CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED -> context.toast(R.string.camera_dnd_error, Toast.LENGTH_LONG)
|
||||
CameraState.ERROR_OTHER_RECOVERABLE_ERROR -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleImageCaptureError(imageCaptureError: Int) {
|
||||
when (imageCaptureError) {
|
||||
ImageCapture.ERROR_FILE_IO -> context.toast(R.string.photo_not_saved)
|
||||
else -> context.toast(R.string.photo_capture_failed)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleVideoRecordingError(error: Int) {
|
||||
when (error) {
|
||||
VideoRecordEvent.Finalize.ERROR_INSUFFICIENT_STORAGE -> context.toast(R.string.video_capture_insufficient_storage_error)
|
||||
VideoRecordEvent.Finalize.ERROR_NONE -> {}
|
||||
else -> context.toast(R.string.video_recording_failed)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,13 +55,12 @@ import com.simplemobiletools.camera.R
|
|||
import com.simplemobiletools.camera.extensions.config
|
||||
import com.simplemobiletools.camera.extensions.toAppFlashMode
|
||||
import com.simplemobiletools.camera.extensions.toCameraSelector
|
||||
import com.simplemobiletools.camera.extensions.toCameraXFlashMode
|
||||
import com.simplemobiletools.camera.extensions.toLensFacing
|
||||
import com.simplemobiletools.camera.helpers.CameraErrorHandler
|
||||
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
||||
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
||||
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
||||
import com.simplemobiletools.camera.interfaces.MyPreview
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
@ -93,6 +92,7 @@ class CameraXPreview(
|
|||
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
private val mediaSoundHelper = MediaSoundHelper()
|
||||
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
||||
private val cameraErrorHandler = CameraErrorHandler(activity)
|
||||
|
||||
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
||||
@SuppressLint("RestrictedApi")
|
||||
|
@ -137,7 +137,7 @@ class CameraXPreview(
|
|||
activity.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
private fun startCamera() {
|
||||
private fun startCamera(switching: Boolean = false) {
|
||||
Log.i(TAG, "startCamera: ")
|
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
|
||||
cameraProviderFuture.addListener({
|
||||
|
@ -147,7 +147,7 @@ class CameraXPreview(
|
|||
setupCameraObservers()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "startCamera: ", e)
|
||||
activity.showErrorToast(activity.getString(R.string.camera_open_error))
|
||||
activity.toast(if (switching) R.string.camera_switch_error else R.string.camera_open_error)
|
||||
}
|
||||
}, mainExecutor)
|
||||
}
|
||||
|
@ -189,48 +189,7 @@ class CameraXPreview(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Handle errors
|
||||
cameraState.error?.let { error ->
|
||||
listener.setCameraAvailable(false)
|
||||
when (error.code) {
|
||||
CameraState.ERROR_STREAM_CONFIG -> {
|
||||
Log.e(TAG, "ERROR_STREAM_CONFIG")
|
||||
// Make sure to setup the use cases properly
|
||||
activity.toast(R.string.camera_unavailable)
|
||||
}
|
||||
CameraState.ERROR_CAMERA_IN_USE -> {
|
||||
Log.e(TAG, "ERROR_CAMERA_IN_USE")
|
||||
// Close the camera or ask user to close another camera app that's using the
|
||||
// camera
|
||||
activity.showErrorToast("Camera is in use by another app, please close")
|
||||
}
|
||||
CameraState.ERROR_MAX_CAMERAS_IN_USE -> {
|
||||
Log.e(TAG, "ERROR_MAX_CAMERAS_IN_USE")
|
||||
// Close another open camera in the app, or ask the user to close another
|
||||
// camera app that's using the camera
|
||||
activity.showErrorToast("Camera is in use by another app, please close")
|
||||
}
|
||||
CameraState.ERROR_OTHER_RECOVERABLE_ERROR -> {
|
||||
Log.e(TAG, "ERROR_OTHER_RECOVERABLE_ERROR")
|
||||
activity.toast(R.string.camera_open_error)
|
||||
}
|
||||
CameraState.ERROR_CAMERA_DISABLED -> {
|
||||
Log.e(TAG, "ERROR_CAMERA_DISABLED")
|
||||
// Ask the user to enable the device's cameras
|
||||
activity.toast(R.string.camera_open_error)
|
||||
}
|
||||
CameraState.ERROR_CAMERA_FATAL_ERROR -> {
|
||||
Log.e(TAG, "ERROR_CAMERA_FATAL_ERROR")
|
||||
// Ask the user to reboot the device to restore camera function
|
||||
activity.toast(R.string.camera_open_error)
|
||||
}
|
||||
CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED -> {
|
||||
// Ask the user to disable the "Do Not Disturb" mode, then reopen the camera
|
||||
Log.e(TAG, "ERROR_DO_NOT_DISTURB_MODE_ENABLED")
|
||||
activity.toast(R.string.camera_open_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
cameraErrorHandler.handleCameraError(cameraState?.error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,7 +312,7 @@ class CameraXPreview(
|
|||
}
|
||||
cameraSelector = newCameraSelector
|
||||
config.lastUsedCameraLens = newCameraSelector.toLensFacing()
|
||||
startCamera()
|
||||
startCamera(switching = true)
|
||||
}
|
||||
|
||||
override fun toggleFlashlight() {
|
||||
|
@ -411,9 +370,9 @@ class CameraXPreview(
|
|||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
listener.toggleBottomButtons(false)
|
||||
activity.showErrorToast("Capture picture $exception")
|
||||
Log.e(TAG, "Error", exception)
|
||||
listener.toggleBottomButtons(false)
|
||||
cameraErrorHandler.handleImageCaptureError(exception.imageCaptureError)
|
||||
}
|
||||
})
|
||||
playShutterSoundIfEnabled()
|
||||
|
@ -477,7 +436,8 @@ class CameraXPreview(
|
|||
playStopVideoRecordingSoundIfEnabled()
|
||||
listener.onVideoRecordingStopped()
|
||||
if (recordEvent.hasError()) {
|
||||
// TODO: Handle errors
|
||||
Log.e(TAG, "recording failed:", recordEvent.cause)
|
||||
cameraErrorHandler.handleVideoRecordingError(recordEvent.error)
|
||||
} else {
|
||||
listener.onMediaCaptured(mediaOutput?.uri ?: recordEvent.outputResults.outputUri)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple Camera</string>
|
||||
<string name="app_launcher_name">Camera</string>
|
||||
|
||||
<!--Errors-->
|
||||
<string name="camera_unavailable">Camera unavailable</string>
|
||||
<string name="camera_open_error">An error occurred accessing the camera</string>
|
||||
<string name="video_creating_error">An error occurred creating the video file</string>
|
||||
|
@ -13,6 +15,15 @@
|
|||
<string name="setting_resolution_failed">Setting proper resolution failed</string>
|
||||
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
|
||||
|
||||
<!--TODO: Add strings in other locales-->
|
||||
<string name="camera_in_use_error">Camera is in use by another app, please close the app and try again</string>
|
||||
<string name="camera_configure_error">An error occurred while configuring the camera</string>
|
||||
<string name="camera_disabled_by_admin_error">Camera is disabled by the admin</string>
|
||||
<string name="camera_dnd_error">"Do Not Disturb" mode is enabled. Please disable and try again</string>
|
||||
|
||||
<string name="photo_capture_failed">Photo capture failed</string>
|
||||
<string name="video_capture_insufficient_storage_error">Video recording failed due to insufficient storage</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">What photo compression quality should I set?</string>
|
||||
<string name="faq_1_text">It depends on your goal. For generic purposes most people advise using 75%-80%, when the image is still really good quality, but the file size is reduced drastically compared to 100%.</string>
|
||||
|
|
Loading…
Reference in New Issue