Project skeleton: Gradle 9.4.1 wrapper, AGP 9.2.1, Compose, manifest with reminder permission set
Milestone 1 of the build brief. Version catalog pins verified against Maven Central / Google Maven on 2026-06-10; Kotlin held at 2.3.10 to match AGP 9.2's bundled KGP (built-in Kotlin) because KSP has no Kotlin 2.4 release yet. compileSdk 37 (forced by core-ktx 1.19), targetSdk 35 per brief. allowBackup=false + full dataExtractionRules opt-out; own encrypted backup comes in milestone 5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
commit
7256c49112
21 changed files with 845 additions and 0 deletions
52
app/build.gradle.kts
Normal file
52
app/build.gradle.kts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
// AGP 9 has built-in Kotlin (org.jetbrains.kotlin.android is gone); only the
|
||||
// compiler sub-plugins are applied separately, pinned to AGP's bundled KGP version.
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "no.naiv.meddetsamme"
|
||||
// compileSdk 37: required by current AndroidX (core 1.19 / compose BOM 2026.05).
|
||||
// targetSdk stays 35 (brief) — compile-against vs runtime-behavior are separate knobs.
|
||||
compileSdk = 37
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "no.naiv.meddetsamme"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
// Built-in Kotlin derives jvmTarget from targetCompatibility — one knob, not two.
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
|
||||
implementation(platform(libs.compose.bom))
|
||||
implementation(libs.compose.ui)
|
||||
implementation(libs.compose.material3)
|
||||
implementation(libs.compose.ui.tooling.preview)
|
||||
debugImplementation(libs.compose.ui.tooling)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
}
|
||||
44
app/src/main/AndroidManifest.xml
Normal file
44
app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Reminders are the point of this app; the permission set exists to keep them
|
||||
firing. See CLAUDE.md "Decisions" before touching any of these. -->
|
||||
|
||||
<!-- Exact alarms for dose times. USE_EXACT_ALARM (not SCHEDULE_EXACT_ALARM):
|
||||
medication reminders are alarm-clock-like core functionality, and this
|
||||
variant needs no runtime grant. Still gated on canScheduleExactAlarms(). -->
|
||||
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<!-- AlarmManager state is wiped on reboot; we re-arm everything from the DB. -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<!-- One-time prompt to exempt us from battery optimisation — the biggest cause
|
||||
of dropped reminders on aggressive OEMs (Samsung/Xiaomi). -->
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<!-- Backups to self-hosted S3 (Garage) and FEST dataset refresh only. -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<!-- No Google Auto Backup: this app runs its own encrypted backup. allowBackup
|
||||
covers pre-12; dataExtractionRules opts out of cloud backup AND device-to-
|
||||
device transfer on 12+. -->
|
||||
<application
|
||||
android:name=".MedDetSammeApp"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="false"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MedDetSamme">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
36
app/src/main/java/no/naiv/meddetsamme/MainActivity.kt
Normal file
36
app/src/main/java/no/naiv/meddetsamme/MainActivity.kt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package no.naiv.meddetsamme
|
||||
|
||||
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.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.Modifier
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
AppRoot()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppRoot() {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Text(
|
||||
text = "Med det samme",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
10
app/src/main/java/no/naiv/meddetsamme/MedDetSammeApp.kt
Normal file
10
app/src/main/java/no/naiv/meddetsamme/MedDetSammeApp.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package no.naiv.meddetsamme
|
||||
|
||||
import android.app.Application
|
||||
|
||||
/**
|
||||
* Application entry point. Later milestones hang app-wide singletons off this
|
||||
* (DB, notification channels, alarm re-arm) — no DI framework at this size,
|
||||
* manual wiring via a small service locator.
|
||||
*/
|
||||
class MedDetSammeApp : Application()
|
||||
21
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
21
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- A capsule at 45°: two half-pill rounded shapes. Placeholder-quality, but ours. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:rotation="45"
|
||||
android:pivotX="54"
|
||||
android:pivotY="54">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M42,30 h24 a12,12 0 0 1 12,12 v0 h-48 v0 a12,12 0 0 1 12,-12 z"
|
||||
android:strokeWidth="0" />
|
||||
<path
|
||||
android:fillColor="#A5D6A7"
|
||||
android:pathData="M30,42 h48 v24 a12,12 0 0 1 -12,12 h-24 a12,12 0 0 1 -12,-12 z"
|
||||
android:strokeWidth="0" />
|
||||
</group>
|
||||
</vector>
|
||||
7
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
7
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- minSdk 26 = adaptive icons everywhere; no legacy PNG densities needed. -->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
4
app/src/main/res/values/colors.xml
Normal file
4
app/src/main/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1B5E20</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 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Med det samme</string>
|
||||
</resources>
|
||||
6
app/src/main/res/values/themes.xml
Normal file
6
app/src/main/res/values/themes.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Compose draws everything; this only styles the window before setContent.
|
||||
Platform theme, so no appcompat/material-components dependency. -->
|
||||
<style name="Theme.MedDetSamme" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Opt out of both Google cloud backup and device-to-device transfer (API 31+).
|
||||
Health data stays on-device; our own encrypted S3 backup is the only copy. -->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" path="." />
|
||||
<exclude domain="database" path="." />
|
||||
<exclude domain="sharedpref" path="." />
|
||||
<exclude domain="file" path="." />
|
||||
<exclude domain="external" path="." />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="root" path="." />
|
||||
<exclude domain="database" path="." />
|
||||
<exclude domain="sharedpref" path="." />
|
||||
<exclude domain="file" path="." />
|
||||
<exclude domain="external" path="." />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
Loading…
Add table
Add a link
Reference in a new issue