From 43ac53b3a42f74f2f65a82a0aff4dcab5b042040 Mon Sep 17 00:00:00 2001 From: Xitee <59659167+Xitee1@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:16:02 +0200 Subject: [PATCH 1/2] chore(deps): upgrade to AGP 9 / Kotlin 2.3 / Compose BOM 2026.03 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring the whole dependency set forward ~15 months: - AGP 8.7.3 → 9.1.1 (requires Gradle 9.3+, compileSdk 36+) - Gradle wrapper 8.10.2 → 9.4.1 - Kotlin 2.1.0 → 2.3.20, KSP 2.1.0-1.0.29 → 2.3.6 - Hilt 2.53.1 → 2.59.2 - Compose BOM 2024.12.01 → 2026.03.01 (Material3 1.4.0) - Lifecycle 2.8.7 → 2.10.0, Navigation 2.8.5 → 2.9.7 - Activity Compose 1.9.3 → 1.13.0, Core KTX 1.15.0 → 1.18.0 - DataStore 1.1.1 → 1.2.1 - kotlinx-serialization 1.7.3 → 1.11.0 - compileSdk / targetSdk 35 → 36 AGP 9 structural changes: - Remove org.jetbrains.kotlin.android plugin (built in to AGP 9) - Drop android.kotlinOptions DSL (no longer available); JVM target flows from compileOptions Hilt Nav Compose 1.3.0: hiltViewModel() moved to a dedicated artifact without a transitive navigation dependency. Swap the four call sites to androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel and add the new hilt-lifecycle-viewmodel-compose dependency to :app and :feature:timer. Opt in to the upcoming Kotlin annotation-default-target behaviour via -Xannotation-default-target=param-property in all Android modules, silencing KT-73255 warnings on @Inject / @StringRes constructor params. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/build.gradle.kts | 16 ++++++------ build.gradle.kts | 1 - core/data/build.gradle.kts | 9 ++++--- core/service/build.gradle.kts | 9 ++++--- feature/timer/build.gradle.kts | 14 ++++++----- .../feature/timer/about/AboutScreen.kt | 2 +- .../feature/timer/settings/SettingsScreen.kt | 2 +- .../timer/settings/ThemePickerScreen.kt | 2 +- .../feature/timer/timer/TimerScreen.kt | 2 +- gradle/libs.versions.toml | 25 ++++++++++--------- gradle/wrapper/gradle-wrapper.properties | 2 +- 11 files changed, 45 insertions(+), 39 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fc69b00..87267ea 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.kotlin.serialization) alias(libs.plugins.hilt.android) @@ -9,12 +8,12 @@ plugins { android { namespace = "dev.xitee.sleeptimer" - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "dev.xitee.sleeptimer" minSdk = 26 - targetSdk = 35 + targetSdk = 36 versionCode = 1 versionName = "1.0.0" } @@ -54,15 +53,17 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" - } - buildFeatures { compose = true } } +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xannotation-default-target=param-property") + } +} + dependencies { implementation(project(":core:data")) implementation(project(":core:service")) @@ -83,6 +84,7 @@ dependencies { implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.androidx.hilt.lifecycle.viewmodel.compose) implementation(libs.kotlinx.serialization.json) implementation(libs.hilt.android) diff --git a/build.gradle.kts b/build.gradle.kts index c82e1bf..a73cca7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,6 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false - alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.hilt.android) apply false diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 4e7393c..cca38d9 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -1,13 +1,12 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) alias(libs.plugins.hilt.android) alias(libs.plugins.ksp) } android { namespace = "dev.xitee.sleeptimer.core.data" - compileSdk = 35 + compileSdk = 36 defaultConfig { minSdk = 26 @@ -17,9 +16,11 @@ android { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xannotation-default-target=param-property") } } diff --git a/core/service/build.gradle.kts b/core/service/build.gradle.kts index 17f38da..75e2a31 100644 --- a/core/service/build.gradle.kts +++ b/core/service/build.gradle.kts @@ -1,13 +1,12 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) alias(libs.plugins.hilt.android) alias(libs.plugins.ksp) } android { namespace = "dev.xitee.sleeptimer.core.service" - compileSdk = 35 + compileSdk = 36 defaultConfig { minSdk = 26 @@ -17,9 +16,11 @@ android { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xannotation-default-target=param-property") } } diff --git a/feature/timer/build.gradle.kts b/feature/timer/build.gradle.kts index eacc10e..0cb663a 100644 --- a/feature/timer/build.gradle.kts +++ b/feature/timer/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.hilt.android) alias(libs.plugins.ksp) @@ -8,7 +7,7 @@ plugins { android { namespace = "dev.xitee.sleeptimer.feature.timer" - compileSdk = 35 + compileSdk = 36 defaultConfig { minSdk = 26 @@ -19,15 +18,17 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" - } - buildFeatures { compose = true } } +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xannotation-default-target=param-property") + } +} + dependencies { implementation(project(":core:data")) implementation(project(":core:service")) @@ -45,6 +46,7 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.androidx.hilt.lifecycle.viewmodel.compose) implementation(libs.hilt.android) ksp(libs.hilt.android.compiler) diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/about/AboutScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/about/AboutScreen.kt index 4780f0d..158fe90 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/about/AboutScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/about/AboutScreen.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.xitee.sleeptimer.feature.timer.R import dev.xitee.sleeptimer.feature.timer.theme.AppThemes diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt index e3aef96..dffa1b4 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt @@ -49,7 +49,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/ThemePickerScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/ThemePickerScreen.kt index e6f7bf8..8b017a1 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/ThemePickerScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/ThemePickerScreen.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.xitee.sleeptimer.core.data.model.ThemeId import dev.xitee.sleeptimer.feature.timer.R diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt index ccc067f..8694be3 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt @@ -52,7 +52,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.xitee.sleeptimer.core.data.util.remainingMillisToDisplayMinutes import dev.xitee.sleeptimer.feature.timer.R diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eca2642..6eee6f9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,16 +1,16 @@ [versions] -agp = "8.7.3" -kotlin = "2.1.0" -ksp = "2.1.0-1.0.29" -hilt = "2.53.1" -composeBom = "2024.12.01" -androidxLifecycle = "2.8.7" -androidxNavigation = "2.8.5" -androidxHiltNavigationCompose = "1.2.0" -androidxDatastore = "1.1.1" -androidxCoreKtx = "1.15.0" -androidxActivityCompose = "1.9.3" -kotlinxSerializationJson = "1.7.3" +agp = "9.1.1" +kotlin = "2.3.20" +ksp = "2.3.6" +hilt = "2.59.2" +composeBom = "2026.03.01" +androidxLifecycle = "2.10.0" +androidxNavigation = "2.9.7" +androidxHiltNavigationCompose = "1.3.0" +androidxDatastore = "1.2.1" +androidxCoreKtx = "1.18.0" +androidxActivityCompose = "1.13.0" +kotlinxSerializationJson = "1.11.0" shizuku = "13.1.5" [libraries] @@ -34,6 +34,7 @@ androidx-datastore-preferences = { group = "androidx.datastore", name = "datasto hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } +androidx-hilt-lifecycle-viewmodel-compose = { group = "androidx.hilt", name = "hilt-lifecycle-viewmodel-compose", version.ref = "androidxHiltNavigationCompose" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72..c61a118 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From fe991031a0cd5fe432255ba5a9d1f11dd7e83210 Mon Sep 17 00:00:00 2001 From: Xitee <59659167+Xitee1@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:30:16 +0200 Subject: [PATCH 2/2] fix(lint): pre-resolve stringResource to satisfy LocalContextGetResourceValueCall Compose UI 1.10 promoted this check from hint to error: calling Context.getString() from inside a Composable does not invalidate on configuration changes, so stale translations can leak. Move the five affected lookups (screen admin description + three Shizuku feature labels + soft-screen-off explanation) to stringResource() at the composable scope and capture the resulting Strings in the surrounding lambdas. SettingsScreen no longer needs LocalContext.current; drop the val and its import. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature/timer/settings/SettingsScreen.kt | 15 +++++++++------ .../sleeptimer/feature/timer/timer/TimerScreen.kt | 3 ++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt index ab836aa..b62ae20 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/settings/SettingsScreen.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -98,7 +97,11 @@ private fun SettingsContent( onNavigateToAbout: () -> Unit, viewModel: SettingsViewModel, ) { - val context = LocalContext.current + val screenDescription = stringResource(R.string.screen_description) + val shizukuSoftScreenOffExplanation = + stringResource(R.string.shizuku_feature_soft_screen_off) + val shizukuWifiExplanation = stringResource(R.string.shizuku_feature_wifi) + val shizukuBluetoothExplanation = stringResource(R.string.shizuku_feature_bluetooth) val deviceAdminLauncher = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult(), @@ -148,14 +151,14 @@ private fun SettingsContent( ) putExtra( DevicePolicyManager.EXTRA_ADD_EXPLANATION, - context.getString(R.string.screen_description), + screenDescription, ) } deviceAdminLauncher.launch(intent) } }, onSoftLockSelected = { - requestWithShizuku(context.getString(R.string.shizuku_feature_soft_screen_off)) { + requestWithShizuku(shizukuSoftScreenOffExplanation) { viewModel.updateScreenOff(true) viewModel.updateSoftScreenOff(true) } @@ -266,7 +269,7 @@ private fun SettingsContent( checked = uiState.settings.turnOffWifi, onCheckedChange = { enabled -> if (enabled) { - requestWithShizuku(context.getString(R.string.shizuku_feature_wifi)) { + requestWithShizuku(shizukuWifiExplanation) { viewModel.updateTurnOffWifi(true) } } else { @@ -282,7 +285,7 @@ private fun SettingsContent( checked = uiState.settings.turnOffBluetooth, onCheckedChange = { enabled -> if (enabled) { - requestWithShizuku(context.getString(R.string.shizuku_feature_bluetooth)) { + requestWithShizuku(shizukuBluetoothExplanation) { viewModel.updateTurnOffBluetooth(true) } } else { diff --git a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt index a413bf4..b402ec8 100644 --- a/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt +++ b/feature/timer/src/main/kotlin/dev/xitee/sleeptimer/feature/timer/timer/TimerScreen.kt @@ -99,6 +99,7 @@ private fun TimerContent( val settings by viewModel.settings.collectAsStateWithLifecycle() val dialState = rememberCircularDialState() val context = LocalContext.current + val screenDescription = stringResource(R.string.screen_description) val orientation by rememberDeviceOrientation() val isLandscape = orientation == DeviceOrientation.LANDSCAPE_LEFT || @@ -145,7 +146,7 @@ private fun TimerContent( ) putExtra( DevicePolicyManager.EXTRA_ADD_EXPLANATION, - context.getString(R.string.screen_description), + screenDescription, ) } adminStartupLauncher.launch(intent)