@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.
@@ -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,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "fastlane", "~> 2.217"
4
+
5
+ # Plugins
6
+ plugins_path = File.join(File.dirname(__FILE__), 'Pluginfile')
7
+ eval_gemfile(plugins_path) if File.exist?(plugins_path)
@@ -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