Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit number of files that can be selected #60

Merged
merged 10 commits into from
Jul 18, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ xcuserdata
.kotlin
.idea/*
!.idea/runConfigurations/
Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly
Expand Down Expand Up @@ -49,41 +50,36 @@ public actual object FileKit {
PickerType.Image,
PickerType.Video,
PickerType.ImageAndVideo -> {
when (mode) {
is PickerMode.Single -> {
val request = when (type) {
PickerType.Image -> PickVisualMediaRequest(ImageOnly)
PickerType.Video -> PickVisualMediaRequest(VideoOnly)
PickerType.ImageAndVideo -> PickVisualMediaRequest(ImageAndVideo)
else -> throw IllegalArgumentException("Unsupported type: $type")
}

val launcher = when {
mode is PickerMode.Single || mode is PickerMode.Multiple && mode.maxItems == 1 -> {
val contract = PickVisualMedia()
val launcher = registry.register(key, contract) { uri ->
registry.register(key, contract) { uri ->
val result = uri?.let { listOf(PlatformFile(it, context)) }
continuation.resume(result)
}

val request = when (type) {
PickerType.Image -> PickVisualMediaRequest(ImageOnly)
PickerType.Video -> PickVisualMediaRequest(VideoOnly)
PickerType.ImageAndVideo -> PickVisualMediaRequest(ImageAndVideo)
else -> throw IllegalArgumentException("Unsupported type: $type")
}

launcher.launch(request)
}

is PickerMode.Multiple -> {
val contract = ActivityResultContracts.PickMultipleVisualMedia()
val launcher = registry.register(key, contract) { uri ->
mode is PickerMode.Multiple -> {
val contract = when {
mode.maxItems != null -> PickMultipleVisualMedia(mode.maxItems)
else -> PickMultipleVisualMedia()
}
registry.register(key, contract) { uri ->
val result = uri.map { PlatformFile(it, context) }
continuation.resume(result)
}

val request = when (type) {
PickerType.Image -> PickVisualMediaRequest(ImageOnly)
PickerType.Video -> PickVisualMediaRequest(VideoOnly)
PickerType.ImageAndVideo -> PickVisualMediaRequest(ImageAndVideo)
else -> throw IllegalArgumentException("Unsupported type: $type")
}

launcher.launch(request)
}

else -> throw IllegalArgumentException("Unsupported mode: $mode")
}
launcher.launch(request)
}

is PickerType.File -> {
Expand All @@ -98,6 +94,8 @@ public actual object FileKit {
}

is PickerMode.Multiple -> {
// TODO there might be a way to limit the amount of documents, but
// I haven't found it yet.
val contract = ActivityResultContracts.OpenMultipleDocuments()
val launcher = registry.register(key, contract) { uris ->
val result = uris.map { PlatformFile(it, context) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@ public sealed class PickerMode<Out> {
}
}

public data object Multiple : PickerMode<PlatformFiles>() {
/**
* @property maxItems sets the limit of how many items can be selected. NOTE: This is only
* supported by Android / iOS and only when picking media files, not any kind of file.
*/
public data class Multiple(val maxItems: Int? = null) : PickerMode<PlatformFiles>() {
init {
require(maxItems == null || maxItems in 1..50) {
"maxItems must be contained between 1 <= maxItems <= 50 but current value is $maxItems"
}
}

override fun parseResult(value: PlatformFiles?): PlatformFiles? {
return value?.takeIf { it.isNotEmpty() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public actual object FileKit {
// Use PHPickerViewController for images and videos
is PickerType.Image,
is PickerType.Video -> callPhPicker(
isMultipleMode = mode is PickerMode.Multiple,
mode = mode,
type = type
)?.map { PlatformFile(it) }?.let { mode.parseResult(it) }

Expand Down Expand Up @@ -163,8 +163,8 @@ public actual object FileKit {
}

@OptIn(ExperimentalForeignApi::class)
private suspend fun callPhPicker(
isMultipleMode: Boolean,
private suspend fun <Out> callPhPicker(
mode: PickerMode<Out>,
type: PickerType,
): List<NSURL>? {
val pickerResults: List<PHPickerResult> = suspendCoroutine { continuation ->
Expand All @@ -177,7 +177,10 @@ public actual object FileKit {
val configuration = PHPickerConfiguration(sharedPhotoLibrary())

// Number of medias to select
configuration.selectionLimit = if (isMultipleMode) 0 else 1
configuration.selectionLimit = when (mode) {
is PickerMode.Multiple -> mode.maxItems?.toLong() ?: 0
PickerMode.Single -> 1
}

// Filter configuration
configuration.filter = when (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public actual object FileKit {

// Set the multiple attribute
multiple = mode is PickerMode.Multiple

// max is not supported for file inputs
}

// Setup the change listener
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public actual object FileKit {

// Open native file picker
val result = when (mode) {
PickerMode.Single -> PlatformFilePicker.current.pickFile(
is PickerMode.Single -> PlatformFilePicker.current.pickFile(
title = title,
initialDirectory = initialDirectory,
fileExtensions = extensions,
parentWindow = platformSettings?.parentWindow,
)?.let { listOf(PlatformFile(it)) }

PickerMode.Multiple -> PlatformFilePicker.current.pickFiles(
is PickerMode.Multiple -> PlatformFilePicker.current.pickFiles(
title = title,
initialDirectory = initialDirectory,
fileExtensions = extensions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ internal class AwtFilePicker : PlatformFilePicker {
// Set multiple mode
dialog.isMultipleMode = isMultipleMode

// MaxItems is not supported by FileDialog

// Set mime types
dialog.filenameFilter = FilenameFilter { _, name ->
fileExtensions?.any { name.endsWith(it) } ?: true
Expand Down
Loading