diff --git a/docs/dialogs/camera-picker.mdx b/docs/dialogs/camera-picker.mdx index 78457b92..950de99a 100644 --- a/docs/dialogs/camera-picker.mdx +++ b/docs/dialogs/camera-picker.mdx @@ -59,10 +59,12 @@ Video capture support is planned for a future release. ## Camera facing You can specify whether the front or rear facing camera is used, using the `cameraFacing` parameter: +- `System` lets the camera app decide which camera to open (default) - `Front` opens the front facing camera (Selfie-Camera) - `Back` opens the rear facing camera -By default, the rear facing camera is used. +By default, the camera app decides which camera to use. +On Android, FileKit only applies camera-facing intent extras when you explicitly request `Front` or `Back`. ```kotlin filekit-dialogs diff --git a/filekit-dialogs-compose/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/compose/FileKitResultLauncher.mobile.kt b/filekit-dialogs-compose/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/compose/FileKitResultLauncher.mobile.kt index 57383664..55cb3636 100644 --- a/filekit-dialogs-compose/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/compose/FileKitResultLauncher.mobile.kt +++ b/filekit-dialogs-compose/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/compose/FileKitResultLauncher.mobile.kt @@ -19,7 +19,7 @@ public class PhotoResultLauncher( @OptIn(ExperimentalUuidApi::class) public fun launch( type: FileKitCameraType = FileKitCameraType.Photo, - cameraFacing: FileKitCameraFacing = FileKitCameraFacing.Back, + cameraFacing: FileKitCameraFacing = FileKitCameraFacing.System, destinationFile: PlatformFile = FileKit.cacheDir / "${Uuid.random()}.jpg", ) { onLaunch(type, cameraFacing, destinationFile) diff --git a/filekit-dialogs/src/androidMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.android.kt b/filekit-dialogs/src/androidMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.android.kt index a9b7ba76..03e64554 100644 --- a/filekit-dialogs/src/androidMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.android.kt +++ b/filekit-dialogs/src/androidMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.android.kt @@ -6,9 +6,7 @@ import android.content.Intent import android.content.Intent.ACTION_VIEW import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION -import android.hardware.camera2.CameraCharacteristics import android.net.Uri -import android.os.Build import android.webkit.MimeTypeMap import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultRegistry @@ -112,7 +110,7 @@ public actual suspend fun FileKit.openDirectoryPicker( * Opens a camera picker dialog. * * @param type The type of media to capture (Image or Video). - * @param cameraFacing The camera facing (Back or Front). + * @param cameraFacing The camera facing (System, Back or Front). * @param destinationFile The file where the captured media will be saved. * @param openCameraSettings Platform-specific settings for the camera. * @return The saved file as a [PlatformFile], or null if cancelled. @@ -152,7 +150,7 @@ public actual suspend fun FileKit.openCameraPicker( * 2. With [setCameraFacing] for reusable contracts (e.g., with rememberLauncherForActivityResult) */ public class TakePictureWithCameraFacing( - cameraFacing: FileKitCameraFacing = FileKitCameraFacing.Back, + cameraFacing: FileKitCameraFacing = FileKitCameraFacing.System, ) : ActivityResultContracts.TakePicture() { private var currentCameraFacing: FileKitCameraFacing = cameraFacing @@ -165,28 +163,36 @@ public class TakePictureWithCameraFacing( } override fun createIntent(context: Context, input: Uri): Intent = super.createIntent(context, input).apply { - // Intent extras taken from the flutter codebase because they are known to work and battle-tested: - // https://github.com/flutter/packages/blob/27a2302a3d716e7ee3abbb08e57c5dfa729c9e2e/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java#L990 - val cameraCharacteristic = when (currentCameraFacing) { - FileKitCameraFacing.Front -> CameraCharacteristics.LENS_FACING_FRONT - FileKitCameraFacing.Back -> CameraCharacteristics.LENS_FACING_BACK + if (currentCameraFacing == FileKitCameraFacing.System) { + return@apply } - putExtra("android.intent.extras.CAMERA_FACING", cameraCharacteristic) + applyCameraFacingExtras(currentCameraFacing) + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - putExtra( - "android.intent.extras.USE_FRONT_CAMERA", - currentCameraFacing == FileKitCameraFacing.Front, - ) - } + private fun Intent.applyCameraFacingExtras(cameraFacing: FileKitCameraFacing) { + val isFront = cameraFacing == FileKitCameraFacing.Front - // Required for Samsung according to https://stackoverflow.com/questions/64263476/android-camera-intent-open-front-camera-instead-of-back-camera - val facing = when (currentCameraFacing) { - FileKitCameraFacing.Front -> "front" - FileKitCameraFacing.Back -> "rear" + // Intent extras adapted from community implementations (expo-image-picker / Flutter). + // They are undocumented, so we apply them only when the caller explicitly requested a facing. + if (isFront) { + // https://github.com/expo/expo/blob/c54eb1e0cbc0f09af9e4308ff76ed9dca457d90e/packages/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/CameraContract.kt#L32 + putExtra("android.intent.extras.LENS_FACING_FRONT", 1) + putExtra("android.intent.extras.CAMERA_FACING", 1) + putExtra("android.intent.extra.USE_FRONT_CAMERA", true) + + // Required for Samsung according to https://stackoverflow.com/questions/64263476/android-camera-intent-open-front-camera-instead-of-back-camera + putExtra("camerafacing", "front") + putExtra("previous_mode", "front") + } else { + // https://github.com/expo/expo/blob/c54eb1e0cbc0f09af9e4308ff76ed9dca457d90e/packages/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/CameraContract.kt#L32 + putExtra("android.intent.extras.LENS_FACING_BACK", 1) + putExtra("android.intent.extras.CAMERA_FACING", 0) + putExtra("android.intent.extra.USE_FRONT_CAMERA", false) + + // Required for Samsung according to https://stackoverflow.com/questions/64263476/android-camera-intent-open-front-camera-instead-of-back-camera + putExtra("camerafacing", "rear") + putExtra("previous_mode", "rear") } - putExtra("camerafacing", facing) - putExtra("previous_mode", facing) } } diff --git a/filekit-dialogs/src/iosMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.ios.kt b/filekit-dialogs/src/iosMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.ios.kt index 0c7a7870..014309e7 100644 --- a/filekit-dialogs/src/iosMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.ios.kt +++ b/filekit-dialogs/src/iosMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.ios.kt @@ -208,7 +208,7 @@ public actual suspend fun FileKit.openFileSaver( * Opens a camera picker dialog. * * @param type The type of media to capture (Image or Video). - * @param cameraFacing The camera facing (Back or Front). + * @param cameraFacing The camera facing (System, Back or Front). * @param destinationFile The file where the captured media will be saved. * @param openCameraSettings Platform-specific settings for the camera. * @return The saved file as a [PlatformFile], or null if cancelled. @@ -248,9 +248,18 @@ public actual suspend fun FileKit.openCameraPicker( UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypeCamera pickerController.delegate = cameraControllerDelegate - pickerController.cameraDevice = when (cameraFacing) { - FileKitCameraFacing.Front -> UIImagePickerControllerCameraDevice.UIImagePickerControllerCameraDeviceFront - FileKitCameraFacing.Back -> UIImagePickerControllerCameraDevice.UIImagePickerControllerCameraDeviceRear + when (cameraFacing) { + FileKitCameraFacing.Front -> { + pickerController.cameraDevice = + UIImagePickerControllerCameraDevice.UIImagePickerControllerCameraDeviceFront + } + + FileKitCameraFacing.Back -> { + pickerController.cameraDevice = + UIImagePickerControllerCameraDevice.UIImagePickerControllerCameraDeviceRear + } + + FileKitCameraFacing.System -> {} } UIApplication.sharedApplication.topMostViewController()?.presentViewController( diff --git a/filekit-dialogs/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.mobile.kt b/filekit-dialogs/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.mobile.kt index 1bf60903..5cf7288d 100644 --- a/filekit-dialogs/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.mobile.kt +++ b/filekit-dialogs/src/mobileMain/kotlin/io/github/vinceglb/filekit/dialogs/FileKit.mobile.kt @@ -8,6 +8,7 @@ import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid public enum class FileKitCameraFacing { + System, Front, Back, } @@ -15,7 +16,7 @@ public enum class FileKitCameraFacing { @OptIn(ExperimentalUuidApi::class) public expect suspend fun FileKit.openCameraPicker( type: FileKitCameraType = FileKitCameraType.Photo, - cameraFacing: FileKitCameraFacing = FileKitCameraFacing.Back, + cameraFacing: FileKitCameraFacing = FileKitCameraFacing.System, destinationFile: PlatformFile = FileKit.cacheDir / "${Uuid.random()}.jpg", openCameraSettings: FileKitOpenCameraSettings = FileKitOpenCameraSettings.createDefault(), ): PlatformFile? diff --git a/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.kt b/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.kt index a3e215d9..6947f7dc 100644 --- a/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.kt +++ b/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import io.github.vinceglb.filekit.PlatformFile internal enum class CameraFacingOption { + System, Front, Back, } diff --git a/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerScreen.kt b/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerScreen.kt index 854b078e..cea247f6 100644 --- a/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerScreen.kt +++ b/sample/shared/src/commonMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerScreen.kt @@ -61,7 +61,7 @@ private fun CameraPickerScreen( onDisplayFileDetails: (file: PlatformFile) -> Unit, ) { var buttonState by remember { mutableStateOf(AppScreenHeaderButtonState.Enabled) } - var cameraFacing by remember { mutableStateOf(CameraFacingOption.Back) } + var cameraFacing by remember { mutableStateOf(CameraFacingOption.System) } var capturedFiles by remember { mutableStateOf(emptyList()) } val cameraLauncher = rememberCameraPickerLauncher { file -> @@ -201,6 +201,11 @@ private fun CameraPickerSettingsCard( } private val facingOptions: List> = listOf( + AppDropdownItem.IconItem( + label = "System Default", + value = CameraFacingOption.System, + icon = LucideIcons.Camera, + ), AppDropdownItem.IconItem( label = "Rear Camera", value = CameraFacingOption.Back, diff --git a/sample/shared/src/mobileMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.mobile.kt b/sample/shared/src/mobileMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.mobile.kt index 9289fe66..a249870f 100644 --- a/sample/shared/src/mobileMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.mobile.kt +++ b/sample/shared/src/mobileMain/kotlin/io/github/vinceglb/filekit/sample/shared/ui/screens/camerapicker/CameraPickerLauncher.mobile.kt @@ -19,6 +19,7 @@ internal actual fun rememberCameraPickerLauncher( override fun launch(cameraFacing: CameraFacingOption) { launcher.launch( cameraFacing = when (cameraFacing) { + CameraFacingOption.System -> FileKitCameraFacing.System CameraFacingOption.Front -> FileKitCameraFacing.Front CameraFacingOption.Back -> FileKitCameraFacing.Back },