M0: Floret skeleton — Material 3 Expressive scaffold over OpenTasks (planned)
All checks were successful
CI / ci (push) Successful in 6m39s

Sibling to Calendula. Pure front-end posture; TaskContract data layer,
task screens and reminder engine to follow (see docs/PLAN.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Luc Makiola
2026-06-17 19:40:53 +02:00
commit 01d1f558b8
40 changed files with 1929 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package de.jeanlucmakiola.floret
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
/**
* Application entry point. Registered as android:name=".FloretApp"
* in AndroidManifest.xml. Hilt initializes its component graph here.
*/
@HiltAndroidApp
class FloretApp : Application()

View File

@@ -0,0 +1,30 @@
package de.jeanlucmakiola.floret
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import dagger.hilt.android.AndroidEntryPoint
import de.jeanlucmakiola.floret.ui.RootScreen
import de.jeanlucmakiola.floret.ui.theme.FloretTheme
/**
* Single activity. M0 hosts only a themed placeholder scaffold; the task
* screens, theme-mode wiring (SettingsViewModel) and notification/widget
* intent routing land in later milestones — mirroring Calendula's MainActivity.
*/
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
FloretTheme {
RootScreen(modifier = Modifier.fillMaxSize())
}
}
}
}

View File

@@ -0,0 +1,48 @@
package de.jeanlucmakiola.floret.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import de.jeanlucmakiola.floret.R
/**
* M0 placeholder root. Proves the theme, Hilt entry point and edge-to-edge
* scaffold render. Replaced in M1 by the lists overview + nav host
* (FloretHost), the analog of Calendula's CalendarHost.
*/
@Composable
fun RootScreen(modifier: Modifier = Modifier) {
Scaffold(modifier = modifier) { inner ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(inner)
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.displaySmall,
color = MaterialTheme.colorScheme.primary,
)
Text(
text = stringResource(R.string.app_tagline),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 8.dp),
)
}
}
}

View File

@@ -0,0 +1,46 @@
package de.jeanlucmakiola.floret.ui.theme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
/**
* Seed color anchoring the entire palette. A warm mauve — sibling to
* Calendula's cool slate ([0xFF5C6B7A]) and deliberately distinct from
* HouseHoldKeaper's sage. Evokes the marigold florets a Calendula head is
* made of, kept muted to match the family's restrained aesthetic.
*
* Placeholder until branding lands; refine alongside the launcher icon.
*/
val FloretSeed: Color = Color(0xFF7A5C6B)
/**
* Fallback light scheme for devices without dynamic color (API < 31) or when
* the user disables it. Dynamic color (API 31+) is the real path; these are a
* hand-picked approximation of the tonal palette generated from [FloretSeed].
*/
val FloretLightFallback = lightColorScheme(
primary = Color(0xFF6A4E5B),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFF6D9E5),
onPrimaryContainer = Color(0xFF2A101C),
secondary = Color(0xFF705560),
onSecondary = Color(0xFFFFFFFF),
background = Color(0xFFFEFBFC),
onBackground = Color(0xFF1F1A1C),
surface = Color(0xFFFEFBFC),
onSurface = Color(0xFF1F1A1C),
)
val FloretDarkFallback = darkColorScheme(
primary = Color(0xFFE2BAC9),
onPrimary = Color(0xFF402533),
primaryContainer = Color(0xFF583B49),
onPrimaryContainer = Color(0xFFF6D9E5),
secondary = Color(0xFFD8BCC6),
onSecondary = Color(0xFF3A2831),
background = Color(0xFF161113),
onBackground = Color(0xFFE9E0E3),
surface = Color(0xFF161113),
onSurface = Color(0xFFE9E0E3),
)

View File

@@ -0,0 +1,48 @@
package de.jeanlucmakiola.floret.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
/**
* App theme. Honors:
* - System light/dark.
* - Dynamic Color on API 31+, else falls back to the hand-tuned scheme
* derived from [FloretSeed].
*
* Mirrors Calendula's theme: MaterialExpressiveTheme routes all component +
* custom motion through MaterialTheme.motionScheme. STANDARD over expressive()
* is the same deliberate choice — spring choreography without the overshoot.
*
* A Settings screen can later override dynamicColor and theme mode; the M0
* foundation just follows the system.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun FloretTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit,
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val ctx = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(ctx) else dynamicLightColorScheme(ctx)
}
darkTheme -> FloretDarkFallback
else -> FloretLightFallback
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = FloretTypography,
motionScheme = MotionScheme.standard(),
content = content,
)
}

View File

@@ -0,0 +1,10 @@
package de.jeanlucmakiola.floret.ui.theme
import androidx.compose.material3.Typography
/**
* Default Material 3 Expressive typography. A custom font + tuned scale will
* land in a later UI-design iteration; the defaults keep the M0 foundation lean
* (same posture as Calendula's scaffolding).
*/
val FloretTypography = Typography()