From d11025192e8d6a66b1afb55c99c97db2a0a4c5dc Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Tue, 22 Aug 2023 20:37:51 +0300 Subject: [PATCH] Move themes to separate files, save theme choice --- build.gradle | 2 + .../org/futo/inputmethod/latin/LatinIME.kt | 69 ++++- .../org/futo/inputmethod/latin/uix/Action.kt | 3 +- .../futo/inputmethod/latin/uix/BaseActions.kt | 274 +++--------------- .../latin/uix/theme/ThemeOptions.kt | 37 +++ .../uix/theme/presets/AMOLEDDarkPurple.kt | 72 +++++ .../uix/theme/presets/ClassicMaterialDark.kt | 121 ++++++++ .../latin/uix/theme/presets/DynamicThemes.kt | 34 +++ .../uix/theme/presets/VoiceInputTheme.kt | 8 + 9 files changed, 378 insertions(+), 242 deletions(-) create mode 100644 java/src/org/futo/inputmethod/latin/uix/theme/ThemeOptions.kt create mode 100644 java/src/org/futo/inputmethod/latin/uix/theme/presets/AMOLEDDarkPurple.kt create mode 100644 java/src/org/futo/inputmethod/latin/uix/theme/presets/ClassicMaterialDark.kt create mode 100644 java/src/org/futo/inputmethod/latin/uix/theme/presets/DynamicThemes.kt create mode 100644 java/src/org/futo/inputmethod/latin/uix/theme/presets/VoiceInputTheme.kt diff --git a/build.gradle b/build.gradle index 7c422a2ce..83042614d 100644 --- a/build.gradle +++ b/build.gradle @@ -128,6 +128,8 @@ dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' + implementation 'androidx.datastore:datastore-preferences:1.0.0' + debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt index aa43719b5..abb14d0eb 100644 --- a/java/src/org/futo/inputmethod/latin/LatinIME.kt +++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt @@ -48,6 +48,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry @@ -55,6 +60,7 @@ import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner import androidx.savedstate.SavedStateRegistry @@ -63,13 +69,26 @@ import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.findViewTreeSavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.google.android.material.color.DynamicColors +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import org.futo.inputmethod.latin.common.Constants import org.futo.inputmethod.latin.uix.Action import org.futo.inputmethod.latin.uix.ActionBar import org.futo.inputmethod.latin.uix.KeyboardManagerForAction import org.futo.inputmethod.latin.uix.theme.DarkColorScheme +import org.futo.inputmethod.latin.uix.theme.ThemeOption +import org.futo.inputmethod.latin.uix.theme.ThemeOptions import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper +import org.futo.inputmethod.latin.uix.theme.presets.DynamicSystemTheme +import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme import kotlin.math.roundToInt interface DynamicThemeProvider { @@ -285,14 +304,36 @@ interface DynamicThemeProviderOwner { } +val Context.dataStore: DataStore by preferencesDataStore(name = "settings") +val THEME_KEY = stringPreferencesKey("activeThemeOption") + +suspend fun Context.getSetting(key: Preferences.Key, default: T): T { + val valueFlow: Flow = + this.dataStore.data.map { preferences -> preferences[key] ?: default }.take(1) + + return valueFlow.first() +} + +suspend fun Context.setSetting(key: Preferences.Key, value: T) { + this.dataStore.edit { preferences -> + preferences[key] = value + } +} + class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner, KeyboardManagerForAction { private var activeColorScheme = DarkColorScheme + private var colorSchemeLoaderJob: Job? = null private var drawableProvider: DynamicThemeProvider? = null override fun getDrawableProvider(): DynamicThemeProvider { if(drawableProvider == null) { + if(colorSchemeLoaderJob != null && !colorSchemeLoaderJob!!.isCompleted) { + runBlocking { + colorSchemeLoaderJob!!.join() + } + } drawableProvider = BasicThemeProvider(this, activeColorScheme) } @@ -310,7 +351,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save // recreate the keyboard if not in action window, if we are in action window then // it'll be recreated when we exit - if(currWindowAction != null) recreateKeyboard() + if(currWindowAction == null) recreateKeyboard() window.window?.navigationBarColor = drawableProvider!!.primaryKeyboardColor setContent() @@ -344,12 +385,15 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save override fun onCreate() { super.onCreate() - activeColorScheme = if(!DynamicColors.isDynamicColorAvailable()) { - DarkColorScheme - } else { - val dCtx = DynamicColors.wrapContextIfAvailable(this) + colorSchemeLoaderJob = lifecycleScope.launch { + var themeKey = this@LatinIME.getSetting(THEME_KEY, DynamicSystemTheme.key) + var themeOption = ThemeOptions[themeKey] + if(themeOption == null || !themeOption.available(this@LatinIME)) { + themeKey = VoiceInputTheme.key + themeOption = ThemeOptions[themeKey]!! + } - dynamicLightColorScheme(dCtx) + activeColorScheme = themeOption.obtainColors(this@LatinIME) } mSavedStateRegistryController.performRestore(null) @@ -566,6 +610,8 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save override fun onWindowShown() { super.onWindowShown() latinIMELegacy.onWindowShown() + + // TODO: Check here if the dynamic color scheme has changed, reset and rebuild if so } override fun onWindowHidden() { @@ -685,7 +731,14 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save ); } - override fun updateTheme(newTheme: ColorScheme) { - updateDrawableProvider(newTheme) + override fun updateTheme(newTheme: ThemeOption) { + assert(newTheme.available(this)) + updateDrawableProvider(newTheme.obtainColors(this)) + + lifecycleScope.launch { + withContext(Dispatchers.Default) { + setSetting(THEME_KEY, newTheme.key) + } + } } } \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/Action.kt b/java/src/org/futo/inputmethod/latin/uix/Action.kt index af8a734e3..e9bbf7efc 100644 --- a/java/src/org/futo/inputmethod/latin/uix/Action.kt +++ b/java/src/org/futo/inputmethod/latin/uix/Action.kt @@ -3,12 +3,13 @@ package org.futo.inputmethod.latin.uix import androidx.annotation.DrawableRes import androidx.compose.material3.ColorScheme import androidx.compose.runtime.Composable +import org.futo.inputmethod.latin.uix.theme.ThemeOption interface KeyboardManagerForAction { fun triggerSystemVoiceInput() - fun updateTheme(newTheme: ColorScheme) + fun updateTheme(newTheme: ThemeOption) } interface ActionWindow { diff --git a/java/src/org/futo/inputmethod/latin/uix/BaseActions.kt b/java/src/org/futo/inputmethod/latin/uix/BaseActions.kt index 1b34f9ea6..71dd8ccfb 100644 --- a/java/src/org/futo/inputmethod/latin/uix/BaseActions.kt +++ b/java/src/org/futo/inputmethod/latin/uix/BaseActions.kt @@ -5,25 +5,33 @@ package org.futo.inputmethod.latin.uix import android.os.Build import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Button +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocal +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.uix.theme.DarkColorScheme +import org.futo.inputmethod.latin.uix.theme.ThemeOptionKeys +import org.futo.inputmethod.latin.uix.theme.ThemeOptions // TODO: For now, this calls CODE_SHORTCUT. In the future, we will want to @@ -31,10 +39,27 @@ import org.futo.inputmethod.latin.uix.theme.DarkColorScheme val VoiceInputAction = Action( icon = R.drawable.mic_fill, name = "Voice Input", - simplePressImpl = { - it.triggerSystemVoiceInput() - }, - windowImpl = null + //simplePressImpl = { + // it.triggerSystemVoiceInput() + //}, + simplePressImpl = null, + windowImpl = object : ActionWindow { + @Composable + override fun windowName(): String { + return "Voice Input" + } + + @Composable + override fun WindowContents(manager: KeyboardManagerForAction) { + Box(modifier = Modifier.fillMaxSize()) { + Icon( + painter = painterResource(id = R.drawable.mic_fill), + contentDescription = null, + modifier = Modifier.align(Alignment.Center).size(48.dp) + ) + } + } + } ) val ThemeAction = Action( @@ -50,238 +75,21 @@ val ThemeAction = Action( @Composable override fun WindowContents(manager: KeyboardManagerForAction) { val context = LocalContext.current - LazyColumn(modifier = Modifier.padding(8.dp, 0.dp).fillMaxWidth()) { - item { - Button(onClick = { - manager.updateTheme(DarkColorScheme) - }) { - Text("Default voice input theme") - } - } - - item { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + LazyColumn(modifier = Modifier + .padding(8.dp, 0.dp) + .fillMaxWidth()) + { + items(ThemeOptionKeys.count()) { + val key = ThemeOptionKeys[it] + val themeOption = ThemeOptions[key] + if(themeOption != null && themeOption.available(context)) { Button(onClick = { - manager.updateTheme(dynamicLightColorScheme(context)) + manager.updateTheme( + themeOption + ) }) { - Text("Dynamic light color scheme") + Text(themeOption.name) } - - Button(onClick = { - manager.updateTheme(dynamicDarkColorScheme(context)) - }) { - Text("Dynamic dark color scheme") - } - } - } - - item { - Button(onClick = { - val md_theme_light_primary = Color(0xFF6750A4) - val md_theme_light_onPrimary = Color(0xFFFFFFFF) - val md_theme_light_primaryContainer = Color(0xFFEADDFF) - val md_theme_light_onPrimaryContainer = Color(0xFF21005D) - val md_theme_light_secondary = Color(0xFF625B71) - val md_theme_light_onSecondary = Color(0xFFFFFFFF) - val md_theme_light_secondaryContainer = Color(0xFFE8DEF8) - val md_theme_light_onSecondaryContainer = Color(0xFF1D192B) - val md_theme_light_tertiary = Color(0xFF7D5260) - val md_theme_light_onTertiary = Color(0xFFFFFFFF) - val md_theme_light_tertiaryContainer = Color(0xFFFFD8E4) - val md_theme_light_onTertiaryContainer = Color(0xFF31111D) - val md_theme_light_error = Color(0xFFB3261E) - val md_theme_light_onError = Color(0xFFFFFFFF) - val md_theme_light_errorContainer = Color(0xFFF9DEDC) - val md_theme_light_onErrorContainer = Color(0xFF410E0B) - val md_theme_light_outline = Color(0xFF79747E) - val md_theme_light_background = Color(0xFFFFFBFE) - val md_theme_light_onBackground = Color(0xFF1C1B1F) - val md_theme_light_surface = Color(0xFFFFFBFE) - val md_theme_light_onSurface = Color(0xFF1C1B1F) - val md_theme_light_surfaceVariant = Color(0xFFE7E0EC) - val md_theme_light_onSurfaceVariant = Color(0xFF49454F) - val md_theme_light_inverseSurface = Color(0xFF313033) - val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4) - val md_theme_light_inversePrimary = Color(0xFFD0BCFF) - val md_theme_light_shadow = Color(0xFF000000) - val md_theme_light_surfaceTint = Color(0xFF6750A4) - val md_theme_light_outlineVariant = Color(0xFFCAC4D0) - val md_theme_light_scrim = Color(0xFF000000) - - manager.updateTheme( - lightColorScheme( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - primaryContainer = md_theme_light_primaryContainer, - onPrimaryContainer = md_theme_light_onPrimaryContainer, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - secondaryContainer = md_theme_light_secondaryContainer, - onSecondaryContainer = md_theme_light_onSecondaryContainer, - tertiary = md_theme_light_tertiary, - onTertiary = md_theme_light_onTertiary, - tertiaryContainer = md_theme_light_tertiaryContainer, - onTertiaryContainer = md_theme_light_onTertiaryContainer, - error = md_theme_light_error, - onError = md_theme_light_onError, - errorContainer = md_theme_light_errorContainer, - onErrorContainer = md_theme_light_onErrorContainer, - outline = md_theme_light_outline, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, - surfaceVariant = md_theme_light_surfaceVariant, - onSurfaceVariant = md_theme_light_onSurfaceVariant, - inverseSurface = md_theme_light_inverseSurface, - inverseOnSurface = md_theme_light_inverseOnSurface, - inversePrimary = md_theme_light_inversePrimary, - surfaceTint = md_theme_light_surfaceTint, - outlineVariant = md_theme_light_outlineVariant, - scrim = md_theme_light_scrim, - ) - ) - }) { - Text("Some random light theme") - } - - Button(onClick = { - val md_theme_dark_primary = Color(0xFFD0BCFF) - val md_theme_dark_onPrimary = Color(0xFF381E72) - val md_theme_dark_primaryContainer = Color(0xFF4F378B) - val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF) - val md_theme_dark_secondary = Color(0xFFCCC2DC) - val md_theme_dark_onSecondary = Color(0xFF332D41) - val md_theme_dark_secondaryContainer = Color(0xFF4A4458) - val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8) - val md_theme_dark_tertiary = Color(0xFFEFB8C8) - val md_theme_dark_onTertiary = Color(0xFF492532) - val md_theme_dark_tertiaryContainer = Color(0xFF633B48) - val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4) - val md_theme_dark_error = Color(0xFFF2B8B5) - val md_theme_dark_onError = Color(0xFF601410) - val md_theme_dark_errorContainer = Color(0xFF8C1D18) - val md_theme_dark_onErrorContainer = Color(0xFFF9DEDC) - val md_theme_dark_outline = Color(0xFF938F99) - val md_theme_dark_background = Color(0xFF1C1B1F) - val md_theme_dark_onBackground = Color(0xFFE6E1E5) - val md_theme_dark_surface = Color(0xFF1C1B1F) - val md_theme_dark_onSurface = Color(0xFFE6E1E5) - val md_theme_dark_surfaceVariant = Color(0xFF49454F) - val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0) - val md_theme_dark_inverseSurface = Color(0xFFE6E1E5) - val md_theme_dark_inverseOnSurface = Color(0xFF313033) - val md_theme_dark_inversePrimary = Color(0xFF6750A4) - val md_theme_dark_shadow = Color(0xFF000000) - val md_theme_dark_surfaceTint = Color(0xFFD0BCFF) - val md_theme_dark_outlineVariant = Color(0xFF49454F) - val md_theme_dark_scrim = Color(0xFF000000) - - manager.updateTheme( - darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - onError = md_theme_dark_onError, - errorContainer = md_theme_dark_errorContainer, - onErrorContainer = md_theme_dark_onErrorContainer, - outline = md_theme_dark_outline, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, - inverseSurface = md_theme_dark_inverseSurface, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim, - ) - ) - }) { - Text("Some random dark theme") - } - - - Button(onClick = { - val md_theme_dark_primary = Color(0xFFD0BCFF) - val md_theme_dark_onPrimary = Color(0xFF381E72) - val md_theme_dark_primaryContainer = Color(0xFF4F378B) - val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF) - val md_theme_dark_secondary = Color(0xFFCCC2DC) - val md_theme_dark_onSecondary = Color(0xFF332D41) - val md_theme_dark_secondaryContainer = Color(0xFF4A4458) - val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8) - val md_theme_dark_tertiary = Color(0xFFEFB8C8) - val md_theme_dark_onTertiary = Color(0xFF492532) - val md_theme_dark_tertiaryContainer = Color(0xFF633B48) - val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4) - val md_theme_dark_error = Color(0xFFF2B8B5) - val md_theme_dark_onError = Color(0xFF601410) - val md_theme_dark_errorContainer = Color(0xFF8C1D18) - val md_theme_dark_onErrorContainer = Color(0xFFF9DEDC) - val md_theme_dark_outline = Color(0xFF938F99) - val md_theme_dark_background = Color(0xFF000000) - val md_theme_dark_onBackground = Color(0xFFE6E1E5) - val md_theme_dark_surface = Color(0xFF000000) - val md_theme_dark_onSurface = Color(0xFFE6E1E5) - val md_theme_dark_surfaceVariant = Color(0xFF49454F) - val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0) - val md_theme_dark_inverseSurface = Color(0xFFE6E1E5) - val md_theme_dark_inverseOnSurface = Color(0xFF313033) - val md_theme_dark_inversePrimary = Color(0xFF6750A4) - val md_theme_dark_shadow = Color(0xFF000000) - val md_theme_dark_surfaceTint = Color(0xFFD0BCFF) - val md_theme_dark_outlineVariant = Color(0xFF49454F) - val md_theme_dark_scrim = Color(0xFF000000) - - manager.updateTheme( - darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - onError = md_theme_dark_onError, - errorContainer = md_theme_dark_errorContainer, - onErrorContainer = md_theme_dark_onErrorContainer, - outline = md_theme_dark_outline, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, - inverseSurface = md_theme_dark_inverseSurface, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim, - ) - ) - }) { - Text("AMOLED dark") } } } diff --git a/java/src/org/futo/inputmethod/latin/uix/theme/ThemeOptions.kt b/java/src/org/futo/inputmethod/latin/uix/theme/ThemeOptions.kt new file mode 100644 index 000000000..63b7db877 --- /dev/null +++ b/java/src/org/futo/inputmethod/latin/uix/theme/ThemeOptions.kt @@ -0,0 +1,37 @@ +package org.futo.inputmethod.latin.uix.theme + +import android.content.Context +import androidx.compose.material3.ColorScheme +import org.futo.inputmethod.latin.uix.theme.presets.AMOLEDDarkPurple +import org.futo.inputmethod.latin.uix.theme.presets.ClassicMaterialDark +import org.futo.inputmethod.latin.uix.theme.presets.DynamicDarkTheme +import org.futo.inputmethod.latin.uix.theme.presets.DynamicLightTheme +import org.futo.inputmethod.latin.uix.theme.presets.DynamicSystemTheme +import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme + +data class ThemeOption( + val key: String, + val name: String, // TODO: @StringRes Int + val available: (Context) -> Boolean, + val obtainColors: (Context) -> ColorScheme, +) + +val ThemeOptions = hashMapOf( + DynamicSystemTheme.key to DynamicSystemTheme, + DynamicDarkTheme.key to DynamicDarkTheme, + DynamicLightTheme.key to DynamicLightTheme, + + ClassicMaterialDark.key to ClassicMaterialDark, + VoiceInputTheme.key to VoiceInputTheme, + AMOLEDDarkPurple.key to AMOLEDDarkPurple, +) + +val ThemeOptionKeys = arrayOf( + DynamicSystemTheme.key, + DynamicDarkTheme.key, + DynamicLightTheme.key, + + ClassicMaterialDark.key, + VoiceInputTheme.key, + AMOLEDDarkPurple.key, +) \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/theme/presets/AMOLEDDarkPurple.kt b/java/src/org/futo/inputmethod/latin/uix/theme/presets/AMOLEDDarkPurple.kt new file mode 100644 index 000000000..78c9c3530 --- /dev/null +++ b/java/src/org/futo/inputmethod/latin/uix/theme/presets/AMOLEDDarkPurple.kt @@ -0,0 +1,72 @@ +package org.futo.inputmethod.latin.uix.theme.presets + +import androidx.compose.material3.darkColorScheme +import androidx.compose.ui.graphics.Color +import org.futo.inputmethod.latin.uix.theme.ThemeOption + +private val md_theme_dark_primary = Color(0xFFD0BCFF) +private val md_theme_dark_onPrimary = Color(0xFF381E72) +private val md_theme_dark_primaryContainer = Color(0xFF4F378B) +private val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF) +private val md_theme_dark_secondary = Color(0xFFCCC2DC) +private val md_theme_dark_onSecondary = Color(0xFF332D41) +private val md_theme_dark_secondaryContainer = Color(0xFF4A4458) +private val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8) +private val md_theme_dark_tertiary = Color(0xFFEFB8C8) +private val md_theme_dark_onTertiary = Color(0xFF492532) +private val md_theme_dark_tertiaryContainer = Color(0xFF633B48) +private val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4) +private val md_theme_dark_error = Color(0xFFF2B8B5) +private val md_theme_dark_onError = Color(0xFF601410) +private val md_theme_dark_errorContainer = Color(0xFF8C1D18) +private val md_theme_dark_onErrorContainer = Color(0xFFF9DEDC) +private val md_theme_dark_outline = Color(0xFF938F99) +private val md_theme_dark_background = Color(0xFF000000) +private val md_theme_dark_onBackground = Color(0xFFE6E1E5) +private val md_theme_dark_surface = Color(0xFF000000) +private val md_theme_dark_onSurface = Color(0xFFE6E1E5) +private val md_theme_dark_surfaceVariant = Color(0xFF49454F) +private val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0) +private val md_theme_dark_inverseSurface = Color(0xFFE6E1E5) +private val md_theme_dark_inverseOnSurface = Color(0xFF313033) +private val md_theme_dark_inversePrimary = Color(0xFF6750A4) +private val md_theme_dark_shadow = Color(0xFF000000) +private val md_theme_dark_surfaceTint = Color(0xFFD0BCFF) +private val md_theme_dark_outlineVariant = Color(0xFF49454F) +private val md_theme_dark_scrim = Color(0xFF000000) + +private val colorScheme = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + onError = md_theme_dark_onError, + errorContainer = md_theme_dark_errorContainer, + onErrorContainer = md_theme_dark_onErrorContainer, + outline = md_theme_dark_outline, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + inverseSurface = md_theme_dark_inverseSurface, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +val AMOLEDDarkPurple = ThemeOption("AMOLEDDarkPurple", "AMOLED Dark Purple", { true }) { + colorScheme +} \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/theme/presets/ClassicMaterialDark.kt b/java/src/org/futo/inputmethod/latin/uix/theme/presets/ClassicMaterialDark.kt new file mode 100644 index 000000000..54d852316 --- /dev/null +++ b/java/src/org/futo/inputmethod/latin/uix/theme/presets/ClassicMaterialDark.kt @@ -0,0 +1,121 @@ +package org.futo.inputmethod.latin.uix.theme.presets + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.darkColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.futo.inputmethod.latin.uix.theme.ThemeOption + + +private val md_theme_dark_primary = Color(0xFF80cbc4) +private val md_theme_dark_onPrimary = Color(0xFFFFFFFF) +private val md_theme_dark_primaryContainer = Color(0xFF00504B) +private val md_theme_dark_onPrimaryContainer = Color(0xFF71F7ED) +private val md_theme_dark_secondary = Color(0xFF80cbc4) +private val md_theme_dark_onSecondary = Color(0xFFFFFFFF) +private val md_theme_dark_secondaryContainer = Color(0xFF34434B) +private val md_theme_dark_onSecondaryContainer = Color(0xFFFFFFFF) +private val md_theme_dark_tertiary = Color(0xFF3582A2) +private val md_theme_dark_onTertiary = Color(0xFFFFFFFF) +private val md_theme_dark_tertiaryContainer = Color(0xFF004D64) +private val md_theme_dark_onTertiaryContainer = Color(0xFFBDE9FF) +private val md_theme_dark_error = Color(0xFFFFB4AB) +private val md_theme_dark_errorContainer = Color(0xFF93000A) +private val md_theme_dark_onError = Color(0xFF690005) +private val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +private val md_theme_dark_background = Color(0xFF21272b) +private val md_theme_dark_onBackground = Color(0xFFFFFFFF) +private val md_theme_dark_surface = Color(0xFF263238) +private val md_theme_dark_onSurface = Color(0xFFFFFFFF) +private val md_theme_dark_surfaceVariant = Color(0xFF3F4947) +private val md_theme_dark_onSurfaceVariant = Color(0xFFBEC9C7) +private val md_theme_dark_outline = Color(0xFF899391) +private val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) +private val md_theme_dark_inverseSurface = Color(0xFFBFE9FF) +private val md_theme_dark_inversePrimary = Color(0xFF006A64) +private val md_theme_dark_shadow = Color(0xFF000000) +private val md_theme_dark_surfaceTint = Color(0xFF4FDBD1) +private val md_theme_dark_outlineVariant = Color(0xFF3F4947) +private val md_theme_dark_scrim = Color(0xFF000000) + +private val colorScheme = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + onError = md_theme_dark_onError, + errorContainer = md_theme_dark_errorContainer, + onErrorContainer = md_theme_dark_onErrorContainer, + outline = md_theme_dark_outline, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + inverseSurface = md_theme_dark_inverseSurface, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +val ClassicMaterialDark = ThemeOption("ClassicMaterialDark", "AOSP Material Dark", { true }) { + colorScheme +} + +@Composable +@Preview +private fun PreviewTheme() { + MaterialTheme(colorScheme) { + Column(modifier = Modifier.fillMaxSize()) { + Spacer(modifier = Modifier.weight(1.5f)) + Surface(color = MaterialTheme.colorScheme.background, modifier = Modifier + .fillMaxWidth() + .height(48.dp)) { + + } + Surface(color = MaterialTheme.colorScheme.surface, modifier = Modifier + .fillMaxWidth() + .weight(1.0f)) { + Box(modifier = Modifier.padding(16.dp)) { + Surface( + color = MaterialTheme.colorScheme.primary, modifier = Modifier + .align( + Alignment.BottomEnd + ) + .height(32.dp) + .width(48.dp), + shape = RoundedCornerShape(8.dp) + ) { + + } + } + } + } + } +} \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/theme/presets/DynamicThemes.kt b/java/src/org/futo/inputmethod/latin/uix/theme/presets/DynamicThemes.kt new file mode 100644 index 000000000..c842ee93b --- /dev/null +++ b/java/src/org/futo/inputmethod/latin/uix/theme/presets/DynamicThemes.kt @@ -0,0 +1,34 @@ +package org.futo.inputmethod.latin.uix.theme.presets + +import android.app.UiModeManager +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import org.futo.inputmethod.latin.uix.theme.ThemeOption + +val DynamicSystemTheme = ThemeOption("DynamicSystem", "Dynamic System", { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S }) { + val uiModeManager = it.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager + when (uiModeManager.nightMode) { + UiModeManager.MODE_NIGHT_YES -> dynamicDarkColorScheme(it) + UiModeManager.MODE_NIGHT_NO -> dynamicLightColorScheme(it) + UiModeManager.MODE_NIGHT_AUTO -> { + val currentNightMode = it.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + if(currentNightMode == Configuration.UI_MODE_NIGHT_NO) { + dynamicLightColorScheme(it) + } else { + dynamicDarkColorScheme(it) + } + } + else -> dynamicDarkColorScheme(it) + } +} + +val DynamicDarkTheme = ThemeOption("DynamicDark", "Dynamic Dark", { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S }) { + dynamicDarkColorScheme(it) +} + +val DynamicLightTheme = ThemeOption("DynamicLight", "Dynamic Light", { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S }) { + dynamicLightColorScheme(it) +} diff --git a/java/src/org/futo/inputmethod/latin/uix/theme/presets/VoiceInputTheme.kt b/java/src/org/futo/inputmethod/latin/uix/theme/presets/VoiceInputTheme.kt new file mode 100644 index 000000000..71c6a0765 --- /dev/null +++ b/java/src/org/futo/inputmethod/latin/uix/theme/presets/VoiceInputTheme.kt @@ -0,0 +1,8 @@ +package org.futo.inputmethod.latin.uix.theme.presets + +import org.futo.inputmethod.latin.uix.theme.DarkColorScheme +import org.futo.inputmethod.latin.uix.theme.ThemeOption + +val VoiceInputTheme = ThemeOption("VoiceInputTheme", "Voice Input Theme", { true }) { + DarkColorScheme +} \ No newline at end of file