mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-14 14:02:44 +00:00
add sliderPreference and a partial PreferencesScreen
This commit is contained in:
parent
e7c2301643
commit
3563ded922
7 changed files with 283 additions and 3 deletions
|
@ -8,6 +8,7 @@ import androidx.annotation.StringRes
|
||||||
import androidx.compose.runtime.Composable
|
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
|
||||||
|
|
||||||
class AllPrefs(context: Context) {
|
class AllPrefs(context: Context) {
|
||||||
private val list = createPrefDefs(context)
|
private val list = createPrefDefs(context)
|
||||||
|
@ -49,7 +50,8 @@ class PrefDef(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPrefDefs(context: Context) = createAboutPrefs(context) + createCorrectionPrefs(context)
|
private fun createPrefDefs(context: Context) = createAboutPrefs(context) +
|
||||||
|
createCorrectionPrefs(context) + createPreferencesPrefs(context)
|
||||||
|
|
||||||
// todo: move somewhere else
|
// todo: move somewhere else
|
||||||
fun Context.getActivity(): ComponentActivity? {
|
fun Context.getActivity(): ComponentActivity? {
|
||||||
|
|
|
@ -22,6 +22,10 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
@ -33,6 +37,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import helium314.keyboard.latin.R
|
import helium314.keyboard.latin.R
|
||||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||||
import helium314.keyboard.latin.utils.Log
|
import helium314.keyboard.latin.utils.Log
|
||||||
|
import helium314.keyboard.settings.dialogs.SliderDialog
|
||||||
|
|
||||||
// taken from StreetComplete (and a bit SCEE)
|
// taken from StreetComplete (and a bit SCEE)
|
||||||
|
|
||||||
|
@ -183,6 +188,51 @@ fun SwitchPreference(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
/** Slider preference for Int or Float (weird casting stuff, but should be fine) */
|
||||||
|
fun <T: Number> SliderPreference(
|
||||||
|
name: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
pref: String,
|
||||||
|
description: @Composable (T) -> String,
|
||||||
|
default: T,
|
||||||
|
range: ClosedFloatingPointRange<Float>,
|
||||||
|
onValueChanged: (Float) -> Unit = { },
|
||||||
|
) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
val prefs = DeviceProtectedUtils.getSharedPreferences(ctx)
|
||||||
|
val b = (ctx.getActivity() as? SettingsActivity2)?.prefChanged?.collectAsState()
|
||||||
|
if (b?.value ?: 0 < 0)
|
||||||
|
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||||
|
val initialValue = if (default is Int) prefs.getInt(pref, default)
|
||||||
|
else if (default is Float) prefs.getFloat(pref, default)
|
||||||
|
else throw IllegalArgumentException("only float and int are supported")
|
||||||
|
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
Preference(
|
||||||
|
name = name,
|
||||||
|
onClick = { showDialog = true },
|
||||||
|
modifier = modifier,
|
||||||
|
description = description(initialValue as T)
|
||||||
|
)
|
||||||
|
if (showDialog)
|
||||||
|
SliderDialog(
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
onDone = {
|
||||||
|
if (default is Int) prefs.edit().putInt(pref, it.toInt()).apply()
|
||||||
|
else prefs.edit().putFloat(pref, it).apply()
|
||||||
|
},
|
||||||
|
initialValue = initialValue.toFloat(),
|
||||||
|
range = range,
|
||||||
|
positionString = {
|
||||||
|
description((if (default is Int) it.toInt() else it) as T)
|
||||||
|
},
|
||||||
|
onValueChanged = onValueChanged,
|
||||||
|
showDefault = true,
|
||||||
|
onDefault = { prefs.edit().remove(pref).apply() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreferencePreview() {
|
private fun PreferencePreview() {
|
||||||
|
@ -196,6 +246,13 @@ private fun PreferencePreview() {
|
||||||
onClick = {},
|
onClick = {},
|
||||||
icon = R.drawable.ic_settings_about_foreground
|
icon = R.drawable.ic_settings_about_foreground
|
||||||
)
|
)
|
||||||
|
SliderPreference(
|
||||||
|
name = "SliderPreference",
|
||||||
|
pref = "",
|
||||||
|
default = 1,
|
||||||
|
description = { it.toString() },
|
||||||
|
range = -5f..5f
|
||||||
|
)
|
||||||
Preference(
|
Preference(
|
||||||
name = "Preference with icon and description",
|
name = "Preference with icon and description",
|
||||||
description = "some text",
|
description = "some text",
|
||||||
|
|
|
@ -14,7 +14,8 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
// more pref screens, seekBarPref, reorderDialog, and other super-custom things
|
// add reorder / enable dialog (maybe not as dialog?)
|
||||||
|
// 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?)
|
||||||
// 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)
|
||||||
|
@ -44,6 +45,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
// -> users confused, but probably better than the 2 above
|
// -> users confused, but probably better than the 2 above
|
||||||
|
|
||||||
// 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)
|
||||||
// bottom text field (though we have the search now anyway)
|
// bottom text field (though we have the search now anyway)
|
||||||
// remove navHost? but probably too useful to have...
|
// remove navHost? but probably too useful to have...
|
||||||
// lazyColumn for prefs (or just in category?)
|
// lazyColumn for prefs (or just in category?)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import helium314.keyboard.settings.screens.AboutScreen
|
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.TextCorrectionScreen
|
import helium314.keyboard.settings.screens.TextCorrectionScreen
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -46,6 +47,7 @@ fun SettingsNavHost(
|
||||||
MainSettingsScreen(
|
MainSettingsScreen(
|
||||||
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) },
|
||||||
onClickBack = ::goBack,
|
onClickBack = ::goBack,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +61,11 @@ fun SettingsNavHost(
|
||||||
onClickBack = ::goBack
|
onClickBack = ::goBack
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(SettingsDestination.Preferences) {
|
||||||
|
PreferencesScreen (
|
||||||
|
onClickBack = ::goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +73,7 @@ object SettingsDestination {
|
||||||
const val Settings = "settings"
|
const val Settings = "settings"
|
||||||
const val About = "about"
|
const val About = "about"
|
||||||
const val TextCorrection = "text_correction"
|
const val TextCorrection = "text_correction"
|
||||||
|
const val Preferences = "preferences"
|
||||||
val navTarget = MutableStateFlow(Settings)
|
val navTarget = MutableStateFlow(Settings)
|
||||||
|
|
||||||
private val navScope = CoroutineScope(Dispatchers.Default)
|
private val navScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package helium314.keyboard.settings.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
|
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.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SliderDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onDone: (Float) -> Unit,
|
||||||
|
initialValue: Float,
|
||||||
|
range: ClosedFloatingPointRange<Float>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
showDefault: Boolean = false,
|
||||||
|
onDefault: () -> Unit? = { },
|
||||||
|
onValueChanged: (Float) -> Unit = { },
|
||||||
|
title: (@Composable () -> Unit)? = null,
|
||||||
|
intermediateSteps: Int? = null,
|
||||||
|
positionString: (@Composable (Float) -> String) = { it.toString() },
|
||||||
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
|
backgroundColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
|
contentColor: Color = contentColorFor(backgroundColor),
|
||||||
|
properties: DialogProperties = DialogProperties()
|
||||||
|
) {
|
||||||
|
var sliderPosition by remember { mutableFloatStateOf(initialValue) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { onDismissRequest(); onDone(sliderPosition) },
|
||||||
|
) { Text(stringResource(android.R.string.ok)) }
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
dismissButton = {
|
||||||
|
Row {
|
||||||
|
if (showDefault)
|
||||||
|
TextButton(onClick = { onDismissRequest(); onDefault() })
|
||||||
|
{ Text(stringResource(R.string.button_default)) }
|
||||||
|
TextButton(onClick = onDismissRequest)
|
||||||
|
{ Text(stringResource(android.R.string.cancel)) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = title,
|
||||||
|
text = {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalTextStyle provides MaterialTheme.typography.bodyLarge
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
if (intermediateSteps == null)
|
||||||
|
Slider(
|
||||||
|
value = sliderPosition,
|
||||||
|
onValueChange = { sliderPosition = it },
|
||||||
|
onValueChangeFinished = { onValueChanged(sliderPosition) },
|
||||||
|
valueRange = range,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Slider(
|
||||||
|
value = sliderPosition,
|
||||||
|
onValueChange = { sliderPosition = it },
|
||||||
|
onValueChangeFinished = { onValueChanged(sliderPosition) },
|
||||||
|
valueRange = range,
|
||||||
|
steps = intermediateSteps
|
||||||
|
)
|
||||||
|
Text(positionString(sliderPosition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = shape,
|
||||||
|
containerColor = backgroundColor,
|
||||||
|
textContentColor = contentColor,
|
||||||
|
properties = properties,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun PreviewSliderDialog() {
|
||||||
|
SliderDialog(
|
||||||
|
onDismissRequest = { },
|
||||||
|
onDone = { },
|
||||||
|
initialValue = 100f,
|
||||||
|
range = 0f..500f,
|
||||||
|
title = { Text("move it") },
|
||||||
|
showDefault = true
|
||||||
|
)
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import helium314.keyboard.settings.getActivity
|
||||||
fun MainSettingsScreen(
|
fun MainSettingsScreen(
|
||||||
onClickAbout: () -> Unit,
|
onClickAbout: () -> Unit,
|
||||||
onClickTextCorrection: () -> Unit,
|
onClickTextCorrection: () -> Unit,
|
||||||
|
onClickPreferences: () -> Unit,
|
||||||
onClickBack: () -> Unit,
|
onClickBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
|
@ -42,6 +43,17 @@ fun MainSettingsScreen(
|
||||||
onClickBack = onClickBack,
|
onClickBack = onClickBack,
|
||||||
title = stringResource(R.string.ime_settings),
|
title = stringResource(R.string.ime_settings),
|
||||||
) {
|
) {
|
||||||
|
Preference(
|
||||||
|
name = stringResource(R.string.settings_screen_preferences),
|
||||||
|
onClick = onClickPreferences,
|
||||||
|
icon = R.drawable.ic_settings_preferences_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,
|
||||||
|
@ -117,7 +129,7 @@ fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
|
||||||
private fun PreviewScreen() {
|
private fun PreviewScreen() {
|
||||||
Theme(true) {
|
Theme(true) {
|
||||||
Surface {
|
Surface {
|
||||||
MainSettingsScreen({}, {}, {})
|
MainSettingsScreen({}, {}, {}, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package helium314.keyboard.settings.screens
|
||||||
|
|
||||||
|
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(
|
||||||
|
onClickBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
SearchPrefScreen(
|
||||||
|
onClickBack = onClickBack,
|
||||||
|
title = stringResource(R.string.settings_screen_preferences),
|
||||||
|
) {
|
||||||
|
SettingsActivity2.allPrefs.map[Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME]!!.Preference()
|
||||||
|
SettingsActivity2.allPrefs.map[Settings.PREF_VIBRATION_DURATION_SETTINGS]!!.Preference()
|
||||||
|
SettingsActivity2.allPrefs.map[Settings.PREF_KEYPRESS_SOUND_VOLUME]!!.Preference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPreferencesPrefs(context: Context) = listOf(
|
||||||
|
PrefDef(context, Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, R.string.clipboard_history_retention_time) { def ->
|
||||||
|
SliderPreference(
|
||||||
|
name = def.title,
|
||||||
|
pref = def.key,
|
||||||
|
default = 10,
|
||||||
|
description = {
|
||||||
|
if (it < 0) stringResource(R.string.settings_no_limit)
|
||||||
|
else stringResource(R.string.abbreviation_unit_minutes, it.toString())
|
||||||
|
},
|
||||||
|
range = -1f..120f,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
PrefDef(context, Settings.PREF_VIBRATION_DURATION_SETTINGS, R.string.prefs_keypress_vibration_duration_settings) { def ->
|
||||||
|
SliderPreference(
|
||||||
|
name = def.title,
|
||||||
|
pref = def.key,
|
||||||
|
default = -1,
|
||||||
|
description = {
|
||||||
|
if (it < 0) stringResource(R.string.settings_system_default)
|
||||||
|
else stringResource(R.string.abbreviation_unit_milliseconds, it.toString())
|
||||||
|
},
|
||||||
|
range = -1f..100f,
|
||||||
|
onValueChanged = { AudioAndHapticFeedbackManager.getInstance().vibrate(it.toLong()) }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
PrefDef(context, Settings.PREF_KEYPRESS_SOUND_VOLUME, R.string.prefs_keypress_sound_volume_settings) { def ->
|
||||||
|
val audioManager = LocalContext.current.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
SliderPreference(
|
||||||
|
name = def.title,
|
||||||
|
pref = def.key,
|
||||||
|
default = -0.01f,
|
||||||
|
description = {
|
||||||
|
if (it < 0) stringResource(R.string.settings_system_default)
|
||||||
|
else (it * 100).toInt().toString()
|
||||||
|
},
|
||||||
|
range = -0.01f..1f,
|
||||||
|
onValueChanged = { audioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, it) }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun Preview() {
|
||||||
|
SettingsActivity2.allPrefs = AllPrefs(LocalContext.current)
|
||||||
|
Theme(true) {
|
||||||
|
Surface {
|
||||||
|
PreferencesScreen { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue