add generic reorder dialog and prelimiary toolbar pref screen

This commit is contained in:
Helium314 2025-01-28 16:14:42 +01:00
parent d61963453f
commit ce37888985
9 changed files with 203 additions and 14 deletions

View file

@ -114,6 +114,7 @@ dependencies {
implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")
implementation("androidx.navigation:navigation-compose:2.8.5") implementation("androidx.navigation:navigation-compose:2.8.5")
implementation("sh.calvin.reorderable:reorderable:2.4.2")
// color picker for user-defined colors // color picker for user-defined colors
implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0") implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0")

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import helium314.keyboard.settings.screens.createAboutPrefs import helium314.keyboard.settings.screens.createAboutPrefs
import helium314.keyboard.settings.screens.createCorrectionPrefs import helium314.keyboard.settings.screens.createCorrectionPrefs
import helium314.keyboard.settings.screens.createPreferencesPrefs import helium314.keyboard.settings.screens.createPreferencesPrefs
import helium314.keyboard.settings.screens.createToolbarPrefs
class AllPrefs(context: Context) { class AllPrefs(context: Context) {
private val list = createPrefDefs(context) private val list = createPrefDefs(context)
@ -51,7 +52,7 @@ class PrefDef(
} }
private fun createPrefDefs(context: Context) = createAboutPrefs(context) + private fun createPrefDefs(context: Context) = createAboutPrefs(context) +
createCorrectionPrefs(context) + createPreferencesPrefs(context) createCorrectionPrefs(context) + createPreferencesPrefs(context) + createToolbarPrefs(context)
// todo: move somewhere else // todo: move somewhere else
fun Context.getActivity(): ComponentActivity? { fun Context.getActivity(): ComponentActivity? {

View file

@ -14,10 +14,13 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
// todo // todo
// add reorder / enable dialog (maybe not as dialog?) // add reorder / enable dialog
// reorder part is already done
// more pref screens, and other super-custom things // more pref screens, and other super-custom things
// consider IME insets when searching // consider IME insets when searching
// improve performance when loading screens with many settings (lazyColumn?) // improve performance when loading screens with many settings (lazyColumn?)
// screens could have a lazy column of preferences and category separators, and the list has an if-setting-then-null for hiding
// lazyColumn also has the key, this should be used! and must be unique
// consider that stuff in composables can get called quite often on any changes -> use remember for things that are slow (maybe add test logging) // consider that stuff in composables can get called quite often on any changes -> use remember for things that are slow (maybe add test logging)
// later // later
@ -29,7 +32,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
// rename some classes // rename some classes
// split the preferences in allPrefs.createDefs into multiple files, this will get horribly long // split the preferences in allPrefs.createDefs into multiple files, this will get horribly long
// maybe have sub-lists in the pref screens using the settings? // maybe have sub-lists in the pref screens using the settings?
// spdx headers everywhere // spdx headers everywhere (except DragDropColumn, which is from stackoverflow without explicit license)
// changes to anything but the compose settings package should not be in the initial PR // changes to anything but the compose settings package should not be in the initial PR
// commit them separately if possible // commit them separately if possible
// though some might be necessary // though some might be necessary
@ -43,6 +46,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
// show as disabled -> users confused // show as disabled -> users confused
// show (but change will not do anything because another setting needs to be enabled first) // show (but change will not do anything because another setting needs to be enabled first)
// -> users confused, but probably better than the 2 above // -> users confused, but probably better than the 2 above
// adjust layout a little, there is too much empty space
// maybe later // maybe later
// weird problem with app sometimes closing on back, but that's related to "old" settings (don't care if all are removed) // weird problem with app sometimes closing on back, but that's related to "old" settings (don't care if all are removed)
@ -69,6 +73,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
// another 300 kb when switching material2 to material3 // another 300 kb when switching material2 to material3
// ca 150 kb reduction when removing androidx.preference // ca 150 kb reduction when removing androidx.preference
// -> too much, but still ok if we can get nicer preference stuff // -> too much, but still ok if we can get nicer preference stuff
// meh, and using a TextField adds another 300 kb... huge chunks for sth that seems so small
class SettingsActivity2 : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener { class SettingsActivity2 : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(this) } private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(this) }

View file

@ -14,6 +14,7 @@ import helium314.keyboard.settings.screens.AboutScreen
import helium314.keyboard.settings.screens.MainSettingsScreen import helium314.keyboard.settings.screens.MainSettingsScreen
import helium314.keyboard.settings.screens.PreferencesScreen import helium314.keyboard.settings.screens.PreferencesScreen
import helium314.keyboard.settings.screens.TextCorrectionScreen import helium314.keyboard.settings.screens.TextCorrectionScreen
import helium314.keyboard.settings.screens.ToolbarScreen
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -48,6 +49,7 @@ fun SettingsNavHost(
onClickAbout = { navController.navigate(SettingsDestination.About) }, onClickAbout = { navController.navigate(SettingsDestination.About) },
onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) }, onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) },
onClickPreferences = { navController.navigate(SettingsDestination.Preferences) }, onClickPreferences = { navController.navigate(SettingsDestination.Preferences) },
onClickToolbar = { navController.navigate(SettingsDestination.Toolbar) },
onClickBack = ::goBack, onClickBack = ::goBack,
) )
} }
@ -66,6 +68,11 @@ fun SettingsNavHost(
onClickBack = ::goBack onClickBack = ::goBack
) )
} }
composable(SettingsDestination.Toolbar) {
ToolbarScreen (
onClickBack = ::goBack
)
}
} }
} }
@ -74,6 +81,7 @@ object SettingsDestination {
const val About = "about" const val About = "about"
const val TextCorrection = "text_correction" const val TextCorrection = "text_correction"
const val Preferences = "preferences" const val Preferences = "preferences"
const val Toolbar = "toolbar"
val navTarget = MutableStateFlow(Settings) val navTarget = MutableStateFlow(Settings)
private val navScope = CoroutineScope(Dispatchers.Default) private val navScope = CoroutineScope(Dispatchers.Default)

View file

@ -0,0 +1,105 @@
package helium314.keyboard.settings.dialogs
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.contentColorFor
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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import helium314.keyboard.latin.R
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyListState
@Composable
fun <T: Any> ReorderDialog(
onDismissRequest: () -> Unit,
onConfirmed: (List<T>) -> Unit,
items: List<T>,
getKey: (T) -> Any, // actually it's not "Any", but "anything that can be stored in a bundle"
displayItem: @Composable (T) -> Unit,
modifier: Modifier = Modifier,
title: @Composable (() -> Unit)? = null,
confirmButtonText: String = stringResource(android.R.string.ok),
cancelButtonText: String = stringResource(android.R.string.cancel),
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(backgroundColor),
properties: DialogProperties = DialogProperties(),
) {
var reorderableItems by remember(items) { mutableStateOf(items) }
val listState = rememberLazyListState()
val dragDropState = rememberReorderableLazyListState(listState) { from, to ->
reorderableItems = reorderableItems.toMutableList().apply {
add(to.index, removeAt(from.index))
}
}
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = { onConfirmed(reorderableItems); onDismissRequest() }) { Text(confirmButtonText) }
},
modifier = modifier,
dismissButton = { TextButton(onClick = onDismissRequest) { Text(cancelButtonText) } },
title = title,
text = {
LazyColumn(
state = listState,
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
items(reorderableItems, key = getKey) { item ->
ReorderableItem(
state = dragDropState,
key = getKey(item)
) { dragging ->
val elevation by animateDpAsState(if (dragging) 4.dp else 0.dp)
Surface(shadowElevation = elevation) {
Row(modifier = Modifier.longPressDraggableHandle()) {
Icon(painterResource(R.drawable.ic_drag_indicator), "Reorder")
displayItem(item)
}
}
}
}
}
},
shape = shape,
containerColor = backgroundColor,
textContentColor = contentColor,
properties = properties,
)
}
@Preview
@Composable
private fun Preview() {
ReorderDialog(
onConfirmed = {},
onDismissRequest = {},
items = listOf(1, 2, 3),
displayItem = { Text(it.toString(), Modifier.fillMaxWidth(), textAlign = TextAlign.Center) },
getKey = { it.toString() }
)
}

View file

@ -1,8 +1,6 @@
package helium314.keyboard.settings.dialogs package helium314.keyboard.settings.dialogs
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
@ -23,7 +21,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import helium314.keyboard.latin.R import helium314.keyboard.latin.R

View file

@ -36,6 +36,7 @@ fun MainSettingsScreen(
onClickAbout: () -> Unit, onClickAbout: () -> Unit,
onClickTextCorrection: () -> Unit, onClickTextCorrection: () -> Unit,
onClickPreferences: () -> Unit, onClickPreferences: () -> Unit,
onClickToolbar: () -> Unit,
onClickBack: () -> Unit, onClickBack: () -> Unit,
) { ) {
val ctx = LocalContext.current val ctx = LocalContext.current
@ -54,6 +55,17 @@ fun MainSettingsScreen(
contentDescription = null contentDescription = null
) )
} }
Preference(
name = stringResource(R.string.settings_screen_toolbar),
onClick = onClickToolbar,
icon = R.drawable.ic_settings_toolbar_foreground
) {
Icon(
painter = painterResource(R.drawable.ic_arrow_left),
modifier = Modifier.scale(-1f, 1f),
contentDescription = null
)
}
Preference( Preference(
name = stringResource(R.string.settings_screen_correction), name = stringResource(R.string.settings_screen_correction),
onClick = onClickTextCorrection, onClick = onClickTextCorrection,
@ -129,7 +141,7 @@ fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
private fun PreviewScreen() { private fun PreviewScreen() {
Theme(true) { Theme(true) {
Surface { Surface {
MainSettingsScreen({}, {}, {}, {}) MainSettingsScreen({}, {}, {}, {}, {})
} }
} }
} }

View file

@ -4,25 +4,18 @@ import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable 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.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.AudioAndHapticFeedbackManager
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.settings.AllPrefs import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.PrefDef import helium314.keyboard.settings.PrefDef
import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.SearchPrefScreen import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2 import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.SliderPreference import helium314.keyboard.settings.SliderPreference
import helium314.keyboard.settings.Theme import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.dialogs.SliderDialog
@Composable @Composable
fun PreferencesScreen( fun PreferencesScreen(

View file

@ -0,0 +1,67 @@
package helium314.keyboard.settings.screens
import android.content.Context
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Surface
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.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.PrefDef
import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.dialogs.ReorderDialog
@Composable
fun ToolbarScreen(
onClickBack: () -> Unit,
) {
SearchPrefScreen(
onClickBack = onClickBack,
title = stringResource(R.string.settings_screen_toolbar),
) {
SettingsActivity2.allPrefs.map[Settings.PREF_PINNED_TOOLBAR_KEYS]!!.Preference()
}
}
fun createToolbarPrefs(context: Context) = listOf(
PrefDef(context, Settings.PREF_PINNED_TOOLBAR_KEYS, R.string.pinned_toolbar_keys) { def ->
var showDialog by remember { mutableStateOf(false) }
Preference(
name = def.title,
onClick = { showDialog = true },
)
if (showDialog) {
ReorderDialog(
onConfirmed = { },
onDismissRequest = { showDialog = false },
items = (1..40).toList(),
displayItem = { Text(it.toString(), Modifier.fillMaxWidth(), textAlign = TextAlign.Center) },
getKey = { it.hashCode() }
)
}
},
)
@Preview
@Composable
private fun Preview() {
SettingsActivity2.allPrefs = AllPrefs(LocalContext.current)
Theme(true) {
Surface {
ToolbarScreen { }
}
}
}