Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
55 changes: 43 additions & 12 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
name: Swift
on: [push]
jobs:
on:
push:
branches: [master]
workflow_dispatch:
pull_request:

jobs:
android:
name: Android
strategy:
fail-fast: false
matrix:
swift: ['6.2.3', 'nightly-6.3']
arch: ['aarch64', 'x86_64', 'armv7']
sdk: ['28', '29', '31', '33']
runs-on: macos-15
timeout-minutes: 30
# os: ['ubuntu-24.04', 'macos-15-intel']
os: ['ubuntu-24.04']
android-sdk: ['nightly-6.3']
android-api: ['28', '29', '31', '33']
android-ndk: ['r27d']
runs-on: ${{ matrix.os }}
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- name: "Build Swift Package for Android"
- uses: actions/checkout@v6
- uses: skiptools/actions/setup-skip@v1
with:
gradle-version: 'none'
install-swift-android-sdk: true
swift-android-sdk-version: ${{ matrix.android-sdk }}
swift-android-ndk-version: ${{ matrix.android-ndk }}

- name: "Build Swift Package for Android (aarch64)"
run: |
ANDROID_SDK_VERSION=${{ matrix.android-api }} skip android build --arch aarch64 --android-api-level ${{ matrix.android-api }}

- name: "Build Swift Package for Android (armv7)"
run: |
brew install skiptools/skip/skip || (brew update && brew install skiptools/skip/skip)
skip android sdk install --version ${{ matrix.swift }}
ANDROID_NDK_ROOT="" ANDROID_SDK_VERSION=${{ matrix.sdk }} skip android build --arch ${{ matrix.arch }} --android-api-level ${{ matrix.sdk }}
ANDROID_SDK_VERSION=${{ matrix.android-api }} skip android build --arch armv7 --android-api-level ${{ matrix.android-api }}

- name: "Build Swift Package for Android (x86_64)"
run: |
ANDROID_SDK_VERSION=${{ matrix.android-api }} skip android build --arch x86_64 --android-api-level ${{ matrix.android-api }}

- name: "Test Swift Package on Android"
# can only lookup the current JavaVirtualMachine on API >= 31
# https://github.com/swiftlang/swift-java/issues/419
if: ${{ matrix.android-api >= 31 }}
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.android-api }}
arch: x86_64
script: |
ANDROID_SDK_VERSION=${{ matrix.android-api }} skip android test --android-api-level ${{ matrix.android-api }} --apk --verbose

15 changes: 15 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ var package = Package(
sdkVersionDefine,
]
),
.testTarget(
name: "AndroidAppTests",
dependencies: [
"AndroidJava",
"AndroidApp",
.product(
name: "AndroidContext",
package: "swift-android-native"
),
.product(
name: "SwiftJava",
package: "swift-java"
),
]
),
.target(
name: "AndroidContent",
dependencies: [
Expand Down
48 changes: 48 additions & 0 deletions Tests/AndroidAppTests/AndroidAppTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Testing
import SwiftJavaJNICore
import SwiftJava
import AndroidOS
import AndroidApp
import AndroidContext
@preconcurrency import AndroidContent

extension AndroidApp.Application {
/// The shared `android.app.Application`
public static func currentApplication() throws -> Application {
let jvm = try JavaVirtualMachine.shared()
let env = try jvm.environment()

// get the low-level application context wrapper from swift-android-native
let lowLevelContext = try AndroidContext.application

// wrap it in the @JavaClass Context
return Application(javaHolder: JavaObjectHolder(object: lowLevelContext.pointer, environment: env))
}
}

extension AndroidContent.Context {
/// The shared `android.content.Context` for the current application
public static func applicationContext() throws -> Context {
// simply cast the Application to a Context
try Application.currentApplication().as(AndroidContent.Context.self)!
}
}

@Test func testAndroidAppContext() throws {
let ctx = try Context.applicationContext()

let name = ctx.getPackageName()
#expect(name == "org.swift.test.swiftandroid", "test harness named by skip android test")

let info: AndroidContent.ApplicationInfo = ctx.getApplicationInfo()
#expect(info.processName == "org.swift.test.swiftandroid")
#expect(info.minSdkVersion >= 28)

let looper: AndroidOS.Looper = ctx.getMainLooper()
#expect(looper.isCurrentThread() == false, "tests should not be running on main thread")

let activityManagerOb = try #require(ctx.getSystemService(ctx.javaClass.ACTIVITY_SERVICE))
let activityManager: ActivityManager = ActivityManager(javaHolder: activityManagerOb.javaHolder)

#expect(activityManager.javaClass.isUserAMonkey() == false, "aren't we all monkeys, though?")
}