diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LayoutParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LayoutParser.kt index 09071993..f2aee604 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LayoutParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LayoutParser.kt @@ -17,11 +17,10 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey import helium314.keyboard.latin.common.splitOnWhitespace -import helium314.keyboard.latin.settings.Defaults.default import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.LayoutType -import helium314.keyboard.latin.utils.LayoutType.Companion.folder +import helium314.keyboard.latin.utils.LayoutUtils import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.getCustomLayoutFiles import helium314.keyboard.latin.utils.prefs @@ -40,7 +39,7 @@ object LayoutParser { if (layoutType == LayoutType.FUNCTIONAL && !params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf()) // no functional keys val layoutName = if (layoutType == LayoutType.MAIN) params.mId.mSubtype.mainLayoutName - else params.mId.mSubtype.layouts[layoutType] ?: Settings.getLayoutName(layoutType, context.prefs()) + else params.mId.mSubtype.layouts[layoutType] ?: Settings.readDefaultLayoutName(layoutType, context.prefs()) return layoutCache.getOrPut(layoutType.name + layoutName) { createCacheLambda(layoutType, layoutName, context) }(params) @@ -105,11 +104,7 @@ object LayoutParser { if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) getCustomLayoutFiles(layoutType, context) .firstOrNull { it.name.startsWith(layoutName) }?.let { return it.readText() } - val layouts = context.assets.list(layoutType.folder)!! - layouts.firstOrNull { it.startsWith("$layoutName.") } - ?.let { return context.assets.open(layoutType.folder + it).reader().readText() } - val fallback = layouts.first { it.startsWith(layoutType.default) } // must exist! - return context.assets.open(layoutType.folder + fallback).reader().readText() + return LayoutUtils.getContent(layoutType, layoutName, context) } // allow commenting lines by starting them with "//" diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt index e178d1dd..9a207944 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt @@ -41,7 +41,7 @@ object Defaults { LayoutType.PHONE -> "phone" LayoutType.PHONE_SYMBOLS -> "phone_symbols" LayoutType.EMOJI_BOTTOM -> "emoji_bottom_row" - LayoutType.CLIPBOARD_BOTTOM -> "clipboard_bottom_row" + LayoutType.CLIPBOARD_BOTTOM -> "clip_bottom_row" } const val PREF_THEME_STYLE = KeyboardTheme.STYLE_MATERIAL diff --git a/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt b/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt index e7caaffc..491bf299 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt @@ -209,7 +209,7 @@ class LanguageSettingsDialog( reloadSetting() } if (isCustom) { - confirmDialog(context, context.getString(R.string.delete_layout, getLayoutDisplayName(layoutSetName)), context.getString(R.string.delete)) { delete() } + confirmDialog(context, context.getString(R.string.delete_layout, getCustomLayoutDisplayName(layoutSetName)), context.getString(R.string.delete)) { delete() } } else { delete() } diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index e537864d..f4690b52 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -551,10 +551,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return new File(DeviceProtectedUtils.getFilesDir(context), "custom_font"); } - public static String getLayoutName(final LayoutType type, final SharedPreferences prefs) { + // "default" layout as in this is used if nothing else is specified in the subtype + public static String readDefaultLayoutName(final LayoutType type, final SharedPreferences prefs) { return prefs.getString(PREF_LAYOUT_PREFIX + type.name(), Defaults.INSTANCE.getDefault(type)); } + public static void writeDefaultLayoutName(final String name, final LayoutType type, final SharedPreferences prefs) { + prefs.edit().putString(PREF_LAYOUT_PREFIX + type.name(), name).apply(); + } + @Nullable public Typeface getCustomTypeface() { if (!sCustomTypefaceLoaded) { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index f029f152..2cc1bc6f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -25,7 +25,6 @@ import helium314.keyboard.latin.R; import helium314.keyboard.latin.RichInputMethodManager; import helium314.keyboard.latin.common.Colors; import helium314.keyboard.latin.permissions.PermissionsUtil; -import helium314.keyboard.latin.utils.CustomLayoutUtilsKt; import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.JniUtils; import helium314.keyboard.latin.utils.Log; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/AdditionalSubtypeUtils.java b/app/src/main/java/helium314/keyboard/latin/utils/AdditionalSubtypeUtils.java index 6a7cf700..fb996e3d 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/AdditionalSubtypeUtils.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/AdditionalSubtypeUtils.java @@ -79,6 +79,7 @@ public final class AdditionalSubtypeUtils { return createAdditionalSubtypeInternal(locale, keyboardLayoutSetName, asciiCapable, true); } + // todo: adjust so we can store more stuff in extra values private static String getPrefSubtype(final InputMethodSubtype subtype) { final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=MAIN:" + keyboardLayoutSetName; @@ -121,7 +122,7 @@ public final class AdditionalSubtypeUtils { final boolean asciiCapable = ScriptUtils.script(locale).equals(ScriptUtils.SCRIPT_LATIN); // Here we assume that all the additional subtypes are EmojiCapable final InputMethodSubtype subtype = createEmojiCapableAdditionalSubtype(locale, keyboardLayoutSetName, asciiCapable); - if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT && !keyboardLayoutSetName.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX)) { + if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT && !LayoutUtilsCustomKt.isCustomLayout(keyboardLayoutSetName)) { // Skip unknown keyboard layout subtype. This may happen when predefined keyboard // layout has been removed. return null; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/LayoutType.kt b/app/src/main/java/helium314/keyboard/latin/utils/LayoutType.kt index e639aadf..563cc7a7 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/LayoutType.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/LayoutType.kt @@ -1,5 +1,6 @@ package helium314.keyboard.latin.utils +import helium314.keyboard.latin.R import java.io.File import java.util.EnumMap @@ -20,5 +21,20 @@ enum class LayoutType { } val LayoutType.folder get() = "layouts${File.separator}${name.lowercase()}${File.separator}" + + val LayoutType.displayNameId get() = when (this) { + MAIN -> TODO() + SYMBOLS -> R.string.layout_symbols + MORE_SYMBOLS -> R.string.layout_symbols_shifted + FUNCTIONAL -> R.string.layout_functional_keys + NUMBER -> R.string.layout_number + NUMBER_ROW -> R.string.layout_number_row + NUMPAD -> R.string.layout_numpad + NUMPAD_LANDSCAPE -> R.string.layout_numpad_landscape + PHONE -> R.string.layout_phone + PHONE_SYMBOLS -> R.string.layout_phone_symbols + EMOJI_BOTTOM -> R.string.layout_emoji_bottom_row + CLIPBOARD_BOTTOM -> R.string.layout_clip_bottom_row + } } } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtils.kt new file mode 100644 index 00000000..517419f9 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtils.kt @@ -0,0 +1,29 @@ +package helium314.keyboard.latin.utils + +import android.content.Context +import helium314.keyboard.latin.settings.Defaults.default +import helium314.keyboard.latin.utils.LayoutType.Companion.folder +import helium314.keyboard.latin.utils.ScriptUtils.script +import java.util.Locale + +// for layouts provided by the app +object LayoutUtils { + fun getAvailableLayouts(layoutType: LayoutType, context: Context, locale: Locale? = null): Collection { + if (layoutType != LayoutType.MAIN) + return context.assets.list(layoutType.folder)?.map { it.substringBefore(".") }.orEmpty() + if (locale == null) + return getAllAvailableSubtypes().mapTo(HashSet()) { it.mainLayoutName()?.substringBefore("+") ?: "qwerty" } + if (locale.script() == ScriptUtils.SCRIPT_LATIN) + return getAllAvailableSubtypes().filter { it.isAsciiCapable && it.mainLayoutName()?.startsWith(CUSTOM_LAYOUT_PREFIX) == false } + .mapTo(HashSet()) { it.mainLayoutName()?.substringBefore("+") ?: "qwerty" } + return getSubtypesForLocale(locale).mapNotNullTo(HashSet()) { it.mainLayoutName() } + } + + fun getContent(layoutType: LayoutType, layoutName: String, context: Context): String { + val layouts = context.assets.list(layoutType.folder)!! + layouts.firstOrNull { it.startsWith("$layoutName.") } + ?.let { return context.assets.open(layoutType.folder + it).reader().readText() } + val fallback = layouts.first { it.startsWith(layoutType.default) } // must exist! + return context.assets.open(layoutType.folder + fallback).reader().readText() + } +} diff --git a/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtilsCustom.kt b/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtilsCustom.kt index 6e017d78..48a1a56a 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtilsCustom.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/LayoutUtilsCustom.kt @@ -5,7 +5,6 @@ import android.content.Context import android.net.Uri import android.provider.OpenableColumns import android.text.InputType -import android.view.inputmethod.InputMethodSubtype import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.core.widget.doAfterTextChanged @@ -26,6 +25,8 @@ import java.io.IOException import java.math.BigInteger import java.util.EnumMap +// todo: object like LayoutUtils + fun loadCustomLayout(uri: Uri?, languageTag: String, context: Context, onAdded: (String) -> Unit) { if (uri == null) return infoDialog(context, context.getString(R.string.layout_error, "layout file not found")) @@ -175,14 +176,23 @@ fun onCustomLayoutFileListChanged() { private fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") -// undo the name changes in loadCustomLayout when clicking ok -fun getLayoutDisplayName(layoutName: String) = +fun getCustomLayoutDisplayName(layoutName: String) = try { - decodeBase36(layoutName.substringAfter(CUSTOM_LAYOUT_PREFIX).substringAfter(".").substringBeforeLast(".")) + decodeBase36(layoutName.substringAfter(CUSTOM_LAYOUT_PREFIX).substringBeforeLast(".")) } catch (_: NumberFormatException) { layoutName } +fun getCustomLayoutName(displayName: String) = CUSTOM_LAYOUT_PREFIX + encodeBase36(displayName) + "." + +fun isCustomLayout(layoutName: String) = layoutName.startsWith(CUSTOM_LAYOUT_PREFIX) + +fun getCustomLayoutFile(layoutName: String, layoutType: LayoutType, context: Context): File { + val file = File(DeviceProtectedUtils.getFilesDir(context), layoutType.folder + layoutName) + file.parentFile?.mkdirs() + return file +} + fun removeCustomLayoutFile(layoutName: String, context: Context) { getCustomLayoutFile(layoutName, context).delete() } @@ -193,7 +203,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String? setText(startContent ?: file.readText()) } val builder = AlertDialog.Builder(context) - .setTitle(getLayoutDisplayName(layoutName)) + .setTitle(getCustomLayoutDisplayName(layoutName)) .setView(editText) .setPositiveButton(R.string.save) { _, _ -> val content = editText.text.toString() diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.java b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.java index 46dfcf3b..ce30920b 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.java @@ -266,8 +266,8 @@ public final class SubtypeLocaleUtils { @Nullable public static String getKeyboardLayoutSetDisplayName(@NonNull final String layoutName) { - if (layoutName.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX)) - return CustomLayoutUtilsKt.getLayoutDisplayName(layoutName); + if (LayoutUtilsCustomKt.isCustomLayout(layoutName)) + return LayoutUtilsCustomKt.getCustomLayoutDisplayName(layoutName); return sKeyboardLayoutToDisplayNameMap.get(layoutName); } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt index 5d56e78b..ab6b11cb 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt @@ -22,6 +22,8 @@ import helium314.keyboard.latin.utils.ScriptUtils.script import org.xmlpull.v1.XmlPullParser import java.util.* +// todo: move some parts, to subtypeUtils, and only keep actual settings? + /** @return enabled subtypes. If no subtypes are enabled, but a contextForFallback is provided, * subtypes for system locales will be returned, or en-US if none found. */ fun getEnabledSubtypes(prefs: SharedPreferences, fallback: Boolean = false): List { @@ -148,6 +150,8 @@ fun hasMatchingSubtypeForLocale(locale: Locale): Boolean { return !resourceSubtypesByLocale[locale].isNullOrEmpty() } +fun getSubtypesForLocale(locale: Locale): List = resourceSubtypesByLocale[locale].orEmpty() + fun getAvailableSubtypeLocales(): Collection { require(initialized) return resourceSubtypesByLocale.keys diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeUtils.kt index c95c261a..4fb0dd14 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeUtils.kt @@ -26,6 +26,6 @@ fun InputMethodSubtype.mainLayoutName(): String? { fun InputMethodSubtype.displayName(context: Context): CharSequence { val layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(this) if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) - return "${LocaleUtils.getLocaleDisplayNameInSystemLocale(locale(), context)} (${getLayoutDisplayName(layoutName)})" + return "${LocaleUtils.getLocaleDisplayNameInSystemLocale(locale(), context)} (${getCustomLayoutDisplayName(layoutName)})" return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(this) } diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt b/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt index d475a148..98ec39af 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt @@ -11,6 +11,7 @@ import helium314.keyboard.settings.screens.createAdvancedSettings import helium314.keyboard.settings.screens.createAppearanceSettings import helium314.keyboard.settings.screens.createCorrectionSettings import helium314.keyboard.settings.screens.createGestureTypingSettings +import helium314.keyboard.settings.screens.createLayoutSettings import helium314.keyboard.settings.screens.createPreferencesSettings import helium314.keyboard.settings.screens.createToolbarSettings @@ -63,7 +64,8 @@ class Setting( // intentionally not putting individual debug settings in here so user knows the context private fun createSettings(context: Context) = createAboutSettings(context) + createAppearanceSettings(context) + createCorrectionSettings(context) + createPreferencesSettings(context) + createToolbarSettings(context) + - createAdvancedSettings(context) + if (JniUtils.sHaveGestureLib) createGestureTypingSettings(context) else emptyList() + createLayoutSettings(context) + createAdvancedSettings(context) + + if (JniUtils.sHaveGestureLib) createGestureTypingSettings(context) else emptyList() object SettingsWithoutKey { const val EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary" diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt index 8d0381cc..60b3b0b7 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt @@ -16,6 +16,7 @@ import helium314.keyboard.settings.screens.AppearanceScreen import helium314.keyboard.settings.screens.ColorsScreen import helium314.keyboard.settings.screens.DebugScreen import helium314.keyboard.settings.screens.GestureTypingScreen +import helium314.keyboard.settings.screens.LayoutScreen import helium314.keyboard.settings.screens.MainSettingsScreen import helium314.keyboard.settings.screens.PreferencesScreen import helium314.keyboard.settings.screens.TextCorrectionScreen @@ -57,6 +58,7 @@ fun SettingsNavHost( onClickAdvanced = { navController.navigate(SettingsDestination.Advanced) }, onClickAppearance = { navController.navigate(SettingsDestination.Appearance) }, onClickLanguage = { navController.navigate(SettingsDestination.Languages) }, + onClickLayouts = { navController.navigate(SettingsDestination.Layouts) }, onClickBack = ::goBack, ) } @@ -92,6 +94,9 @@ fun SettingsNavHost( composable(SettingsDestination.Languages) { // LanguageScreen(onClickBack = ::goBack) } + composable(SettingsDestination.Layouts) { + LayoutScreen(onClickBack = ::goBack) + } composable(SettingsDestination.Colors) { ColorsScreen(isNight = false, onClickBack = ::goBack) } @@ -117,6 +122,7 @@ object SettingsDestination { const val ColorsNight = "colors_night" const val PersonalDictionary = "personal_dictionary" const val Languages = "languages" + const val Layouts = "layouts" val navTarget = MutableStateFlow(Settings) private val navScope = CoroutineScope(Dispatchers.Default) 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 2a4424ae..48452361 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ColorThemePickerDialog.kt @@ -181,7 +181,7 @@ private fun AddColorRow(onDismissRequest: () -> Unit, userColors: Collection Unit, item: String, isSelected: Boolean, isUser: Boolean, targetScreen: String, prefKey: String) { +private fun ColorItemRow(onDismissRequest: () -> Unit, item: String, isSelected: Boolean, isUser: Boolean, targetScreen: String, prefKey: String) { val ctx = LocalContext.current val prefs = ctx.prefs() Row( @@ -217,6 +217,7 @@ fun ColorItemRow(onDismissRequest: () -> Unit, item: String, isSelected: Boolean IconButton( onClick = { onDismissRequest() + // todo: maybe no need to set it as default when using the navigation specials prefs.edit().putString(prefKey, item).apply() SettingsDestination.navigateTo(targetScreen) keyboardNeedsReload = true @@ -272,7 +273,7 @@ private fun loadColorString(colorString: String, prefs: SharedPreferences): Bool @Preview @Composable -private fun PreviewListPickerDialog() { +private fun Preview() { ColorThemePickerDialog( onDismissRequest = {}, setting = Setting(LocalContext.current, "", R.string.settings) {}, diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt index 76cb21be..0d820b61 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt @@ -3,7 +3,9 @@ package helium314.keyboard.settings.dialogs import android.widget.Toast import androidx.compose.foundation.layout.imePadding +import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,50 +14,78 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.window.DialogProperties import helium314.keyboard.latin.R +import helium314.keyboard.latin.utils.LayoutType import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.checkLayout import helium314.keyboard.latin.utils.getCustomLayoutFile -import helium314.keyboard.latin.utils.getLayoutDisplayName +import helium314.keyboard.latin.utils.getCustomLayoutDisplayName +import helium314.keyboard.latin.utils.getCustomLayoutName +import helium314.keyboard.latin.utils.getStringResourceOrName +import helium314.keyboard.latin.utils.isCustomLayout import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged import helium314.keyboard.settings.keyboardNeedsReload import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +// todo: make it wider! +// maybe make it a completely separate dialog, not even using the 3-button thing? +// though we could provide with parameter, and maybe some sort of reduce-padding option @Composable fun LayoutEditDialog( onDismissRequest: () -> Unit, - layoutName: String, + layoutType: LayoutType, + initialLayoutName: String, startContent: String? = null, - displayName: String? = null + isNameValid: (String) -> Boolean ) { val ctx = LocalContext.current - val file = getCustomLayoutFile(layoutName, ctx) val scope = rememberCoroutineScope() var job: Job? = null - var showDeleteConfirmation by rememberSaveable { mutableStateOf(false) } + val startIsCustom = isCustomLayout(initialLayoutName) + var displayNameValue by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf(TextFieldValue( + if (startIsCustom) getCustomLayoutDisplayName(initialLayoutName) + else initialLayoutName.getStringResourceOrName("layout_", ctx) + )) + } + val nameValid = displayNameValue.text.isNotBlank() + && ( + (startIsCustom && getCustomLayoutName(displayNameValue.text) == initialLayoutName) + || isNameValid(getCustomLayoutName(displayNameValue.text)) + ) + TextInputDialog( - onDismissRequest = onDismissRequest, + onDismissRequest = { + job?.cancel() + onDismissRequest() + }, onConfirmed = { - file.parentFile?.mkdir() - file.writeText(it) + val newLayoutName = getCustomLayoutName(displayNameValue.text) + if (startIsCustom && initialLayoutName != newLayoutName) + getCustomLayoutFile(initialLayoutName, layoutType, ctx).delete() + getCustomLayoutFile(newLayoutName, layoutType, ctx).writeText(it) onCustomLayoutFileListChanged() keyboardNeedsReload = true }, confirmButtonText = stringResource(R.string.save), - neutralButtonText = if (displayName != null && file.exists()) stringResource(R.string.delete) else null, - onNeutral = { - if (!file.exists()) return@TextInputDialog - file.delete() - onCustomLayoutFileListChanged() - keyboardNeedsReload = true - }, - initialText = startContent ?: file.readText(), + initialText = startContent ?: getCustomLayoutFile(initialLayoutName, layoutType, ctx).readText(), singleLine = false, - title = { Text(displayName ?: getLayoutDisplayName(layoutName)) }, + title = { + TextField( + value = displayNameValue, + onValueChange = { displayNameValue = it }, + isError = !nameValid, + supportingText = { if (!nameValid) Text(stringResource(R.string.name_invalid)) }, + trailingIcon = { if (!nameValid) Icon(painterResource(R.drawable.ic_close), null) }, +// textStyle = MaterialTheme.typography.titleMedium, // todo: only makes it a tiny bit smaller, find a better way + ) + }, checkTextValid = { val valid = checkLayout(it, ctx) job?.cancel() @@ -68,23 +98,12 @@ fun LayoutEditDialog( Toast.makeText(ctx, ctx.getString(R.string.layout_error, message), Toast.LENGTH_LONG).show() } } - valid + valid && nameValid // don't allow saving with invalid name, but inform user about issues with layout content }, modifier = Modifier.imePadding(), // decorFitsSystemWindows = false is necessary so the dialog is not covered by keyboard // but this also stops the background from being darkened... great idea to combine both + // todo: also it results in an ugly effect when adding a new layout... need to find something else properties = DialogProperties(decorFitsSystemWindows = false) ) - if (showDeleteConfirmation) - ConfirmationDialog( - onDismissRequest = { showDeleteConfirmation = false }, - onConfirmed = { - onDismissRequest() - file.delete() - onCustomLayoutFileListChanged() - keyboardNeedsReload = true - }, - text = { Text(stringResource(R.string.delete_layout, displayName ?: "")) }, - confirmButtonText = stringResource(R.string.delete) - ) } diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt new file mode 100644 index 00000000..9de1c623 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutPickerDialog.kt @@ -0,0 +1,248 @@ +// 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 +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import helium314.keyboard.latin.R +import helium314.keyboard.latin.settings.Defaults.default +import helium314.keyboard.latin.settings.Settings +import helium314.keyboard.latin.utils.LayoutType +import helium314.keyboard.latin.utils.LayoutUtils +import helium314.keyboard.latin.utils.Log +import helium314.keyboard.latin.utils.checkLayout +import helium314.keyboard.latin.utils.getActivity +import helium314.keyboard.latin.utils.getCustomLayoutDisplayName +import helium314.keyboard.latin.utils.getCustomLayoutFiles +import helium314.keyboard.latin.utils.getCustomLayoutName +import helium314.keyboard.latin.utils.getStringResourceOrName +import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged +import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.Setting +import helium314.keyboard.settings.SettingsActivity +import helium314.keyboard.settings.keyboardNeedsReload + +// modified copy of ColorPickerDialog, later check whether stuff can be re-used +@Composable +fun LayoutPickerDialog( + onDismissRequest: () -> Unit, + setting: Setting, + layoutType: LayoutType, +) { + val ctx = LocalContext.current + val prefs = ctx.prefs() + val b = (ctx.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState() + if ((b?.value ?: 0) < 0) + Log.v("irrelevant", "stupid way to trigger recomposition on preference change") + + 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 = getCustomLayoutFiles(layoutType, ctx).map { it.name }.sorted() + val layouts = internalLayouts + customLayouts + "" + + val state = rememberLazyListState() + LaunchedEffect(currentLayout) { + val index = layouts.indexOfFirst { it == currentLayout } + if (index != -1) state.scrollToItem(index, -state.layoutInfo.viewportSize.height / 3) + } + 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 = !checkLayout(content, ctx) + if (!errorDialog) + 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) }, + title = { Text(setting.title) }, + text = { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.bodyLarge + ) { + LazyColumn(state = state) { + items(layouts) { item -> + if (item == "") { + AddLayoutRow({ newLayoutDialog = it to "" }, customLayouts) + } else { + LayoutItemRow( + onDismissRequest = onDismissRequest, + onClickEdit = { newLayoutDialog = it }, + onDelete = { deletedLayout -> + if (item == deletedLayout) { + prefs.edit().remove(Settings.PREF_LAYOUT_PREFIX + layoutType.name).apply() + keyboardNeedsReload = true + } + getCustomLayoutFiles(layoutType, ctx).firstOrNull { it.name == deletedLayout }?.delete() + onCustomLayoutFileListChanged() + }, + layoutType = layoutType, + layoutName = item, + isSelected = item == currentLayout, + isCustom = item in customLayouts + ) + } + } + } + } + }, + ) + if (errorDialog) + InfoDialog(stringResource(R.string.file_read_error)) { errorDialog = false } + if (newLayoutDialog != null) { + LayoutEditDialog( + onDismissRequest = { newLayoutDialog = null }, + layoutType = layoutType, + initialLayoutName = newLayoutDialog?.first ?: layoutType.default, + startContent = newLayoutDialog?.second, + isNameValid = { it.isNotBlank() && it !in customLayouts } + ) + } +} + +@Composable +private fun AddLayoutRow(onNewLayout: (String) -> Unit, userLayouts: Collection) { + var textValue by remember { mutableStateOf(TextFieldValue()) } + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(start = 10.dp) + ) { + Icon(painterResource(R.drawable.ic_plus), stringResource(R.string.add)) + TextField( + value = textValue, + onValueChange = { textValue = it }, + modifier = Modifier.weight(1f), + singleLine = true + ) + IconButton( + enabled = textValue.text.isNotEmpty() && getCustomLayoutName(textValue.text) !in userLayouts, + onClick = { onNewLayout(textValue.text) } + ) { Icon(painterResource(R.drawable.ic_edit), null) } + } +} + +@Composable +private fun LayoutItemRow( + onDismissRequest: () -> Unit, + onClickEdit: (Pair) -> Unit, + onDelete: (String) -> Unit, + layoutType: LayoutType, + layoutName: String, + isSelected: Boolean, + isCustom: Boolean, +) { + val ctx = LocalContext.current + val prefs = ctx.prefs() + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable { + onDismissRequest() + Settings.writeDefaultLayoutName(layoutName, layoutType, prefs) + keyboardNeedsReload = true + } + .padding(start = 6.dp) + .heightIn(min = 40.dp) + ) { + RadioButton( + selected = isSelected, + onClick = { + onDismissRequest() + Settings.writeDefaultLayoutName(layoutName, layoutType, prefs) + keyboardNeedsReload = true + } + ) + Text( + text = if (isCustom) getCustomLayoutDisplayName(layoutName) + else layoutName.getStringResourceOrName("layout_", ctx), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.weight(1f), + ) + if (isCustom) { + var showDeleteDialog by remember { mutableStateOf(false) } + IconButton( + onClick = { showDeleteDialog = true } + ) { Icon(painterResource(R.drawable.ic_bin), null) } + if (showDeleteDialog) + ConfirmationDialog( + onDismissRequest = { showDeleteDialog = false }, + text = { Text(stringResource(R.string.delete_layout, getCustomLayoutDisplayName(layoutName))) }, + confirmButtonText = stringResource(R.string.delete), + onConfirmed = { + showDeleteDialog = false + onDelete(layoutName) + } + ) + } + IconButton( + onClick = { onClickEdit(layoutName to (if (isCustom) null else LayoutUtils.getContent(layoutType, layoutName, ctx))) } + ) { Icon(painterResource(R.drawable.ic_edit), null) } + } +} + +@Preview +@Composable +private fun Preview() { + LayoutPickerDialog( + onDismissRequest = {}, + setting = Setting(LocalContext.current, "", R.string.settings) {}, + layoutType = LayoutType.SYMBOLS + ) +} diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt index 9e73ea9c..f083fb50 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn +import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -48,7 +49,7 @@ fun ThreeButtonAlertDialog( properties = properties ) { Box( - modifier = modifier.sizeIn(minWidth = 280.dp, maxWidth = 560.dp), + modifier = modifier.widthIn(min = 280.dp, max = 560.dp), propagateMinConstraints = true ) { Surface( diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/LayoutEditPreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/LayoutEditPreference.kt index 6d71e54a..c1010254 100644 --- a/app/src/main/java/helium314/keyboard/settings/preferences/LayoutEditPreference.kt +++ b/app/src/main/java/helium314/keyboard/settings/preferences/LayoutEditPreference.kt @@ -14,7 +14,7 @@ import helium314.keyboard.settings.Setting import helium314.keyboard.settings.dialogs.LayoutEditDialog import helium314.keyboard.settings.dialogs.ListPickerDialog import java.io.File - +/* @Composable fun LayoutEditPreference( setting: Setting, @@ -57,3 +57,4 @@ fun LayoutEditPreference( ) } } +*/ \ No newline at end of file diff --git a/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt index 500dc04d..fd25a2b6 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt @@ -27,11 +27,6 @@ import helium314.keyboard.latin.common.splitOnWhitespace import helium314.keyboard.latin.settings.DebugSettings import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings -import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_NORMAL -import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS -import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED -import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX -import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.prefs import helium314.keyboard.settings.SettingsContainer import helium314.keyboard.settings.preferences.ListPreference @@ -47,7 +42,6 @@ import helium314.keyboard.settings.Theme import helium314.keyboard.settings.dialogs.TextInputDialog import helium314.keyboard.settings.keyboardNeedsReload import helium314.keyboard.settings.preferences.BackupRestorePreference -import helium314.keyboard.settings.preferences.LayoutEditPreference import helium314.keyboard.settings.preferences.LoadGestureLibPreference @Composable @@ -192,23 +186,6 @@ fun createAdvancedSettings(context: Context) = listOf( ) ListPreference(it, items, Defaults.PREF_MORE_POPUP_KEYS) { KeyboardLayoutSet.onSystemLocaleChanged() } }, -/* Setting(context, SettingsWithoutKey.CUSTOM_SYMBOLS_NUMBER_LAYOUTS, R.string.customize_symbols_number_layouts) { setting -> - LayoutEditPreference( - setting = setting, - items = RawKeyboardParser.symbolAndNumberLayouts, - getItemName = { it.getStringResourceOrName("layout_", LocalContext.current) }, - getDefaultLayout = { LocalContext.current.assets.list("layouts")?.firstOrNull { it.startsWith("$it.") } } - ) - }, - Setting(context, SettingsWithoutKey.CUSTOM_FUNCTIONAL_LAYOUTS, R.string.customize_functional_key_layouts) { setting -> - LayoutEditPreference( - setting = setting, - items = listOf(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) - .map { it.substringBeforeLast(".") }, - getItemName = { it.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", LocalContext.current) }, - getDefaultLayout = { if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" } - ) - },*/ // todo: these settings are disabled for now -> remove them and use a layoutScreen instead Setting(context, SettingsWithoutKey.BACKUP_RESTORE, R.string.backup_restore_title) { BackupRestorePreference(it) }, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/ColorsScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/ColorsScreen.kt index c3781889..5f40689f 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/ColorsScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/ColorsScreen.kt @@ -136,7 +136,7 @@ fun ColorsScreen( nameField = it }, isError = !nameValid, -// supportingText = { if (!nameValid) Text("name already in use") } // this is cutting off bottom half of the actual text... +// supportingText = { if (!nameValid) Text(stringResource(R.string.name_invalid) } // todo: this is cutting off bottom half of the actual text... trailingIcon = { if (!nameValid) Icon(painterResource(R.drawable.ic_close), null) } ) }, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/LayoutScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/LayoutScreen.kt new file mode 100644 index 00000000..e7095bc7 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/screens/LayoutScreen.kt @@ -0,0 +1,68 @@ +package helium314.keyboard.settings.screens + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +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.settings.Settings +import helium314.keyboard.latin.utils.LayoutType +import helium314.keyboard.latin.utils.LayoutType.Companion.displayNameId +import helium314.keyboard.latin.utils.Log +import helium314.keyboard.latin.utils.getActivity +import helium314.keyboard.latin.utils.getCustomLayoutDisplayName +import helium314.keyboard.latin.utils.getStringResourceOrName +import helium314.keyboard.latin.utils.isCustomLayout +import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.SearchSettingsScreen +import helium314.keyboard.settings.Setting +import helium314.keyboard.settings.SettingsActivity +import helium314.keyboard.settings.dialogs.LayoutPickerDialog +import helium314.keyboard.settings.preferences.Preference + +@Composable +fun LayoutScreen( + onClickBack: () -> Unit, +) { + // todo: enable main layouts + // which layouts to show? all is too much, maybe limit to latin and layouts for enabled locales (and system locales?) + SearchSettingsScreen( + onClickBack = onClickBack, + title = stringResource(R.string.keyboard_layout_set), + settings = LayoutType.entries.filter { it != LayoutType.MAIN }.map { Settings.PREF_LAYOUT_PREFIX + it.name } + ) +} + +fun createLayoutSettings(context: Context) = listOf( + Setting(context, Settings.PREF_LAYOUT_PREFIX + LayoutType.MAIN, R.string.customize_functional_key_layouts) { // todo: title + // todo: actual content + }, +) + LayoutType.entries.filter { it != LayoutType.MAIN }.map { layoutType -> + Setting(context, Settings.PREF_LAYOUT_PREFIX + layoutType, layoutType.displayNameId) { setting -> + val ctx = LocalContext.current + val prefs = ctx.prefs() + val b = (ctx.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState() + if ((b?.value ?: 0) < 0) + Log.v("irrelevant", "stupid way to trigger recomposition on preference change") + var showDialog by rememberSaveable { mutableStateOf(false) } + val currentLayout = Settings.readDefaultLayoutName(layoutType, prefs) + val displayName = if (isCustomLayout(currentLayout)) getCustomLayoutDisplayName(currentLayout) + else currentLayout.getStringResourceOrName("layout_", ctx) + Preference( + name = setting.title, + description = displayName, + onClick = { showDialog = true } + ) + if (showDialog) + LayoutPickerDialog( + onDismissRequest = { showDialog = false }, + setting = setting, + layoutType = layoutType + ) + } +} diff --git a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt index 8bd011ad..42f87b87 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt @@ -40,6 +40,7 @@ fun MainSettingsScreen( onClickAdvanced: () -> Unit, onClickAppearance: () -> Unit, onClickLanguage: () -> Unit, + onClickLayouts: () -> Unit, onClickBack: () -> Unit, ) { val ctx = LocalContext.current @@ -116,6 +117,17 @@ fun MainSettingsScreen( contentDescription = null ) } + Preference( + name = stringResource(R.string.keyboard_layout_set), + onClick = onClickLayouts, + icon = R.drawable.ic_ime_switcher + ) { + Icon( + painter = painterResource(R.drawable.ic_arrow_left), + modifier = Modifier.scale(-1f, 1f), + contentDescription = null + ) + } Preference( name = stringResource(R.string.settings_screen_advanced), onClick = onClickAdvanced, @@ -181,7 +193,7 @@ fun MainSettingsScreen( private fun PreviewScreen() { Theme(true) { Surface { - MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {}, {}) + MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {}, {}, {}) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee1f8b97..904937a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -987,6 +987,8 @@ New dictionary: Customize icons Really reset all customized icons? - + Really delete %s? + + Invalid name