From 6c4c6b8965a6b5ac3726f92c050d24a067b03c88 Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Wed, 13 Mar 2024 15:26:23 -0500 Subject: [PATCH] Request mic permission during setup, add test text field --- build.gradle | 12 +- java/res/values/strings-uix.xml | 1 + .../org/futo/inputmethod/latin/LatinIME.kt | 4 +- .../latin/uix/settings/SettingsActivity.kt | 15 ++- .../latin/uix/settings/SettingsUtils.kt | 4 +- .../inputmethod/latin/uix/settings/Setup.kt | 87 ++++++++++++- .../latin/uix/settings/pages/Home.kt | 122 ++++++++++++------ voiceinput-shared/build.gradle | 12 +- 8 files changed, 191 insertions(+), 66 deletions(-) diff --git a/build.gradle b/build.gradle index 8e500f26a..6bd19e7c7 100644 --- a/build.gradle +++ b/build.gradle @@ -144,12 +144,12 @@ android { dependencies { implementation 'androidx.core:core-ktx:1.12.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.lifecycle:lifecycle-runtime:2.6.2' - implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.2' - implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' + implementation 'androidx.lifecycle:lifecycle-runtime:2.7.0' + implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0' implementation 'androidx.activity:activity-compose:1.8.2' - implementation platform('androidx.compose:compose-bom:2022.10.00') + implementation platform('androidx.compose:compose-bom:2024.02.02') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' @@ -157,7 +157,7 @@ dependencies { implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.navigation:navigation-compose:2.7.6' + implementation 'androidx.navigation:navigation-compose:2.7.7' implementation 'com.google.code.findbugs:jsr305:3.0.2' diff --git a/java/res/values/strings-uix.xml b/java/res/values/strings-uix.xml index 95f66c272..bc0dfda75 100644 --- a/java/res/values/strings-uix.xml +++ b/java/res/values/strings-uix.xml @@ -39,4 +39,5 @@ Blacklist Blacklist \"%1$s\" from being suggested? + Try typing hereā€¦ \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt index 2aca377cf..431426639 100644 --- a/java/src/org/futo/inputmethod/latin/LatinIME.kt +++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt @@ -292,7 +292,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save key(legacyInputView) { AndroidView(factory = { legacyInputView!! - }, update = { }, modifier = modifier) + }, modifier = modifier) } } @@ -305,7 +305,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save latinIMELegacy.setComposeInputView(it) } - latinIMELegacy.setInputView(legacyInputView) + latinIMELegacy.setInputView(newView) } override fun setInputView(view: View?) { diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt index 19627c2d1..d6e7a3851 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt @@ -1,9 +1,11 @@ package org.futo.inputmethod.latin.uix.settings +import android.Manifest import android.app.Activity import android.content.Context import android.content.Context.INPUT_METHOD_SERVICE import android.content.Intent +import android.content.pm.PackageManager import android.os.Bundle import android.provider.Settings import android.view.inputmethod.InputMethodManager @@ -27,9 +29,12 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.uix.THEME_KEY +import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT import org.futo.inputmethod.latin.uix.deferGetSetting +import org.futo.inputmethod.latin.uix.getSetting import org.futo.inputmethod.latin.uix.theme.StatusBarColorSetter import org.futo.inputmethod.latin.uix.theme.ThemeOption import org.futo.inputmethod.latin.uix.theme.ThemeOptions @@ -68,10 +73,10 @@ class SettingsActivity : ComponentActivity() { private val inputMethodEnabled = mutableStateOf(false) private val inputMethodSelected = mutableStateOf(false) + private val micPermissionGrantedOrUsingSystem = mutableStateOf(false) private var wasImeEverDisabled = false - private var fileBeingSaved: File? = null fun updateFileBeingSaved(to: File) { fileBeingSaved = to @@ -82,12 +87,16 @@ class SettingsActivity : ComponentActivity() { } @OptIn(DelicateCoroutinesApi::class) - private fun updateSystemState() { + fun updateSystemState() { val inputMethodEnabled = isInputMethodEnabled() val inputMethodSelected = isDefaultIMECurrent() this.inputMethodEnabled.value = inputMethodEnabled this.inputMethodSelected.value = inputMethodSelected + this.micPermissionGrantedOrUsingSystem.value = (checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) || runBlocking { + getSetting(USE_SYSTEM_VOICE_INPUT) + } + if(!inputMethodEnabled) { wasImeEverDisabled = true } else if(wasImeEverDisabled) { @@ -138,7 +147,7 @@ class SettingsActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - SetupOrMain(inputMethodEnabled.value, inputMethodSelected.value) { + SetupOrMain(inputMethodEnabled.value, inputMethodSelected.value, micPermissionGrantedOrUsingSystem.value) { SettingsNavigator(navController = navController) } } diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsUtils.kt b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsUtils.kt index d15ec490c..603969868 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsUtils.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsUtils.kt @@ -9,11 +9,13 @@ import androidx.compose.runtime.Composable import org.futo.inputmethod.latin.utils.UncachedInputMethodManagerUtils @Composable -fun SetupOrMain(inputMethodEnabled: Boolean, inputMethodSelected: Boolean, main: @Composable () -> Unit) { +fun SetupOrMain(inputMethodEnabled: Boolean, inputMethodSelected: Boolean, micPermissionGrantedOrUsingSystem: Boolean, main: @Composable () -> Unit) { if (!inputMethodEnabled) { SetupEnableIME() } else if (!inputMethodSelected) { SetupChangeDefaultIME() + } else if (!micPermissionGrantedOrUsingSystem) { + SetupEnableMic() } else { main() } diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/Setup.kt b/java/src/org/futo/inputmethod/latin/uix/settings/Setup.kt index 620d6476d..ed829adeb 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/Setup.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/Setup.kt @@ -1,9 +1,13 @@ package org.futo.inputmethod.latin.uix.settings +import android.Manifest import android.content.Context import android.content.Intent +import android.net.Uri import android.provider.Settings import android.view.inputmethod.InputMethodManager +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -17,6 +21,10 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -25,6 +33,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.futo.inputmethod.latin.R +import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT import org.futo.inputmethod.latin.uix.theme.Typography @Composable @@ -93,10 +102,10 @@ fun SetupEnableIME() { SetupContainer { Column { - Step(fraction = 1.0f/3.0f, text = "Setup - Step 1 of 2") + Step(fraction = 1.0f/3.0f, text = "Setup - Step 1 of 3") Text( - "To use FUTO Keyboard, you must first enable FUTO Keyboard as an input method.", + "Welcome to FUTO Keyboard pre-alpha! Please keep in mind things may be rough. This is not a finished product in any way.\n\nFirst, enable FUTO Keyboard as an input method.", textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) @@ -123,14 +132,16 @@ fun SetupChangeDefaultIME() { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inputMethodManager.showInputMethodPicker() + + (context as SettingsActivity).updateSystemState() } SetupContainer { Column { - Step(fraction = 2.0f/3.0f, text = "Setup - Step 2 of 2") + Step(fraction = 2.0f/3.0f, text = "Setup - Step 2 of 3") Text( - "Next, select FUTO Keyboard as your active input method.", + "Please select FUTO Keyboard as your active input method.", textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) @@ -144,4 +155,70 @@ fun SetupChangeDefaultIME() { } } } -} \ No newline at end of file +} + + +@Composable +@Preview +fun SetupEnableMic(onClick: () -> Unit = { }) { + val launcher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if(isGranted) { onClick() } + } + + val context = LocalContext.current + + var askedCount by remember { mutableStateOf(0) } + val askMicAccess = { + if (askedCount++ >= 2) { + val packageName = context.packageName + val myAppSettings = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse( + "package:$packageName" + ) + ) + myAppSettings.addCategory(Intent.CATEGORY_DEFAULT) + myAppSettings.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(myAppSettings) + } else { + launcher.launch(Manifest.permission.RECORD_AUDIO) + } + onClick() + } + + val (useSystemVoiceInput, setUseSystemVoiceInput) = useDataStore(key = USE_SYSTEM_VOICE_INPUT.key, default = USE_SYSTEM_VOICE_INPUT.default) + + SetupContainer { + Column { + Step(fraction = 0.9f, text = "Step 3 of 3") + Text( + "Choose whether you want to use built-in voice input, or the system voice input.", + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Button( + onClick = askMicAccess, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text("Use built-in (mic permission needed)") + } + + Button( + onClick = { + setUseSystemVoiceInput(true) + (context as SettingsActivity).updateSystemState() + }, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp, 4.dp) + ) { + Text("Use system") + } + } + } +} + 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 8d6e88152..0e932fd37 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 @@ -1,16 +1,26 @@ package org.futo.inputmethod.latin.uix.settings.pages +import android.widget.EditText +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import org.futo.inputmethod.latin.BuildConfig @@ -18,64 +28,90 @@ import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.uix.settings.NavigationItem import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle import org.futo.inputmethod.latin.uix.settings.ScreenTitle -import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.openLanguageSettings import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.updates.ConditionalUpdate +@Composable +fun AndroidTextInput() { + val context = LocalContext.current + val bgColor = MaterialTheme.colorScheme.background + val fgColor = MaterialTheme.colorScheme.onBackground + + if(!LocalInspectionMode.current) { + val editText = remember { + EditText(context).apply { + setHint(R.string.try_typing) + setBackgroundColor(bgColor.toArgb()) + setTextColor(fgColor.toArgb()) + setHintTextColor(fgColor.copy(alpha = 0.7f).toArgb()) + } + } + AndroidView({ editText }, modifier = Modifier.fillMaxWidth().padding(8.dp)) + } +} + @Preview(showBackground = true) @Composable fun HomeScreen(navController: NavHostController = rememberNavController()) { val context = LocalContext.current - ScrollableList { - Spacer(modifier = Modifier.height(24.dp)) - ScreenTitle("FUTO Keyboard Settings") + val scrollState = rememberScrollState() + Column { + Column( + modifier = Modifier + .weight(1.0f).fillMaxWidth() + .verticalScroll(scrollState) + ) { + Spacer(modifier = Modifier.height(24.dp)) + ScreenTitle("FUTO Keyboard Settings") - ConditionalUpdate(navController) + ConditionalUpdate(navController) - NavigationItem( - title = "Languages", - style = NavigationItemStyle.HomePrimary, - navigate = { context.openLanguageSettings() }, - icon = painterResource(id = R.drawable.globe) - ) + NavigationItem( + title = "Languages", + style = NavigationItemStyle.HomePrimary, + navigate = { context.openLanguageSettings() }, + icon = painterResource(id = R.drawable.globe) + ) - NavigationItem( - title = "Predictive Text", - style = NavigationItemStyle.HomeSecondary, - navigate = { navController.navigate("predictiveText") }, - icon = painterResource(id = R.drawable.shift) - ) + NavigationItem( + title = "Predictive Text", + style = NavigationItemStyle.HomeSecondary, + navigate = { navController.navigate("predictiveText") }, + icon = painterResource(id = R.drawable.shift) + ) - NavigationItem( - title = "Typing Preferences", - style = NavigationItemStyle.HomeSecondary, - navigate = { navController.navigate("typing") }, - icon = painterResource(id = R.drawable.delete) - ) + NavigationItem( + title = "Typing Preferences", + style = NavigationItemStyle.HomeSecondary, + navigate = { navController.navigate("typing") }, + icon = painterResource(id = R.drawable.delete) + ) - NavigationItem( - title = "Voice Input", - style = NavigationItemStyle.HomeSecondary, - navigate = { navController.navigate("voiceInput") }, - icon = painterResource(id = R.drawable.mic_fill) - ) + NavigationItem( + title = "Voice Input", + style = NavigationItemStyle.HomeSecondary, + navigate = { navController.navigate("voiceInput") }, + icon = painterResource(id = R.drawable.mic_fill) + ) - NavigationItem( - title = "Theme", - style = NavigationItemStyle.HomeTertiary, - navigate = { navController.navigate("themes") }, - icon = painterResource(id = R.drawable.eye) - ) + NavigationItem( + title = "Theme", + style = NavigationItemStyle.HomeTertiary, + navigate = { navController.navigate("themes") }, + icon = painterResource(id = R.drawable.eye) + ) - Spacer(modifier = Modifier.height(32.dp)) - Text( - "v${BuildConfig.VERSION_NAME}", - style = Typography.labelSmall, - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center - ) - Spacer(modifier = Modifier.height(32.dp)) + Spacer(modifier = Modifier.height(32.dp)) + Text( + "v${BuildConfig.VERSION_NAME}", + style = Typography.labelSmall, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + Spacer(modifier = Modifier.height(32.dp)) + } + AndroidTextInput() } } \ No newline at end of file diff --git a/voiceinput-shared/build.gradle b/voiceinput-shared/build.gradle index e77e870b8..ddbff311c 100644 --- a/voiceinput-shared/build.gradle +++ b/voiceinput-shared/build.gradle @@ -41,12 +41,12 @@ android { dependencies { implementation 'androidx.core:core-ktx:1.12.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.lifecycle:lifecycle-runtime:2.6.2' - implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.2' - implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' + implementation 'androidx.lifecycle:lifecycle-runtime:2.7.0' + implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0' implementation 'androidx.activity:activity-compose:1.8.2' - implementation platform('androidx.compose:compose-bom:2022.10.00') + implementation platform('androidx.compose:compose-bom:2024.02.02') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' @@ -54,7 +54,7 @@ dependencies { implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.navigation:navigation-compose:2.7.6' + implementation 'androidx.navigation:navigation-compose:2.7.7' implementation 'androidx.datastore:datastore-preferences:1.0.0' implementation(name:'vad-release', ext:'aar')