Skip to content

Commit

Permalink
chore: restrict video capture mode to api level 22 above
Browse files Browse the repository at this point in the history
  • Loading branch information
ujizin committed Dec 13, 2023
1 parent b96934c commit ee4a760
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.ujizin.camposer.extensions

import android.content.ContentValues
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.camera.core.ImageCapture
import androidx.camera.view.video.OutputFileOptions
import com.ujizin.camposer.state.CameraState
Expand Down Expand Up @@ -43,13 +45,15 @@ public suspend fun CameraState.takePicture(
/**
* Transform toggle recording file to suspend function
* */
@RequiresApi(Build.VERSION_CODES.M)
public suspend fun CameraState.toggleRecording(file: File): Uri? = suspendCancellableCoroutine { cont ->
with(cont) { toggleRecording(file, ::toggleRecordContinuation) }
}

/**
* Transform toggle recording content values options to suspend function
* */
@RequiresApi(Build.VERSION_CODES.M)
public suspend fun CameraState.toggleRecording(
contentValues: ContentValues,
saveCollection: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
Expand All @@ -60,6 +64,7 @@ public suspend fun CameraState.toggleRecording(
/**
* Transform toggle recording output files options to suspend function
* */
@RequiresApi(Build.VERSION_CODES.M)
public suspend fun CameraState.toggleRecording(
outputFileOptions: OutputFileOptions
): Uri? = suspendCancellableCoroutine { cont ->
Expand Down
17 changes: 17 additions & 0 deletions camposer/src/main/java/com/ujizin/camposer/state/CameraState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import android.content.ContentValues
import android.content.Context
import android.hardware.camera2.CameraManager
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Log
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageAnalysis
Expand Down Expand Up @@ -322,6 +325,13 @@ public class CameraState(context: Context) {
}
}

/**
* Return if video is supported.
* */

@ChecksSdkIntAtLeast(Build.VERSION_CODES.M)
public var isVideoSupported: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M

/**
* Return true if it's recording.
* */
Expand Down Expand Up @@ -417,6 +427,7 @@ public class CameraState(context: Context) {
* @param onResult Callback called when [VideoCaptureResult] is ready
* */
@OptIn(markerClass = [ExperimentalVideo::class])
@RequiresApi(Build.VERSION_CODES.M)
public fun startRecording(file: File, onResult: (VideoCaptureResult) -> Unit) {
startRecording(OutputFileOptions.builder(file).build(), onResult)
}
Expand All @@ -429,6 +440,7 @@ public class CameraState(context: Context) {
* @param onResult Callback called when [VideoCaptureResult] is ready
* */
@OptIn(markerClass = [ExperimentalVideo::class])
@RequiresApi(Build.VERSION_CODES.M)
public fun startRecording(
saveCollection: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
contentValues: ContentValues,
Expand All @@ -447,6 +459,7 @@ public class CameraState(context: Context) {
* @param onResult Callback called when [VideoCaptureResult] is ready
* */
@ExperimentalVideo
@RequiresApi(Build.VERSION_CODES.M)
public fun startRecording(
outputFileOptions: OutputFileOptions,
onResult: (VideoCaptureResult) -> Unit,
Expand Down Expand Up @@ -484,13 +497,15 @@ public class CameraState(context: Context) {
* Stop recording camera.
* */
@OptIn(markerClass = [ExperimentalVideo::class])
@RequiresApi(Build.VERSION_CODES.M)
public fun stopRecording() {
controller.stopRecording()
}

/**
* Toggle recording camera.
* */
@RequiresApi(Build.VERSION_CODES.M)
public fun toggleRecording(
file: File,
onResult: (VideoCaptureResult) -> Unit
Expand All @@ -504,6 +519,7 @@ public class CameraState(context: Context) {
/**
* Toggle recording camera.
* */
@RequiresApi(Build.VERSION_CODES.M)
public fun toggleRecording(
contentValues: ContentValues,
saveCollection: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
Expand All @@ -518,6 +534,7 @@ public class CameraState(context: Context) {
/**
* Toggle recording camera.
* */
@RequiresApi(Build.VERSION_CODES.M)
@OptIn(markerClass = [ExperimentalVideo::class])
public fun toggleRecording(
outputFileOptions: OutputFileOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.ujizin.camposer.state

import android.os.Build
import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
import androidx.camera.view.CameraController.IMAGE_CAPTURE
import androidx.camera.view.CameraController.VIDEO_CAPTURE
import androidx.camera.view.video.ExperimentalVideo
Expand All @@ -15,5 +17,6 @@ import androidx.camera.view.video.ExperimentalVideo
@OptIn(markerClass = [ExperimentalVideo::class])
public enum class CaptureMode(internal val value: Int) {
Image(IMAGE_CAPTURE),
@RequiresApi(Build.VERSION_CODES.M)
Video(VIDEO_CAPTURE),
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ fun CameraSection(
cameraOption = cameraOption,
hasFlashUnit = hasFlashUnit,
qrCodeText = qrCodeText,
isVideoSupported = cameraState.isVideoSupported,
onFlashModeChanged = { flash ->
enableTorch = flash == Flash.Always
flashMode = flash.toFlashMode()
Expand Down Expand Up @@ -161,6 +162,7 @@ fun CameraInnerContent(
hasFlashUnit: Boolean,
qrCodeText: String?,
lastPicture: File?,
isVideoSupported: Boolean,
onGalleryClick: () -> Unit,
onFlashModeChanged: (Flash) -> Unit,
onZoomFinish: () -> Unit,
Expand Down Expand Up @@ -199,6 +201,7 @@ fun CameraInnerContent(
qrCodeText = qrCodeText,
onTakePicture = onTakePicture,
isRecording = isRecording,
isVideoSupported = isVideoSupported,
onRecording = onRecording,
onSwitchCamera = onSwitchCamera,
onCameraOptionChanged = onCameraOptionChanged,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import android.util.Log
import androidx.camera.core.ImageProxy
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.zxing.BarcodeFormat
import com.google.zxing.DecodeHintType
import com.google.zxing.MultiFormatReader
import com.ujizin.camposer.state.CameraState
import com.ujizin.camposer.state.ImageCaptureResult
import com.ujizin.camposer.state.VideoCaptureResult
import com.ujizin.sample.data.local.datasource.FileDataSource
import com.ujizin.sample.data.local.datasource.UserDataSource
import com.ujizin.sample.domain.User
import com.ujizin.sample.extensions.getQRCodeResult
import com.google.zxing.BarcodeFormat
import com.google.zxing.DecodeHintType
import com.google.zxing.MultiFormatReader
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.onStart
Expand Down Expand Up @@ -80,10 +80,12 @@ class CameraViewModel(
onResult = ::onVideoResult
)

else -> toggleRecording(
fileDataSource.getFile("mp4"),
onResult = ::onVideoResult
)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
toggleRecording(
fileDataSource.getFile("mp4"),
onResult = ::onVideoResult
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fun ActionBox(
isRecording: Boolean,
qrCodeText: String?,
lastPicture: File?,
isVideoSupported: Boolean,
onGalleryClick: () -> Unit,
onTakePicture: () -> Unit,
onSwitchCamera: () -> Unit,
Expand All @@ -32,6 +33,7 @@ fun ActionBox(
qrCodeText = qrCodeText)
OptionSection(
modifier = Modifier.fillMaxWidth(),
isVideoSupported = isVideoSupported,
currentCameraOption = cameraOption,
onCameraOptionChanged = onCameraOptionChanged
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.ujizin.sample.feature.camera.model.CameraOption

@Composable
fun OptionSection(
modifier: Modifier = Modifier,
currentCameraOption: CameraOption,
isVideoSupported: Boolean,
onCameraOptionChanged: (CameraOption) -> Unit,
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.Center,
) {
CameraOption.values().forEach { option ->
if (!isVideoSupported && option == CameraOption.Video) return@forEach

Text(
modifier = Modifier
.clickable(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ujizin.sample.feature.camera.model

import android.os.Build
import androidx.annotation.StringRes
import com.ujizin.camposer.state.CaptureMode
import com.ujizin.sample.R
Expand All @@ -11,6 +12,10 @@ enum class CameraOption(@StringRes val titleRes: Int) {

fun toCaptureMode(): CaptureMode = when(this) {
QRCode, Photo -> CaptureMode.Image
Video -> CaptureMode.Video
Video -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
CaptureMode.Video
} else {
throw IllegalStateException("Camera state not support video capture mode")
}
}
}

0 comments on commit ee4a760

Please sign in to comment.