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
package helium314.keyboard.settings.dialogs
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
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.SettingsActivity
import helium314.keyboard.settings.SettingsDestination
import helium314.keyboard.settings.filePicker
import helium314.keyboard.settings.keyboardNeedsReload
import helium314.keyboard.settings.screens.SaveThoseColors
import kotlinx.serialization.SerializationException
@ -119,9 +117,7 @@ fun ColorThemePickerDialog(
},
)
var errorDialog by remember { mutableStateOf(false) }
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
val loadFilePicker = filePicker { uri ->
ctx.getActivity()?.contentResolver?.openInputStream(uri)?.use {
errorDialog = !loadColorString(it.reader().readText(), prefs)
if (!errorDialog)

View file

@ -1,11 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
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.layout.Arrangement
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.SettingsActivity
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
// todo:
// call SubtypeSettings.onRenameLayout on rename!
@Composable
fun LayoutPickerDialog(
onDismissRequest: () -> Unit,
@ -67,7 +66,6 @@ fun LayoutPickerDialog(
val currentLayout = Settings.readDefaultLayoutName(layoutType, prefs)
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 layouts = internalLayouts + customLayouts + ""
@ -78,35 +76,16 @@ fun LayoutPickerDialog(
}
var errorDialog by rememberSaveable { mutableStateOf(false) }
var newLayoutDialog: Pair<String, String?>? by rememberSaveable { mutableStateOf(null) }
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
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)
val picker = layoutFilePicker { content, name ->
newLayoutDialog = (name ?: layoutType.default) to content
}
}
ThreeButtonAlertDialog(
onDismissRequest = onDismissRequest,
cancelButtonText = stringResource(R.string.dialog_close),
onConfirmed = { },
confirmButtonText = null,
neutralButtonText = stringResource(R.string.button_load_custom),
onNeutral = {
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) },
onNeutral = { picker.launch(layoutIntent) },
title = { Text(setting.title) },
text = {
CompositionLocalProvider(

View file

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

View file

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.preferences
import android.app.Activity
import android.content.Intent
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -23,6 +20,7 @@ import helium314.keyboard.latin.utils.JniUtils
import helium314.keyboard.latin.utils.protectedPrefs
import helium314.keyboard.settings.Setting
import helium314.keyboard.settings.dialogs.ConfirmationDialog
import helium314.keyboard.settings.filePicker
import java.io.File
import java.io.FileInputStream
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
}
var tempFilePath: String? by rememberSaveable { mutableStateOf(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
val launcher = filePicker { uri ->
val tmpfile = File(ctx.filesDir.absolutePath + File.separator + "tmplib")
try {
val otherTemporaryFile = File(ctx.filesDir.absolutePath + File.separator + "tmpfile")