Merge branch 'feat/pure-black-theme' into release/v1.0.0

This commit is contained in:
makiolaj committed 2026-10-28 14:46:02 +01:00
commit c4fd0476e4
11 files changed
+43 -6

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "floret-kit"]
path = floret-kit
url = https://codeberg.org/jlmakiola/floret-kit.git
branch = feat/clockula
branch = main
+2
View File
@@ -87,6 +87,8 @@ The first release of Clockula: alarms, timers, a stopwatch and a world clock.
with Clockula. A voice-set alarm cleans up after itself once dismissed.
- Settings: theme, dynamic colour, language, and the defaults every new alarm
and timer inherits.
- A Black theme next to Light and Dark: dark with pure black backgrounds,
for AMOLED screens.
- Choose how long a ringing alarm or a finished timer keeps going before it
stops on its own: 1, 5, 10, 15, 20 or 25 minutes, or Never, separately for
alarms and timers. Ten minutes stays the default, and Never still stops
@@ -30,6 +30,7 @@ import de.jeanlucmakiola.floret.crash.CrashReporter
import de.jeanlucmakiola.floret.crash.submitCrashReport
import de.jeanlucmakiola.floret.prefs.Appearance
import de.jeanlucmakiola.floret.prefs.isDark
import de.jeanlucmakiola.floret.prefs.isPureBlack
import javax.inject.Inject
/**
@@ -105,6 +106,7 @@ class MainActivity : ComponentActivity() {
ClockulaTheme(
darkTheme = darkTheme,
dynamicColor = appearance.dynamicColor,
pureBlack = appearance.themeMode.isPureBlack,
) {
ClockulaShell(
modifier = Modifier.fillMaxSize(),
@@ -18,6 +18,7 @@ import de.jeanlucmakiola.clockula.ui.theme.ClockulaTheme
import de.jeanlucmakiola.floret.components.CollapsingScaffold
import de.jeanlucmakiola.floret.prefs.Appearance
import de.jeanlucmakiola.floret.prefs.isDark
import de.jeanlucmakiola.floret.prefs.isPureBlack
import javax.inject.Inject
/**
@@ -47,7 +48,11 @@ class ScreenSaverSettingsActivity : ComponentActivity() {
navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT) { darkTheme },
)
}
ClockulaTheme(darkTheme = darkTheme, dynamicColor = appearance.dynamicColor) {
ClockulaTheme(
darkTheme = darkTheme,
dynamicColor = appearance.dynamicColor,
pureBlack = appearance.themeMode.isPureBlack,
) {
CollapsingScaffold(title = stringResource(R.string.screen_saver_title), onBack = ::finish) {
ScreenSaverSettingsSection(inApp = false)
}
@@ -140,6 +140,9 @@ internal fun AppearanceScreen(
options = ThemeMode.entries.toList(),
selected = state.appearance.themeMode,
label = { themeModeLabel(it) },
summary = { mode ->
if (mode == ThemeMode.BLACK) stringResource(R.string.settings_theme_black_summary) else null
},
onSelect = viewModel::onThemeModeSelected,
onDismiss = { showTheme = false },
)
@@ -152,6 +155,7 @@ private fun themeModeLabel(mode: ThemeMode): String = stringResource(
ThemeMode.SYSTEM -> R.string.settings_theme_system
ThemeMode.LIGHT -> R.string.settings_theme_light
ThemeMode.DARK -> R.string.settings_theme_dark
ThemeMode.BLACK -> R.string.settings_theme_black
},
)
@@ -9,12 +9,14 @@ import de.jeanlucmakiola.floret.identity.FloretExpressiveTheme
* colour on API 31+, system light/dark, the standard motion scheme) fed with
* Clockula's own identity — its seed-derived fallback schemes
* ([ClockulaLightFallback] / [ClockulaDarkFallback]) and [ClockulaTypography].
* The mechanics live in floret-kit; the look stays here.
* The mechanics live in floret-kit; the look stays here. [pureBlack] is the
* AMOLED take on the dark theme (`ThemeMode.BLACK`).
*/
@Composable
fun ClockulaTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
pureBlack: Boolean = false,
content: @Composable () -> Unit,
) {
FloretExpressiveTheme(
@@ -22,6 +24,7 @@ fun ClockulaTheme(
darkScheme = ClockulaDarkFallback,
darkTheme = darkTheme,
dynamicColor = dynamicColor,
pureBlack = pureBlack,
typography = ClockulaTypography,
content = content,
)
@@ -27,6 +27,7 @@ import de.jeanlucmakiola.clockula.ui.widget.config.QuickTimerPicker
import de.jeanlucmakiola.clockula.ui.widget.openAppIntent
import de.jeanlucmakiola.floret.prefs.Appearance
import de.jeanlucmakiola.floret.prefs.isDark
import de.jeanlucmakiola.floret.prefs.isPureBlack
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -78,7 +79,11 @@ class TilePreferencesActivity : ComponentActivity() {
// before the stored duration arrives.
val duration by settingsPrefs.quickTileDuration.collectAsState(initial = null)
val darkTheme = appearance.themeMode.isDark(isSystemInDarkTheme())
ClockulaTheme(darkTheme = darkTheme, dynamicColor = appearance.dynamicColor) {
ClockulaTheme(
darkTheme = darkTheme,
dynamicColor = appearance.dynamicColor,
pureBlack = appearance.themeMode.isPureBlack,
) {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.surface) {}
val current = duration ?: return@ClockulaTheme
QuickTimerPicker(
@@ -21,6 +21,7 @@ import de.jeanlucmakiola.clockula.data.prefs.SettingsPrefs
import de.jeanlucmakiola.clockula.ui.theme.ClockulaTheme
import de.jeanlucmakiola.floret.prefs.Appearance
import de.jeanlucmakiola.floret.prefs.isDark
import de.jeanlucmakiola.floret.prefs.isPureBlack
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -94,7 +95,11 @@ abstract class WidgetConfigActivity : ComponentActivity() {
private fun ConfigTheme(content: @Composable () -> Unit) {
val appearance by settingsPrefs.appearance.collectAsState(initial = Appearance())
val darkTheme = appearance.themeMode.isDark(isSystemInDarkTheme())
ClockulaTheme(darkTheme = darkTheme, dynamicColor = appearance.dynamicColor) {
ClockulaTheme(
darkTheme = darkTheme,
dynamicColor = appearance.dynamicColor,
pureBlack = appearance.themeMode.isPureBlack,
) {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.surface) {}
content()
}
+2
View File
@@ -329,6 +329,8 @@
<string name="settings_theme_system">Follow system</string>
<string name="settings_theme_light">Light</string>
<string name="settings_theme_dark">Dark</string>
<string name="settings_theme_black">Black</string>
<string name="settings_theme_black_summary">Dark with pure black backgrounds, for AMOLED screens</string>
<string name="settings_dynamic_color">Dynamic colour</string>
<string name="settings_dynamic_color_hint">Use colours from your wallpaper</string>
<string name="settings_dynamic_color_unavailable">Needs Android 12 or newer</string>
@@ -77,6 +77,15 @@ class SettingsPrefsTest {
assertThat(prefs.themeMode.first()).isEqualTo(ThemeMode.DARK)
}
@Test
fun `the pure-black theme mode round-trips`(@TempDir tempDir: Path) = runTest {
val prefs = SettingsPrefs(PrefStore(newDataStore(tempDir)))
prefs.setThemeMode(ThemeMode.BLACK)
assertThat(prefs.themeMode.first()).isEqualTo(ThemeMode.BLACK)
}
@Test
fun `backup settings are applied together using normal preference clamps`(@TempDir tempDir: Path) = runTest {
val prefs = SettingsPrefs(PrefStore(newDataStore(tempDir)))