Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
005df7db19 | ||
|
|
0a92dce8a9 | ||
|
|
cd705961ca | ||
|
|
a390296dc2 | ||
|
|
c4fd0476e4 | ||
|
|
b03b23da30 |
No files matched your search
+1
-1
@@ -1,4 +1,4 @@
|
||||
[submodule "floret-kit"]
|
||||
path = floret-kit
|
||||
url = https://codeberg.org/jlmakiola/floret-kit.git
|
||||
branch = feat/clockula
|
||||
branch = main
|
||||
@@ -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,
|
||||
navigation bar included, 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
|
||||
|
||||
@@ -32,8 +32,8 @@ android {
|
||||
// versionCode is derived from versionName by scripts/version_info.sh
|
||||
// (1.0.0-beta.1 -> 1000001, 1.0.0 -> 1000099), and CI fails if the
|
||||
// committed one doesn't match. See docs/RELEASING.md.
|
||||
versionCode = 1000005
|
||||
versionName = "1.0.0-beta.5"
|
||||
versionCode = 1000007
|
||||
versionName = "1.0.0-beta.7"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
@@ -100,14 +101,17 @@ class MainActivity : ComponentActivity() {
|
||||
// flag: with the device dark and ThemeMode.LIGHT stored, deriving the
|
||||
// icon colours from the system would paint white icons onto a light
|
||||
// background. Re-applied whenever the stored preference changes.
|
||||
LaunchedEffect(darkTheme) { applyEdgeToEdge(darkTheme) }
|
||||
val pureBlack = darkTheme && appearance.themeMode.isPureBlack
|
||||
LaunchedEffect(darkTheme, pureBlack) { applyEdgeToEdge(darkTheme, pureBlack) }
|
||||
|
||||
ClockulaTheme(
|
||||
darkTheme = darkTheme,
|
||||
dynamicColor = appearance.dynamicColor,
|
||||
pureBlack = pureBlack,
|
||||
) {
|
||||
ClockulaShell(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
pureBlack = pureBlack,
|
||||
openRequest = openRequest,
|
||||
// Consumed once: a request left standing would re-select the
|
||||
// tab on every recomposition and fight the user's taps.
|
||||
@@ -134,10 +138,11 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyEdgeToEdge(darkTheme: Boolean) {
|
||||
private fun applyEdgeToEdge(darkTheme: Boolean, pureBlack: Boolean) {
|
||||
val darkScrim = if (pureBlack) NAV_BAR_BLACK_SCRIM else NAV_BAR_DARK_SCRIM
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT) { darkTheme },
|
||||
navigationBarStyle = SystemBarStyle.auto(NAV_BAR_LIGHT_SCRIM, NAV_BAR_DARK_SCRIM) { darkTheme },
|
||||
navigationBarStyle = SystemBarStyle.auto(NAV_BAR_LIGHT_SCRIM, darkScrim) { darkTheme },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,3 +190,6 @@ class MainActivity : ComponentActivity() {
|
||||
/** The scrims androidx uses for a three-button navigation bar, matched exactly. */
|
||||
private val NAV_BAR_LIGHT_SCRIM = Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
|
||||
private val NAV_BAR_DARK_SCRIM = Color.argb(0x80, 0x1b, 0x1b, 0x1b)
|
||||
|
||||
/** The Black theme's: the dark one's alpha over black, so the bar reads black (#28). */
|
||||
private val NAV_BAR_BLACK_SCRIM = Color.argb(0x80, 0x00, 0x00, 0x00)
|
||||
+6
-1
@@ -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
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItem
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
|
||||
@@ -77,10 +79,15 @@ import kotlin.math.sign
|
||||
* state is saveable, so the tab survives rotation and process death with no
|
||||
* mirror of ours to drift — and the `NavHost` holds only what is pushed over
|
||||
* the tabs: the editors, the timer setup page, Settings.
|
||||
*
|
||||
* With [pureBlack] the bar sits on the black ground rather than on
|
||||
* `surfaceContainer`, so the bottom of the screen is black down to the
|
||||
* system navigation bar (#28).
|
||||
*/
|
||||
@Composable
|
||||
fun ClockulaShell(
|
||||
modifier: Modifier = Modifier,
|
||||
pureBlack: Boolean = false,
|
||||
openRequest: ShellRequest? = null,
|
||||
onRequestConsumed: () -> Unit = {},
|
||||
viewModel: ShellViewModel = hiltViewModel(),
|
||||
@@ -192,6 +199,14 @@ fun ClockulaShell(
|
||||
enabled = back is ShellBackAction.GoTo,
|
||||
),
|
||||
navigationSuiteType = navigationSuiteType,
|
||||
navigationSuiteColors = if (pureBlack) {
|
||||
NavigationSuiteDefaults.colors(
|
||||
shortNavigationBarContainerColor = MaterialTheme.colorScheme.background,
|
||||
navigationBarContainerColor = MaterialTheme.colorScheme.background,
|
||||
)
|
||||
} else {
|
||||
NavigationSuiteDefaults.colors()
|
||||
},
|
||||
) {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
CompositionLocalProvider(
|
||||
|
||||
@@ -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(
|
||||
|
||||
+6
-1
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
|
||||
+1
-1
Submodule floret-kit updated: 02bd14abdb...8593181618.
Reference in new issue
Block a user