mirror of
https://github.com/SimpleMobileTools/Simple-Camera.git
synced 2025-06-27 09:02:59 +02:00
handle some camera errors
- add CameraErrorHandler to handle - errors during camera lifecycle - when capturing images - when recording videos
This commit is contained in:
@ -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.config
|
||||||
import com.simplemobiletools.camera.extensions.toAppFlashMode
|
import com.simplemobiletools.camera.extensions.toAppFlashMode
|
||||||
import com.simplemobiletools.camera.extensions.toCameraSelector
|
import com.simplemobiletools.camera.extensions.toCameraSelector
|
||||||
import com.simplemobiletools.camera.extensions.toCameraXFlashMode
|
|
||||||
import com.simplemobiletools.camera.extensions.toLensFacing
|
import com.simplemobiletools.camera.extensions.toLensFacing
|
||||||
|
import com.simplemobiletools.camera.helpers.CameraErrorHandler
|
||||||
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
||||||
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
||||||
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
||||||
import com.simplemobiletools.camera.interfaces.MyPreview
|
import com.simplemobiletools.camera.interfaces.MyPreview
|
||||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
|
||||||
import com.simplemobiletools.commons.extensions.toast
|
import com.simplemobiletools.commons.extensions.toast
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@ -93,6 +92,7 @@ class CameraXPreview(
|
|||||||
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
private val mediaSoundHelper = MediaSoundHelper()
|
private val mediaSoundHelper = MediaSoundHelper()
|
||||||
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
||||||
|
private val cameraErrorHandler = CameraErrorHandler(activity)
|
||||||
|
|
||||||
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
@ -137,7 +137,7 @@ class CameraXPreview(
|
|||||||
activity.lifecycle.addObserver(this)
|
activity.lifecycle.addObserver(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCamera() {
|
private fun startCamera(switching: Boolean = false) {
|
||||||
Log.i(TAG, "startCamera: ")
|
Log.i(TAG, "startCamera: ")
|
||||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
|
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
|
||||||
cameraProviderFuture.addListener({
|
cameraProviderFuture.addListener({
|
||||||
@ -147,7 +147,7 @@ class CameraXPreview(
|
|||||||
setupCameraObservers()
|
setupCameraObservers()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "startCamera: ", e)
|
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)
|
}, mainExecutor)
|
||||||
}
|
}
|
||||||
@ -189,48 +189,7 @@ class CameraXPreview(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle errors
|
cameraErrorHandler.handleCameraError(cameraState?.error)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +312,7 @@ class CameraXPreview(
|
|||||||
}
|
}
|
||||||
cameraSelector = newCameraSelector
|
cameraSelector = newCameraSelector
|
||||||
config.lastUsedCameraLens = newCameraSelector.toLensFacing()
|
config.lastUsedCameraLens = newCameraSelector.toLensFacing()
|
||||||
startCamera()
|
startCamera(switching = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toggleFlashlight() {
|
override fun toggleFlashlight() {
|
||||||
@ -411,9 +370,9 @@ class CameraXPreview(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(exception: ImageCaptureException) {
|
override fun onError(exception: ImageCaptureException) {
|
||||||
listener.toggleBottomButtons(false)
|
|
||||||
activity.showErrorToast("Capture picture $exception")
|
|
||||||
Log.e(TAG, "Error", exception)
|
Log.e(TAG, "Error", exception)
|
||||||
|
listener.toggleBottomButtons(false)
|
||||||
|
cameraErrorHandler.handleImageCaptureError(exception.imageCaptureError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
playShutterSoundIfEnabled()
|
playShutterSoundIfEnabled()
|
||||||
@ -477,7 +436,8 @@ class CameraXPreview(
|
|||||||
playStopVideoRecordingSoundIfEnabled()
|
playStopVideoRecordingSoundIfEnabled()
|
||||||
listener.onVideoRecordingStopped()
|
listener.onVideoRecordingStopped()
|
||||||
if (recordEvent.hasError()) {
|
if (recordEvent.hasError()) {
|
||||||
// TODO: Handle errors
|
Log.e(TAG, "recording failed:", recordEvent.cause)
|
||||||
|
cameraErrorHandler.handleVideoRecordingError(recordEvent.error)
|
||||||
} else {
|
} else {
|
||||||
listener.onMediaCaptured(mediaOutput?.uri ?: recordEvent.outputResults.outputUri)
|
listener.onMediaCaptured(mediaOutput?.uri ?: recordEvent.outputResults.outputUri)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Simple Camera</string>
|
<string name="app_name">Simple Camera</string>
|
||||||
<string name="app_launcher_name">Camera</string>
|
<string name="app_launcher_name">Camera</string>
|
||||||
|
|
||||||
|
<!--Errors-->
|
||||||
<string name="camera_unavailable">Camera unavailable</string>
|
<string name="camera_unavailable">Camera unavailable</string>
|
||||||
<string name="camera_open_error">An error occurred accessing the camera</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>
|
<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="setting_resolution_failed">Setting proper resolution failed</string>
|
||||||
<string name="video_recording_failed">Video recording failed, try using a different resolution</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 -->
|
<!-- FAQ -->
|
||||||
<string name="faq_1_title">What photo compression quality should I set?</string>
|
<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>
|
<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>
|
||||||
|
Reference in New Issue
Block a user