From 954a134df829f841556fad53230537c94e1b9dc0 Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 11:52:37 -0400 Subject: [PATCH 1/9] Add theme override preference --- .../java/moe/apex/breadboard/preferences/Pref.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index c65ca44..9b3c43b 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -97,6 +97,7 @@ data object PrefNames { const val INTERNAL_IGNORE_LIST = "internal_ignore_list" const val AUTOPLAY_VIDEOS = "autoplay_videos" const val UNIFIED_INFO_SHEET = "unified_info_sheet" + const val THEME = "theme" } @@ -131,6 +132,7 @@ object PreferenceKeys { val INTERNAL_IGNORE_LIST = stringSetPreferencesKey(PrefNames.INTERNAL_IGNORE_LIST) val AUTOPLAY_VIDEOS = stringPreferencesKey(PrefNames.AUTOPLAY_VIDEOS) val UNIFIED_INFO_SHEET = booleanPreferencesKey(PrefNames.UNIFIED_INFO_SHEET) + var THEME = stringPreferencesKey(PrefNames.THEME) } @@ -262,6 +264,7 @@ data class Prefs( internalIgnoreList = emptySet(), autoplayVideos = AutoplayVideosMode.OFF, unifiedInfoSheet = false, // Unified is called 'Classic' in the UI + theme = Theme.AUTO, ) } @@ -325,7 +328,8 @@ class UserPreferencesRepository(private val dataStore: DataStore) { PreferenceKeys.RECOMMENDATIONS_WEIGHTED_SELECTION to PrefMeta(PrefCategory.SETTING), PreferenceKeys.INTERNAL_IGNORE_LIST_TIMESTAMP to PrefMeta(PrefCategory.SETTING, exportable = false), PreferenceKeys.INTERNAL_IGNORE_LIST to PrefMeta(PrefCategory.SETTING, exportable = false), - PreferenceKeys.UNIFIED_INFO_SHEET to PrefMeta(PrefCategory.SETTING) + PreferenceKeys.UNIFIED_INFO_SHEET to PrefMeta(PrefCategory.SETTING), + PreferenceKeys.THEME to PrefMeta(PrefCategory.SETTINGS) ) } @@ -854,3 +858,9 @@ enum class ImageSource(override val label: String, val imageBoard: ImageBoard) : YANDERE("Yande.re", Yandere), R34("Rule34", Rule34) } + +enum class Theme(override val label: String) : PrefEnum { + DARK("Always use dark mode"), + LIGHT("Always use light mode"), + AUTO("Use system default") +} \ No newline at end of file From 67634d12215e25364859d369c16264a72598e5dc Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:13:23 -0400 Subject: [PATCH 2/9] Make theme reflect preference selection --- .../main/java/moe/apex/breadboard/ui/theme/Theme.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt index b63b841..191db10 100644 --- a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt +++ b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import moe.apex.breadboard.prefs private val DarkColorScheme = darkColorScheme( @@ -53,11 +54,19 @@ val LocalBreadboardColors = staticCompositionLocalOf { ) } +fun shouldUseDarkMode(): Boolean { + val preferences = LocalPreferences.current + + val userSelectedDarkMode = preferences.theme == Theme.DARK + val followSystemDarkMode = preferences.theme == Theme.AUTO && isSystemInDarkTheme() + + return userSelectedDarkMode || followSystemDarkMode +} @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun BreadboardTheme( - darkTheme: Boolean = isSystemInDarkTheme(), + darkTheme: Boolean = shouldUseDarkMode(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit From dbf4b25ee3bd6d565da88f1baaec8c5cf06f3e31 Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:13:57 -0400 Subject: [PATCH 3/9] Add Appearance/Theme to preferences page --- .../breadboard/preferences/Preferences.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt b/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt index a2e54d8..9b218dd 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt @@ -365,6 +365,28 @@ fun PreferencesScreen(navController: NavHostController) { } } } + + item { + ExpressiveGroup(title = "Appearance") { + item { + EnumPref( + title = "Theme", + summary = currentSettings.theme.label, + enumItems = Theme.entries, + infoText = "Select Light or Dark to always use that theme, or Auto to follow the system theme.", + selectedItem = currentSettings.theme, + onSelection = { + scope.launch { + preferencesRepository.updatePref( + PreferenceKeys.THEME, + it + ) + } + } + ) + } + } + } item { ExpressiveGroup(title = "Content") { From f6afff908e3f1061e5481b8620a9b4e4e71029ae Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:15:08 -0400 Subject: [PATCH 4/9] Add theme to mapUserPreferences --- app/src/main/java/moe/apex/breadboard/preferences/Pref.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index 9b3c43b..2ecb2cd 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -813,6 +813,8 @@ class UserPreferencesRepository(private val dataStore: DataStore) { val autoplayVideos = preferences[PreferenceKeys.AUTOPLAY_VIDEOS]?.let { AutoplayVideosMode.valueOf(it) } ?: Prefs.DEFAULT.autoplayVideos val unifiedInfoSheet = preferences[PreferenceKeys.UNIFIED_INFO_SHEET] ?: Prefs.DEFAULT.unifiedInfoSheet + val theme = preferences[PreferenceKeys.THEME]?.let { Theme.valueOf(it) } ?: Prefs.DEFAULT.theme + return Prefs( dataSaver, storageLocation, @@ -843,7 +845,8 @@ class UserPreferencesRepository(private val dataStore: DataStore) { internalIgnoreListTimestamp, internalIgnoreList, autoplayVideos, - unifiedInfoSheet + unifiedInfoSheet, + theme ) } } From caf8c142420faec3f47716e59c4ca8273a255dcb Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:19:39 -0400 Subject: [PATCH 5/9] Replace "Mode" with "Theme" --- app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt index 191db10..04c8e42 100644 --- a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt +++ b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt @@ -54,7 +54,7 @@ val LocalBreadboardColors = staticCompositionLocalOf { ) } -fun shouldUseDarkMode(): Boolean { +fun shouldUseDarkTheme(): Boolean { val preferences = LocalPreferences.current val userSelectedDarkMode = preferences.theme == Theme.DARK @@ -66,7 +66,7 @@ fun shouldUseDarkMode(): Boolean { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun BreadboardTheme( - darkTheme: Boolean = shouldUseDarkMode(), + darkTheme: Boolean = shouldUseDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit From 5ac962bbb6fa45697ae53ecf383477c915a207bb Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:39:59 -0400 Subject: [PATCH 6/9] Add theme property to Prefs --- app/src/main/java/moe/apex/breadboard/preferences/Pref.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index 2ecb2cd..4dee12d 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -230,7 +230,8 @@ data class Prefs( val internalIgnoreListTimestamp: Long, val internalIgnoreList: Set, val autoplayVideos: AutoplayVideosMode, - val unifiedInfoSheet: Boolean + val unifiedInfoSheet: Boolean, + val theme: Theme, ) { companion object { val DEFAULT = Prefs( @@ -264,7 +265,7 @@ data class Prefs( internalIgnoreList = emptySet(), autoplayVideos = AutoplayVideosMode.OFF, unifiedInfoSheet = false, // Unified is called 'Classic' in the UI - theme = Theme.AUTO, + theme = Theme.AUTO ) } From 8ec61b8eb0446e2e54484e2ab9caf1817c1b638e Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 12:44:51 -0400 Subject: [PATCH 7/9] Remove trailing S --- app/src/main/java/moe/apex/breadboard/preferences/Pref.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index 4dee12d..7d947e2 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -330,7 +330,7 @@ class UserPreferencesRepository(private val dataStore: DataStore) { PreferenceKeys.INTERNAL_IGNORE_LIST_TIMESTAMP to PrefMeta(PrefCategory.SETTING, exportable = false), PreferenceKeys.INTERNAL_IGNORE_LIST to PrefMeta(PrefCategory.SETTING, exportable = false), PreferenceKeys.UNIFIED_INFO_SHEET to PrefMeta(PrefCategory.SETTING), - PreferenceKeys.THEME to PrefMeta(PrefCategory.SETTINGS) + PreferenceKeys.THEME to PrefMeta(PrefCategory.SETTING) ) } From d08eb0a15e894fc445204ab5219186f985813190 Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 13:11:50 -0400 Subject: [PATCH 8/9] Add imports and make function Composable --- app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt index 04c8e42..a78fe4d 100644 --- a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt +++ b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt @@ -14,6 +14,8 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import moe.apex.breadboard.preferences.LocalPreferences +import moe.apex.breadboard.preferences.Theme import moe.apex.breadboard.prefs @@ -54,6 +56,7 @@ val LocalBreadboardColors = staticCompositionLocalOf { ) } +@Composable() fun shouldUseDarkTheme(): Boolean { val preferences = LocalPreferences.current From 33f745776e3489f790a1a9304c72836c38c981bb Mon Sep 17 00:00:00 2001 From: Alexander Paolini <30964205+alexanderpaolini@users.noreply.github.com> Date: Sat, 9 May 2026 14:10:15 -0400 Subject: [PATCH 9/9] Change from mode selection to dark theme toggle Following pixel's standards --- .../moe/apex/breadboard/preferences/Pref.kt | 22 +++++++++---------- .../breadboard/preferences/Preferences.kt | 11 +++++----- .../moe/apex/breadboard/ui/theme/Theme.kt | 9 ++++---- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index 7d947e2..3f0efb3 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -97,7 +97,7 @@ data object PrefNames { const val INTERNAL_IGNORE_LIST = "internal_ignore_list" const val AUTOPLAY_VIDEOS = "autoplay_videos" const val UNIFIED_INFO_SHEET = "unified_info_sheet" - const val THEME = "theme" + const val DARK_THEME = "dark_theme" } @@ -132,7 +132,7 @@ object PreferenceKeys { val INTERNAL_IGNORE_LIST = stringSetPreferencesKey(PrefNames.INTERNAL_IGNORE_LIST) val AUTOPLAY_VIDEOS = stringPreferencesKey(PrefNames.AUTOPLAY_VIDEOS) val UNIFIED_INFO_SHEET = booleanPreferencesKey(PrefNames.UNIFIED_INFO_SHEET) - var THEME = stringPreferencesKey(PrefNames.THEME) + var DARK_THEME = stringPreferencesKey(PrefNames.DARK_THEME) } @@ -231,7 +231,7 @@ data class Prefs( val internalIgnoreList: Set, val autoplayVideos: AutoplayVideosMode, val unifiedInfoSheet: Boolean, - val theme: Theme, + val darkTheme: DarkTheme, ) { companion object { val DEFAULT = Prefs( @@ -265,7 +265,7 @@ data class Prefs( internalIgnoreList = emptySet(), autoplayVideos = AutoplayVideosMode.OFF, unifiedInfoSheet = false, // Unified is called 'Classic' in the UI - theme = Theme.AUTO + darkTheme = DarkTheme.AUTO ) } @@ -330,7 +330,7 @@ class UserPreferencesRepository(private val dataStore: DataStore) { PreferenceKeys.INTERNAL_IGNORE_LIST_TIMESTAMP to PrefMeta(PrefCategory.SETTING, exportable = false), PreferenceKeys.INTERNAL_IGNORE_LIST to PrefMeta(PrefCategory.SETTING, exportable = false), PreferenceKeys.UNIFIED_INFO_SHEET to PrefMeta(PrefCategory.SETTING), - PreferenceKeys.THEME to PrefMeta(PrefCategory.SETTING) + PreferenceKeys.DARK_THEME to PrefMeta(PrefCategory.SETTING) ) } @@ -814,7 +814,7 @@ class UserPreferencesRepository(private val dataStore: DataStore) { val autoplayVideos = preferences[PreferenceKeys.AUTOPLAY_VIDEOS]?.let { AutoplayVideosMode.valueOf(it) } ?: Prefs.DEFAULT.autoplayVideos val unifiedInfoSheet = preferences[PreferenceKeys.UNIFIED_INFO_SHEET] ?: Prefs.DEFAULT.unifiedInfoSheet - val theme = preferences[PreferenceKeys.THEME]?.let { Theme.valueOf(it) } ?: Prefs.DEFAULT.theme + val darkTheme = preferences[PreferenceKeys.DARK_THEME]?.let { DarkTheme.valueOf(it) } ?: Prefs.DEFAULT.darkTheme return Prefs( dataSaver, @@ -847,7 +847,7 @@ class UserPreferencesRepository(private val dataStore: DataStore) { internalIgnoreList, autoplayVideos, unifiedInfoSheet, - theme + darkTheme, ) } } @@ -863,8 +863,8 @@ enum class ImageSource(override val label: String, val imageBoard: ImageBoard) : R34("Rule34", Rule34) } -enum class Theme(override val label: String) : PrefEnum { - DARK("Always use dark mode"), - LIGHT("Always use light mode"), - AUTO("Use system default") +enum class DarkTheme(override val label: String) : PrefEnum { + ON("Always"), + OFF("Never"), + AUTO("Follow system") } \ No newline at end of file diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt b/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt index 9b218dd..c4891fb 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Preferences.kt @@ -370,15 +370,14 @@ fun PreferencesScreen(navController: NavHostController) { ExpressiveGroup(title = "Appearance") { item { EnumPref( - title = "Theme", - summary = currentSettings.theme.label, - enumItems = Theme.entries, - infoText = "Select Light or Dark to always use that theme, or Auto to follow the system theme.", - selectedItem = currentSettings.theme, + title = "Dark theme", + summary = currentSettings.darkTheme.label, + enumItems = DarkTheme.entries, + selectedItem = currentSettings.darkTheme, onSelection = { scope.launch { preferencesRepository.updatePref( - PreferenceKeys.THEME, + PreferenceKeys.DARK_THEME, it ) } diff --git a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt index a78fe4d..544f292 100644 --- a/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt +++ b/app/src/main/java/moe/apex/breadboard/ui/theme/Theme.kt @@ -15,8 +15,7 @@ import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import moe.apex.breadboard.preferences.LocalPreferences -import moe.apex.breadboard.preferences.Theme -import moe.apex.breadboard.prefs +import moe.apex.breadboard.preferences.DarkTheme private val DarkColorScheme = darkColorScheme( @@ -56,12 +55,12 @@ val LocalBreadboardColors = staticCompositionLocalOf { ) } -@Composable() +@Composable fun shouldUseDarkTheme(): Boolean { val preferences = LocalPreferences.current - val userSelectedDarkMode = preferences.theme == Theme.DARK - val followSystemDarkMode = preferences.theme == Theme.AUTO && isSystemInDarkTheme() + val userSelectedDarkMode = preferences.darkTheme == DarkTheme.ON + val followSystemDarkMode = preferences.darkTheme == DarkTheme.AUTO && isSystemInDarkTheme() return userSelectedDarkMode || followSystemDarkMode }