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 helium314.keyboard.settings.screens.createAboutPrefs
|
||||
import helium314.keyboard.settings.screens.createCorrectionPrefs
|
||||
import helium314.keyboard.settings.screens.createPreferencesPrefs
|
||||
|
||||
class AllPrefs(context: 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
|
||||
fun Context.getActivity(): ComponentActivity? {
|
||||
|
|
|
@ -22,6 +22,10 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
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.Modifier
|
||||
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.utils.DeviceProtectedUtils
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.settings.dialogs.SliderDialog
|
||||
|
||||
// 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
|
||||
@Composable
|
||||
private fun PreferencePreview() {
|
||||
|
@ -196,6 +246,13 @@ private fun PreferencePreview() {
|
|||
onClick = {},
|
||||
icon = R.drawable.ic_settings_about_foreground
|
||||
)
|
||||
SliderPreference(
|
||||
name = "SliderPreference",
|
||||
pref = "",
|
||||
default = 1,
|
||||
description = { it.toString() },
|
||||
range = -5f..5f
|
||||
)
|
||||
Preference(
|
||||
name = "Preference with icon and description",
|
||||
description = "some text",
|
||||
|
|
|
@ -14,7 +14,8 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
|
@ -44,6 +45,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
// -> users confused, but probably better than the 2 above
|
||||
|
||||
// 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)
|
||||
// remove navHost? but probably too useful to have...
|
||||
// lazyColumn for prefs (or just in category?)
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -46,6 +47,7 @@ fun SettingsNavHost(
|
|||
MainSettingsScreen(
|
||||
onClickAbout = { navController.navigate(SettingsDestination.About) },
|
||||
onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) },
|
||||
onClickPreferences = { navController.navigate(SettingsDestination.Preferences) },
|
||||
onClickBack = ::goBack,
|
||||
)
|
||||
}
|
||||
|
@ -59,6 +61,11 @@ fun SettingsNavHost(
|
|||
onClickBack = ::goBack
|
||||
)
|
||||
}
|
||||
composable(SettingsDestination.Preferences) {
|
||||
PreferencesScreen (
|
||||
onClickBack = ::goBack
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +73,7 @@ object SettingsDestination {
|
|||
const val Settings = "settings"
|
||||
const val About = "about"
|
||||
const val TextCorrection = "text_correction"
|
||||
const val Preferences = "preferences"
|
||||
val navTarget = MutableStateFlow(Settings)
|
||||
|
||||
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(
|
||||
onClickAbout: () -> Unit,
|
||||
onClickTextCorrection: () -> Unit,
|
||||
onClickPreferences: () -> Unit,
|
||||
onClickBack: () -> Unit,
|
||||
) {
|
||||
val ctx = LocalContext.current
|
||||
|
@ -42,6 +43,17 @@ fun MainSettingsScreen(
|
|||
onClickBack = onClickBack,
|
||||
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(
|
||||
name = stringResource(R.string.settings_screen_correction),
|
||||
onClick = onClickTextCorrection,
|
||||
|
@ -117,7 +129,7 @@ fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
|
|||
private fun PreviewScreen() {
|
||||
Theme(true) {
|
||||
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