Request mic permission during setup, add test text field

This commit is contained in:
Aleksandras Kostarevas 2024-03-13 15:26:23 -05:00
parent 350b8e8fcf
commit 6c4c6b8965
8 changed files with 191 additions and 66 deletions

View File

@ -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'

View File

@ -39,4 +39,5 @@
<string name="blacklist">Blacklist</string>
<string name="blacklist_from_suggestions">Blacklist \"%1$s\" from being suggested?</string>
<string name="try_typing">Try typing here…</string>
</resources>

View File

@ -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?) {

View File

@ -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)
}
}

View File

@ -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()
}

View File

@ -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()
)
@ -145,3 +156,69 @@ fun SetupChangeDefaultIME() {
}
}
}
@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")
}
}
}
}

View File

@ -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()
}
}

View File

@ -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')