Make settings screens fully usable on Android 15 (#1484)

enable edge to edge in settings for all Android versions to avoid minor glitches and have more consistent appearance
This commit is contained in:
Eran Leshem 2025-04-21 09:06:41 +03:00 committed by GitHub
parent d15a97ccba
commit d9f17733d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 301 additions and 229 deletions

View file

@ -6,9 +6,14 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -20,6 +25,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
@ -58,7 +64,11 @@ fun SearchSettingsScreen(
content = { content = {
if (content != null) content() if (content != null) content()
else { else {
Column(Modifier.verticalScroll(rememberScrollState())) { Scaffold(contentWindowInsets = WindowInsets.systemBars.union(WindowInsets.ime)) { innerPadding ->
Column(
Modifier.verticalScroll(rememberScrollState())
.then(Modifier.padding(bottom = innerPadding.calculateBottomPadding()))
) {
settings.forEach { settings.forEach {
if (it is Int) { if (it is Int) {
PreferenceCategory(stringResource(it)) PreferenceCategory(stringResource(it))
@ -89,6 +99,7 @@ fun SearchSettingsScreen(
// } // }
// } // }
} }
}
}, },
filteredItems = { SettingsActivity.settingsContainer.filter(it) }, filteredItems = { SettingsActivity.settingsContainer.filter(it) },
itemContent = { it.Preference() } itemContent = { it.Preference() }
@ -195,7 +206,8 @@ fun <T: Any?> SearchScreen(
} }
} else { } else {
val items = filteredItems(searchText.text) val items = filteredItems(searchText.text)
LazyColumn { Scaffold(contentWindowInsets = WindowInsets.systemBars.union(WindowInsets.ime)) { innerPadding ->
LazyColumn(contentPadding = PaddingValues.Absolute(bottom = innerPadding.calculateBottomPadding())) {
items(items) { items(items) {
itemContent(it) itemContent(it)
} }
@ -204,6 +216,7 @@ fun <T: Any?> SearchScreen(
} }
} }
} }
}
// from StreetComplete // from StreetComplete
/** Expandable text field that can be dismissed and requests focus when it is expanded */ /** Expandable text field that can be dismissed and requests focus when it is expanded */

View file

@ -12,7 +12,14 @@ import android.view.WindowInsets.Type
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -20,6 +27,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import helium314.keyboard.compat.locale import helium314.keyboard.compat.locale
@ -98,10 +106,12 @@ class SettingsActivity : ComponentActivity(), SharedPreferences.OnSharedPreferen
|| !UncachedInputMethodManagerUtils.isThisImeEnabled(this, imm) || !UncachedInputMethodManagerUtils.isThisImeEnabled(this, imm)
) } ) }
if (spellchecker) if (spellchecker)
Column { // lazy way of implementing spell checker settings Scaffold(contentWindowInsets = WindowInsets.systemBars.union(WindowInsets.ime)) { innerPadding ->
Column(Modifier.padding(innerPadding)) { // lazy way of implementing spell checker settings
settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference() settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference()
settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference() settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference()
} }
}
else else
SettingsNavHost(onClickBack = { this.finish() }) SettingsNavHost(onClickBack = { this.finish() })
if (dictUri != null) { if (dictUri != null) {
@ -143,6 +153,8 @@ class SettingsActivity : ComponentActivity(), SharedPreferences.OnSharedPreferen
} }
intent = null intent = null
} }
enableEdgeToEdge()
} }
override fun onStart() { override fun onStart() {

View file

@ -2,8 +2,14 @@
package helium314.keyboard.settings.screens package helium314.keyboard.settings.screens
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -42,7 +48,11 @@ fun MainSettingsScreen(
settings = emptyList(), settings = emptyList(),
) { ) {
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(true) val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(true)
Column(Modifier.verticalScroll(rememberScrollState())) { Scaffold(contentWindowInsets = WindowInsets.systemBars.union(WindowInsets.ime)) { innerPadding ->
Column(
Modifier.verticalScroll(rememberScrollState())
.then(Modifier.padding(bottom = innerPadding.calculateBottomPadding()))
) {
Preference( Preference(
name = stringResource(R.string.language_and_layouts_title), name = stringResource(R.string.language_and_layouts_title),
description = enabledSubtypes.joinToString(", ") { it.displayName(ctx) }, description = enabledSubtypes.joinToString(", ") { it.displayName(ctx) },
@ -98,6 +108,7 @@ fun MainSettingsScreen(
} }
} }
} }
}
@Preview @Preview
@Composable @Composable

View file

@ -10,7 +10,9 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
@ -177,6 +179,7 @@ fun PersonalDictionaryScreen(
text = { Text(stringResource(R.string.user_dict_add_word_button)) }, text = { Text(stringResource(R.string.user_dict_add_word_button)) },
icon = { Icon(painter = painterResource(R.drawable.ic_edit), stringResource(R.string.user_dict_add_word_button)) }, icon = { Icon(painter = painterResource(R.drawable.ic_edit), stringResource(R.string.user_dict_add_word_button)) },
modifier = Modifier.wrapContentSize(Alignment.BottomEnd).padding(all = 12.dp) modifier = Modifier.wrapContentSize(Alignment.BottomEnd).padding(all = 12.dp)
.then(Modifier.systemBarsPadding().imePadding())
) )
} }

View file

@ -4,8 +4,12 @@ import android.content.Context
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -13,6 +17,7 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -140,8 +145,10 @@ fun SubtypeScreen(
itemContent = { }, itemContent = { },
filteredItems = { emptyList<String>() } filteredItems = { emptyList<String>() }
) { ) {
Scaffold(contentWindowInsets = WindowInsets.systemBars.union(WindowInsets.ime)) { innerPadding ->
Column( Column(
modifier = Modifier.verticalScroll(scrollState).padding(horizontal = 12.dp), modifier = Modifier.verticalScroll(scrollState).padding(horizontal = 12.dp)
.then(Modifier.padding(bottom = innerPadding.calculateBottomPadding())),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
MainLayoutRow(currentSubtype, customMainLayouts) { setCurrentSubtype(it) } MainLayoutRow(currentSubtype, customMainLayouts) { setCurrentSubtype(it) }
@ -172,7 +179,10 @@ fun SubtypeScreen(
if (currentSubtype.locale.script() == ScriptUtils.SCRIPT_LATIN) { if (currentSubtype.locale.script() == ScriptUtils.SCRIPT_LATIN) {
WithSmallTitle(stringResource(R.string.show_popup_keys_title)) { WithSmallTitle(stringResource(R.string.show_popup_keys_title)) {
val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS) val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
val value = explicitValue ?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)!! val value = explicitValue ?: prefs.getString(
Settings.PREF_MORE_POPUP_KEYS,
Defaults.PREF_MORE_POPUP_KEYS
)!!
Row { Row {
TextButton(onClick = { showMorePopupsDialog = true }, Modifier.weight(1f)) TextButton(onClick = { showMorePopupsDialog = true }, Modifier.weight(1f))
{ Text(stringResource(morePopupKeysResId(value))) } { Text(stringResource(morePopupKeysResId(value))) }
@ -187,7 +197,10 @@ fun SubtypeScreen(
val checked = currentSubtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean() val checked = currentSubtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean()
Text(stringResource(R.string.localized_number_row), Modifier.weight(1f)) Text(stringResource(R.string.localized_number_row), Modifier.weight(1f))
Switch( Switch(
checked = checked ?: prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW), checked = checked ?: prefs.getBoolean(
Settings.PREF_LOCALIZED_NUMBER_ROW,
Defaults.PREF_LOCALIZED_NUMBER_ROW
),
onCheckedChange = { onCheckedChange = {
setCurrentSubtype(currentSubtype.with(ExtraValue.LOCALIZED_NUMBER_ROW, it.toString())) setCurrentSubtype(currentSubtype.with(ExtraValue.LOCALIZED_NUMBER_ROW, it.toString()))
} }
@ -198,7 +211,10 @@ fun SubtypeScreen(
} }
} }
HorizontalDivider() HorizontalDivider()
Text(stringResource(R.string.settings_screen_secondary_layouts), style = MaterialTheme.typography.titleMedium) Text(
stringResource(R.string.settings_screen_secondary_layouts),
style = MaterialTheme.typography.titleMedium
)
LayoutType.entries.forEach { type -> LayoutType.entries.forEach { type ->
if (type == LayoutType.MAIN) return@forEach if (type == LayoutType.MAIN) return@forEach
WithSmallTitle(stringResource(type.displayNameId)) { WithSmallTitle(stringResource(type.displayNameId)) {
@ -212,11 +228,14 @@ fun SubtypeScreen(
onSelected = { onSelected = {
setCurrentSubtype(currentSubtype.withLayout(type, it)) setCurrentSubtype(currentSubtype.withLayout(type, it))
}, },
extraButton = { DefaultButton(explicitLayout == null) { extraButton = {
DefaultButton(explicitLayout == null) {
setCurrentSubtype(currentSubtype.withoutLayout(type)) setCurrentSubtype(currentSubtype.withoutLayout(type))
} }, }
},
) { ) {
val displayName = if (LayoutUtilsCustom.isCustomLayout(it)) LayoutUtilsCustom.getDisplayName(it) val displayName =
if (LayoutUtilsCustom.isCustomLayout(it)) LayoutUtilsCustom.getDisplayName(it)
else it.getStringResourceOrName("layout_", ctx) else it.getStringResourceOrName("layout_", ctx)
var showLayoutEditDialog by remember { mutableStateOf(false) } var showLayoutEditDialog by remember { mutableStateOf(false) }
Row( Row(
@ -226,7 +245,14 @@ fun SubtypeScreen(
) { ) {
Text(displayName) Text(displayName)
if (LayoutUtilsCustom.isCustomLayout(it)) if (LayoutUtilsCustom.isCustomLayout(it))
IconButton({ showLayoutEditDialog = true }) { Icon(painterResource(R.drawable.ic_edit), stringResource(R.string.edit_layout)) } IconButton({
showLayoutEditDialog = true
}) {
Icon(
painterResource(R.drawable.ic_edit),
stringResource(R.string.edit_layout)
)
}
} }
if (showLayoutEditDialog) if (showLayoutEditDialog)
LayoutEditDialog( LayoutEditDialog(
@ -260,7 +286,10 @@ fun SubtypeScreen(
val setting = currentSubtype.getExtraValueOf(ExtraValue.POPUP_ORDER) val setting = currentSubtype.getExtraValueOf(ExtraValue.POPUP_ORDER)
PopupOrderDialog( PopupOrderDialog(
onDismissRequest = { showKeyOrderDialog = false }, onDismissRequest = { showKeyOrderDialog = false },
initialValue = setting ?: prefs.getString(Settings.PREF_POPUP_KEYS_ORDER, Defaults.PREF_POPUP_KEYS_ORDER)!!, initialValue = setting ?: prefs.getString(
Settings.PREF_POPUP_KEYS_ORDER,
Defaults.PREF_POPUP_KEYS_ORDER
)!!,
title = stringResource(R.string.popup_order), title = stringResource(R.string.popup_order),
showDefault = setting != null, showDefault = setting != null,
onConfirmed = { onConfirmed = {
@ -275,7 +304,10 @@ fun SubtypeScreen(
val setting = currentSubtype.getExtraValueOf(ExtraValue.HINT_ORDER) val setting = currentSubtype.getExtraValueOf(ExtraValue.HINT_ORDER)
PopupOrderDialog( PopupOrderDialog(
onDismissRequest = { showHintOrderDialog = false }, onDismissRequest = { showHintOrderDialog = false },
initialValue = setting ?: prefs.getString(Settings.PREF_POPUP_KEYS_LABELS_ORDER, Defaults.PREF_POPUP_KEYS_LABELS_ORDER)!!, initialValue = setting ?: prefs.getString(
Settings.PREF_POPUP_KEYS_LABELS_ORDER,
Defaults.PREF_POPUP_KEYS_LABELS_ORDER
)!!,
title = stringResource(R.string.hint_source), title = stringResource(R.string.hint_source),
showDefault = setting != null, showDefault = setting != null,
onConfirmed = { onConfirmed = {
@ -299,6 +331,7 @@ fun SubtypeScreen(
) )
} }
} }
}
// from ReorderSwitchPreference // from ReorderSwitchPreference