diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt index f8c82c6ab..62abe81dc 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt @@ -14,6 +14,9 @@ object Key { const val APP_EXPERT = "isExpert" const val APP_THEME = "appTheme" const val NIGHT_THEME = "nightTheme" + // Remembers the user's night-mode setting before the dark-only Dracula theme + // forced it on, so it can be restored when switching to another theme. + const val NIGHT_THEME_BEFORE_DRACULA = "nightThemeBeforeDracula" const val APP_LANGUAGE = "appLanguage" const val SERVICE_MODE = "serviceMode" const val MODE_VPN = "vpn" diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt index 71dbcdb56..ebac2397e 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt @@ -103,6 +103,9 @@ object DataStore : OnPreferenceDataStoreChangeListener { var isExpert by configurationStore.boolean(Key.APP_EXPERT) var appTheme by configurationStore.int(Key.APP_THEME) var nightTheme by configurationStore.stringToInt(Key.NIGHT_THEME) + // -1 = not set (no Dracula override active). Otherwise holds the night-mode + // value to restore when leaving the dark-only Dracula theme. + var nightThemeBeforeDracula by configurationStore.int(Key.NIGHT_THEME_BEFORE_DRACULA) { -1 } var appLanguage by configurationStore.string(Key.APP_LANGUAGE) { "" } var serviceMode by configurationStore.string(Key.SERVICE_MODE) { Key.MODE_VPN } diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt index 6790491d4..e245e658e 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt @@ -65,11 +65,37 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { addPreferencesFromResource(R.xml.global_preferences) val appTheme = findPreference(Key.APP_THEME)!! + val nightTheme = findPreference(Key.NIGHT_THEME)!! appTheme.setOnPreferenceChangeListener { _, newTheme -> if (DataStore.serviceState.started) { SagerNet.reloadService() } - val theme = Theme.getTheme(newTheme as Int) + val themeId = newTheme as Int + val previousTheme = DataStore.appTheme // still the old value at this point + // Dracula is a dark-only theme: force night mode on so its #282a36 + // canvas (values-night) takes effect instead of a washed-out light surface. + // Remember the prior night-mode setting so it can be restored on exit. + if (themeId == Theme.DRACULA) { + if (previousTheme != Theme.DRACULA && DataStore.nightTheme != 1) { + DataStore.nightThemeBeforeDracula = DataStore.nightTheme + Theme.currentNightMode = 1 + // nightTheme.value persists to configurationStore (same key as + // DataStore.nightTheme) and refreshes the picker, so no separate + // DataStore.nightTheme write is needed. + nightTheme.value = "1" + Theme.applyNightTheme() + } + } else if (previousTheme == Theme.DRACULA) { + // Leaving Dracula: restore the night-mode setting we forced on. + val restore = DataStore.nightThemeBeforeDracula + if (restore != -1) { + DataStore.nightThemeBeforeDracula = -1 + Theme.currentNightMode = restore + nightTheme.value = restore.toString() + Theme.applyNightTheme() + } + } + val theme = Theme.getTheme(themeId) app.setTheme(theme) requireActivity().apply { setTheme(theme) @@ -78,9 +104,11 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { true } - val nightTheme = findPreference(Key.NIGHT_THEME)!! nightTheme.setOnPreferenceChangeListener { _, newTheme -> Theme.currentNightMode = (newTheme as String).toInt() + // A manual night-mode change takes precedence: drop any pending + // Dracula restore so we don't override the user's choice later. + DataStore.nightThemeBeforeDracula = -1 Theme.applyNightTheme() true } diff --git a/app/src/main/java/io/nekohasekai/sagernet/utils/Theme.kt b/app/src/main/java/io/nekohasekai/sagernet/utils/Theme.kt index 693c89e39..0f24ad364 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/utils/Theme.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/utils/Theme.kt @@ -31,6 +31,7 @@ object Theme { const val BLUE_GREY = 20 const val BLACK = 21 const val VERDANT_MINT = 22 + const val DRACULA = 23 private fun defaultTheme() = PINK_SSR @@ -74,6 +75,7 @@ object Theme { BLUE_GREY -> R.style.Theme_SagerNet_BlueGrey BLACK -> R.style.Theme_SagerNet_Black VERDANT_MINT -> R.style.Theme_SagerNet_VerdantMint + DRACULA -> R.style.Theme_SagerNet_Dracula else -> getTheme(defaultTheme()) } } @@ -102,6 +104,7 @@ object Theme { BLUE_GREY -> R.style.Theme_SagerNet_Dialog_BlueGrey BLACK -> R.style.Theme_SagerNet_Dialog_Black VERDANT_MINT -> R.style.Theme_SagerNet_Dialog_VerdantMint + DRACULA -> R.style.Theme_SagerNet_Dialog_Dracula else -> getDialogTheme(defaultTheme()) } } diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 3e71f5ae5..b14f9246c 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -34,4 +34,9 @@ #2C2A17 #151515 #10241C + + #343746 + #282A36 + #343746 + #F8F8F2 \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 000000000..9b3c36627 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,44 @@ + + + + + + + diff --git a/app/src/main/res/values-v26/themes.xml b/app/src/main/res/values-v26/themes.xml index 335453e03..6bce0da1c 100644 --- a/app/src/main/res/values-v26/themes.xml +++ b/app/src/main/res/values-v26/themes.xml @@ -2,6 +2,9 @@ + + + + +