diff --git a/app/src/main/java/dev/kaizen/app/haptics/Haptics.kt b/app/src/main/java/dev/kaizen/app/haptics/Haptics.kt index ee74a59..58ea9c1 100644 --- a/app/src/main/java/dev/kaizen/app/haptics/Haptics.kt +++ b/app/src/main/java/dev/kaizen/app/haptics/Haptics.kt @@ -36,47 +36,53 @@ class Haptics(private val vibrator: Vibrator?) { } } - /** Firmer click — send button. */ + /** Firmer click — send button, satisfying "message sent" confirmation. */ fun click() { val vib = active() ?: return if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_CLICK)) { - vib.composePrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK to 0.85f) + vib.composePrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK to 1.0f) } else { vib.predefinedOrOneShot(soft = false) } } - /** Very soft — the model received the prompt and starts thinking. */ + /** Double-tap — the model received the prompt and starts thinking. */ fun thinkingStart() { - val vib = active() ?: return - if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_TICK)) { - vib.composePrimitives(VibrationEffect.Composition.PRIMITIVE_TICK to 0.22f) - } else { - vib.predefinedOrOneShot(soft = true) - } - } - - /** "The answer is arriving" — a three-step rising crescendo (the key moment). */ - fun responseStart() { val vib = active() ?: return if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_TICK)) { vib.composePrimitives( - Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.30f, 0), - Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.60f, 40), - Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 1.00f, 40), + Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0), + Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.7f, 60), ) } else { vib.predefinedOrOneShot(soft = false) } } - /** Gentle settle — stream finished. */ + /** Rising crescendo — first token arrived, answer is streaming. */ + fun responseStart() { + val vib = active() ?: return + if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_CLICK)) { + vib.composePrimitives( + Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, 0), + Triple(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.7f, 50), + Triple(VibrationEffect.Composition.PRIMITIVE_CLICK, 1.0f, 50), + ) + } else { + vib.predefinedOrOneShot(soft = false) + } + } + + /** Firm settle — stream finished, answer complete. */ fun responseEnd() { val vib = active() ?: return - if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_TICK)) { - vib.composePrimitives(VibrationEffect.Composition.PRIMITIVE_TICK to 0.3f) + if (supportsPrimitive(vib, VibrationEffect.Composition.PRIMITIVE_CLICK)) { + vib.composePrimitives( + Triple(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.8f, 0), + Triple(VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, 80), + ) } else { - vib.predefinedOrOneShot(soft = true) + vib.predefinedOrOneShot(soft = false) } } diff --git a/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt b/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt index b24080d..b61e882 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt @@ -45,6 +45,7 @@ import dev.kaizen.app.ui.theme.themes.KaizenThemeId import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.platform.LocalContext import dev.kaizen.app.chat.MeshBackground /** @@ -129,7 +130,8 @@ fun SettingsScreen( SettingsOptionRow(title = stringResource(R.string.settings_language_title), description = stringResource(R.string.settings_language_desc), icon = Icons.Rounded.Language) { Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) { listOf("system" to R.string.settings_language_system, "de" to R.string.settings_language_de, "en" to R.string.settings_language_en).forEach { (code, labelRes) -> - AppearanceTogglePill(selected = locale == code, label = stringResource(labelRes), onClick = { viewModel.selectLocale(code) }) + val ctx = LocalContext.current + AppearanceTogglePill(selected = locale == code, label = stringResource(labelRes), onClick = { viewModel.selectLocale(code, ctx as? android.app.Activity) }) } } } diff --git a/app/src/main/java/dev/kaizen/app/settings/SettingsViewModel.kt b/app/src/main/java/dev/kaizen/app/settings/SettingsViewModel.kt index 403cd6b..110bfe5 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SettingsViewModel.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SettingsViewModel.kt @@ -51,8 +51,22 @@ class SettingsViewModel(private val store: SecureStore? = null) : ViewModel() { _userEmail.value = email } - fun selectLocale(locale: String) { + fun selectLocale(locale: String, activity: android.app.Activity? = null) { _locale.value = locale store?.saveLocale(locale) + val localeList = if (locale == "system") { + android.os.LocaleList.getDefault() + } else { + android.os.LocaleList(java.util.Locale.forLanguageTag(locale)) + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { + activity?.getSystemService(android.app.LocaleManager::class.java)?.applicationLocales = localeList + } else { + val config = android.content.res.Configuration(activity?.resources?.configuration) + config.setLocales(localeList) + @Suppress("DEPRECATION") + activity?.resources?.updateConfiguration(config, activity.resources.displayMetrics) + activity?.recreate() + } } }