M0: Floret skeleton — Material 3 Expressive scaffold over OpenTasks (planned)
All checks were successful
CI / ci (push) Successful in 6m39s
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:
36
app/src/main/AndroidManifest.xml
Normal file
36
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!--
|
||||
M0 skeleton: no permissions yet. Tasks-provider permissions
|
||||
(org.dmfs.permission.* / org.tasks.permission.*), POST_NOTIFICATIONS,
|
||||
RECEIVE_BOOT_COMPLETED, USE_EXACT_ALARM and the <queries> package
|
||||
visibility block arrive with the data layer and reminder engine
|
||||
(see docs/PLAN.md, §5–7).
|
||||
-->
|
||||
|
||||
<application
|
||||
android:name=".FloretApp"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Floret"
|
||||
tools:targetApi="35">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
11
app/src/main/java/de/jeanlucmakiola/floret/FloretApp.kt
Normal file
11
app/src/main/java/de/jeanlucmakiola/floret/FloretApp.kt
Normal 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()
|
||||
30
app/src/main/java/de/jeanlucmakiola/floret/MainActivity.kt
Normal file
30
app/src/main/java/de/jeanlucmakiola/floret/MainActivity.kt
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
app/src/main/java/de/jeanlucmakiola/floret/ui/RootScreen.kt
Normal file
48
app/src/main/java/de/jeanlucmakiola/floret/ui/RootScreen.kt
Normal 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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
46
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Color.kt
Normal file
46
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Color.kt
Normal 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),
|
||||
)
|
||||
48
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Theme.kt
Normal file
48
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Theme.kt
Normal 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,
|
||||
)
|
||||
}
|
||||
10
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Type.kt
Normal file
10
app/src/main/java/de/jeanlucmakiola/floret/ui/theme/Type.kt
Normal 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()
|
||||
5
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
5
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/ic_launcher_background" />
|
||||
</shape>
|
||||
22
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
22
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Floret launcher icon foreground — PLACEHOLDER.
|
||||
|
||||
A simple rounded check mark inside the 108dp adaptive-icon canvas
|
||||
(72dp safe zone). Deliberately not Calendula's calendar mark, so the two
|
||||
apps never look alike. Replace with real branding (a stylized floret)
|
||||
when the design lands — see docs/PLAN.md §9.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeWidth="9"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:pathData="M36,55 l13,13 l25,-27" />
|
||||
</vector>
|
||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
6
app/src/main/res/values/colors.xml
Normal file
6
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<!-- Seed color: warm mauve. Material 3 derives Light & Dark schemes from this. -->
|
||||
<color name="seed">#FF7A5C6B</color>
|
||||
<!-- Adaptive icon background -->
|
||||
<color name="ic_launcher_background">#FF7A5C6B</color>
|
||||
</resources>
|
||||
4
app/src/main/res/values/strings.xml
Normal file
4
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">Floret</string>
|
||||
<string name="app_tagline">A modern Material 3 Expressive task app.\nComing into bloom.</string>
|
||||
</resources>
|
||||
7
app/src/main/res/values/themes.xml
Normal file
7
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.Floret" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
4
app/src/main/res/xml/backup_rules.xml
Normal file
4
app/src/main/res/xml/backup_rules.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<!-- No file-based backups; settings live in DataStore which is backed up by default. -->
|
||||
</full-backup-content>
|
||||
8
app/src/main/res/xml/data_extraction_rules.xml
Normal file
8
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- Allow DataStore backup, exclude nothing extra. -->
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
Reference in New Issue
Block a user