@craft-native/android 0.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +606 -0
- package/dist/index.js +410 -0
- package/dist/index.js.map +10 -0
- package/package.json +38 -0
- package/templates/AndroidManifest.xml.template +43 -0
- package/templates/CraftBridge.kt.template +3880 -0
- package/templates/CraftBridgeExtensions.kt.template +383 -0
- package/templates/CraftHealthConnect.kt.template +200 -0
- package/templates/CraftHealthConnectStub.kt.template +28 -0
- package/templates/CraftWidgetProvider.kt.template +246 -0
- package/templates/LocationRecordingService.kt.template +160 -0
- package/templates/MainActivity.kt.template +230 -0
- package/templates/build.gradle.kts.app.template +102 -0
- package/templates/build.gradle.kts.project.template +6 -0
- package/templates/fastlane/Appfile +25 -0
- package/templates/fastlane/Fastfile +192 -0
- package/templates/fastlane/Gemfile +7 -0
- package/templates/github-workflow-android.yml +248 -0
- package/templates/github-workflow-test.yml +329 -0
- package/templates/proguard-rules.pro.template +9 -0
- package/templates/settings.gradle.kts.template +18 -0
- package/templates/test-bridges.html +1463 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
plugins {
|
|
2
|
+
id("com.android.application")
|
|
3
|
+
id("org.jetbrains.kotlin.android")
|
|
4
|
+
{{GOOGLE_SERVICES_PLUGIN}}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
android {
|
|
8
|
+
namespace = "{{PACKAGE_NAME}}"
|
|
9
|
+
compileSdk = {{COMPILE_SDK}}
|
|
10
|
+
|
|
11
|
+
defaultConfig {
|
|
12
|
+
applicationId = "{{PACKAGE_NAME}}"
|
|
13
|
+
minSdk = {{MIN_SDK}}
|
|
14
|
+
targetSdk = {{TARGET_SDK}}
|
|
15
|
+
versionCode = {{VERSION_CODE}}
|
|
16
|
+
versionName = "{{VERSION_NAME}}"
|
|
17
|
+
|
|
18
|
+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
buildTypes {
|
|
22
|
+
release {
|
|
23
|
+
isMinifyEnabled = true
|
|
24
|
+
isShrinkResources = true
|
|
25
|
+
proguardFiles(
|
|
26
|
+
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
27
|
+
"proguard-rules.pro"
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
debug {
|
|
31
|
+
isMinifyEnabled = false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
compileOptions {
|
|
36
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
37
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
kotlinOptions {
|
|
41
|
+
jvmTarget = "17"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
buildFeatures {
|
|
45
|
+
viewBinding = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Android's default AAPT ignore pattern drops underscore-prefixed asset
|
|
49
|
+
// directories. STX intentionally emits its browser runtime and generated
|
|
50
|
+
// styles under `_stx`, so keep those files in the APK.
|
|
51
|
+
androidResources {
|
|
52
|
+
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
dependencies {
|
|
57
|
+
// AndroidX Core
|
|
58
|
+
implementation("androidx.core:core-ktx:1.12.0")
|
|
59
|
+
implementation("androidx.appcompat:appcompat:1.6.1")
|
|
60
|
+
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
|
61
|
+
implementation("androidx.webkit:webkit:1.12.1")
|
|
62
|
+
implementation("com.google.android.material:material:1.11.0")
|
|
63
|
+
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
|
|
64
|
+
|
|
65
|
+
// Biometric
|
|
66
|
+
implementation("androidx.biometric:biometric:1.1.0")
|
|
67
|
+
|
|
68
|
+
// Security / Encrypted SharedPreferences
|
|
69
|
+
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
|
70
|
+
|
|
71
|
+
// Lifecycle (for app state monitoring)
|
|
72
|
+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
|
73
|
+
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
|
|
74
|
+
|
|
75
|
+
// Google Play Location Services (for geolocation)
|
|
76
|
+
implementation("com.google.android.gms:play-services-location:21.1.0")
|
|
77
|
+
|
|
78
|
+
// Google Play In-App Review
|
|
79
|
+
implementation("com.google.android.play:review:2.0.1")
|
|
80
|
+
implementation("com.google.android.play:review-ktx:2.0.1")
|
|
81
|
+
|
|
82
|
+
// Native push notifications (included only when enabled)
|
|
83
|
+
{{FIREBASE_MESSAGING_DEPENDENCY}}
|
|
84
|
+
|
|
85
|
+
// Android Health Connect (included only when enabled)
|
|
86
|
+
{{HEALTH_CONNECT_DEPENDENCIES}}
|
|
87
|
+
|
|
88
|
+
// On-device computer vision APIs exposed by CraftBridge
|
|
89
|
+
implementation("com.google.mlkit:barcode-scanning:17.3.0")
|
|
90
|
+
implementation("com.google.mlkit:image-labeling:17.0.9")
|
|
91
|
+
implementation("com.google.mlkit:object-detection:17.0.2")
|
|
92
|
+
implementation("com.google.mlkit:text-recognition:16.0.1")
|
|
93
|
+
|
|
94
|
+
// Google Play Billing (In-App Purchase)
|
|
95
|
+
implementation("com.android.billingclient:billing:6.1.0")
|
|
96
|
+
implementation("com.android.billingclient:billing-ktx:6.1.0")
|
|
97
|
+
|
|
98
|
+
// Testing
|
|
99
|
+
testImplementation("junit:junit:4.13.2")
|
|
100
|
+
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
|
101
|
+
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
|
102
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
|
2
|
+
plugins {
|
|
3
|
+
id("com.android.application") version "8.9.1" apply false
|
|
4
|
+
id("org.jetbrains.kotlin.android") version "2.0.21" apply false
|
|
5
|
+
{{GOOGLE_SERVICES_PLUGIN}}
|
|
6
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Android Fastlane Appfile
|
|
2
|
+
# Stores Google Play Store configuration
|
|
3
|
+
|
|
4
|
+
# Your app's package name
|
|
5
|
+
# package_name("com.yourcompany.craftapp")
|
|
6
|
+
|
|
7
|
+
# Path to your Google Play JSON key file
|
|
8
|
+
# json_key_file("./play-store-key.json")
|
|
9
|
+
|
|
10
|
+
# ==================== Example Configuration ====================
|
|
11
|
+
# Uncomment and modify:
|
|
12
|
+
|
|
13
|
+
# package_name("com.yourcompany.craftapp")
|
|
14
|
+
# json_key_file("./play-store-key.json")
|
|
15
|
+
|
|
16
|
+
# ==================== Setting up Google Play API ====================
|
|
17
|
+
# 1. Go to Google Play Console > Setup > API access
|
|
18
|
+
# 2. Create a service account (or link existing)
|
|
19
|
+
# 3. Grant "Release Manager" permissions to the service account
|
|
20
|
+
# 4. Download the JSON key file
|
|
21
|
+
# 5. Store the JSON key securely (add to .gitignore)
|
|
22
|
+
#
|
|
23
|
+
# For CI/CD:
|
|
24
|
+
# - Store JSON key content as a secret (PLAY_STORE_JSON_KEY)
|
|
25
|
+
# - In CI, decode and save: echo "$PLAY_STORE_JSON_KEY" > play-store-key.json
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Craft Android Fastlane Configuration
|
|
2
|
+
# Copy this directory to your android/ folder
|
|
3
|
+
# Run: cd android && bundle install && bundle exec fastlane <lane>
|
|
4
|
+
|
|
5
|
+
default_platform(:android)
|
|
6
|
+
|
|
7
|
+
platform :android do
|
|
8
|
+
# ==================== Build ====================
|
|
9
|
+
|
|
10
|
+
desc "Build Debug APK"
|
|
11
|
+
lane :build_debug do
|
|
12
|
+
gradle(task: "assembleDebug")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
desc "Build Release APK"
|
|
16
|
+
lane :build_release do
|
|
17
|
+
gradle(
|
|
18
|
+
task: "assembleRelease",
|
|
19
|
+
properties: {
|
|
20
|
+
"android.injected.signing.store.file" => ENV["KEYSTORE_PATH"] || "release.keystore",
|
|
21
|
+
"android.injected.signing.store.password" => ENV["KEYSTORE_PASSWORD"],
|
|
22
|
+
"android.injected.signing.key.alias" => ENV["KEY_ALIAS"],
|
|
23
|
+
"android.injected.signing.key.password" => ENV["KEY_PASSWORD"]
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Build Release Bundle (AAB)"
|
|
29
|
+
lane :build_bundle do
|
|
30
|
+
gradle(
|
|
31
|
+
task: "bundleRelease",
|
|
32
|
+
properties: {
|
|
33
|
+
"android.injected.signing.store.file" => ENV["KEYSTORE_PATH"] || "release.keystore",
|
|
34
|
+
"android.injected.signing.store.password" => ENV["KEYSTORE_PASSWORD"],
|
|
35
|
+
"android.injected.signing.key.alias" => ENV["KEY_ALIAS"],
|
|
36
|
+
"android.injected.signing.key.password" => ENV["KEY_PASSWORD"]
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# ==================== Deploy ====================
|
|
42
|
+
|
|
43
|
+
desc "Deploy to Play Store Internal Track"
|
|
44
|
+
lane :internal do
|
|
45
|
+
build_bundle
|
|
46
|
+
|
|
47
|
+
upload_to_play_store(
|
|
48
|
+
track: "internal",
|
|
49
|
+
aab: "app/build/outputs/bundle/release/app-release.aab",
|
|
50
|
+
skip_upload_metadata: true,
|
|
51
|
+
skip_upload_images: true,
|
|
52
|
+
skip_upload_screenshots: true
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc "Deploy to Play Store Alpha Track"
|
|
57
|
+
lane :alpha do
|
|
58
|
+
build_bundle
|
|
59
|
+
|
|
60
|
+
upload_to_play_store(
|
|
61
|
+
track: "alpha",
|
|
62
|
+
aab: "app/build/outputs/bundle/release/app-release.aab",
|
|
63
|
+
skip_upload_metadata: true,
|
|
64
|
+
skip_upload_images: true,
|
|
65
|
+
skip_upload_screenshots: true
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc "Deploy to Play Store Beta Track"
|
|
70
|
+
lane :beta do
|
|
71
|
+
build_bundle
|
|
72
|
+
|
|
73
|
+
upload_to_play_store(
|
|
74
|
+
track: "beta",
|
|
75
|
+
aab: "app/build/outputs/bundle/release/app-release.aab",
|
|
76
|
+
skip_upload_metadata: true,
|
|
77
|
+
skip_upload_images: true,
|
|
78
|
+
skip_upload_screenshots: true
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
desc "Deploy to Play Store Production"
|
|
83
|
+
lane :release do
|
|
84
|
+
build_bundle
|
|
85
|
+
|
|
86
|
+
upload_to_play_store(
|
|
87
|
+
track: "production",
|
|
88
|
+
aab: "app/build/outputs/bundle/release/app-release.aab",
|
|
89
|
+
skip_upload_metadata: false,
|
|
90
|
+
skip_upload_images: true,
|
|
91
|
+
skip_upload_screenshots: true,
|
|
92
|
+
rollout: "0.1" # 10% rollout, increase manually
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ==================== Testing ====================
|
|
97
|
+
|
|
98
|
+
desc "Run Unit Tests"
|
|
99
|
+
lane :test do
|
|
100
|
+
gradle(task: "testDebugUnitTest")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
desc "Run Instrumented Tests"
|
|
104
|
+
lane :instrumented_tests do
|
|
105
|
+
gradle(task: "connectedDebugAndroidTest")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
desc "Run Lint"
|
|
109
|
+
lane :lint do
|
|
110
|
+
gradle(task: "lint")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ==================== Version Management ====================
|
|
114
|
+
|
|
115
|
+
desc "Increment version code"
|
|
116
|
+
lane :bump_version_code do
|
|
117
|
+
# Read current version code
|
|
118
|
+
version_code = android_get_version_code(gradle_file: "app/build.gradle")
|
|
119
|
+
new_version_code = version_code.to_i + 1
|
|
120
|
+
|
|
121
|
+
# Update version code
|
|
122
|
+
android_set_version_code(
|
|
123
|
+
version_code: new_version_code,
|
|
124
|
+
gradle_file: "app/build.gradle"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
UI.success("Version code bumped to #{new_version_code}")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
desc "Set version name"
|
|
131
|
+
lane :set_version do |options|
|
|
132
|
+
version = options[:version] || prompt(text: "Version name: ")
|
|
133
|
+
|
|
134
|
+
android_set_version_name(
|
|
135
|
+
version_name: version,
|
|
136
|
+
gradle_file: "app/build.gradle"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
UI.success("Version name set to #{version}")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# ==================== Keystore Management ====================
|
|
143
|
+
|
|
144
|
+
desc "Generate release keystore"
|
|
145
|
+
lane :generate_keystore do
|
|
146
|
+
store_password = prompt(text: "Keystore password: ", secure_text: true)
|
|
147
|
+
key_alias = prompt(text: "Key alias: ")
|
|
148
|
+
key_password = prompt(text: "Key password: ", secure_text: true)
|
|
149
|
+
full_name = prompt(text: "Full name (CN): ")
|
|
150
|
+
org = prompt(text: "Organization (O): ")
|
|
151
|
+
country = prompt(text: "Country code (C): ")
|
|
152
|
+
|
|
153
|
+
sh(
|
|
154
|
+
"keytool -genkey -v -keystore release.keystore " \
|
|
155
|
+
"-alias #{key_alias} -keyalg RSA -keysize 2048 -validity 10000 " \
|
|
156
|
+
"-storepass #{store_password} -keypass #{key_password} " \
|
|
157
|
+
"-dname \"CN=#{full_name}, O=#{org}, C=#{country}\""
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
UI.success("Keystore generated at release.keystore")
|
|
161
|
+
UI.important("Store this file and passwords securely!")
|
|
162
|
+
UI.important("Add to .gitignore: *.keystore")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# ==================== Utilities ====================
|
|
166
|
+
|
|
167
|
+
desc "Clean build"
|
|
168
|
+
lane :clean do
|
|
169
|
+
gradle(task: "clean")
|
|
170
|
+
UI.success("Build cleaned!")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# ==================== CI/CD ====================
|
|
174
|
+
|
|
175
|
+
desc "CI build and test"
|
|
176
|
+
lane :ci do
|
|
177
|
+
lint
|
|
178
|
+
test
|
|
179
|
+
build_release
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
desc "CI deploy to internal track"
|
|
183
|
+
lane :ci_internal do
|
|
184
|
+
# Decode keystore from base64 environment variable
|
|
185
|
+
if ENV["KEYSTORE_BASE64"]
|
|
186
|
+
sh("echo $KEYSTORE_BASE64 | base64 -d > release.keystore")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
bump_version_code
|
|
190
|
+
internal
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Android Build and Test Workflow
|
|
2
|
+
# Copy this file to your project's .github/workflows/android.yml
|
|
3
|
+
|
|
4
|
+
name: Android Build
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main, develop]
|
|
9
|
+
paths:
|
|
10
|
+
- 'android/**'
|
|
11
|
+
- '*.kt'
|
|
12
|
+
- '*.java'
|
|
13
|
+
- 'gradle/**'
|
|
14
|
+
- '*.gradle*'
|
|
15
|
+
pull_request:
|
|
16
|
+
branches: [main]
|
|
17
|
+
paths:
|
|
18
|
+
- 'android/**'
|
|
19
|
+
- '*.kt'
|
|
20
|
+
- '*.java'
|
|
21
|
+
- 'gradle/**'
|
|
22
|
+
- '*.gradle*'
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
|
|
25
|
+
env:
|
|
26
|
+
JAVA_VERSION: '17'
|
|
27
|
+
GRADLE_VERSION: '8.2'
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
build:
|
|
31
|
+
name: Build Android App
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- name: Checkout
|
|
36
|
+
uses: actions/checkout@v4
|
|
37
|
+
|
|
38
|
+
- name: Set up JDK
|
|
39
|
+
uses: actions/setup-java@v4
|
|
40
|
+
with:
|
|
41
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
42
|
+
distribution: 'temurin'
|
|
43
|
+
cache: 'gradle'
|
|
44
|
+
|
|
45
|
+
- name: Grant execute permission for gradlew
|
|
46
|
+
run: chmod +x android/gradlew
|
|
47
|
+
|
|
48
|
+
- name: Run Lint
|
|
49
|
+
working-directory: android
|
|
50
|
+
run: ./gradlew lint
|
|
51
|
+
|
|
52
|
+
- name: Build Debug APK
|
|
53
|
+
working-directory: android
|
|
54
|
+
run: ./gradlew assembleDebug
|
|
55
|
+
|
|
56
|
+
- name: Run Unit Tests
|
|
57
|
+
working-directory: android
|
|
58
|
+
run: ./gradlew testDebugUnitTest
|
|
59
|
+
|
|
60
|
+
- name: Upload Debug APK
|
|
61
|
+
uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: debug-apk
|
|
64
|
+
path: android/app/build/outputs/apk/debug/*.apk
|
|
65
|
+
retention-days: 7
|
|
66
|
+
|
|
67
|
+
- name: Upload Lint Results
|
|
68
|
+
if: always()
|
|
69
|
+
uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: lint-results
|
|
72
|
+
path: android/app/build/reports/lint-results*.html
|
|
73
|
+
retention-days: 7
|
|
74
|
+
|
|
75
|
+
- name: Upload Test Results
|
|
76
|
+
if: always()
|
|
77
|
+
uses: actions/upload-artifact@v4
|
|
78
|
+
with:
|
|
79
|
+
name: test-results
|
|
80
|
+
path: android/app/build/reports/tests/
|
|
81
|
+
retention-days: 7
|
|
82
|
+
|
|
83
|
+
build-release:
|
|
84
|
+
name: Build Release
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
needs: build
|
|
87
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
88
|
+
|
|
89
|
+
steps:
|
|
90
|
+
- name: Checkout
|
|
91
|
+
uses: actions/checkout@v4
|
|
92
|
+
|
|
93
|
+
- name: Set up JDK
|
|
94
|
+
uses: actions/setup-java@v4
|
|
95
|
+
with:
|
|
96
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
97
|
+
distribution: 'temurin'
|
|
98
|
+
cache: 'gradle'
|
|
99
|
+
|
|
100
|
+
- name: Grant execute permission for gradlew
|
|
101
|
+
run: chmod +x android/gradlew
|
|
102
|
+
|
|
103
|
+
- name: Build Release APK (unsigned)
|
|
104
|
+
working-directory: android
|
|
105
|
+
run: ./gradlew assembleRelease
|
|
106
|
+
|
|
107
|
+
- name: Build Release Bundle (unsigned)
|
|
108
|
+
working-directory: android
|
|
109
|
+
run: ./gradlew bundleRelease
|
|
110
|
+
|
|
111
|
+
- name: Upload Release APK
|
|
112
|
+
uses: actions/upload-artifact@v4
|
|
113
|
+
with:
|
|
114
|
+
name: release-apk
|
|
115
|
+
path: android/app/build/outputs/apk/release/*.apk
|
|
116
|
+
retention-days: 7
|
|
117
|
+
|
|
118
|
+
- name: Upload Release Bundle
|
|
119
|
+
uses: actions/upload-artifact@v4
|
|
120
|
+
with:
|
|
121
|
+
name: release-bundle
|
|
122
|
+
path: android/app/build/outputs/bundle/release/*.aab
|
|
123
|
+
retention-days: 7
|
|
124
|
+
|
|
125
|
+
# Optional: Signed build for Play Store
|
|
126
|
+
# Requires: KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD secrets
|
|
127
|
+
build-signed:
|
|
128
|
+
name: Build Signed Release
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
needs: build
|
|
131
|
+
if: false # Set to true when you have signing configured
|
|
132
|
+
|
|
133
|
+
steps:
|
|
134
|
+
- name: Checkout
|
|
135
|
+
uses: actions/checkout@v4
|
|
136
|
+
|
|
137
|
+
- name: Set up JDK
|
|
138
|
+
uses: actions/setup-java@v4
|
|
139
|
+
with:
|
|
140
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
141
|
+
distribution: 'temurin'
|
|
142
|
+
cache: 'gradle'
|
|
143
|
+
|
|
144
|
+
- name: Grant execute permission for gradlew
|
|
145
|
+
run: chmod +x android/gradlew
|
|
146
|
+
|
|
147
|
+
- name: Decode Keystore
|
|
148
|
+
env:
|
|
149
|
+
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
|
150
|
+
run: |
|
|
151
|
+
echo "$KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore
|
|
152
|
+
|
|
153
|
+
- name: Build Signed Release APK
|
|
154
|
+
working-directory: android
|
|
155
|
+
env:
|
|
156
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
157
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
158
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
159
|
+
run: |
|
|
160
|
+
./gradlew assembleRelease \
|
|
161
|
+
-Pandroid.injected.signing.store.file=release.keystore \
|
|
162
|
+
-Pandroid.injected.signing.store.password="$KEYSTORE_PASSWORD" \
|
|
163
|
+
-Pandroid.injected.signing.key.alias="$KEY_ALIAS" \
|
|
164
|
+
-Pandroid.injected.signing.key.password="$KEY_PASSWORD"
|
|
165
|
+
|
|
166
|
+
- name: Build Signed Release Bundle
|
|
167
|
+
working-directory: android
|
|
168
|
+
env:
|
|
169
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
170
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
171
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
172
|
+
run: |
|
|
173
|
+
./gradlew bundleRelease \
|
|
174
|
+
-Pandroid.injected.signing.store.file=release.keystore \
|
|
175
|
+
-Pandroid.injected.signing.store.password="$KEYSTORE_PASSWORD" \
|
|
176
|
+
-Pandroid.injected.signing.key.alias="$KEY_ALIAS" \
|
|
177
|
+
-Pandroid.injected.signing.key.password="$KEY_PASSWORD"
|
|
178
|
+
|
|
179
|
+
- name: Upload Signed APK
|
|
180
|
+
uses: actions/upload-artifact@v4
|
|
181
|
+
with:
|
|
182
|
+
name: signed-apk
|
|
183
|
+
path: android/app/build/outputs/apk/release/*.apk
|
|
184
|
+
retention-days: 7
|
|
185
|
+
|
|
186
|
+
- name: Upload Signed Bundle
|
|
187
|
+
uses: actions/upload-artifact@v4
|
|
188
|
+
with:
|
|
189
|
+
name: signed-bundle
|
|
190
|
+
path: android/app/build/outputs/bundle/release/*.aab
|
|
191
|
+
retention-days: 7
|
|
192
|
+
|
|
193
|
+
- name: Cleanup Keystore
|
|
194
|
+
if: always()
|
|
195
|
+
run: rm -f android/app/release.keystore
|
|
196
|
+
|
|
197
|
+
# Instrumented tests on emulator
|
|
198
|
+
instrumented-tests:
|
|
199
|
+
name: Run Instrumented Tests
|
|
200
|
+
runs-on: ubuntu-latest
|
|
201
|
+
needs: build
|
|
202
|
+
if: false # Enable when you have instrumented tests
|
|
203
|
+
|
|
204
|
+
steps:
|
|
205
|
+
- name: Checkout
|
|
206
|
+
uses: actions/checkout@v4
|
|
207
|
+
|
|
208
|
+
- name: Set up JDK
|
|
209
|
+
uses: actions/setup-java@v4
|
|
210
|
+
with:
|
|
211
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
212
|
+
distribution: 'temurin'
|
|
213
|
+
cache: 'gradle'
|
|
214
|
+
|
|
215
|
+
- name: Enable KVM
|
|
216
|
+
run: |
|
|
217
|
+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
218
|
+
sudo udevadm control --reload-rules
|
|
219
|
+
sudo udevadm trigger --name-match=kvm
|
|
220
|
+
|
|
221
|
+
- name: AVD cache
|
|
222
|
+
uses: actions/cache@v4
|
|
223
|
+
id: avd-cache
|
|
224
|
+
with:
|
|
225
|
+
path: |
|
|
226
|
+
~/.android/avd/*
|
|
227
|
+
~/.android/adb*
|
|
228
|
+
key: avd-29-default
|
|
229
|
+
|
|
230
|
+
- name: Create AVD and generate snapshot
|
|
231
|
+
if: steps.avd-cache.outputs.cache-hit != 'true'
|
|
232
|
+
uses: reactivecircus/android-emulator-runner@v2
|
|
233
|
+
with:
|
|
234
|
+
api-level: 29
|
|
235
|
+
force-avd-creation: false
|
|
236
|
+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
237
|
+
disable-animations: true
|
|
238
|
+
script: echo "Generated AVD snapshot for caching."
|
|
239
|
+
|
|
240
|
+
- name: Run Instrumented Tests
|
|
241
|
+
uses: reactivecircus/android-emulator-runner@v2
|
|
242
|
+
with:
|
|
243
|
+
api-level: 29
|
|
244
|
+
force-avd-creation: false
|
|
245
|
+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
246
|
+
disable-animations: true
|
|
247
|
+
working-directory: android
|
|
248
|
+
script: ./gradlew connectedDebugAndroidTest
|