Skip to content

Commit 2ee59e4

Browse files
authored
Android compose samples: Jet Lagged (#5341)
## Summary This PR builds on top of #5285 and #5285 , and extends the work by creating a new linking android resources method that triggers the merging algorithm (with flat files instead of zips and auto overlay, with -R to add each library resource) ![Screenshot From 2025-06-16 18-24-28](https://github.com/user-attachments/assets/24f4c67e-95c0-464d-acd6-0f02cc6e10f8) ## Android Dependency Resolution changes - New configuration parameters were needed to include "common" dependencies otherwise some of them were failing to be resolved for android ## Android Resources This is the major change that allows for [JetLagged](https://github.com/android/compose-samples/tree/main/JetLagged) from compose samples to work (only to run the App for now). The key change was to overlay the flat files of the libraries (with `-R` parameter) instead of linking them together as command inputs (without -R) This allows for any conflicts of values that are the same to be resolved and it took some trial and error (I have a few versions of compose sample branches with a lot of failing attempts). Another change is to use the android resources from resolved run maven deps although I have a suspicion we'll come back to that later on, I don't think the dependency resolution and packaging work is finished.
1 parent 5af9985 commit 2ee59e4

File tree

10 files changed

+318
-131
lines changed

10 files changed

+318
-131
lines changed

.github/workflows/run-tests.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ jobs:
186186
install-android-sdk: true
187187
install-sbt: false
188188

189+
- java-version: 17
190+
millargs: "'example.thirdparty[android-compose-samples].local.daemon'"
191+
install-android-sdk: true
192+
install-sbt: false
193+
189194
- java-version: 17
190195
millargs: "'{example,integration}.migrating.__.local.daemon'"
191196
install-android-sdk: false

example/androidlib/kotlin/2-compose/build.mill

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ object app extends AndroidAppKotlinModule {
3535

3636
def mvnDeps: T[Seq[Dep]] = Seq(
3737
mvn"androidx.core:core-ktx:1.15.0",
38+
mvn"androidx.collection:collection-jvm:1.4.4",
39+
mvn"androidx.collection:collection-ktx:1.4.4",
3840
mvn"androidx.appcompat:appcompat:1.7.0",
3941
mvn"androidx.annotation:annotation:1.9.1",
4042
mvn"androidx.activity:activity-compose:1.10.0",

example/package.mill

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,11 @@ $txt
319319
"arrow" -> ("arrow-kt/arrow", "bc9bf92cc98e01c21bdd2bf8640cf7db0f97204a"),
320320
"ollama-js" -> ("ollama/ollama-js", "99293abe2c7c27ce7e76e8b4a98cae948f00058d"),
321321
"androidtodo" -> ("android/architecture-samples", "b3437ab428f6fd91804b28801650d590ff52971c"),
322-
"android-endless-tunnel" -> ("android/ndk-samples", "46ac919196faf1efcfe8018a0dcc79d4f8fbeca7")
322+
"android-endless-tunnel" -> ("android/ndk-samples", "46ac919196faf1efcfe8018a0dcc79d4f8fbeca7"),
323+
"android-compose-samples" -> (
324+
"android/compose-samples",
325+
"e4e6f0f96618f1ba04aa88d64ca19a166a662424"
326+
)
323327
)
324328
object thirdparty extends Cross[ThirdPartyModule](build.listIn(moduleDir / "thirdparty"))
325329
trait ThirdPartyModule extends ExampleCrossModule {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import mill._, androidlib._, kotlinlib._
2+
import hilt.AndroidHiltSupport
3+
4+
object Versions {
5+
val kotlinVersion = "2.1.20"
6+
val kotlinLanguageVersion = "1.9"
7+
8+
val androidCompileSdk = 33
9+
val androidMinSdk = 21
10+
}
11+
12+
// Create and configure an Android SDK module to manage Android SDK paths and tools.
13+
object androidSdkModule0 extends AndroidSdkModule {
14+
def buildToolsVersion = "35.0.0"
15+
}
16+
17+
object JetLagged extends mill.define.Module {
18+
object app extends AndroidAppKotlinModule with AndroidR8AppModule {
19+
def kotlinVersion = Versions.kotlinVersion
20+
21+
def kotlinLanguageVersion = Versions.kotlinLanguageVersion
22+
23+
def androidIsDebug = true
24+
25+
// FIXME: ideally R8 should compile without erroring, but the app seems to be working
26+
// without some reportedly missing classes.
27+
override def androidR8Args = Seq("--map-diagnostics", "error", "warning")
28+
29+
override def androidDebugSettings: T[AndroidBuildTypeSettings] = Task {
30+
AndroidBuildTypeSettings(
31+
isMinifyEnabled = false,
32+
isShrinkEnabled = false
33+
).withDefaultProguardFile("proguard-android-optimize.txt")
34+
.withProguardLocalFiles(
35+
Seq(
36+
moduleDir / "proguard-rules.pro"
37+
)
38+
)
39+
}
40+
41+
override def androidApplicationNamespace = "com.example.jetlagged"
42+
43+
override def androidApplicationId = "com.example.jetlagged"
44+
45+
override def kotlincOptions = super.kotlincOptions() ++ Seq(
46+
"-jvm-target",
47+
"17"
48+
)
49+
50+
def bomMvnDeps = super.mvnDeps() ++ Seq(
51+
mvn"androidx.compose:compose-bom:2025.05.00"
52+
)
53+
54+
def mvnDeps = super.mvnDeps() ++ Seq(
55+
mvn"com.google.accompanist:accompanist-adaptive:0.37.3",
56+
mvn"androidx.appcompat:appcompat:1.7.0",
57+
mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2",
58+
mvn"androidx.concurrent:concurrent-futures:1.1.0",
59+
mvn"androidx.core:core-ktx:1.16.0",
60+
mvn"androidx.activity:activity-compose:1.10.1",
61+
mvn"androidx.lifecycle:lifecycle-common:2.9.0",
62+
mvn"androidx.lifecycle:lifecycle-process:2.9.0",
63+
mvn"androidx.lifecycle:lifecycle-runtime-compose:2.9.0",
64+
mvn"androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0",
65+
mvn"androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0",
66+
mvn"androidx.navigation:navigation-compose:2.9.0",
67+
mvn"androidx.emoji2:emoji2:1.5.0",
68+
mvn"androidx.emoji2:emoji2-views:1.5.0",
69+
mvn"androidx.emoji2:emoji2-bundled:1.5.0",
70+
mvn"androidx.window:window:1.4.0",
71+
mvn"androidx.window.extensions.core:core:1.0.0",
72+
mvn"androidx.constraintlayout:constraintlayout-compose:1.1.1",
73+
mvn"io.coil-kt:coil-compose:2.7.0",
74+
mvn"androidx.customview:customview-poolingcontainer:1.0.0",
75+
mvn"androidx.tracing:tracing:1.2.0",
76+
mvn"org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1",
77+
78+
// version is resolved from compose-bom
79+
mvn"androidx.compose.runtime:runtime",
80+
mvn"androidx.compose.foundation:foundation",
81+
mvn"androidx.compose.foundation:foundation-layout",
82+
mvn"androidx.compose.ui:ui-util",
83+
mvn"androidx.compose.material3:material3",
84+
mvn"androidx.compose.animation:animation",
85+
mvn"androidx.compose.animation:animation-tooling-internal",
86+
mvn"androidx.compose.material:material-icons-extended",
87+
mvn"androidx.compose.material:material",
88+
mvn"androidx.compose.material3:material3-window-size-class",
89+
mvn"androidx.compose.ui:ui-text-google-fonts",
90+
mvn"androidx.compose.ui:ui-tooling-preview",
91+
mvn"androidx.compose.ui:ui-unit",
92+
mvn"androidx.compose.ui:ui-text",
93+
mvn"androidx.compose.ui:ui-graphics",
94+
95+
// debug dependencies
96+
mvn"androidx.compose.ui:ui-tooling",
97+
mvn"androidx.compose.ui:ui-test-manifest"
98+
)
99+
100+
def androidEnableCompose = true
101+
override def kotlinUseEmbeddableCompiler: Task[Boolean] = Task { true }
102+
103+
def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
104+
105+
def androidCompileSdk = Versions.androidCompileSdk
106+
107+
def androidMinSdk = Versions.androidMinSdk
108+
109+
}
110+
}
111+
112+
/** Usage
113+
114+
> ./mill JetLagged.app.androidApk
115+
116+
> ./mill show JetLagged.app.createAndroidVirtualDevice
117+
...Name: test, DeviceId: medium_phone...
118+
119+
> ./mill show JetLagged.app.startAndroidEmulator
120+
121+
> ./mill show JetLagged.app.androidInstall
122+
...All files should be loaded. Notifying the device...
123+
124+
> ./mill show JetLagged.app.androidRun --activity com.example.jetlagged.MainActivity
125+
[
126+
"Starting: Intent { cmp=com.example.jetlagged/.MainActivity }",
127+
"Status: ok",
128+
"LaunchState: COLD",
129+
"Activity: com.example.jetlagged/.MainActivity",
130+
"TotalTime: ...",
131+
"WaitTime: ...",
132+
"Complete"
133+
]
134+
135+
> ./mill show JetLagged.app.stopAndroidEmulator
136+
137+
> ./mill show JetLagged.app.deleteAndroidVirtualDevice
138+
139+
*/

example/thirdparty/androidtodo/build.mill

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,7 @@ object app extends AndroidAppKotlinModule with AndroidR8AppModule with AndroidBu
6060
mvn"androidx.compose:compose-bom:2024.12.01"
6161
)
6262

63-
// versions are resolved with compose-bom
64-
def composeDeps = Seq(
65-
mvn"androidx.compose.foundation:foundation",
66-
mvn"androidx.compose.foundation:foundation-layout",
67-
mvn"androidx.compose.animation:animation",
68-
mvn"androidx.compose.material3:material3",
69-
mvn"androidx.compose.material:material",
70-
mvn"androidx.compose.material:material-icons-extended",
71-
mvn"androidx.compose.ui:ui-tooling-preview",
72-
mvn"androidx.compose.ui:ui",
73-
mvn"androidx.compose.ui:ui-unit",
74-
mvn"androidx.compose.ui:ui-text",
75-
mvn"androidx.compose.ui:ui-graphics",
76-
// debug
77-
mvn"androidx.compose.ui:ui-tooling",
78-
mvn"androidx.compose.ui:ui-test-manifest"
79-
)
80-
81-
def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ composeDeps ++ Seq(
63+
def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(
8264
mvn"androidx.core:core-ktx:1.15.0",
8365
mvn"androidx.appcompat:appcompat:1.7.0",
8466
mvn"androidx.annotation:annotation:1.9.1",
@@ -100,7 +82,23 @@ object app extends AndroidAppKotlinModule with AndroidR8AppModule with AndroidBu
10082
mvn"androidx.hilt:hilt-navigation-compose:1.2.0",
10183
mvn"com.google.accompanist:accompanist-swiperefresh:0.36.0",
10284
mvn"androidx.customview:customview-poolingcontainer:1.0.0",
103-
mvn"androidx.tracing:tracing:1.2.0"
85+
mvn"androidx.tracing:tracing:1.2.0",
86+
87+
// versions are resolved with compose-bom
88+
mvn"androidx.compose.foundation:foundation",
89+
mvn"androidx.compose.foundation:foundation-layout",
90+
mvn"androidx.compose.animation:animation",
91+
mvn"androidx.compose.material3:material3",
92+
mvn"androidx.compose.material:material",
93+
mvn"androidx.compose.material:material-icons-extended",
94+
mvn"androidx.compose.ui:ui-tooling-preview",
95+
mvn"androidx.compose.ui:ui",
96+
mvn"androidx.compose.ui:ui-unit",
97+
mvn"androidx.compose.ui:ui-text",
98+
mvn"androidx.compose.ui:ui-graphics",
99+
// debug
100+
mvn"androidx.compose.ui:ui-tooling",
101+
mvn"androidx.compose.ui:ui-test-manifest"
104102
)
105103

106104
def kotlinSymbolProcessors: T[Seq[Dep]] = Seq(

libs/androidlib/src/mill/androidlib/AndroidAppBundle.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ trait AndroidAppBundle extends AndroidAppModule with JavaModule {
2828
*/
2929
def androidBundleZip: T[PathRef] = Task {
3030
val dexFile = androidDex().path
31-
val resFile = androidCompiledResources().resApkFile.path
31+
val resFile = androidLinkedResources().path / "apk/res.apk"
3232
val baseDir = Task.dest / "base"
3333
val appDir = Task.dest / "app"
3434

libs/androidlib/src/mill/androidlib/AndroidAppKotlinModule.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ trait AndroidAppKotlinModule extends AndroidKotlinModule with AndroidAppModule {
208208
}
209209

210210
private def resourceApkPath: Task[PathRef] = Task {
211-
outer.androidCompiledResources().resApkFile
211+
PathRef(outer.androidLinkedResources().path / "apk/res.apk")
212212
}
213213

214214
// TODO previews must be source controlled to be used as a base

libs/androidlib/src/mill/androidlib/AndroidAppModule.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ trait AndroidAppModule extends AndroidModule { outer =>
232232
def androidUnsignedApk: T[PathRef] = Task {
233233
val unsignedApk = Task.dest / "app.unsigned.apk"
234234

235-
os.copy(androidCompiledResources().resApkFile.path, unsignedApk)
235+
os.copy(androidLinkedResources().path / "apk/res.apk", unsignedApk)
236236
val dexFiles = os.walk(androidDex().path)
237237
.filter(_.ext == "dex")
238238
.map(os.zip.ZipSource.fromPath)
@@ -806,7 +806,7 @@ trait AndroidAppModule extends AndroidModule { outer =>
806806
.flatMap(_.proguardRules)
807807
.map(p => os.read(p.path))
808808
.appendedAll(mainDexPlatformRules)
809-
.appended(os.read(androidCompiledResources().mainDexRulesProFile.path))
809+
.appended(os.read(androidLinkedResources().path / "proguard/main-dex-rules.pro"))
810810
.mkString("\n")
811811
os.write(proguardFile, knownProguardRules)
812812

@@ -1090,6 +1090,7 @@ trait AndroidAppModule extends AndroidModule { outer =>
10901090
case class UnpackedDep(
10911091
name: String,
10921092
classesJar: Option[PathRef],
1093+
repackagedJars: Seq[PathRef],
10931094
proguardRules: Option[PathRef],
10941095
androidResources: Option[PathRef],
10951096
manifest: Option[PathRef],

0 commit comments

Comments
 (0)