New launcher icon, version 0.2.0, lint cleanup
Icon: a capsule in a hurry — stadium pill at 45° with motion lines
trailing, «med det samme» as imagery. versionCode 2 / versionName 0.2.0
with a bump-on-release note. Lint: KTX conversions (prefs.edit{},
toUri, createBitmap), mipmap-anydpi-v26 merged into mipmap-anydpi
(minSdk 26 makes the qualifier dead weight), compose-bom 2026.05.01,
Gradle wrapper 9.5.1, and the three deliberate-decision warnings
(BatteryLife/OldTargetApi/NewerVersionAvailable) disabled with
documented reasons. Lint now runs clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
90eb15ca0b
commit
80c8c4788a
9 changed files with 52 additions and 27 deletions
|
|
@ -31,8 +31,10 @@ android {
|
||||||
applicationId = "no.naiv.meddetsamme"
|
applicationId = "no.naiv.meddetsamme"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
// Bump both for every release installed on the phone — versionName for
|
||||||
versionName = "0.1.0"
|
// humans, versionCode so dumpsys/install history can tell builds apart.
|
||||||
|
versionCode = 2
|
||||||
|
versionName = "0.2.0"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +69,14 @@ android {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
// Deliberate decisions, not oversights — see CLAUDE.md:
|
||||||
|
// BatteryLife: decision #4, sideloaded app (Play policy n/a).
|
||||||
|
// OldTargetApi: targetSdk 35 is pinned by the brief.
|
||||||
|
// NewerVersionAvailable: Kotlin 2.4 is blocked until KSP supports it.
|
||||||
|
disable += listOf("BatteryLife", "OldTargetApi", "NewerVersionAvailable")
|
||||||
|
}
|
||||||
|
|
||||||
// MigrationTestHelper reads the exported schema JSONs as androidTest assets.
|
// MigrationTestHelper reads the exported schema JSONs as androidTest assets.
|
||||||
sourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas")
|
sourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ package no.naiv.meddetsamme.alarm
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Battery-optimisation ("Doze whitelist") exemption — brief decision #4: the
|
* Battery-optimisation ("Doze whitelist") exemption — brief decision #4: the
|
||||||
|
|
@ -24,6 +24,6 @@ object BatteryOptimization {
|
||||||
fun requestExemptionIntent(context: Context): Intent =
|
fun requestExemptionIntent(context: Context): Intent =
|
||||||
Intent(
|
Intent(
|
||||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||||
Uri.parse("package:${context.packageName}"),
|
"package:${context.packageName}".toUri(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.security.keystore.KeyGenParameterSpec
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
import android.security.keystore.KeyProperties
|
import android.security.keystore.KeyProperties
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
|
import androidx.core.content.edit
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.KeyGenerator
|
import javax.crypto.KeyGenerator
|
||||||
|
|
@ -37,22 +38,22 @@ class SettingsStore(context: Context) {
|
||||||
/** Non-secret flags can live in plaintext prefs. */
|
/** Non-secret flags can live in plaintext prefs. */
|
||||||
var batteryPromptShown: Boolean
|
var batteryPromptShown: Boolean
|
||||||
get() = prefs.getBoolean("battery_prompt_shown", false)
|
get() = prefs.getBoolean("battery_prompt_shown", false)
|
||||||
set(v) = prefs.edit().putBoolean("battery_prompt_shown", v).apply()
|
set(v) = prefs.edit { putBoolean("battery_prompt_shown", v) }
|
||||||
|
|
||||||
/** FEST dataset URL — not a secret, plaintext pref. */
|
/** FEST dataset URL — not a secret, plaintext pref. */
|
||||||
var festUrl: String?
|
var festUrl: String?
|
||||||
get() = prefs.getString("fest_url", null)
|
get() = prefs.getString("fest_url", null)
|
||||||
set(v) = prefs.edit().putString("fest_url", v).apply()
|
set(v) = prefs.edit { putString("fest_url", v) }
|
||||||
|
|
||||||
/** One-time "backup isn't set up" card on the Today screen. */
|
/** One-time "backup isn't set up" card on the Today screen. */
|
||||||
var backupNudgeDismissed: Boolean
|
var backupNudgeDismissed: Boolean
|
||||||
get() = prefs.getBoolean("backup_nudge_dismissed", false)
|
get() = prefs.getBoolean("backup_nudge_dismissed", false)
|
||||||
set(v) = prefs.edit().putBoolean("backup_nudge_dismissed", v).apply()
|
set(v) = prefs.edit { putBoolean("backup_nudge_dismissed", v) }
|
||||||
|
|
||||||
/** Daily low-stock/rx check time, minutes after midnight. Default 10:00. */
|
/** Daily low-stock/rx check time, minutes after midnight. Default 10:00. */
|
||||||
var supplyCheckMinuteOfDay: Int
|
var supplyCheckMinuteOfDay: Int
|
||||||
get() = prefs.getInt("supply_check_minute", 600)
|
get() = prefs.getInt("supply_check_minute", 600)
|
||||||
set(v) = prefs.edit().putInt("supply_check_minute", v.coerceIn(0, 1439)).apply()
|
set(v) = prefs.edit { putInt("supply_check_minute", v.coerceIn(0, 1439)) }
|
||||||
|
|
||||||
val isBackupConfigured: Boolean
|
val isBackupConfigured: Boolean
|
||||||
get() = !s3Endpoint.isNullOrBlank() && !s3Bucket.isNullOrBlank() &&
|
get() = !s3Endpoint.isNullOrBlank() && !s3Bucket.isNullOrBlank() &&
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.net.toUri
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -111,14 +112,11 @@ fun FormAvatar(form: MedForm) {
|
||||||
* reading only. Decision #8 bans Felleskatalogen as a DATA source (licensed
|
* reading only. Decision #8 bans Felleskatalogen as a DATA source (licensed
|
||||||
* editorial content, no API); pointing the human at their website is fine.
|
* editorial content, no API); pointing the human at their website is fine.
|
||||||
*/
|
*/
|
||||||
fun felleskatalogenIntent(name: String): android.content.Intent =
|
fun felleskatalogenIntent(name: String): android.content.Intent {
|
||||||
android.content.Intent(
|
val url = "https://www.felleskatalogen.no/medisin/sok?sokord=" +
|
||||||
android.content.Intent.ACTION_VIEW,
|
java.net.URLEncoder.encode(name.trim(), "UTF-8")
|
||||||
android.net.Uri.parse(
|
return android.content.Intent(android.content.Intent.ACTION_VIEW, url.toUri())
|
||||||
"https://www.felleskatalogen.no/medisin/sok?sokord=" +
|
}
|
||||||
java.net.URLEncoder.encode(name.trim(), "UTF-8"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State + validation for an [InventoryItem] form — shared between the
|
* State + validation for an [InventoryItem] form — shared between the
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ fun SummaryPreviewScreen(nav: Nav) {
|
||||||
renderer.openPage(i).use { page ->
|
renderer.openPage(i).use { page ->
|
||||||
// 2x point size — crisp on phone screens without
|
// 2x point size — crisp on phone screens without
|
||||||
// burning memory on huge bitmaps.
|
// burning memory on huge bitmaps.
|
||||||
val bmp = Bitmap.createBitmap(page.width * 2, page.height * 2, Bitmap.Config.ARGB_8888)
|
val bmp = androidx.core.graphics.createBitmap(page.width * 2, page.height * 2)
|
||||||
bmp.eraseColor(android.graphics.Color.WHITE)
|
bmp.eraseColor(android.graphics.Color.WHITE)
|
||||||
page.render(bmp, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
|
page.render(bmp, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
|
||||||
rendered += bmp
|
rendered += bmp
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,37 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- A capsule at 45°: two half-pill rounded shapes. Placeholder-quality, but ours. -->
|
<!-- A capsule in a hurry: stadium-shaped pill flying up-right at 45°, with
|
||||||
|
motion lines trailing — «med det samme» as an icon. Safe zone: center 66
|
||||||
|
of the 108 viewport. -->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<group
|
<group
|
||||||
android:rotation="45"
|
android:rotation="-45"
|
||||||
android:pivotX="54"
|
android:pivotX="54"
|
||||||
android:pivotY="54">
|
android:pivotY="54">
|
||||||
|
<!-- motion lines trailing behind (left of) the capsule -->
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFFFFF"
|
android:fillColor="#80FFFFFF"
|
||||||
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:pathData="M18,51.5 h12 a2.5,2.5 0 0 1 0,5 h-12 a2.5,2.5 0 0 1 0,-5 z" />
|
||||||
android:strokeWidth="0" />
|
<path
|
||||||
|
android:fillColor="#59FFFFFF"
|
||||||
|
android:pathData="M14,43.5 h10 a2.5,2.5 0 0 1 0,5 h-10 a2.5,2.5 0 0 1 0,-5 z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#59FFFFFF"
|
||||||
|
android:pathData="M14,59.5 h10 a2.5,2.5 0 0 1 0,5 h-10 a2.5,2.5 0 0 1 0,-5 z" />
|
||||||
|
<!-- capsule: full stadium in pale green -->
|
||||||
<path
|
<path
|
||||||
android:fillColor="#A5D6A7"
|
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:pathData="M50,43 h26 a11,11 0 0 1 0,22 h-26 a11,11 0 0 1 0,-22 z" />
|
||||||
android:strokeWidth="0" />
|
<!-- leading half in white -->
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M63,43 h13 a11,11 0 0 1 0,22 h-13 z" />
|
||||||
|
<!-- highlight on the leading cap -->
|
||||||
|
<path
|
||||||
|
android:fillColor="#33FFFFFF"
|
||||||
|
android:pathData="M72,46.5 a3,3 0 1 1 0,6 a3,3 0 1 1 0,-6 z" />
|
||||||
</group>
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- minSdk 26 = adaptive icons everywhere; no legacy PNG densities needed. -->
|
<!-- minSdk 26 = adaptive icons everywhere (anydpi, no -v26 qualifier needed). -->
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
agp = "9.2.1"
|
agp = "9.2.1"
|
||||||
kotlin = "2.3.10"
|
kotlin = "2.3.10"
|
||||||
ksp = "2.3.9" # KSP versioning decoupled from Kotlin since 2.3.0 (no more <kotlin>-<ksp> pairs)
|
ksp = "2.3.9" # KSP versioning decoupled from Kotlin since 2.3.0 (no more <kotlin>-<ksp> pairs)
|
||||||
composeBom = "2026.05.00"
|
composeBom = "2026.05.01"
|
||||||
activityCompose = "1.13.0"
|
activityCompose = "1.13.0"
|
||||||
coreKtx = "1.19.0"
|
coreKtx = "1.19.0"
|
||||||
lifecycle = "2.10.0"
|
lifecycle = "2.10.0"
|
||||||
|
|
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue