move file pickers to separate composable

This commit is contained in:
Helium314 2025-02-21 05:04:57 +01:00
parent 4efc33dba4
commit 05fc53c96f
5 changed files with 76 additions and 52 deletions

View file

@ -0,0 +1,61 @@
package helium314.keyboard.settings
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.OpenableColumns
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import helium314.keyboard.latin.R
import helium314.keyboard.latin.utils.LayoutUtilsCustom
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.settings.dialogs.InfoDialog
val layoutIntent = Intent(Intent.ACTION_OPEN_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/*", "application/octet-stream", "application/json"))
.setType("*/*")
@Composable
fun filePicker(onUri: (Uri) -> Unit) =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
onUri(uri)
}
@Composable
fun layoutFilePicker(
onSuccess: (content: String, name: String?) -> Unit
): ManagedActivityResultLauncher<Intent, ActivityResult> {
val ctx = LocalContext.current
var errorDialog by remember { mutableStateOf(false) }
val loadFilePicker = filePicker { uri ->
val cr = ctx.getActivity()?.contentResolver ?: return@filePicker
val name = cr.query(uri, null, null, null, null)?.use { c ->
if (!c.moveToFirst()) return@use null
val index = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (index < 0) null
else c.getString(index)
}
cr.openInputStream(uri)?.use {
val content = it.reader().readText()
errorDialog = !LayoutUtilsCustom.checkLayout(content, ctx)
if (!errorDialog)
onSuccess(content, name)
}
}
if (errorDialog)
InfoDialog(stringResource(R.string.file_read_error)) { errorDialog = false }
return loadFilePicker
}

View file

@ -1,13 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.dialogs package helium314.keyboard.settings.dialogs
import android.app.Activity
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -52,6 +49,7 @@ import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.Setting import helium314.keyboard.settings.Setting
import helium314.keyboard.settings.SettingsActivity import helium314.keyboard.settings.SettingsActivity
import helium314.keyboard.settings.SettingsDestination import helium314.keyboard.settings.SettingsDestination
import helium314.keyboard.settings.filePicker
import helium314.keyboard.settings.keyboardNeedsReload import helium314.keyboard.settings.keyboardNeedsReload
import helium314.keyboard.settings.screens.SaveThoseColors import helium314.keyboard.settings.screens.SaveThoseColors
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
@ -119,9 +117,7 @@ fun ColorThemePickerDialog(
}, },
) )
var errorDialog by remember { mutableStateOf(false) } var errorDialog by remember { mutableStateOf(false) }
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { val loadFilePicker = filePicker { uri ->
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
ctx.getActivity()?.contentResolver?.openInputStream(uri)?.use { ctx.getActivity()?.contentResolver?.openInputStream(uri)?.use {
errorDialog = !loadColorString(it.reader().readText(), prefs) errorDialog = !loadColorString(it.reader().readText(), prefs)
if (!errorDialog) if (!errorDialog)

View file

@ -1,11 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.dialogs package helium314.keyboard.settings.dialogs
import android.app.Activity
import android.content.Intent
import android.provider.OpenableColumns
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -51,8 +46,12 @@ import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.Setting import helium314.keyboard.settings.Setting
import helium314.keyboard.settings.SettingsActivity import helium314.keyboard.settings.SettingsActivity
import helium314.keyboard.settings.keyboardNeedsReload import helium314.keyboard.settings.keyboardNeedsReload
import helium314.keyboard.settings.layoutFilePicker
import helium314.keyboard.settings.layoutIntent
// modified copy of ColorPickerDialog, later check whether stuff can be re-used // modified copy of ColorPickerDialog, later check whether stuff can be re-used
// todo:
// call SubtypeSettings.onRenameLayout on rename!
@Composable @Composable
fun LayoutPickerDialog( fun LayoutPickerDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
@ -67,7 +66,6 @@ fun LayoutPickerDialog(
val currentLayout = Settings.readDefaultLayoutName(layoutType, prefs) val currentLayout = Settings.readDefaultLayoutName(layoutType, prefs)
val internalLayouts = LayoutUtils.getAvailableLayouts(layoutType, ctx) val internalLayouts = LayoutUtils.getAvailableLayouts(layoutType, ctx)
// todo: getCustomLayoutFiles does not work nicely for main layout, but currently this dialog is not used for them
val customLayouts = LayoutUtilsCustom.getLayoutFiles(layoutType, ctx).map { it.name }.sorted() val customLayouts = LayoutUtilsCustom.getLayoutFiles(layoutType, ctx).map { it.name }.sorted()
val layouts = internalLayouts + customLayouts + "" val layouts = internalLayouts + customLayouts + ""
@ -78,22 +76,8 @@ fun LayoutPickerDialog(
} }
var errorDialog by rememberSaveable { mutableStateOf(false) } var errorDialog by rememberSaveable { mutableStateOf(false) }
var newLayoutDialog: Pair<String, String?>? by rememberSaveable { mutableStateOf(null) } var newLayoutDialog: Pair<String, String?>? by rememberSaveable { mutableStateOf(null) }
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { val picker = layoutFilePicker { content, name ->
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult newLayoutDialog = (name ?: layoutType.default) to content
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
val cr = ctx.getActivity()?.contentResolver ?: return@rememberLauncherForActivityResult
val name = cr.query(uri, null, null, null, null)?.use { c ->
if (!c.moveToFirst()) return@use null
val index = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (index < 0) null
else c.getString(index)
}
cr.openInputStream(uri)?.use {
val content = it.reader().readText()
errorDialog = !LayoutUtilsCustom.checkLayout(content, ctx)
if (!errorDialog)
newLayoutDialog = (name ?: layoutType.default) to content
}
} }
ThreeButtonAlertDialog( ThreeButtonAlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@ -101,12 +85,7 @@ fun LayoutPickerDialog(
onConfirmed = { }, onConfirmed = { },
confirmButtonText = null, confirmButtonText = null,
neutralButtonText = stringResource(R.string.button_load_custom), neutralButtonText = stringResource(R.string.button_load_custom),
onNeutral = { onNeutral = { picker.launch(layoutIntent) },
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/*", "application/octet-stream", "application/json"))
.setType("*/*")
loadFilePicker.launch(intent) },
title = { Text(setting.title) }, title = { Text(setting.title) },
text = { text = {
CompositionLocalProvider( CompositionLocalProvider(

View file

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.preferences package helium314.keyboard.settings.preferences
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -27,7 +24,6 @@ import helium314.keyboard.latin.R
import helium314.keyboard.latin.checkVersionUpgrade import helium314.keyboard.latin.checkVersionUpgrade
import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.common.FileUtils
import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.LocaleUtils.constructLocale
import helium314.keyboard.latin.settings.Defaults
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DeviceProtectedUtils
@ -35,7 +31,6 @@ import helium314.keyboard.latin.utils.ExecutorUtils
import helium314.keyboard.latin.utils.LayoutUtilsCustom import helium314.keyboard.latin.utils.LayoutUtilsCustom
import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.SubtypeSettings import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.protectedPrefs import helium314.keyboard.latin.utils.protectedPrefs
@ -43,6 +38,7 @@ import helium314.keyboard.settings.Setting
import helium314.keyboard.settings.SettingsActivity import helium314.keyboard.settings.SettingsActivity
import helium314.keyboard.settings.dialogs.ConfirmationDialog import helium314.keyboard.settings.dialogs.ConfirmationDialog
import helium314.keyboard.settings.dialogs.InfoDialog import helium314.keyboard.settings.dialogs.InfoDialog
import helium314.keyboard.settings.filePicker
import helium314.keyboard.settings.keyboardNeedsReload import helium314.keyboard.settings.keyboardNeedsReload
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
@ -67,12 +63,10 @@ fun BackupRestorePreference(setting: Setting) {
"custom_background_image.*".toRegex(), "custom_background_image.*".toRegex(),
"custom_font".toRegex(), "custom_font".toRegex(),
) } ) }
val backupLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val backupLauncher = filePicker { uri ->
if (result.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
// zip all files matching the backup patterns // zip all files matching the backup patterns
// essentially this is the typed words information, and user-added dictionaries // essentially this is the typed words information, and user-added dictionaries
val filesDir = ctx.filesDir ?: return@rememberLauncherForActivityResult val filesDir = ctx.filesDir ?: return@filePicker
val filesPath = filesDir.path + File.separator val filesPath = filesDir.path + File.separator
val files = mutableListOf<File>() val files = mutableListOf<File>()
filesDir.walk().forEach { file -> filesDir.walk().forEach { file ->
@ -125,9 +119,7 @@ fun BackupRestorePreference(setting: Setting) {
} }
wait.await() wait.await()
} }
val restoreLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val restoreLauncher = filePicker { uri ->
if (result.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
val wait = CountDownLatch(1) val wait = CountDownLatch(1)
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute { ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute {
try { try {

View file

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.preferences package helium314.keyboard.settings.preferences
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -23,6 +20,7 @@ import helium314.keyboard.latin.utils.JniUtils
import helium314.keyboard.latin.utils.protectedPrefs import helium314.keyboard.latin.utils.protectedPrefs
import helium314.keyboard.settings.Setting import helium314.keyboard.settings.Setting
import helium314.keyboard.settings.dialogs.ConfirmationDialog import helium314.keyboard.settings.dialogs.ConfirmationDialog
import helium314.keyboard.settings.filePicker
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
@ -43,9 +41,7 @@ fun LoadGestureLibPreference(setting: Setting) {
Runtime.getRuntime().exit(0) // exit will restart the app, so library will be loaded Runtime.getRuntime().exit(0) // exit will restart the app, so library will be loaded
} }
var tempFilePath: String? by rememberSaveable { mutableStateOf(null) } var tempFilePath: String? by rememberSaveable { mutableStateOf(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val launcher = filePicker { uri ->
if (result.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
val tmpfile = File(ctx.filesDir.absolutePath + File.separator + "tmplib") val tmpfile = File(ctx.filesDir.absolutePath + File.separator + "tmplib")
try { try {
val otherTemporaryFile = File(ctx.filesDir.absolutePath + File.separator + "tmpfile") val otherTemporaryFile = File(ctx.filesDir.absolutePath + File.separator + "tmpfile")