Published to Google Play Store. - Detail screen: prev/next bottom nav bar, delete navigates to next item with undo snackbar - Delete auto-fires app's Delete action button if available (like Gmail delete) - New app_actions table: captures notification action buttons per app (DB migration 7→8) - Filter rule editor: shows known action buttons for selected app (excluding reply actions) - Filter rule editor: dynamic placeholder text based on match field, auto-fill from source notification - Filter rule editor: "Create filter" button added to detail screen - Filter UX: renamed "tap" → "hit" action button, clearer dropdown labels throughout - Filter UX: long text auto-shortened for patterns, URL-safe sentence detection - Filter UX: frequency limiter now supports up to 24 hours - Bookmark icon: orange badge style, visible on both items and collapsed group headers - Read/unread: Gmail-style — bold text + dot + tinted background for unread, normal for read - Pro mode defaults to true on debug builds - Rule name auto-generates on save (app name + action + pattern) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
179 lines
5.4 KiB
Kotlin
179 lines
5.4 KiB
Kotlin
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.compose)
|
|
alias(libs.plugins.kotlin.serialization)
|
|
alias(libs.plugins.ksp)
|
|
alias(libs.plugins.hilt)
|
|
}
|
|
|
|
import java.io.File
|
|
import java.io.FileInputStream
|
|
import java.time.LocalDate
|
|
import java.util.Properties
|
|
|
|
val keystorePropsFile = file("${System.getProperty("user.home")}/AndroidStudioProjects/rounding/.keystore/rmt.properties")
|
|
val keystoreProps = Properties()
|
|
if (keystorePropsFile.exists()) {
|
|
keystoreProps.load(FileInputStream(keystorePropsFile))
|
|
}
|
|
|
|
android {
|
|
namespace = "com.roundingmobile.sni"
|
|
compileSdk = 36
|
|
|
|
signingConfigs {
|
|
create("rmt") {
|
|
storeFile = file("${System.getProperty("user.home")}/AndroidStudioProjects/rounding/.keystore/${keystoreProps.getProperty("keystore", "")}")
|
|
storePassword = keystoreProps.getProperty("keystore.password", "")
|
|
keyAlias = keystoreProps.getProperty("release.alias", "")
|
|
keyPassword = keystoreProps.getProperty("alias.password", "")
|
|
}
|
|
}
|
|
|
|
defaultConfig {
|
|
applicationId = "com.roundingmobile.sni"
|
|
minSdk = 27
|
|
targetSdk = 35
|
|
versionCode = 7
|
|
versionName = "1.0.0-beta07"
|
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
|
|
flavorDimensions += "tier"
|
|
productFlavors {
|
|
create("prod") {
|
|
dimension = "tier"
|
|
applicationId = "com.roundingmobile.sni"
|
|
buildConfigField("boolean", "TEST_INSTRUMENTED", "false")
|
|
buildConfigField("boolean", "ADS_ENABLED", "true")
|
|
buildConfigField("int", "RETENTION_LIMIT_HOURS", "0")
|
|
}
|
|
create("dev") {
|
|
dimension = "tier"
|
|
applicationId = "com.roundingmobile.sni.dev"
|
|
buildConfigField("boolean", "TEST_INSTRUMENTED", "true")
|
|
buildConfigField("boolean", "ADS_ENABLED", "false")
|
|
buildConfigField("int", "RETENTION_LIMIT_HOURS", "0")
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
signingConfig = signingConfigs.getByName("rmt")
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
}
|
|
debug {
|
|
isMinifyEnabled = false
|
|
signingConfig = signingConfigs.getByName("rmt")
|
|
// No applicationIdSuffix — debug and release share the same package name
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
|
|
packaging {
|
|
resources {
|
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Rename APKs after assembly
|
|
tasks.whenTaskAdded {
|
|
if (name.startsWith("assemble") && !name.contains("Test")) {
|
|
doLast {
|
|
val apkDir = layout.buildDirectory.dir("outputs/apk").get().asFile
|
|
apkDir.walkTopDown().filter { it.extension == "apk" }.forEach { apk ->
|
|
val oldName = apk.name
|
|
if (oldName.startsWith("rmt-sni")) return@forEach // already renamed
|
|
// Parse: app-<flavor>-<buildType>.apk
|
|
val parts = oldName.removeSuffix(".apk").split("-")
|
|
if (parts.size >= 3) {
|
|
val flavor = parts[1]
|
|
val version = android.defaultConfig.versionName ?: "0"
|
|
val date = LocalDate.now().toString()
|
|
val newName = "rmt-sni.${flavor}.v${version}.${date}.apk"
|
|
val newFile = File(apk.parentFile, newName)
|
|
apk.renameTo(newFile)
|
|
println("Renamed: $oldName → $newName")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ksp {
|
|
arg("room.schemaLocation", "$projectDir/schemas")
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
// AndroidX Core
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.activity.compose)
|
|
|
|
// Compose
|
|
implementation(platform(libs.compose.bom))
|
|
implementation(libs.compose.ui)
|
|
implementation(libs.compose.ui.graphics)
|
|
implementation(libs.compose.ui.tooling.preview)
|
|
implementation(libs.compose.material3)
|
|
implementation(libs.compose.material.icons.extended)
|
|
debugImplementation(libs.compose.ui.tooling)
|
|
|
|
// Navigation
|
|
implementation(libs.navigation.compose)
|
|
|
|
// Lifecycle
|
|
implementation(libs.lifecycle.runtime.compose)
|
|
implementation(libs.lifecycle.viewmodel.compose)
|
|
|
|
// Hilt
|
|
implementation(libs.hilt.android)
|
|
ksp(libs.hilt.compiler)
|
|
implementation(libs.hilt.navigation.compose)
|
|
|
|
// Room
|
|
implementation(libs.room.runtime)
|
|
implementation(libs.room.ktx)
|
|
ksp(libs.room.compiler)
|
|
|
|
// WorkManager
|
|
implementation(libs.work.runtime.ktx)
|
|
implementation(libs.hilt.work)
|
|
|
|
// Coroutines
|
|
implementation(libs.coroutines.core)
|
|
implementation(libs.coroutines.android)
|
|
|
|
// Security
|
|
implementation(libs.security.crypto)
|
|
implementation(libs.biometric)
|
|
|
|
// Serialization
|
|
implementation(libs.kotlinx.serialization.json)
|
|
|
|
// Testing
|
|
testImplementation(libs.junit)
|
|
androidTestImplementation(libs.androidx.junit)
|
|
androidTestImplementation(libs.androidx.espresso.core)
|
|
}
|