Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/dialogs/camera-picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

<CodeGroup>
```kotlin filekit-dialogs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand All @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

public enum class FileKitCameraFacing {
System,
Front,
Back,
}

@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?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import io.github.vinceglb.filekit.PlatformFile

internal enum class CameraFacingOption {
System,
Front,
Back,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<PlatformFile>()) }

val cameraLauncher = rememberCameraPickerLauncher { file ->
Expand Down Expand Up @@ -201,6 +201,11 @@ private fun CameraPickerSettingsCard(
}

private val facingOptions: List<AppDropdownItem.IconItem<CameraFacingOption>> = listOf(
AppDropdownItem.IconItem(
label = "System Default",
value = CameraFacingOption.System,
icon = LucideIcons.Camera,
),
AppDropdownItem.IconItem(
label = "Rear Camera",
value = CameraFacingOption.Back,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down