diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a8941e866..e711f9c0e 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -114,6 +114,7 @@ dependencies { implementation("androidx.compose.ui:ui-tooling-preview") debugImplementation("androidx.compose.ui:ui-tooling") implementation("androidx.navigation:navigation-compose:2.8.5") + implementation("sh.calvin.reorderable:reorderable:2.4.2") // color picker for user-defined colors implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0") diff --git a/app/src/main/java/helium314/keyboard/settings/AllPrefs.kt b/app/src/main/java/helium314/keyboard/settings/AllPrefs.kt index 5e41f00eb..19eee83e4 100644 --- a/app/src/main/java/helium314/keyboard/settings/AllPrefs.kt +++ b/app/src/main/java/helium314/keyboard/settings/AllPrefs.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import helium314.keyboard.settings.screens.createAboutPrefs import helium314.keyboard.settings.screens.createCorrectionPrefs import helium314.keyboard.settings.screens.createPreferencesPrefs +import helium314.keyboard.settings.screens.createToolbarPrefs class AllPrefs(context: Context) { private val list = createPrefDefs(context) @@ -51,7 +52,7 @@ class PrefDef( } private fun createPrefDefs(context: Context) = createAboutPrefs(context) + - createCorrectionPrefs(context) + createPreferencesPrefs(context) + createCorrectionPrefs(context) + createPreferencesPrefs(context) + createToolbarPrefs(context) // todo: move somewhere else fun Context.getActivity(): ComponentActivity? { diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt index 84f298d10..32768f320 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt @@ -14,10 +14,13 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils import kotlinx.coroutines.flow.MutableStateFlow // 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 // consider IME insets when searching // 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) // later @@ -29,7 +32,7 @@ import kotlinx.coroutines.flow.MutableStateFlow // rename some classes // 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? -// 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 // commit them separately if possible // though some might be necessary @@ -43,6 +46,7 @@ import kotlinx.coroutines.flow.MutableStateFlow // show as disabled -> users confused // show (but change will not do anything because another setting needs to be enabled first) // -> users confused, but probably better than the 2 above +// adjust layout a little, there is too much empty space // maybe later // 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 // ca 150 kb reduction when removing androidx.preference // -> 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 { private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(this) } diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt index 9a584c2fc..a69946e17 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt @@ -14,6 +14,7 @@ import helium314.keyboard.settings.screens.AboutScreen import helium314.keyboard.settings.screens.MainSettingsScreen import helium314.keyboard.settings.screens.PreferencesScreen import helium314.keyboard.settings.screens.TextCorrectionScreen +import helium314.keyboard.settings.screens.ToolbarScreen import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -48,6 +49,7 @@ fun SettingsNavHost( onClickAbout = { navController.navigate(SettingsDestination.About) }, onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) }, onClickPreferences = { navController.navigate(SettingsDestination.Preferences) }, + onClickToolbar = { navController.navigate(SettingsDestination.Toolbar) }, onClickBack = ::goBack, ) } @@ -66,6 +68,11 @@ fun SettingsNavHost( onClickBack = ::goBack ) } + composable(SettingsDestination.Toolbar) { + ToolbarScreen ( + onClickBack = ::goBack + ) + } } } @@ -74,6 +81,7 @@ object SettingsDestination { const val About = "about" const val TextCorrection = "text_correction" const val Preferences = "preferences" + const val Toolbar = "toolbar" val navTarget = MutableStateFlow(Settings) private val navScope = CoroutineScope(Dispatchers.Default) diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt new file mode 100644 index 000000000..9ba187f9b --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt @@ -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 ReorderDialog( + onDismissRequest: () -> Unit, + onConfirmed: (List) -> Unit, + items: List, + 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() } + ) +} diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt index 1fc854bce..ea583de30 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt @@ -1,8 +1,6 @@ package helium314.keyboard.settings.dialogs -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import helium314.keyboard.latin.R diff --git a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt index fca15b450..871bb3d1e 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt @@ -36,6 +36,7 @@ fun MainSettingsScreen( onClickAbout: () -> Unit, onClickTextCorrection: () -> Unit, onClickPreferences: () -> Unit, + onClickToolbar: () -> Unit, onClickBack: () -> Unit, ) { val ctx = LocalContext.current @@ -54,6 +55,17 @@ fun MainSettingsScreen( 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( name = stringResource(R.string.settings_screen_correction), onClick = onClickTextCorrection, @@ -129,7 +141,7 @@ fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) { private fun PreviewScreen() { Theme(true) { Surface { - MainSettingsScreen({}, {}, {}, {}) + MainSettingsScreen({}, {}, {}, {}, {}) } } } diff --git a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt index 767b95f85..555e48f76 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt @@ -4,25 +4,18 @@ import android.content.Context import android.media.AudioManager import androidx.compose.material3.Surface 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.res.stringResource import androidx.compose.ui.tooling.preview.Preview import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.R import helium314.keyboard.latin.settings.Settings -import helium314.keyboard.latin.utils.DeviceProtectedUtils 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.SliderPreference import helium314.keyboard.settings.Theme -import helium314.keyboard.settings.dialogs.SliderDialog @Composable fun PreferencesScreen( diff --git a/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt new file mode 100644 index 000000000..888721ece --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt @@ -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 { } + } + } +}