desktop: send images and files (#2691)
* desktop: send images and files * expect/actual * file filter on non-Linux OSes --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
2389e870b3
commit
27f4661ac4
@@ -32,6 +32,15 @@ actual fun rememberFileChooserLauncher(getContent: Boolean, onResult: (URI?) ->
|
||||
return FileChooserLauncher(launcher)
|
||||
}
|
||||
|
||||
@Composable
|
||||
actual fun rememberFileChooserMultipleLauncher(onResult: (List<URI>) -> Unit): FileChooserMultipleLauncher {
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.GetMultipleContents(),
|
||||
onResult = { onResult(it.map { it.toURI() }) }
|
||||
)
|
||||
return FileChooserMultipleLauncher(launcher)
|
||||
}
|
||||
|
||||
actual class FileChooserLauncher actual constructor() {
|
||||
private lateinit var launcher: ManagedActivityResultLauncher<String, Uri?>
|
||||
|
||||
@@ -44,5 +53,17 @@ actual class FileChooserLauncher actual constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
actual class FileChooserMultipleLauncher actual constructor() {
|
||||
private lateinit var launcher: ManagedActivityResultLauncher<String, List<Uri>>
|
||||
|
||||
constructor(launcher: ManagedActivityResultLauncher<String, List<Uri>>): this() {
|
||||
this.launcher = launcher
|
||||
}
|
||||
|
||||
actual suspend fun launch(input: String) {
|
||||
launcher.launch(input)
|
||||
}
|
||||
}
|
||||
|
||||
actual fun URI.inputStream(): InputStream? = androidAppContext.contentResolver.openInputStream(toUri())
|
||||
actual fun URI.outputStream(): OutputStream = androidAppContext.contentResolver.openOutputStream(toUri())!!
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import chat.simplex.common.views.newchat.ActionButton
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
actual fun ChooseAttachmentButtons(attachmentOption: MutableState<AttachmentOption?>, hide: () -> Unit) {
|
||||
ActionButton(Modifier.fillMaxWidth(0.25f), null, stringResource(MR.strings.use_camera_button), icon = painterResource(MR.images.ic_camera_enhance)) {
|
||||
attachmentOption.value = AttachmentOption.CameraPhoto
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(0.33f), null, stringResource(MR.strings.gallery_image_button), icon = painterResource(MR.images.ic_add_photo)) {
|
||||
attachmentOption.value = AttachmentOption.GalleryImage
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(0.50f), null, stringResource(MR.strings.gallery_video_button), icon = painterResource(MR.images.ic_smart_display)) {
|
||||
attachmentOption.value = AttachmentOption.GalleryVideo
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(1f), null, stringResource(MR.strings.choose_file), icon = painterResource(MR.images.ic_note_add)) {
|
||||
attachmentOption.value = AttachmentOption.File
|
||||
hide()
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,15 @@ fun getLoadedFilePath(file: CIFile?): String? {
|
||||
@Composable
|
||||
expect fun rememberFileChooserLauncher(getContent: Boolean, onResult: (URI?) -> Unit): FileChooserLauncher
|
||||
|
||||
expect fun rememberFileChooserMultipleLauncher(onResult: (List<URI>) -> Unit): FileChooserMultipleLauncher
|
||||
|
||||
expect class FileChooserLauncher() {
|
||||
suspend fun launch(input: String)
|
||||
}
|
||||
|
||||
expect class FileChooserMultipleLauncher() {
|
||||
suspend fun launch(input: String)
|
||||
}
|
||||
|
||||
expect fun URI.inputStream(): InputStream?
|
||||
expect fun URI.outputStream(): OutputStream
|
||||
|
||||
@@ -175,12 +175,12 @@ fun ComposeView(
|
||||
val content = ArrayList<UploadContent>()
|
||||
val imagesPreview = ArrayList<String>()
|
||||
uris.forEach { uri ->
|
||||
var bitmap: ImageBitmap? = null
|
||||
var bitmap: ImageBitmap?
|
||||
when {
|
||||
isImage(uri) -> {
|
||||
// Image
|
||||
val drawable = getDrawableFromUri(uri)
|
||||
bitmap = if (drawable != null) getBitmapFromUri(uri) else null
|
||||
bitmap = getBitmapFromUri(uri)
|
||||
if (isAnimImage(uri, drawable)) {
|
||||
// It's a gif or webp
|
||||
val fileSize = getFileSize(uri)
|
||||
|
||||
@@ -34,7 +34,7 @@ fun ChatArchiveView(m: ChatModel, title: String, archiveName: String, archiveTim
|
||||
ChatArchiveLayout(
|
||||
title,
|
||||
archiveTime,
|
||||
saveArchive = { withApi { saveArchiveLauncher.launch(archivePath.substringAfterLast("/")) }},
|
||||
saveArchive = { withApi { saveArchiveLauncher.launch(archivePath.substringAfterLast(File.separator)) }},
|
||||
deleteArchiveAlert = { deleteArchiveAlert(m, archivePath) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -457,7 +457,7 @@ private fun exportArchive(
|
||||
try {
|
||||
val archiveFile = exportChatArchive(m, chatArchiveName, chatArchiveTime, chatArchiveFile)
|
||||
chatArchiveFile.value = archiveFile
|
||||
saveArchiveLauncher.launch(archiveFile.substringAfterLast("/"))
|
||||
saveArchiveLauncher.launch(archiveFile.substringAfterLast(File.separator))
|
||||
progressIndicator.value = false
|
||||
} catch (e: Error) {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_exporting_chat_database), e.toString())
|
||||
|
||||
@@ -5,11 +5,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.views.newchat.ActionButton
|
||||
import chat.simplex.res.MR
|
||||
|
||||
sealed class AttachmentOption {
|
||||
object CameraPhoto: AttachmentOption()
|
||||
@@ -19,10 +15,7 @@ sealed class AttachmentOption {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChooseAttachmentView(
|
||||
attachmentOption: MutableState<AttachmentOption?>,
|
||||
hide: () -> Unit
|
||||
) {
|
||||
fun ChooseAttachmentView(attachmentOption: MutableState<AttachmentOption?>, hide: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -37,22 +30,10 @@ fun ChooseAttachmentView(
|
||||
.padding(horizontal = 8.dp, vertical = 30.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
ActionButton(Modifier.fillMaxWidth(0.25f), null, stringResource(MR.strings.use_camera_button), icon = painterResource(MR.images.ic_camera_enhance)) {
|
||||
attachmentOption.value = AttachmentOption.CameraPhoto
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(0.33f), null, stringResource(MR.strings.gallery_image_button), icon = painterResource(MR.images.ic_add_photo)) {
|
||||
attachmentOption.value = AttachmentOption.GalleryImage
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(0.50f), null, stringResource(MR.strings.gallery_video_button), icon = painterResource(MR.images.ic_smart_display)) {
|
||||
attachmentOption.value = AttachmentOption.GalleryVideo
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(1f), null, stringResource(MR.strings.choose_file), icon = painterResource(MR.images.ic_note_add)) {
|
||||
attachmentOption.value = AttachmentOption.File
|
||||
hide()
|
||||
}
|
||||
ChooseAttachmentButtons(attachmentOption, hide)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
expect fun ChooseAttachmentButtons(attachmentOption: MutableState<AttachmentOption?>, hide: () -> Unit)
|
||||
|
||||
@@ -394,6 +394,7 @@
|
||||
<string name="use_camera_button">Camera</string>
|
||||
<string name="from_gallery_button">From Gallery</string>
|
||||
<string name="choose_file">File</string>
|
||||
<string name="choose_file_title">Choose a file</string>
|
||||
<string name="gallery_image_button">Image</string>
|
||||
<string name="gallery_video_button">Video</string>
|
||||
|
||||
|
||||
@@ -38,8 +38,20 @@ fun showApp() = application {
|
||||
FileDialogChooser(
|
||||
title = "SimpleX",
|
||||
isLoad = true,
|
||||
params = simplexWindowState.openDialog.params,
|
||||
onResult = {
|
||||
simplexWindowState.openDialog.onResult(it)
|
||||
simplexWindowState.openDialog.onResult(it.firstOrNull())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (simplexWindowState.openMultipleDialog.isAwaiting) {
|
||||
FileDialogChooser(
|
||||
title = "SimpleX",
|
||||
isLoad = true,
|
||||
params = simplexWindowState.openMultipleDialog.params,
|
||||
onResult = {
|
||||
simplexWindowState.openMultipleDialog.onResult(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -48,7 +60,8 @@ fun showApp() = application {
|
||||
FileDialogChooser(
|
||||
title = "SimpleX",
|
||||
isLoad = false,
|
||||
onResult = { simplexWindowState.saveDialog.onResult(it) }
|
||||
params = simplexWindowState.saveDialog.params,
|
||||
onResult = { simplexWindowState.saveDialog.onResult(it.firstOrNull()) }
|
||||
)
|
||||
}
|
||||
val toasts = remember { simplexWindowState.toasts }
|
||||
@@ -97,16 +110,25 @@ fun showApp() = application {
|
||||
class SimplexWindowState {
|
||||
val backstack = mutableStateListOf<() -> Unit>()
|
||||
val openDialog = DialogState<File?>()
|
||||
val openMultipleDialog = DialogState<List<File>>()
|
||||
val saveDialog = DialogState<File?>()
|
||||
val toasts = mutableStateListOf<Pair<String, Long>>()
|
||||
}
|
||||
|
||||
data class DialogParams(
|
||||
val allowMultiple: Boolean = false,
|
||||
val fileFilter: ((File?) -> Boolean)? = null,
|
||||
val fileFilterDescription: String = "",
|
||||
)
|
||||
|
||||
class DialogState<T> {
|
||||
private var onResult: CompletableDeferred<T>? by mutableStateOf(null)
|
||||
var params = DialogParams()
|
||||
val isAwaiting get() = onResult != null
|
||||
|
||||
suspend fun awaitResult(): T {
|
||||
suspend fun awaitResult(params: DialogParams = DialogParams()): T {
|
||||
onResult = CompletableDeferred()
|
||||
this.params = params
|
||||
val result = onResult!!.await()
|
||||
onResult = null
|
||||
return result
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.DesktopApp
|
||||
import chat.simplex.common.simplexWindowState
|
||||
import chat.simplex.common.*
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.res.MR
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
|
||||
@@ -31,6 +32,10 @@ actual val databaseExportDir: File = tmpDir
|
||||
actual fun rememberFileChooserLauncher(getContent: Boolean, onResult: (URI?) -> Unit): FileChooserLauncher =
|
||||
remember { FileChooserLauncher(getContent, onResult) }
|
||||
|
||||
@Composable
|
||||
actual fun rememberFileChooserMultipleLauncher(onResult: (List<URI>) -> Unit): FileChooserMultipleLauncher =
|
||||
remember { FileChooserMultipleLauncher(onResult) }
|
||||
|
||||
actual class FileChooserLauncher actual constructor() {
|
||||
var getContent: Boolean = false
|
||||
lateinit var onResult: (URI?) -> Unit
|
||||
@@ -41,10 +46,50 @@ actual class FileChooserLauncher actual constructor() {
|
||||
}
|
||||
|
||||
actual suspend fun launch(input: String) {
|
||||
val res = if (getContent) simplexWindowState.openDialog.awaitResult() else simplexWindowState.saveDialog.awaitResult()
|
||||
onResult(if (!getContent && input.isNotEmpty() && res != null) File(res, input).toURI() else res?.toURI())
|
||||
val res = if (getContent) {
|
||||
val params = DialogParams(
|
||||
allowMultiple = false,
|
||||
fileFilter = fileFilter(input),
|
||||
fileFilterDescription = fileFilterDescription(input),
|
||||
)
|
||||
simplexWindowState.openDialog.awaitResult(params)
|
||||
} else {
|
||||
simplexWindowState.saveDialog.awaitResult()
|
||||
}
|
||||
onResult(res?.toURI())
|
||||
}
|
||||
}
|
||||
|
||||
actual class FileChooserMultipleLauncher actual constructor() {
|
||||
lateinit var onResult: (List<URI>) -> Unit
|
||||
|
||||
constructor(onResult: (List<URI>) -> Unit): this() {
|
||||
this.onResult = onResult
|
||||
}
|
||||
|
||||
actual suspend fun launch(input: String) {
|
||||
val params = DialogParams(
|
||||
allowMultiple = true,
|
||||
fileFilter = fileFilter(input),
|
||||
fileFilterDescription = fileFilterDescription(input),
|
||||
)
|
||||
onResult(simplexWindowState.openMultipleDialog.awaitResult(params).map { it.toURI() })
|
||||
}
|
||||
}
|
||||
|
||||
private fun fileFilter(input: String): (File?) -> Boolean = when(input) {
|
||||
"image/*" -> { file -> if (file?.isDirectory == true) true else if (file != null) isImage(file.toURI()) else false }
|
||||
"video/*" -> { file -> if (file?.isDirectory == true) true else if (file != null) isVideo(file.toURI()) else false }
|
||||
"*/*" -> { _ -> true }
|
||||
else -> { _ -> true }
|
||||
}
|
||||
|
||||
private fun fileFilterDescription(input: String): String = when(input) {
|
||||
"image/*" -> generalGetString(MR.strings.gallery_image_button)
|
||||
"video/*" -> generalGetString(MR.strings.gallery_video_button)
|
||||
"*/*" -> generalGetString(MR.strings.choose_file)
|
||||
else -> ""
|
||||
}
|
||||
|
||||
actual fun URI.inputStream(): InputStream? = File(URI("file:" + toString().removePrefix("file:"))).inputStream()
|
||||
actual fun URI.outputStream(): OutputStream = File(URI("file:" + toString().removePrefix("file:"))).outputStream()
|
||||
|
||||
@@ -41,7 +41,20 @@ actual fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String {
|
||||
}
|
||||
return str
|
||||
}
|
||||
actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream = TODO()
|
||||
actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream {
|
||||
var img = image
|
||||
var stream = compressImageData(img, usePng)
|
||||
while (stream.size() > maxDataSize) {
|
||||
val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble())
|
||||
val clippedRatio = kotlin.math.min(ratio, 2.0)
|
||||
val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
val height = img.height * width / img.width
|
||||
img = img.scale(width, height)
|
||||
stream = compressImageData(img, usePng)
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
actual fun cropToSquare(image: ImageBitmap): ImageBitmap {
|
||||
var xOffset = 0
|
||||
var yOffset = 0
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import java.net.URI
|
||||
|
||||
fun isVideo(uri: URI): Boolean {
|
||||
val path = uri.path.lowercase()
|
||||
return path.endsWith(".mov") ||
|
||||
path.endsWith(".avi") ||
|
||||
path.endsWith(".mp4") ||
|
||||
path.endsWith(".mpg") ||
|
||||
path.endsWith(".mpeg") ||
|
||||
path.endsWith(".mkv")
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package chat.simplex.common.views.chat
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.views.helpers.AttachmentOption
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
@@ -11,12 +12,27 @@ actual fun AttachmentSelection(
|
||||
processPickedFile: (URI?, String?) -> Unit,
|
||||
processPickedMedia: (List<URI>, String?) -> Unit
|
||||
) {
|
||||
val imageLauncher = rememberFileChooserMultipleLauncher {
|
||||
processPickedMedia(it, null)
|
||||
}
|
||||
val videoLauncher = rememberFileChooserMultipleLauncher {
|
||||
processPickedMedia(it, null)
|
||||
}
|
||||
val filesLauncher = rememberFileChooserLauncher(true) {
|
||||
if (it != null) processPickedFile(it, null)
|
||||
}
|
||||
LaunchedEffect(attachmentOption.value) {
|
||||
when (attachmentOption.value) {
|
||||
AttachmentOption.CameraPhoto -> {}
|
||||
AttachmentOption.GalleryImage -> {}
|
||||
AttachmentOption.GalleryVideo -> {}
|
||||
AttachmentOption.File -> {}
|
||||
AttachmentOption.GalleryImage -> {
|
||||
imageLauncher.launch("image/*")
|
||||
}
|
||||
AttachmentOption.GalleryVideo -> {
|
||||
videoLauncher.launch("video/*")
|
||||
}
|
||||
AttachmentOption.File -> {
|
||||
filesLauncher.launch("*/*")
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
attachmentOption.value = null
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import chat.simplex.common.views.newchat.ActionButton
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
actual fun ChooseAttachmentButtons(attachmentOption: MutableState<AttachmentOption?>, hide: () -> Unit) {
|
||||
ActionButton(Modifier.fillMaxWidth(0.5f), null, stringResource(MR.strings.gallery_image_button), icon = painterResource(MR.images.ic_add_photo)) {
|
||||
attachmentOption.value = AttachmentOption.GalleryImage
|
||||
hide()
|
||||
}
|
||||
ActionButton(Modifier.fillMaxWidth(1f), null, stringResource(MR.strings.choose_file), icon = painterResource(MR.images.ic_note_add)) {
|
||||
attachmentOption.value = AttachmentOption.File
|
||||
hide()
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package chat.simplex.common.views.helpers
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.input.key.*
|
||||
import androidx.compose.ui.window.*
|
||||
import chat.simplex.common.DialogParams
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.awt.FileDialog
|
||||
@@ -35,13 +37,13 @@ actual fun DefaultDialog(
|
||||
fun FrameWindowScope.FileDialogChooser(
|
||||
title: String,
|
||||
isLoad: Boolean,
|
||||
extensions: List<FileFilter> = emptyList(),
|
||||
onResult: (result: File?) -> Unit
|
||||
params: DialogParams,
|
||||
onResult: (result: List<File>) -> Unit
|
||||
) {
|
||||
if (isLinux()) {
|
||||
FileDialogChooserMultiple(title, isLoad, extensions) { onResult(it.firstOrNull()) }
|
||||
FileDialogChooserMultiple(title, isLoad, params.allowMultiple, params.fileFilter, params.fileFilterDescription, onResult)
|
||||
} else {
|
||||
FileDialogAwt(title, isLoad, onResult)
|
||||
FileDialogAwt(title, isLoad, params.allowMultiple, params.fileFilter, onResult)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +51,9 @@ fun FrameWindowScope.FileDialogChooser(
|
||||
fun FrameWindowScope.FileDialogChooserMultiple(
|
||||
title: String,
|
||||
isLoad: Boolean,
|
||||
extensions: List<FileFilter> = emptyList(),
|
||||
allowMultiple: Boolean,
|
||||
fileFilter: ((File?) -> Boolean)? = null,
|
||||
fileFilterDescription: String? = null,
|
||||
onResult: (result: List<File>) -> Unit
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -57,9 +61,15 @@ fun FrameWindowScope.FileDialogChooserMultiple(
|
||||
val job = scope.launch(Dispatchers.Main) {
|
||||
val fileChooser = JFileChooser()
|
||||
fileChooser.dialogTitle = title
|
||||
fileChooser.isMultiSelectionEnabled = isLoad
|
||||
fileChooser.isAcceptAllFileFilterUsed = extensions.isEmpty()
|
||||
extensions.forEach { fileChooser.addChoosableFileFilter(it) }
|
||||
fileChooser.isMultiSelectionEnabled = allowMultiple && isLoad
|
||||
fileChooser.isAcceptAllFileFilterUsed = fileFilter == null
|
||||
if (fileFilter != null && fileFilterDescription != null) {
|
||||
fileChooser.addChoosableFileFilter(object: FileFilter() {
|
||||
override fun accept(file: File?): Boolean = fileFilter(file)
|
||||
|
||||
override fun getDescription(): String = fileFilterDescription
|
||||
})
|
||||
}
|
||||
val returned = if (isLoad) {
|
||||
fileChooser.showOpenDialog(window)
|
||||
} else {
|
||||
@@ -69,7 +79,11 @@ fun FrameWindowScope.FileDialogChooserMultiple(
|
||||
val result = when (returned) {
|
||||
JFileChooser.APPROVE_OPTION -> {
|
||||
if (isLoad) {
|
||||
fileChooser.selectedFiles.filter { it.canRead() }
|
||||
when {
|
||||
allowMultiple -> fileChooser.selectedFiles.filter { it.canRead() }
|
||||
fileChooser.selectedFile != null && fileChooser.selectedFile.canRead() -> listOf(fileChooser.selectedFile)
|
||||
else -> emptyList()
|
||||
}
|
||||
} else {
|
||||
if (!fileChooser.fileFilter.accept(fileChooser.selectedFile)) {
|
||||
val ext = (fileChooser.fileFilter as FileNameExtensionFilter).extensions[0]
|
||||
@@ -78,7 +92,7 @@ fun FrameWindowScope.FileDialogChooserMultiple(
|
||||
listOf(fileChooser.selectedFile)
|
||||
}
|
||||
}
|
||||
else -> listOf();
|
||||
else -> emptyList()
|
||||
}
|
||||
onResult(result)
|
||||
}
|
||||
@@ -95,22 +109,30 @@ fun FrameWindowScope.FileDialogChooserMultiple(
|
||||
private fun FrameWindowScope.FileDialogAwt(
|
||||
title: String,
|
||||
isLoad: Boolean,
|
||||
onResult: (result: File?) -> Unit
|
||||
allowMultiple: Boolean,
|
||||
fileFilter: ((File?) -> Boolean)? = null,
|
||||
onResult: (result: List<File>) -> Unit
|
||||
) = AwtWindow(
|
||||
create = {
|
||||
object: FileDialog(window, "Choose a file", if (isLoad) LOAD else SAVE) {
|
||||
object: FileDialog(window, generalGetString(MR.strings.choose_file_title), if (isLoad) LOAD else SAVE) {
|
||||
override fun setVisible(value: Boolean) {
|
||||
super.setVisible(value)
|
||||
if (value) {
|
||||
if (file != null) {
|
||||
onResult(File(directory).resolve(file))
|
||||
if (files != null) {
|
||||
onResult(files.toList())
|
||||
} else {
|
||||
onResult(null)
|
||||
onResult(emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
this.title = title
|
||||
this.isMultipleMode = allowMultiple && isLoad
|
||||
if (fileFilter != null) {
|
||||
this.setFilenameFilter { dir, file ->
|
||||
fileFilter(File(dir.absolutePath + File.separator + file))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
dispose = FileDialog::dispose
|
||||
|
||||
@@ -44,13 +44,8 @@ actual fun GetImageBottomSheet(
|
||||
}
|
||||
}
|
||||
val pickImageLauncher = rememberFileChooserLauncher(true, processPickedImage)
|
||||
// LALAL
|
||||
/*ActionButton(null, stringResource(MR.strings.use_camera_button), icon = painterResource(MR.images.ic_photo_camera)) {
|
||||
hideBottomSheet()
|
||||
}*/
|
||||
ActionButton(null, stringResource(MR.strings.from_gallery_button), icon = painterResource(MR.images.ic_image)) {
|
||||
// LALAL support providing file extensions
|
||||
withApi { pickImageLauncher.launch("") }
|
||||
withApi { pickImageLauncher.launch("image/*") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user