Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/actions/setup-android/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
runs:
using: "composite"
steps:
- name: Set ANDROID_SDK_ROOT
run: echo "ANDROID_SDK_ROOT=$HOME/.android/sdk" >> $GITHUB_ENV
shell: bash

- uses: android-actions/setup-android@v3
with:
log-accepted-android-sdk-licenses: false
cmdline-tools-version: 11076708
packages: tools platform-tools emulator system-images;android-35;google_apis_playstore;x86_64
packages: tools cmdline-tools;latest platform-tools emulator system-images;android-35;google_apis_playstore;x86_64

- name: Enable KVM group perms
run: |
Expand Down
7 changes: 5 additions & 2 deletions example/android/javalib/1-hello-world/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,11 @@ object app extends AndroidAppModule {

/** Usage

> ./mill show app.androidApk
".../out/app/androidApk.dest/app.apk"
> ./mill show app.androidReleaseApk
".../out/app/androidReleaseApk.dest/app.apk"

> ./mill show app.androidDebugApk
".../out/app/androidDebugApk.dest/app.apk"

*/

Expand Down
7 changes: 5 additions & 2 deletions example/android/javalib/2-app-bundle/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ object bundle extends AndroidAppBundle {

/** Usage

> ./mill show bundle.androidBundle
".../out/bundle/androidBundle.dest/signedBundle.aab"
> ./mill show bundle.androidReleaseBundle
".../out/bundle/androidReleaseBundle.dest/signedBundle.aab"

> ./mill show bundle.androidDebugBundle
".../out/bundle/androidDebugBundle.dest/signedBundle.aab"

*/

Expand Down
2 changes: 1 addition & 1 deletion example/android/javalib/3-linting/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ AndroidManifest.xml:3: Error: Avoid hardcoding the debug mode; leaving it out al
> ./mill app.androidLintRun # Rerun it for new changes to reflect

> cat out/app/androidLintRun.dest/report.txt # Check the content of report again
../../lint-baseline.xml: Information: 1 errors/warnings were listed in the baseline file (../../lint-baseline.xml) but not found in the project; perhaps they have been fixed? Unmatched issue types: HardcodedDebugMode [LintBaseline]
../../lint-baseline.xml: Information: 1 errors/warnings were listed in the baseline file (../../lint-baseline.xml) but not found in the project; perhaps they have been fixed? Unmatched issue types: HardcodedDebugMode [LintBaselineFixed]

*/

Expand Down
2 changes: 1 addition & 1 deletion example/android/javalib/4-sum-lib-java/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Publish ... to /home/.../.m2/repository/...
},
"payload": [
[
".../out/lib/androidAar.dest/library.aar",
".../out/lib/androidReleaseAar.dest/library.aar",
"lib-0.0.1.aar"
],
...
Expand Down
24 changes: 24 additions & 0 deletions example/android/javalib/5-R8/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFilex

-keep class com.helloworld.** { *; }

Binary file added example/android/javalib/5-R8/app/releaseKey.jks
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.helloworld.app;

import static org.junit.Assert.*;

import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.helloworld.SampleLogic;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.helloworld.app", appContext.getPackageName());
assertEquals(32.0f, SampleLogic.textSize(), 0.0001f);
}
}
15 changes: 15 additions & 0 deletions example/android/javalib/5-R8/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.helloworld.app" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="35"/>
<application android:label="@string/app_name" android:theme="@android:style/Theme.Light.NoTitleBar" android:debuggable="true">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.helloworld.app" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.helloworld;

public class SampleLogic {

public static float textSize() {
return 32f;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.helloworld.app;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Create a new TextView
TextView textView = new TextView(this);

// Set the text to the string resource
textView.setText(getString(R.string.hello_world));

// Set text size
textView.setTextSize(32);

// Center the text within the view
textView.setGravity(Gravity.CENTER);

// Set the layout parameters (width and height)
textView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

// Set the text color using a resource
textView.setTextColor(getResources().getColor(R.color.text_green));

// Set the background color using a resource
textView.setBackgroundColor(getResources().getColor(R.color.white));

// Set the content view to display the TextView
setContentView(textView);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<color name="white">#FFFFFF</color>
<color name="text_green">#34A853</color>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">HelloWorldApp</string>
<string name="hello_world">Hello, World Java!</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.helloworld;

import static org.junit.Assert.*;

import com.helloworld.app.R;
import org.junit.Test;

/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void textSize_isCorrect() {

assertEquals(32f, SampleLogic.textSize(), 0.000001f);
assertEquals(0x7f010000, R.color.text_green);
}
}
24 changes: 24 additions & 0 deletions example/android/javalib/5-R8/app/test-proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Proguard rules for instrumented tests

# Suppress warnings (so that you aren’t spammed with messages for unused rules)
-ignorewarnings

# Do not run any code optimizations – we want to keep our test code unchanged.
-dontoptimize

# Keep all annotation metadata (needed for reflection-based test frameworks)
-keepattributes *Annotation*

# Keep all Espresso framework classes and specifically ensure that the idling resources aren’t stripped
-keep class androidx.test.espresso.** { *; }
-keep class androidx.test.espresso.IdlingRegistry { *; }
-keep class androidx.test.espresso.IdlingResource { *; }

# Suppress notes and warnings for older JUnit and Android test classes
-dontnote junit.framework.**
-dontnote junit.runner.**

-dontwarn androidx.test.**
-dontwarn org.junit.**
-dontwarn org.hamcrest.**
-dontwarn com.squareup.javawriter.JavaWriter
146 changes: 146 additions & 0 deletions example/android/javalib/5-R8/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// This section sets up a basic Android project using Mill.
// We utilize `AndroidAppModule` and `AndroidSdkModule` to streamline the process of
// building an Android application with minimal configuration.
//
// By extending `AndroidAppModule`, we inherit all Android-related tasks such as
// resource generation, APK building, DEX conversion, and APK signing.
// Additionally, `AndroidSdkModule` is embedded, making SDK management seamless.

//// SNIPPET:BUILD
package build

import mill._, javalib._

import mill.javalib.android.{AndroidAppModule, AndroidSdkModule}
import mill.javalib.android.AndroidTestModule

// Create and configure an Android SDK module to manage Android SDK paths and tools.
object androidSdkModule0 extends AndroidSdkModule {
def buildToolsVersion = "35.0.0"
}

// Actual android application
object app extends AndroidAppModule {
def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
def androidMinSdk = 19
def androidCompileSdk = 35

// Configuration for ReleaseKey
def androidReleaseKeyName: T[Option[String]] = Task { Some("releaseKey.jks") }
def androidReleaseKeyAlias: T[Option[String]] = Task { Some("releaseKey") }
def androidReleaseKeyPass: T[Option[String]] = Task { Some("MillBuildTool") }
def androidReleaseKeyStorePass: T[Option[String]] = Task { Some("MillBuildTool") }

override def androidVirtualDeviceIdentifier: String = "java-test"

object test extends AndroidAppTests with TestModule.Junit4 {
def testFramework = "com.novocode.junit.JUnitFramework"
def ivyDeps = super.ivyDeps() ++ Seq(
ivy"junit:junit:4.13.2"
)
}

object it extends AndroidAppInstrumentedTests with AndroidTestModule.AndroidJUnit {

def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)

override def instrumentationPackage = "com.helloworld.app"

/* TODO currently the dependency resolution ignores the platform type and kotlinx-coroutines-core has
* conflicting classes with kotlinx-coroutines-core-jvm . Remove the exclusions once the dependency
* resolution resolves conflicts between androidJvm and jvm platform types
*/
def ivyDeps = super.ivyDeps() ++ Seq(
ivy"androidx.test.ext:junit:1.2.1".exclude((
"org.jetbrains.kotlinx",
"kotlinx-coroutines-core-jvm"
)),
ivy"androidx.test:runner:1.6.2",
ivy"androidx.test.espresso:espresso-core:3.5.1".exclude((
"org.jetbrains.kotlinx",
"kotlinx-coroutines-core-jvm"
)),
ivy"junit:junit:4.13.2"
)
}

}

////SNIPPET:END

/** Usage

> ./mill show app.androidReleaseApk
".../out/app/androidReleaseApk.dest/app.apk"

> ./mill show app.androidDebugApk
".../out/app/androidDebugApk.dest/app.apk"

*/

// This command triggers the build process, which installs the Android Setup, compiles the Java
// code, generates Android resources, converts Java bytecode to DEX format, packages everything
// into an APK, optimizes the APK using `zipalign`, and finally signs it.
//
// This Mill build configuration is designed to build a simple "Hello World" Android application.
// By extending `AndroidAppModule`, we leverage its predefined Android build tasks, ensuring that
// all necessary steps (resource generation, APK creation, and signing) are executed automatically.
//
// #### Project Structure:
// The project follows the standard Android app layout. Below is a typical project folder structure:
//
// ----
// .
//├── app
//│ └── src
//│ ├── androidTest/java/com/helloworld/app/ExampleInstrumentedTest.java
//│ ├── main
//│ │ ├── AndroidManifest.xml
//│ │ ├── java/com/helloworld/app/MainActivity.java
//│ │ └── res
//│ │ └── values
//│ │ ├── colors.xml
//│ │ └── strings.xml
//│ └── test/java/com/helloworld/app/ExampleUnitTest.java
//└── build.mill
// ----
//

/** Usage

> ./mill show app.test
...compiling 2 Java source...

> cat out/app/test/testForked.dest/worker-0/out.json
["",[{"fullyQualifiedName":"com.helloworld.ExampleUnitTest.textSize_isCorrect","selector":"com.helloworld.ExampleUnitTest.textSize_isCorrect","duration":...,"status":"Success"}]]

*/

// This command runs unit tests on your local environment.

/** Usage

> ./mill show app.createAndroidVirtualDevice
...Name: java-test, DeviceId: medium_phone...

> ./mill show app.startAndroidEmulator

> ./mill show app.androidReleaseInstall
...All files should be loaded. Notifying the device...

> ./mill show app.androidDebugInstall
...All files should be loaded. Notifying the device...

> ./mill show app.stopAndroidEmulator

> ./mill show app.deleteAndroidVirtualDevice

*/

// The android tests (existing typically in androidTest directory, aka instrumented tests)
// typically run on an android device.
// The createAndroidVirtualDevice command creates an AVD (Android Virtual Device)
// and the startAndroidEmulator command starts the AVD. The it task runs the android tests
// against the available AVD. The stopAndroidEmulator command stops the AVD and the
// destroyAndroidVirtualDevice command destroys the AVD.
// The provided commands can be used in a CI/CD pipeline assuming the right setup is in place.
Loading
Loading