From fa77f149ce72cdc74491426af357077b3ccabada Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Wed, 29 Apr 2026 23:48:28 +0300 Subject: [PATCH 1/4] Add :sample:android-app standalone Android application module (#171) Creates a new :sample:android-app module (com.android.application) that depends on :sample, :featured-debug-ui, and :featured-platform. Wires DataStoreConfigValueProvider via defaultLocalProvider(context) and shows FeatureFlagsDebugScreen on demand. Also populates :sample's androidMain manifest and MainActivity so the existing KMP sample module can run as a self-contained Android activity. Co-Authored-By: Claude Sonnet 4.6 --- sample/android-app/build.gradle.kts | 46 +++++++++++++++++++ .../android-app/src/main/AndroidManifest.xml | 18 ++++++++ .../featured/sample/MainActivity.kt | 40 ++++++++++++++++ sample/build.gradle.kts | 6 +++ sample/src/androidMain/AndroidManifest.xml | 16 ++++++- .../androidbroadcast/featured/MainActivity.kt | 44 ++++++++++++++++++ settings.gradle.kts | 1 + 7 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 sample/android-app/build.gradle.kts create mode 100644 sample/android-app/src/main/AndroidManifest.xml create mode 100644 sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt create mode 100644 sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts new file mode 100644 index 0000000..997f54c --- /dev/null +++ b/sample/android-app/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.composeCompiler) +} + +android { + namespace = "dev.androidbroadcast.featured.sample.app" + compileSdk = + libs.versions.android.compileSdk + .get() + .toInt() + + defaultConfig { + applicationId = "dev.androidbroadcast.featured.sample" + minSdk = + libs.versions.android.minSdk + .get() + .toInt() + targetSdk = + libs.versions.android.compileSdk + .get() + .toInt() + versionCode = 1 + versionName = "1.0.0" + } + + buildFeatures { + compose = true + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + ) + } + } +} + +dependencies { + implementation(project(":sample")) + implementation(project(":featured-debug-ui")) + implementation(project(":featured-platform")) + implementation(libs.androidx.activity.compose) +} diff --git a/sample/android-app/src/main/AndroidManifest.xml b/sample/android-app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a05dc57 --- /dev/null +++ b/sample/android-app/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt b/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt new file mode 100644 index 0000000..070dc12 --- /dev/null +++ b/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt @@ -0,0 +1,40 @@ +package dev.androidbroadcast.featured.sample + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import dev.androidbroadcast.featured.ConfigValues +import dev.androidbroadcast.featured.SampleApp +import dev.androidbroadcast.featured.debugui.FeatureFlagsDebugScreen +import dev.androidbroadcast.featured.platform.defaultLocalProvider + +class MainActivity : ComponentActivity() { + // ConfigValues is held at Activity scope for this sample. + // In production, move to Application or a DI singleton to avoid + // recreating (and re-opening) the DataStore file on every rotation. + private val configValues by lazy { + ConfigValues(localProvider = defaultLocalProvider(this)) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + var showDebug by rememberSaveable { mutableStateOf(false) } + + if (showDebug) { + FeatureFlagsDebugScreen(configValues = configValues) + } else { + SampleApp( + configValues = configValues, + onOpenDebugUi = { showDebug = true }, + ) + } + } + } +} diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index e5f59e6..3c61e1e 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -53,6 +53,12 @@ kotlin { implementation(project(":core")) implementation(project(":featured-registry")) + implementation(project(":featured-debug-ui")) + } + + androidMain.dependencies { + implementation(project(":featured-platform")) + implementation(libs.androidx.activity.compose) } jvmMain.dependencies { diff --git a/sample/src/androidMain/AndroidManifest.xml b/sample/src/androidMain/AndroidManifest.xml index b2d3ea1..cf38138 100644 --- a/sample/src/androidMain/AndroidManifest.xml +++ b/sample/src/androidMain/AndroidManifest.xml @@ -1,2 +1,16 @@ - + + + + + + + + + + diff --git a/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt b/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt new file mode 100644 index 0000000..507c101 --- /dev/null +++ b/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt @@ -0,0 +1,44 @@ +package dev.androidbroadcast.featured + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import dev.androidbroadcast.featured.debugui.FeatureFlagsDebugScreen +import dev.androidbroadcast.featured.platform.defaultLocalProvider + +class MainActivity : ComponentActivity() { + // Singleton ConfigValues for this process — DataStore must not be opened more than once + // per file per process; lazy + applicationContext satisfies that contract. + private val configValues by lazy { + ConfigValues( + localProvider = defaultLocalProvider(applicationContext), + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + MaterialTheme { + var showDebug by rememberSaveable { mutableStateOf(false) } + + if (showDebug) { + BackHandler { showDebug = false } + FeatureFlagsDebugScreen(configValues = configValues) + } else { + SampleApp( + configValues = configValues, + onOpenDebugUi = { showDebug = true }, + ) + } + } + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 1bfaa11..7556a0c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,7 @@ plugins { include(":featured-gradle-plugin") include(":sample") +include(":sample:android-app") include(":core") include(":featured-compose") include(":featured-registry") From ee946b996a1f6ad8b64ae4331e131933adc7d3ce Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Wed, 29 Apr 2026 23:49:45 +0300 Subject: [PATCH 2/4] Handle system back to close debug screen in MainActivity --- .../kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt b/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt index 070dc12..e32a1d1 100644 --- a/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt +++ b/sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.compose.BackHandler import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -28,6 +29,7 @@ class MainActivity : ComponentActivity() { var showDebug by rememberSaveable { mutableStateOf(false) } if (showDebug) { + BackHandler { showDebug = false } FeatureFlagsDebugScreen(configValues = configValues) } else { SampleApp( From a3da7e773db8de804a3129677634b6917794de05 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Thu, 30 Apr 2026 08:45:19 +0300 Subject: [PATCH 3/4] Fix simplify issues in sample/android-app - Delete accidental MainActivity from :sample KMP library module - Revert :sample androidMain manifest to empty (library must not declare launcher Activity) - Remove androidMain deps block from :sample (belonged to deleted MainActivity) - Remove featured-debug-ui from :sample commonMain (only needed in android-app) - android-app: fix import order for BackHandler - android-app: use applicationContext instead of this for defaultLocalProvider - android-app: add shrinkResources = true alongside isMinifyEnabled - android-app: add androidx.appcompat for Theme.AppCompat.Light.NoActionBar - android-app: remove tools:replace (library manifest no longer declares theme) --- sample/android-app/build.gradle.kts | 2 + .../android-app/src/main/AndroidManifest.xml | 6 +-- .../featured/sample/MainActivity.kt | 4 +- sample/build.gradle.kts | 6 --- sample/src/androidMain/AndroidManifest.xml | 16 +------ .../androidbroadcast/featured/MainActivity.kt | 44 ------------------- 6 files changed, 7 insertions(+), 71 deletions(-) delete mode 100644 sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts index 997f54c..d343f60 100644 --- a/sample/android-app/build.gradle.kts +++ b/sample/android-app/build.gradle.kts @@ -31,6 +31,7 @@ android { buildTypes { release { isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), ) @@ -43,4 +44,5 @@ dependencies { implementation(project(":featured-debug-ui")) implementation(project(":featured-platform")) implementation(libs.androidx.activity.compose) + implementation(libs.androidx.appcompat) } diff --git a/sample/android-app/src/main/AndroidManifest.xml b/sample/android-app/src/main/AndroidManifest.xml index a05dc57..caea1b2 100644 --- a/sample/android-app/src/main/AndroidManifest.xml +++ b/sample/android-app/src/main/AndroidManifest.xml @@ -1,10 +1,8 @@ - + + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> - - - - - - - - - - + diff --git a/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt b/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt deleted file mode 100644 index 507c101..0000000 --- a/sample/src/androidMain/kotlin/dev/androidbroadcast/featured/MainActivity.kt +++ /dev/null @@ -1,44 +0,0 @@ -package dev.androidbroadcast.featured - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.BackHandler -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import dev.androidbroadcast.featured.debugui.FeatureFlagsDebugScreen -import dev.androidbroadcast.featured.platform.defaultLocalProvider - -class MainActivity : ComponentActivity() { - // Singleton ConfigValues for this process — DataStore must not be opened more than once - // per file per process; lazy + applicationContext satisfies that contract. - private val configValues by lazy { - ConfigValues( - localProvider = defaultLocalProvider(applicationContext), - ) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContent { - MaterialTheme { - var showDebug by rememberSaveable { mutableStateOf(false) } - - if (showDebug) { - BackHandler { showDebug = false } - FeatureFlagsDebugScreen(configValues = configValues) - } else { - SampleApp( - configValues = configValues, - onOpenDebugUi = { showDebug = true }, - ) - } - } - } - } -} From 4fd4e6edc705ceb9f14fa9b398a9a8cb3cf76840 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Thu, 30 Apr 2026 10:01:24 +0300 Subject: [PATCH 4/4] Use android.targetSdk catalog key instead of compileSdk for targetSdk --- sample/android-app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts index d343f60..7ec60e0 100644 --- a/sample/android-app/build.gradle.kts +++ b/sample/android-app/build.gradle.kts @@ -17,7 +17,7 @@ android { .get() .toInt() targetSdk = - libs.versions.android.compileSdk + libs.versions.android.targetSdk .get() .toInt() versionCode = 1