diff --git a/README.md b/README.md
index b091df7bcc..e6d706af60 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,11 @@ Please check the GitHub repository to report issues: [https://github.com/futo-or
The source code is hosted on our [internal GitLab](https://gitlab.futo.org/keyboard/latinime) and mirrored to [GitHub](https://github.com/futo-org/android-keyboard/). As registration is closed on our internal GitLab, we use GitHub instead for issues and pull requests.
+If you want to help translate the app, please do so via our Pontoon instance: https://i18n-keyboard.futo.org/
+
Due to custom license, pull requests to this repository require signing a [CLA](https://cla.futo.org/) which you can do after opening a PR. Contributions to the [layouts repo](https://github.com/futo-org/futo-keyboard-layouts) don't require CLA as they're Apache-2.0
-If you want to help translate the app, please do so via our Pontoon instance: https://i18n-keyboard.futo.org/
+Please do not submit AI-generated pull requests, as these tend to make the codebase more difficult to understand. Pull requests made with clear heavy use of AI may be closed without comment. Please also avoid using AI to write issues, and try to explain it in your own words instead.
## Layouts
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index f6fc23d292..db1ed1a0c6 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -85,6 +85,21 @@
+
+
+
+
+
+
+
+
Voice Input
Built-in voice input is disabled!
+
+ Using external engine: %s
+ External engine
+ None (built-in)
+ Note: the %s app will record your speech and may send it to online servers, it is not covered by our privacy policy.
+ Switch voice input engine to %s?
+ Switch
+ Cancel
+
Disable built-in voice input
Use voice input provided by external app
Indication sounds
diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt
index 44243e6c35..3e933a3a54 100644
--- a/java/src/org/futo/inputmethod/latin/LatinIME.kt
+++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt
@@ -940,6 +940,13 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont
// TODO: Spell checker service
}
+ /// Switch to voice IME or to internal one if it's no longer present.
+ fun trySwitchToShortcutIMEorInternal() {
+ if(!latinIMELegacy.mRichImm.switchToShortcutIme(this)) {
+ uixManager.activateInternalVoiceIME()
+ }
+ }
+
override val foldState: FoldingOptions
get() = uixManager.foldingOptions.value
diff --git a/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java b/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
index 7807c3dc84..8f0a320a76 100644
--- a/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
+++ b/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
@@ -133,7 +133,7 @@ public interface SuggestionStripController {
private View mComposeInputView;
private InsetsUpdater mInsetsUpdater;
- private RichInputMethodManager mRichImm;
+ public RichInputMethodManager mRichImm;
public final KeyboardSwitcher mKeyboardSwitcher;
private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
@@ -727,7 +727,7 @@ public void onCodeInput(final int codePoint, final int x, final int y,
// completely replace #onCodeInput.
public void onEvent(@Nonnull final Event event) {
if (Constants.CODE_SHORTCUT == event.mKeyCode) {
- mRichImm.switchToShortcutIme(mInputMethodService);
+ getLatinIME().trySwitchToShortcutIMEorInternal();
}
/*final InputTransaction completeInputTransaction =
mInputLogic.onCodeInput(mSettings.getCurrent(), event,
diff --git a/java/src/org/futo/inputmethod/latin/RichInputMethodManager.java b/java/src/org/futo/inputmethod/latin/RichInputMethodManager.java
index a3d51b30e6..c0dacd6e10 100644
--- a/java/src/org/futo/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/org/futo/inputmethod/latin/RichInputMethodManager.java
@@ -30,7 +30,9 @@
import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.compat.InputMethodManagerCompatWrapper;
import org.futo.inputmethod.latin.settings.Settings;
+import org.futo.inputmethod.latin.uix.DataStoreHelper;
import org.futo.inputmethod.latin.uix.PreferenceUtils;
+import org.futo.inputmethod.latin.uix.SettingsKt;
import org.futo.inputmethod.latin.utils.AdditionalSubtypeUtils;
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils;
@@ -40,6 +42,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import javax.annotation.Nonnull;
@@ -63,6 +66,8 @@ private RichInputMethodManager() {
private InputMethodManagerCompatWrapper mImmWrapper;
private InputMethodInfoCache mInputMethodInfoCache;
private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
+
+ private String mShortcutPackage;
private InputMethodInfo mShortcutInputMethodInfo;
private InputMethodSubtype mShortcutSubtype;
@@ -279,40 +284,67 @@ private void updateShortcutIme() {
+ (mShortcutSubtype == null ? "" : (
mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
}
- final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
- final Locale systemLocale = mContext.getResources().getConfiguration().locale;
- // TODO: Update an icon for shortcut IME
- final Map> shortcuts =
- getInputMethodManager().getShortcutInputMethodsAndSubtypes();
+ mShortcutPackage = DataStoreHelper.getSetting(SettingsKt.getSYSTEM_VOICE_INPUT_PACKAGE());
+ if(mShortcutPackage.isEmpty()) {
+ mShortcutPackage = null;
+ }
+
+
+
mShortcutInputMethodInfo = null;
mShortcutSubtype = null;
- for (final InputMethodInfo imi : shortcuts.keySet()) {
- final List subtypes = shortcuts.get(imi);
- // TODO: Returns the first found IMI for now. Should handle all shortcuts as
- // appropriate.
- mShortcutInputMethodInfo = imi;
- // TODO: Pick up the first found subtype for now. Should handle all subtypes
- // as appropriate.
- mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
- break;
+
+ if(mShortcutPackage != null) {
+ final List inputMethodList = getInputMethodManager().getEnabledInputMethodList();
+ for (final InputMethodInfo imi : inputMethodList) {
+ if(!mShortcutPackage.equals(imi.getPackageName())) continue;
+
+ mShortcutInputMethodInfo = imi;
+ final List subtypes = getInputMethodManager()
+ .getEnabledInputMethodSubtypeList(imi, true);
+
+ for(int i=0; i> shortcuts =
+ getInputMethodManager().getShortcutInputMethodsAndSubtypes();
+ for (final InputMethodInfo imi : shortcuts.keySet()) {
+ final List subtypes = shortcuts.get(imi);
+ mShortcutInputMethodInfo = imi;
+ mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
+ break;
+ }
}
+
+
if (DEBUG) {
Log.d(TAG, "Update shortcut IME to : "
+ (mShortcutInputMethodInfo == null
? "" : mShortcutInputMethodInfo.getId()) + ", "
+ (mShortcutSubtype == null ? "" : (
- mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+ mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))
+ + " tgt=" + (mShortcutPackage == null ? "" : mShortcutPackage));
}
}
- public void switchToShortcutIme(final InputMethodService context) {
+ public boolean switchToShortcutIme(final InputMethodService context) {
if (mShortcutInputMethodInfo == null) {
- return;
+ return false;
}
final String imiId = mShortcutInputMethodInfo.getId();
switchToTargetIME(imiId, mShortcutSubtype, context);
+ return true;
}
private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
diff --git a/java/src/org/futo/inputmethod/latin/uix/Settings.kt b/java/src/org/futo/inputmethod/latin/uix/Settings.kt
index a775644048..a3061d7e49 100644
--- a/java/src/org/futo/inputmethod/latin/uix/Settings.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/Settings.kt
@@ -374,6 +374,11 @@ val USE_SYSTEM_VOICE_INPUT = SettingsKey(
default = false
)
+val SYSTEM_VOICE_INPUT_PACKAGE = SettingsKey(
+ key = stringPreferencesKey("systemVoiceInputPackage"),
+ default = ""
+)
+
val USE_TRANSFORMER_FINETUNING = SettingsKey(
key = booleanPreferencesKey("useTransformerFinetuning2"),
default = false
diff --git a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt
index 6191a1e09c..8a40ed6b4e 100644
--- a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt
@@ -648,12 +648,13 @@ class UixManager(private val latinIME: LatinIME) {
val isMainKeyboardHidden get() = mainKeyboardHidden.value
- private fun onActionActivatedInternal(rawAction: Action) {
+ private fun onActionActivatedInternal(rawAction: Action, skipOverride: Boolean = false) {
resizers.hideResizer()
- val action = runBlocking {
+ val action = if(skipOverride)
+ rawAction
+ else
ActionRegistry.getActionOverride(latinIME, rawAction)
- }
if (action.windowImpl != null) {
enterActionWindowView(action)
@@ -664,11 +665,13 @@ class UixManager(private val latinIME: LatinIME) {
}
}
- private fun onActionAltActivatedInternal(rawAction: Action) {
- val action = runBlocking {
- ActionRegistry.getActionOverride(latinIME, rawAction)
- }
+ /// public function to skip action override for voice input
+ fun activateInternalVoiceIME() {
+ onActionActivatedInternal(org.futo.inputmethod.latin.uix.actions.VoiceInputAction, skipOverride = true)
+ }
+ private fun onActionAltActivatedInternal(rawAction: Action) {
+ val action = ActionRegistry.getActionOverride(latinIME, rawAction)
action.altPressImpl?.invoke(keyboardManagerForAction, persistentStates[action])
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/Registry.kt b/java/src/org/futo/inputmethod/latin/uix/actions/Registry.kt
index 8130eae061..75f2f974d8 100644
--- a/java/src/org/futo/inputmethod/latin/uix/actions/Registry.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/Registry.kt
@@ -68,7 +68,7 @@ private fun List.verifyNamesAreUnique(): List {
}
object ActionRegistry {
- suspend fun getActionOverride(context: Context, action: Action): Action {
+ fun getActionOverride(context: Context, action: Action): Action {
return if(action == VoiceInputAction || action == SystemVoiceInputAction) {
val useSystemVoiceInput = context.getSetting(USE_SYSTEM_VOICE_INPUT)
if(useSystemVoiceInput) {
diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/VoiceInputSwitchActivity.kt b/java/src/org/futo/inputmethod/latin/uix/settings/VoiceInputSwitchActivity.kt
new file mode 100644
index 0000000000..580698726a
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/settings/VoiceInputSwitchActivity.kt
@@ -0,0 +1,131 @@
+package org.futo.inputmethod.latin.uix.settings
+
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.runBlocking
+import org.futo.inputmethod.latin.R
+import org.futo.inputmethod.latin.uix.SYSTEM_VOICE_INPUT_PACKAGE
+import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT
+import org.futo.inputmethod.latin.uix.getSetting
+import org.futo.inputmethod.latin.uix.setSetting
+import org.futo.inputmethod.latin.uix.theme.UixThemeAuto
+
+class VoiceInputSwitchActivity : ComponentActivity() {
+ private var targetPackage: String? = null
+ private val label = mutableStateOf("?")
+
+ private fun switch() {
+ runBlocking {
+ setSetting(SYSTEM_VOICE_INPUT_PACKAGE, targetPackage!!)
+ setSetting(USE_SYSTEM_VOICE_INPUT, true)
+ }
+ }
+
+ private fun updateContent() {
+ setContent {
+ DataStoreCacheProvider {
+ UixThemeAuto {
+ Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Surface(Modifier.width(380.dp).height(240.dp).padding(8.dp), shape = RoundedCornerShape(16.dp)) {
+ Column(Modifier.padding(8.dp)) {
+ Text(stringResource(R.string.voice_input_settings_backend_system_confirm, label.value))
+
+ Row(Modifier.align(Alignment.End), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ TextButton(onClick = {
+ setResult(RESULT_CANCELED)
+ finish()
+ }) {
+ Text(stringResource(R.string.voice_input_settings_backend_system_confirm_no))
+ }
+
+ TextButton(onClick = {
+ switch()
+ setResult(65)
+ finish()
+ }) {
+ Text(stringResource(R.string.voice_input_settings_backend_system_confirm_yes))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val targetPackage = intent.getStringExtra("targetPackage")
+ this.targetPackage = targetPackage
+
+ if(targetPackage == null) {
+ Log.e("VoiceInputSwitchActivity", "Called without a targetPackage.")
+ finish()
+ return
+ }
+
+ val mode = intent.getStringExtra("mode")
+ when(mode) {
+ "popup" -> {
+ label.value = try {
+ packageManager.getPackageInfo(targetPackage, 0)?.let {
+ packageManager.getApplicationLabel(it.applicationInfo!!)
+ }!!.toString()
+ }catch(e: Exception) {
+ e.printStackTrace()
+ setResult(RESULT_CANCELED)
+ finish()
+ return
+ }
+
+ updateContent()
+ }
+ "switch" -> {
+ if(targetPackage.startsWith("org.futo.")) {
+ switch()
+ setResult(66)
+ finish()
+ } else {
+ setResult(RESULT_CANCELED)
+ finish()
+ }
+ }
+ "check" -> {
+ val current = getSetting(SYSTEM_VOICE_INPUT_PACKAGE)
+ if(current == targetPackage) {
+ setResult(67)
+ finish()
+ } else {
+ setResult(68)
+ finish()
+ }
+ }
+ else -> {
+ setResult(RESULT_CANCELED)
+ finish()
+ }
+ }
+
+ }
+}
diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/DevSettings.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/DevSettings.kt
index 6859bca80f..3a3df0bab2 100644
--- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/DevSettings.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/DevSettings.kt
@@ -1,6 +1,5 @@
package org.futo.inputmethod.latin.uix.settings.pages
-import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.compose.foundation.background
@@ -68,15 +67,13 @@ import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.SettingToggleDataStore
import org.futo.inputmethod.latin.uix.settings.SettingToggleRaw
+import org.futo.inputmethod.latin.uix.settings.VoiceInputSwitchActivity
import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useDataStoreValue
import org.futo.inputmethod.latin.uix.theme.TonalPalette
-import org.futo.inputmethod.latin.uix.theme.Tones
-import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.uix.theme.dynamicTonalPalette
import org.futo.inputmethod.latin.uix.theme.serialization.AlphaOrder
import org.futo.inputmethod.latin.uix.theme.serialization.argbLongToHexColorString
-import org.futo.inputmethod.latin.uix.theme.serialization.long
import org.futo.inputmethod.latin.xlm.AllowTransformerOnNonQWERTYLayouts
import org.futo.inputmethod.updates.DISABLE_UPDATE_REMINDER
import org.futo.inputmethod.updates.dismissedMigrateUpdateNotice
@@ -349,6 +346,18 @@ fun DeveloperScreen(navController: NavHostController = rememberNavController())
)
ScreenTitle(title = "Here be dragons")
+ val context = LocalContext.current
+ NavigationItem(
+ title = "Show voice input switcher",
+ style = NavigationItemStyle.MiscNoArrow,
+ navigate = {
+ val intent = Intent(context, VoiceInputSwitchActivity::class.java)
+ intent.putExtra("targetPackage", context.packageName)
+ intent.putExtra("mode", "popup")
+ context.startActivity(intent)
+ }
+ )
+
SettingToggleDataStore(
"Use expandable suggestions UI for all languages",
UseExpandableSuggestionsForGeneralIME,
diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Home.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Home.kt
index 043e352e15..261ad6a630 100644
--- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Home.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Home.kt
@@ -32,6 +32,7 @@ import androidx.navigation.compose.rememberNavController
import org.futo.inputmethod.latin.BuildConfig
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.LocalNavController
+import org.futo.inputmethod.latin.uix.SYSTEM_VOICE_INPUT_PACKAGE
import org.futo.inputmethod.latin.uix.TextEditPopupActivity
import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT
import org.futo.inputmethod.latin.uix.settings.NavigationItem
@@ -87,11 +88,15 @@ val HomeScreenLite = UserSettingsMenu(
name = R.string.voice_input_settings_title
) {
val navController = LocalNavController.current
+ val useExternal = useDataStoreValue(USE_SYSTEM_VOICE_INPUT)
+ val externalPkg = useDataStoreValue(SYSTEM_VOICE_INPUT_PACKAGE)
+ val name = usePackageReadableName(externalPkg)
+
NavigationItem(
title = stringResource(R.string.voice_input_settings_title),
style = NavigationItemStyle.HomePrimary,
- subtitle = if(useDataStoreValue(USE_SYSTEM_VOICE_INPUT)) {
- stringResource(R.string.voice_input_settings_builtin_disabled_notice)
+ subtitle = if(useExternal && name != null) {
+ stringResource(R.string.voice_input_settings_builtin_disabled_notice2, name)
} else { null },
navigate = { navController!!.navigate(VoiceInputMenu.navPath) },
icon = painterResource(R.drawable.mic_fill)
diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/VoiceInput.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/VoiceInput.kt
index 2fff8bdb4b..29ac803324 100644
--- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/VoiceInput.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/VoiceInput.kt
@@ -1,7 +1,12 @@
package org.futo.inputmethod.latin.uix.settings.pages
-import android.content.Intent
+import android.view.inputmethod.InputMethodManager
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalResources
+import androidx.compose.ui.res.stringResource
+import androidx.core.content.getSystemService
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.ANIMATE_BUBBLE
import org.futo.inputmethod.latin.uix.AUDIO_FOCUS
@@ -9,12 +14,16 @@ import org.futo.inputmethod.latin.uix.CAN_EXPAND_SPACE
import org.futo.inputmethod.latin.uix.DISALLOW_SYMBOLS
import org.futo.inputmethod.latin.uix.ENABLE_SOUND
import org.futo.inputmethod.latin.uix.PREFER_BLUETOOTH
+import org.futo.inputmethod.latin.uix.SYSTEM_VOICE_INPUT_PACKAGE
import org.futo.inputmethod.latin.uix.USE_PERSONAL_DICT
import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT
import org.futo.inputmethod.latin.uix.USE_VAD_AUTOSTOP
-import org.futo.inputmethod.latin.uix.VERBOSE_PROGRESS
+import org.futo.inputmethod.latin.uix.settings.DropDownPickerSettingItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
+import org.futo.inputmethod.latin.uix.settings.Tip
+import org.futo.inputmethod.latin.uix.settings.UserSetting
import org.futo.inputmethod.latin.uix.settings.UserSettingsMenu
+import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useDataStoreValue
import org.futo.inputmethod.latin.uix.settings.userSettingNavigationItem
import org.futo.inputmethod.latin.uix.settings.userSettingToggleDataStore
@@ -23,15 +32,89 @@ private val visibilityCheckNotSystemVoiceInput = @Composable {
useDataStoreValue(USE_SYSTEM_VOICE_INPUT) == false
}
+private data class VoiceIMEInfo(
+ val builtin: Boolean,
+ val name: String,
+ val packageName: String,
+)
+
+@Composable
+fun usePackageReadableName(pkg: String): String? {
+ val context = LocalContext.current
+ return remember(pkg) {
+ try {
+ context.packageManager.getPackageInfo(pkg, 0)
+ } catch(e: Exception) {
+ null
+ }?.applicationInfo?.let {
+ context.packageManager.getApplicationLabel(it).toString()
+ }
+ }
+}
+
val VoiceInputMenu = UserSettingsMenu(
title = R.string.voice_input_settings_title,
navPath = "voiceInput", registerNavPath = true,
settings = listOf(
- userSettingToggleDataStore(
- title = R.string.voice_input_settings_disable_builtin_voice_input,
- subtitle = R.string.voice_input_settings_disable_builtin_voice_input_subtitle,
- setting = USE_SYSTEM_VOICE_INPUT
- ),
+ UserSetting(
+ name = R.string.voice_input_settings_backend_system,
+ searchTagList = listOf(
+ R.string.voice_input_settings_disable_builtin_voice_input,
+ R.string.voice_input_settings_disable_builtin_voice_input_subtitle
+ )
+ ) {
+ val useExternal = useDataStore(USE_SYSTEM_VOICE_INPUT)
+ val externalPkg = useDataStore(SYSTEM_VOICE_INPUT_PACKAGE)
+
+ val context = LocalContext.current
+ val res = LocalResources.current
+ val options = remember(externalPkg.value) {
+ val imm = context.getSystemService()!!
+ buildList {
+ add(VoiceIMEInfo(true, "", ""))
+ addAll(imm.enabledInputMethodList.filter { im ->
+ im.packageName == externalPkg.value ||
+ (0 until im.subtypeCount).map { im.getSubtypeAt(it) }
+ .any { it.mode.lowercase() == "voice" }
+ }.map {
+ VoiceIMEInfo(false, it.loadLabel(context.packageManager)?.toString() ?: it.packageName, it.packageName)
+ })
+ }
+ }
+
+ val currOption = remember(externalPkg.value) {
+ if(externalPkg.value == "") options[0] else
+ options.find { it.packageName == externalPkg.value }
+ }
+
+
+ DropDownPickerSettingItem(
+ stringResource(R.string.voice_input_settings_backend_system),
+ options,
+ currOption,
+ {
+ useExternal.setValue(!it.builtin)
+ externalPkg.setValue(it.packageName ?: "")
+ },
+ {
+ if(it.builtin) res.getString(R.string.voice_input_settings_backend_system_internal)
+ else it.name
+ }
+ )
+
+ if(useExternal.value && externalPkg.value.isNotEmpty()) {
+ val privacyWhitelist = listOf(
+ "org.futo.voiceinput",
+ "org.futo.voiceinput.dev",
+ "dev.notune.transcribe",
+ "dev.soupslurpr.transcribro"
+ )
+
+ if(!privacyWhitelist.contains(externalPkg.value))
+ Tip(stringResource(R.string.voice_input_settings_backend_system_external_warning,
+ currOption?.name ?: externalPkg.value))
+ }
+ },
//if(!systemVoiceInput.value) {
userSettingToggleDataStore(
diff --git a/tools/make-keyboard-text-py/locales/ru.json b/tools/make-keyboard-text-py/locales/ru.json
index 0cca089410..5b1c3a33f3 100644
--- a/tools/make-keyboard-text-py/locales/ru.json
+++ b/tools/make-keyboard-text-py/locales/ru.json
@@ -1,7 +1,7 @@
{
"locale": "ru",
"morekeys": {
- "cyrillic_ie": [
+ "code_u0435": [
"ё"
],
"cyrillic_soft_sign": [