diff --git a/app/src/main/java/helium314/keyboard/settings/FilePicker.kt b/app/src/main/java/helium314/keyboard/settings/FilePicker.kt new file mode 100644 index 000000000..8330544d4 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/FilePicker.kt @@ -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 { + 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 +} + diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt index 5a97401b7..67d5ebcf8 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt @@ -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) diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt index 695369439..191932c62 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt @@ -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,22 +76,8 @@ fun LayoutPickerDialog( } var errorDialog by rememberSaveable { mutableStateOf(false) } var newLayoutDialog: Pair? 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) - newLayoutDialog = (name ?: layoutType.default) to content - } + val picker = layoutFilePicker { content, name -> + newLayoutDialog = (name ?: layoutType.default) to content } ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, @@ -101,12 +85,7 @@ fun LayoutPickerDialog( 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( diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/BackupRestorePreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/BackupRestorePreference.kt index 1ae97df25..762f06290 100644 --- a/app/src/main/java/helium314/keyboard/settings/preferences/BackupRestorePreference.kt +++ b/app/src/main/java/helium314/keyboard/settings/preferences/BackupRestorePreference.kt @@ -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() 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 { diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/LoadGestureLibPreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/LoadGestureLibPreference.kt index d52ecfb4f..01566aac8 100644 --- a/app/src/main/java/helium314/keyboard/settings/preferences/LoadGestureLibPreference.kt +++ b/app/src/main/java/helium314/keyboard/settings/preferences/LoadGestureLibPreference.kt @@ -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")