Skip to content

Commit b393a31

Browse files
authored
Merge pull request #2 from PureSwift/feature/android-demo-app
Add Android demo app
2 parents cad8b26 + 4b2f829 commit b393a31

File tree

74 files changed

+3014
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3014
-8
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ xcuserdata/
2121
*.moved-aside
2222
*.xccheckout
2323
*.xcscmblueprint
24+
*.DS_Store
2425

2526
## Obj-C/Swift specific
2627
*.hmap
@@ -70,4 +71,9 @@ AndroidBluetooth.xcodeproj/*
7071
Package.resolved
7172

7273
# VS Code
73-
.vscode
74+
.vscode
75+
76+
# Android
77+
*.so
78+
.gradle
79+
.idea

Demo/Package.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// swift-tools-version: 6.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "SwiftAndroidApp",
6+
platforms: [
7+
.macOS(.v15),
8+
],
9+
products: [
10+
.library(
11+
name: "SwiftAndroidApp",
12+
type: .dynamic,
13+
targets: ["SwiftAndroidApp"]
14+
),
15+
],
16+
dependencies: [
17+
.package(
18+
path: "../"
19+
),
20+
.package(
21+
url: "https://github.com/PureSwift/Android.git",
22+
branch: "master"
23+
),
24+
],
25+
targets: [
26+
.target(
27+
name: "SwiftAndroidApp",
28+
dependencies: [
29+
.product(
30+
name: "AndroidBluetooth",
31+
package: "AndroidBluetooth"
32+
),
33+
.product(
34+
name: "AndroidKit",
35+
package: "Android"
36+
)
37+
],
38+
path: "./app/src/main/swift",
39+
swiftSettings: [
40+
.swiftLanguageMode(.v5)
41+
]
42+
)
43+
]
44+
)

Demo/app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

Demo/app/build.gradle.kts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.kotlin.compose)
5+
}
6+
7+
android {
8+
namespace = "com.pureswift.swiftandroid"
9+
compileSdk = 35
10+
11+
defaultConfig {
12+
applicationId = "com.pureswift.swiftandroid"
13+
minSdk = 29
14+
targetSdk = 35
15+
versionCode = 1
16+
versionName = "1.0"
17+
ndk {
18+
//noinspection ChromeOsAbiSupport
19+
abiFilters += listOf("arm64-v8a")
20+
}
21+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
22+
}
23+
24+
buildTypes {
25+
release {
26+
isMinifyEnabled = false
27+
proguardFiles(
28+
getDefaultProguardFile("proguard-android-optimize.txt"),
29+
"proguard-rules.pro"
30+
)
31+
}
32+
}
33+
compileOptions {
34+
sourceCompatibility = JavaVersion.VERSION_11
35+
targetCompatibility = JavaVersion.VERSION_11
36+
}
37+
kotlinOptions {
38+
jvmTarget = "11"
39+
}
40+
buildFeatures {
41+
compose = true
42+
}
43+
packaging {
44+
resources {
45+
excludes += listOf("/META-INF/{AL2.0,LGPL2.1}")
46+
}
47+
jniLibs {
48+
keepDebugSymbols += listOf(
49+
"*/arm64-v8a/*.so",
50+
"*/armeabi-v7a/*.so",
51+
"*/x86_64/*.so"
52+
)
53+
}
54+
}
55+
}
56+
57+
// Compile native Swift code for the demo app with `skip android build`.
58+
val buildSwift by tasks.registering(Exec::class) {
59+
group = "build"
60+
description = "Build native Swift sources for Android"
61+
workingDir(rootProject.projectDir)
62+
commandLine("bash", "build-swift.sh")
63+
}
64+
65+
tasks.named("preBuild") {
66+
dependsOn(buildSwift)
67+
}
68+
69+
dependencies {
70+
71+
implementation(libs.androidx.core.ktx)
72+
implementation(libs.androidx.lifecycle.runtime.ktx)
73+
implementation(libs.androidx.activity.compose)
74+
implementation(platform(libs.androidx.compose.bom))
75+
implementation(libs.androidx.ui)
76+
implementation(libs.androidx.ui.graphics)
77+
implementation(libs.androidx.ui.tooling.preview)
78+
implementation(libs.androidx.recyclerview)
79+
implementation(libs.androidx.navigation.runtime)
80+
implementation(libs.androidx.material3)
81+
implementation(libs.material)
82+
testImplementation(libs.junit)
83+
androidTestImplementation(libs.androidx.junit)
84+
androidTestImplementation(libs.androidx.espresso.core)
85+
androidTestImplementation(platform(libs.androidx.compose.bom))
86+
androidTestImplementation(libs.androidx.ui.test.junit4)
87+
debugImplementation(libs.androidx.ui.tooling)
88+
debugImplementation(libs.androidx.ui.test.manifest)
89+
}

Demo/app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.pureswift.swiftandroid
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.pureswift.swiftandroid", appContext.packageName)
23+
}
24+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<!-- Android 12+ (API 31+) -->
6+
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
7+
android:usesPermissionFlags="neverForLocation" />
8+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
9+
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
10+
11+
<!-- Legacy support for Android 11 and below -->
12+
<uses-permission android:name="android.permission.BLUETOOTH"
13+
android:maxSdkVersion="30" />
14+
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
15+
android:maxSdkVersion="30" />
16+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
17+
android:maxSdkVersion="30" />
18+
19+
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
20+
21+
<application
22+
android:name=".Application"
23+
android:allowBackup="true"
24+
android:dataExtractionRules="@xml/data_extraction_rules"
25+
android:fullBackupContent="@xml/backup_rules"
26+
android:icon="@mipmap/ic_launcher"
27+
android:label="@string/app_name"
28+
android:roundIcon="@mipmap/ic_launcher_round"
29+
android:supportsRtl="true"
30+
android:theme="@style/Theme.SwiftAndroid"
31+
tools:targetApi="31">
32+
<activity
33+
android:name=".MainActivity"
34+
android:exported="true"
35+
android:label="@string/app_name"
36+
android:theme="@style/Theme.SwiftAndroid">
37+
<intent-filter>
38+
<action android:name="android.intent.action.MAIN" />
39+
40+
<category android:name="android.intent.category.LAUNCHER" />
41+
</intent-filter>
42+
</activity>
43+
</application>
44+
45+
</manifest>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package com.example.swift;
16+
17+
public class HelloSubclass extends HelloSwift {
18+
private String greeting;
19+
20+
public HelloSubclass(String greeting) {
21+
this.greeting = greeting;
22+
}
23+
24+
public void greetMe() {
25+
super.greet(greeting);
26+
}
27+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package com.example.swift;
16+
17+
import java.util.function.Predicate;
18+
19+
public class HelloSwift {
20+
public double value;
21+
public static double initialValue = 3.14159;
22+
public String name = "Java";
23+
24+
static {
25+
System.loadLibrary("SwiftAndroidApp");
26+
}
27+
28+
public HelloSwift() {
29+
this.value = initialValue;
30+
}
31+
32+
public native int sayHello(int x, int y);
33+
public native String throwMessageFromSwift(String message) throws Exception;
34+
35+
// To be called back by the native code
36+
public double sayHelloBack(int i) {
37+
System.out.println("And hello back from " + name + "! You passed me " + i);
38+
return value;
39+
}
40+
41+
public void greet(String name) {
42+
System.out.println("Salutations, " + name);
43+
}
44+
45+
public Predicate<Integer> lessThanTen() {
46+
Predicate<Integer> predicate = i -> (i < 10);
47+
return predicate;
48+
}
49+
50+
public String[] doublesToStrings(double[] doubles) {
51+
int size = doubles.length;
52+
String[] strings = new String[size];
53+
54+
for(int i = 0; i < size; i++) {
55+
strings[i] = "" + doubles[i];
56+
}
57+
58+
return strings;
59+
}
60+
61+
public void throwMessage(String message) throws Exception {
62+
throw new Exception(message);
63+
}
64+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package com.example.swift;
16+
17+
import java.lang.annotation.Retention;
18+
import java.lang.annotation.RetentionPolicy;
19+
20+
@Retention(RetentionPolicy.RUNTIME)
21+
public @interface ThreadSafe {
22+
}

0 commit comments

Comments
 (0)