From 7b24999f0c52c1a35ffcd931be1cb80f5c777e2b Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 02:49:29 -0400 Subject: [PATCH 01/10] add documentation that gradlew assembleDebug doesnt work anymore, the project requires assembleStandardDebug. UPdated 16kb alignment script to reflect this incase its needed again in the future --- packages/mobile/README.md | 3 +++ packages/mobile/scripts/build_apk_and_test_alignment.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/mobile/README.md b/packages/mobile/README.md index 048ddcd83b..34e3dd3d14 100644 --- a/packages/mobile/README.md +++ b/packages/mobile/README.md @@ -65,6 +65,9 @@ Quiet Mobile is a React Native app for Android and iOS that shares a Node.js [ba ``` You should see your phone under "List of Devices Attached". If you do not, try `adb kill-server` or restarting your phone and your dev machine. You device should not appear as "Unauthorized". If it does, make sure you have accepted any "USB Debugging" prompts on your device. + +**NOTE:** In order to build Quiet, you can't run a plan `./gradlew assembleDebug`, you *must* run `./gradlew assembleStandardDebug`. + 1. Run Quiet From the `packages/mobile` directory diff --git a/packages/mobile/scripts/build_apk_and_test_alignment.sh b/packages/mobile/scripts/build_apk_and_test_alignment.sh index adb4b759d5..3b9cb093ed 100755 --- a/packages/mobile/scripts/build_apk_and_test_alignment.sh +++ b/packages/mobile/scripts/build_apk_and_test_alignment.sh @@ -1,7 +1,7 @@ #!/bin/bash cd ../android -./gradlew clean assembleDebug +./gradlew clean assembleStandardDebug cd ../scripts ./check_elf_alignment.sh ../android/app/build/outputs/apk/standard/debug/app-standard-debug.apk From e6171e1e95a254bef17f2cf3a715b254c92af346 Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 02:52:34 -0400 Subject: [PATCH 02/10] Android project does not need these two fresco dependencies, speeds up gradle sync+build a good bit --- packages/mobile/android/app/build.gradle | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index db788c0697..aeaa7990b9 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -306,15 +306,12 @@ dependencies { implementation("androidx.work:work-runtime:2.11.1") implementation("androidx.work:work-runtime-ktx:2.11.1") - // For animated GIF support - implementation("com.facebook.fresco:fresco:3.6.0") - implementation("com.facebook.fresco:animated-gif:3.6.0") implementation("com.goterl:lazysodium-android:5.2.0@aar") implementation("net.java.dev.jna:jna:5.18.1@aar") implementation("com.apicatalog:copper-multibase:4.1.0") implementation("org.msgpack:msgpack-core:0.9.12") - implementation("com.google.guava:guava:33.5.0-android") + } // Run this once to be able to run the application with BUCK From 3bce1dee5bd691da7dc0c1408a541d9f3424ba20 Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 03:00:15 -0400 Subject: [PATCH 03/10] Upgrade android dependencies - androidx work classes to latest release - updated firebsae bill of materials to latest v33 release - use the newly stable version of androidx.security crypto library intsead of alpha - updated guava, gson - migrated to firebase bill of materials 34... https://www.blamechris.com/archery-apprentice-docs/developer-guide/guides/firebase-ktx-deprecation this required switching from firebase-messaging-ktx to jsut firebase-messaging in the 34 series, all KTX modules are now just part of the main API https://firebase.google.com/docs/android/kotlin-migration FirebaseMessagingModule.kt never used any of these extension functions, so this required no code changes, just build script changes --- packages/mobile/android/app/build.gradle | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index aeaa7990b9..e3c52edc53 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -290,10 +290,10 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") implementation("androidx.core:core-ktx:1.16.0") - implementation("com.google.code.gson:gson:2.13.2") - implementation(platform("com.google.firebase:firebase-bom:33.14.0")) - implementation("com.google.firebase:firebase-messaging-ktx") - implementation("androidx.security:security-crypto:1.1.0-alpha06") + implementation("com.google.code.gson:gson:2.14.0") + implementation(platform("com.google.firebase:firebase-bom:34.13.0")) + implementation("com.google.firebase:firebase-messaging") + implementation("androidx.security:security-crypto:1.1.0") implementation(project(":react-native-fs")) // comes from npm module implementation(group: "commons-io", name: "commons-io", version: "2.20.0") @@ -303,14 +303,15 @@ dependencies { } // Work manager - implementation("androidx.work:work-runtime:2.11.1") - implementation("androidx.work:work-runtime-ktx:2.11.1") + implementation("androidx.work:work-runtime:2.11.2") + implementation("androidx.work:work-runtime-ktx:2.11.2") + // ListenableFuture class used with WorkManager: + implementation("com.google.guava:guava:33.6.0-android") implementation("com.goterl:lazysodium-android:5.2.0@aar") implementation("net.java.dev.jna:jna:5.18.1@aar") implementation("com.apicatalog:copper-multibase:4.1.0") implementation("org.msgpack:msgpack-core:0.9.12") - implementation("com.google.guava:guava:33.5.0-android") } From 9ed6dce10be0b57db2f14b1a3ab79b382c74f0df Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 03:13:08 -0400 Subject: [PATCH 04/10] use KTX extension functions on shared preference, removed nullable on always-nonnull type --- .../com/quietmobile/Push/MsgpackDecoder.kt | 4 +- .../java/com/quietmobile/Push/QuietStorage.kt | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Push/MsgpackDecoder.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Push/MsgpackDecoder.kt index ca9afb8480..d92a299e54 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Push/MsgpackDecoder.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Push/MsgpackDecoder.kt @@ -73,7 +73,7 @@ object MsgpackDecoder { } } - private fun readPositiveIntOrRecord(): Any? { + private fun readPositiveIntOrRecord(): Any { val value = unpacker.unpackInt() val record = records[value] return if (record != null) readRecord(record) else value @@ -101,7 +101,7 @@ object MsgpackDecoder { return MutableList(count) { readValue() } } - private fun readRecordDefinition(recordId: Int): Any? { + private fun readRecordDefinition(recordId: Int): Any { val keysValue = readValue() as? List<*> ?: throw IllegalStateException("Invalid msgpackr record definition") val keys = keysValue.map { diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Push/QuietStorage.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Push/QuietStorage.kt index 7477b45652..c8d8b70737 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Push/QuietStorage.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Push/QuietStorage.kt @@ -6,6 +6,7 @@ import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import com.apicatalog.base.Base58 as CopperBase58 import org.json.JSONObject +import androidx.core.content.edit object QuietStorage { private const val ENCRYPTED_PREFS_NAME = "quiet.secure.storage" @@ -44,11 +45,11 @@ object QuietStorage { @JvmStatic fun saveDeviceCredentials(deviceId: String, teamId: String, signingPrivateKey: String) { - securePrefs().edit() - .putString(DEVICE_ID_KEY, deviceId) - .putString(TEAM_ID_KEY, teamId) - .putString("$DEVICE_PRIVATE_KEY_PREFIX$deviceId", signingPrivateKey) - .apply() + securePrefs().edit { + putString(DEVICE_ID_KEY, deviceId) + .putString(TEAM_ID_KEY, teamId) + .putString("$DEVICE_PRIVATE_KEY_PREFIX$deviceId", signingPrivateKey) + } } @JvmStatic @@ -62,7 +63,7 @@ object QuietStorage { @JvmStatic fun addLfaKey(keyName: String, key: String) { - securePrefs().edit().putString(keyName, key).apply() + securePrefs().edit { putString(keyName, key) } } @JvmStatic @@ -72,7 +73,7 @@ object QuietStorage { fun saveQssUrl(teamId: String, url: String) { val current = JSONObject(regularPrefs().getString(QSS_URLS_KEY, "{}") ?: "{}") current.put(teamId, url) - regularPrefs().edit().putString(QSS_URLS_KEY, current.toString()).apply() + regularPrefs().edit { putString(QSS_URLS_KEY, current.toString()) } } @JvmStatic @@ -87,11 +88,11 @@ object QuietStorage { if (seq <= current) { return } - regularPrefs().edit() - .putLong(lastSyncSeqKey(teamId), seq) - .remove(LAST_SYNC_SEQ_KEY) - .remove(LAST_SYNC_TEAM_ID_KEY) - .apply() + regularPrefs().edit { + putLong(lastSyncSeqKey(teamId), seq) + .remove(LAST_SYNC_SEQ_KEY) + .remove(LAST_SYNC_TEAM_ID_KEY) + } } @JvmStatic @@ -112,7 +113,7 @@ object QuietStorage { @JvmStatic fun setAppForeground(foreground: Boolean) { - regularPrefs().edit().putBoolean(APP_FOREGROUND_KEY, foreground).apply() + regularPrefs().edit { putBoolean(APP_FOREGROUND_KEY, foreground) } } @JvmStatic @@ -120,7 +121,7 @@ object QuietStorage { @JvmStatic fun setTeamQssEnabled(enabled: Boolean) { - regularPrefs().edit().putBoolean(TEAM_QSS_ENABLED_KEY, enabled).apply() + regularPrefs().edit { putBoolean(TEAM_QSS_ENABLED_KEY, enabled) } } @JvmStatic @@ -128,7 +129,7 @@ object QuietStorage { @JvmStatic fun setUserBackgroundTorEnabled(enabled: Boolean) { - regularPrefs().edit().putBoolean(USER_BACKGROUND_TOR_ENABLED_KEY, enabled).apply() + regularPrefs().edit { putBoolean(USER_BACKGROUND_TOR_ENABLED_KEY, enabled) } } @JvmStatic @@ -139,7 +140,7 @@ object QuietStorage { fun saveUserMetadata(userId: String, nickname: String) { val current = JSONObject(regularPrefs().getString(USER_METADATA_KEY, "{}") ?: "{}") current.put(userId, nickname) - regularPrefs().edit().putString(USER_METADATA_KEY, current.toString()).apply() + regularPrefs().edit { putString(USER_METADATA_KEY, current.toString()) } } @JvmStatic @@ -180,8 +181,8 @@ object QuietStorage { @JvmStatic fun clearAll() { - securePrefs().edit().clear().apply() - regularPrefs().edit().clear().apply() + securePrefs().edit { clear() } + regularPrefs().edit { clear() } } @Synchronized @@ -269,9 +270,9 @@ object QuietStorage { private fun saveDisplayedNotificationHashes(hashes: JSONObject) { regularPrefs() - .edit() - .putString(DISPLAYED_NOTIFICATION_HASHES_KEY, hashes.toString()) - .apply() + .edit { + putString(DISPLAYED_NOTIFICATION_HASHES_KEY, hashes.toString()) + } } private data class DisplayedNotificationHash( From f95b890fbb4e2516465b00b95c63007de07b8528 Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 03:28:56 -0400 Subject: [PATCH 05/10] now that the dependencies we can update are updated, use gradle versions catalog to pin android dep versions across multiple gradle scripts --- packages/mobile/android/app/build.gradle | 75 +++++++------------ packages/mobile/android/build.gradle | 19 +++-- .../mobile/android/gradle/libs.versions.toml | 59 +++++++++++++++ 3 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 packages/mobile/android/gradle/libs.versions.toml diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index e3c52edc53..447f75d8b6 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -93,23 +93,6 @@ project.ext.react = [ // Run Proguard to shrink the Java bytecode in release builds. def enableProguardInReleaseBuilds = false -/** - * The preferred build flavor of JavaScriptCore. - * - * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - * - * Using this 16KB Aligned build from Maven Central - * https://github.com/react-native-community/jsc-android-buildscripts/pull/184 - * https://repo1.maven.org/maven2/io/github/react-native-community/jsc-android-intl/ - */ -def jscFlavor = "io.github.react-native-community:jsc-android-intl:2026004.0.1@aar" - /** * Whether to enable the Hermes VM. * @@ -149,7 +132,6 @@ android { ndkPath = getNDK() ndkVersion = rootProject.ext.ndkVersion - buildToolsVersion(rootProject.ext.buildToolsVersion) compileSdkVersion(rootProject.ext.compileSdkVersion) compileOptions { @@ -267,15 +249,10 @@ android { } dependencies { - implementation("androidx.appcompat:appcompat:1.7.1") - implementation(fileTree(dir: "libs", include: ["*.jar", "*.so"])) // this picks up Quiet's libtor.so - // version set by the React Native Gradle Plugin in npm - implementation("com.facebook.react:react-android") - implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0") - + //noinspection GradleDynamicVersion // TODO this needs to be re-configured on npm side... androidTestImplementation("com.wix:detox:+") - androidTestImplementation("androidx.test:core:1.7.0") - androidTestImplementation("androidx.test.ext:junit:1.3.0") + androidTestImplementation(libs.androidx.core) + androidTestImplementation(libs.androidx.junit) if (enableHermes) { //noinspection GradleDynamicVersion @@ -283,36 +260,34 @@ dependencies { exclude group: "com.facebook.fbjni" } } else { - implementation(jscFlavor) + implementation(libs.jsc.android.intl) } - // TODO these can't be further updated until Quiet targets API 36 - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") - implementation("androidx.core:core-ktx:1.16.0") - - implementation("com.google.code.gson:gson:2.14.0") - implementation(platform("com.google.firebase:firebase-bom:34.13.0")) - implementation("com.google.firebase:firebase-messaging") - implementation("androidx.security:security-crypto:1.1.0") - implementation(project(":react-native-fs")) // comes from npm module - implementation(group: "commons-io", name: "commons-io", version: "2.20.0") + // for tor binary libtor.so + implementation(fileTree(dir: "libs", include: ["*.jar", "*.so"])) - // Websockets connection - implementation("io.socket:socket.io-client:2.1.2") { + implementation(libs.socket.io.client) { exclude(group: "org.json", module: "json") } - // Work manager - implementation("androidx.work:work-runtime:2.11.2") - implementation("androidx.work:work-runtime-ktx:2.11.2") - // ListenableFuture class used with WorkManager: - implementation("com.google.guava:guava:33.6.0-android") - - implementation("com.goterl:lazysodium-android:5.2.0@aar") - implementation("net.java.dev.jna:jna:5.18.1@aar") - implementation("com.apicatalog:copper-multibase:4.1.0") - implementation("org.msgpack:msgpack-core:0.9.12") - + implementation(project(":react-native-fs")) + implementation(libs.commons.io) + implementation(libs.androidx.appcompat) + implementation(libs.react.android) + implementation(libs.androidx.swiperefreshlayout) + implementation(libs.kotlin.stdlib.jdk7) + implementation(libs.androidx.core.ktx) + implementation(libs.gson) + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.messaging) + implementation(libs.androidx.security.crypto) + implementation(libs.androidx.work.runtime) + implementation(libs.androidx.work.runtime.ktx) + implementation(libs.guava) + implementation(libs.lazysodium.android) + implementation(libs.jna) + implementation(libs.copper.multibase) + implementation(libs.msgpack.core) } // Run this once to be able to run the application with BUCK diff --git a/packages/mobile/android/build.gradle b/packages/mobile/android/build.gradle index b6d49b4af1..ba2cf62c1b 100644 --- a/packages/mobile/android/build.gradle +++ b/packages/mobile/android/build.gradle @@ -1,13 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. - buildscript { - ext.kotlin_version = "2.0.21" ext { - buildToolsVersion = "35.0.0" + // TODO: this field is unused in modern gradle plugins, but quiet's outdated react native + // android libs rely on it being here for now... + buildToolsVersion = "36.0.0" minSdkVersion = 26 - compileSdkVersion = 35 + compileSdkVersion = 36 targetSdkVersion = 35 - ndkVersion = "28.2.13676358" } repositories { @@ -15,11 +14,11 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.13.2") - classpath("com.google.gms:google-services:4.4.2") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("de.undercouch:gradle-download-task:5.6.0") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + classpath(libs.gradle) + classpath(libs.google.services) + classpath(libs.react.native.gradle.plugin) + classpath(libs.gradle.download.task) + classpath(libs.kotlin.gradle.plugin) // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/packages/mobile/android/gradle/libs.versions.toml b/packages/mobile/android/gradle/libs.versions.toml new file mode 100644 index 0000000000..1f3a9fb6b5 --- /dev/null +++ b/packages/mobile/android/gradle/libs.versions.toml @@ -0,0 +1,59 @@ +[versions] + +# these can't be updated unitl we target android 36 which requires a serious react native upgrdae +gradle = "8.13.2" +kotlin = "2.0.21" +coreKtx = "1.16.0" + +commonsIo = "2.22.0" +jsc-android-intl = "2026004.0.1" +appcompat = "1.7.1" +core = "1.7.0" +guava = "33.6.0-android" +junit = "1.3.0" +gradleDownloadTask = "5.7.0" +copperMultibase = "4.1.0" +firebaseBom = "34.13.0" +googleServices = "4.4.4" +lazysodiumAndroid = "5.2.0" +jna = "5.18.1" +msgpackCore = "0.9.12" +securityCrypto = "1.1.0" +gson = "2.14.0" +socketIoClient = "2.1.2" +swiperefreshlayout = "1.2.0" +workRuntime = "2.11.2" +workRuntimeKtx = "2.11.2" + +[libraries] +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } +androidx-core = { module = "androidx.test:core", version.ref = "core" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } +androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junit" } +androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } +androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } +androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "workRuntime" } +commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" } +copper-multibase = { module = "com.apicatalog:copper-multibase", version.ref = "copperMultibase" } +google-services = { module = "com.google.gms:google-services", version.ref = "googleServices" } +gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } +gradle-download-task = { module = "de.undercouch:gradle-download-task", version.ref = "gradleDownloadTask" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" } +firebase-messaging = { module = "com.google.firebase:firebase-messaging" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } +lazysodium-android = { module = "com.goterl:lazysodium-android@aar", version.ref = "lazysodiumAndroid" } +jna = { module = "net.java.dev.jna:jna@aar", version.ref = "jna" } +msgpack-core = { module = "org.msgpack:msgpack-core", version.ref = "msgpackCore" } +react-android = { module = "com.facebook.react:react-android" } +react-native-gradle-plugin = { module = "com.facebook.react:react-native-gradle-plugin" } +socket-io-client = { module = "io.socket:socket.io-client", version.ref = "socketIoClient" } +jsc-android-intl = { module = "io.github.react-native-community:jsc-android-intl@aar", version.ref = "jsc-android-intl" } + + + +[plugins] + From 0d542142bd99a4d3539db65000d5accfc8729af7 Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 04:23:12 -0400 Subject: [PATCH 06/10] fixed tasks.getByPath event registration in app/build.gradle - this type of thing is depends on the order the tasks (aka methods) are declared in the build script. this removes the warning, but is also needed when upgrading to newer gradle versions (which eventually you have no choice but to do) --- packages/mobile/android/app/build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 447f75d8b6..8f7834f414 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -393,11 +393,13 @@ tasks.register("GenerateNodeProjectAssetsLists") { } project.android.sourceSets.main.assets.srcDirs += "${rootProject.layout.buildDirectory.get()}/nodejs-assets/" - -tasks.getByPath(":${project.name}:preBuild").dependsOn GenerateNodeProjectAssetsLists - def abi_name = "arm64" +tasks.named("preBuild") { + dependsOn(tasks.named("GenerateNodeProjectAssetsLists")) + dependsOn("GenerateNodeNativeAssetsLists${abi_name}") +} + tasks.register("GenerateNodeNativeAssetsLists${abi_name}") { description = "Generates a list for runtime copying" mkdir("${rootProject.layout.buildDirectory.get()}/nodejs-native-assets/nodejs-native-assets-${abi_name}/") @@ -433,6 +435,4 @@ tasks.register("GenerateNodeNativeAssetsLists${abi_name}") { } } -tasks.getByPath(":${project.name}:preBuild").dependsOn("GenerateNodeNativeAssetsLists${abi_name}") - project.android.sourceSets.main.assets.srcDirs += "${rootProject.layout.buildDirectory.get()}/nodejs-native-assets/" From 7274c2680a26c163d3410aba17e73ce68e049223 Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 04:25:20 -0400 Subject: [PATCH 07/10] version catalog resolves aar, dont need --- packages/mobile/android/gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/gradle/libs.versions.toml b/packages/mobile/android/gradle/libs.versions.toml index 1f3a9fb6b5..c632dd2c0f 100644 --- a/packages/mobile/android/gradle/libs.versions.toml +++ b/packages/mobile/android/gradle/libs.versions.toml @@ -45,13 +45,13 @@ firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "fir guava = { module = "com.google.guava:guava", version.ref = "guava" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } -lazysodium-android = { module = "com.goterl:lazysodium-android@aar", version.ref = "lazysodiumAndroid" } -jna = { module = "net.java.dev.jna:jna@aar", version.ref = "jna" } +lazysodium-android = { module = "com.goterl:lazysodium-android", version.ref = "lazysodiumAndroid" } +jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } msgpack-core = { module = "org.msgpack:msgpack-core", version.ref = "msgpackCore" } react-android = { module = "com.facebook.react:react-android" } react-native-gradle-plugin = { module = "com.facebook.react:react-native-gradle-plugin" } socket-io-client = { module = "io.socket:socket.io-client", version.ref = "socketIoClient" } -jsc-android-intl = { module = "io.github.react-native-community:jsc-android-intl@aar", version.ref = "jsc-android-intl" } +jsc-android-intl = { module = "io.github.react-native-community:jsc-android-intl", version.ref = "jsc-android-intl" } From 450c2cc749de6c2377c17d65644d2fc933345c5c Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 04:47:57 -0400 Subject: [PATCH 08/10] bump to gradle 8.14.5, we can't switch to gradle 9 until we can update the android gralde plugin, which like so many things here, requires a react native upgrade --- .../mobile/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties b/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties index a39bd5126b..854b4b34ac 100644 --- a/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists networkTimeout=10000 From 0d135294c62109d2e58cb26515e496226f0e3f8b Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 04:57:12 -0400 Subject: [PATCH 09/10] Repositories and gradle configuration was declared at all three levels of the gradle project (app/build.gradle, build.gradle, settings.gradle) Made it so project wasn't redefining settings across 3 gradle files. updated root build.gradle and settings.gradle to new Kotlin DSL --- packages/mobile/android/app/build.gradle | 81 ++++++++----------- .../quietmobile/Backend/BackendWorkManager.kt | 8 +- .../Notification/NotificationHandler.kt | 2 + packages/mobile/android/build.gradle | 43 ---------- packages/mobile/android/build.gradle.kts | 23 ++++++ packages/mobile/android/settings.gradle | 14 ---- packages/mobile/android/settings.gradle.kts | 32 ++++++++ 7 files changed, 95 insertions(+), 108 deletions(-) delete mode 100644 packages/mobile/android/build.gradle create mode 100644 packages/mobile/android/build.gradle.kts delete mode 100644 packages/mobile/android/settings.gradle create mode 100644 packages/mobile/android/settings.gradle.kts diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 8f7834f414..a222279cc7 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -1,7 +1,5 @@ apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" apply plugin: "kotlin-android" - apply plugin: "com.facebook.react" react { @@ -10,7 +8,6 @@ react { def googleServicesJsonPath = file("google-services.json") - /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). @@ -128,45 +125,44 @@ def getNDK() { return rootProject.ext.has("NDK_PATH") ? rootProject.ext.get("NDK_PATH") : null } -android { - ndkPath = getNDK() - ndkVersion = rootProject.ext.ndkVersion + android { + ndkPath = getNDK() + ndkVersion = "28.2.13676358" - compileSdkVersion(rootProject.ext.compileSdkVersion) - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - packagingOptions { - jniLibs { - useLegacyPackaging = true - pickFirsts += ["lib/arm64-v8a/libc++_shared.so", "lib/arm64-v8a/libc++_shared.so"] + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } - } - namespace "com.quietmobile" - - defaultConfig { - applicationId = "com.quietmobile" - minSdkVersion(rootProject.ext.minSdkVersion) - targetSdkVersion(rootProject.ext.targetSdkVersion) - versionCode 615 - versionName "7.1.0" - resValue("string", "build_config_package", "com.quietmobile") - testBuildType = System.getProperty("testBuildType", "debug") - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - ndk { - //noinspection ChromeOsAbiSupport - abiFilters "arm64-v8a" + packagingOptions { + jniLibs { + useLegacyPackaging = true + pickFirsts += ["lib/arm64-v8a/libc++_shared.so", "lib/arm64-v8a/libc++_shared.so"] + } } - externalNativeBuild { - cmake { - cppFlags "" + + namespace "com.quietmobile" + compileSdkVersion = rootProject.ext.compileSdkVersion + defaultConfig { + applicationId = "com.quietmobile" + targetSdk = rootProject.ext.targetSdkVersion + minSdk = rootProject.ext.minSdkVersion + versionCode = 615 + versionName = "7.1.0" + resValue("string", "build_config_package", "com.quietmobile") + testBuildType = System.getProperty("testBuildType", "debug") + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + ndk { + //noinspection ChromeOsAbiSupport + abiFilters "arm64-v8a" + } + externalNativeBuild { + cmake { + cppFlags "" + } } } - } splits { abi { reset() @@ -319,19 +315,6 @@ if (googleServicesJsonPath.exists()) { logger.warn("google-services.json not found in packages/mobile/android/app; Firebase runtime setup will remain incomplete until it is added.") } -repositories { - mavenCentral() - maven { - name = "Central Portal Snapshots" - url = uri("https://central.sonatype.com/repository/maven/") - - // Only search this repository for the specific dependency - content { - includeModule("com.goterl", "lazysodium-java") - } - } -} - tasks.register("CopyNodeProjectAssetsFolder", Copy) { description = "Copies the Node Project to a build folder for manipulation." @@ -436,3 +419,5 @@ tasks.register("GenerateNodeNativeAssetsLists${abi_name}") { } project.android.sourceSets.main.assets.srcDirs += "${rootProject.layout.buildDirectory.get()}/nodejs-native-assets/" +apply from: "../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle" +applyNativeModulesAppBuildGradle(project) \ No newline at end of file diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorkManager.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorkManager.kt index a870c83381..21b0731919 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorkManager.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorkManager.kt @@ -37,17 +37,19 @@ class BackendWorkManager(private val context: Context) { } if (BackendWorker.isShutdownInProgress()) { - Log.i(TAG, "Skipping enqueueRequests because shutdown is in progress: " + BackendWorker.lifecycleSummary()) + Log.i(TAG, "Skipping enqueueRequests because shutdown is in progress: ${BackendWorker.lifecycleSummary()}") return } if (BackendWorker.isNodeRuntimeActive() || BackendWorker.isStartupInProgress()) { - Log.i(TAG, "Skipping enqueueRequests because backend lifecycle is active: " + BackendWorker.lifecycleSummary()) + Log.i(TAG, "Skipping enqueueRequests because backend lifecycle is active: ${BackendWorker.lifecycleSummary()}") return } if (running || enqueued) { - Log.i(TAG, "Skipping enqueueRequests because matching work already exists running=" + running + " enqueued=" + enqueued) + Log.i(TAG, + "Skipping enqueueRequests because matching work already exists running=$running enqueued=$enqueued" + ) return } diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt index 6be079a00c..738d2c8e0d 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt @@ -103,6 +103,7 @@ class NotificationHandler(private val context: Context) { NotificationManagerCompat.from(context.applicationContext) Log.i(TAG, "Posting group notification group=$group notificationId=$id") + // TODO app crashes if user does not grant permission notificationManager.notify(id, groupBuilder.build()) } @@ -161,6 +162,7 @@ class NotificationHandler(private val context: Context) { TAG, "Posting message notification channelId=$channelId notificationId=$id channelName=$channelName", ) + // TODO app crashes if user does not grant permission notificationManager.notify(id, builder.build()) } diff --git a/packages/mobile/android/build.gradle b/packages/mobile/android/build.gradle deleted file mode 100644 index ba2cf62c1b..0000000000 --- a/packages/mobile/android/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext { - // TODO: this field is unused in modern gradle plugins, but quiet's outdated react native - // android libs rely on it being here for now... - buildToolsVersion = "36.0.0" - minSdkVersion = 26 - compileSdkVersion = 36 - targetSdkVersion = 35 - ndkVersion = "28.2.13676358" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath(libs.gradle) - classpath(libs.google.services) - classpath(libs.react.native.gradle.plugin) - classpath(libs.gradle.download.task) - classpath(libs.kotlin.gradle.plugin) - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url("$rootDir/../node_modules/react-native/android") - } - google() - maven { url("$rootDir/../node_modules/detox/Detox-android") } - maven { url("https://mvnrepository.com/artifact/commons-io/commons-io") } - } -} - -apply plugin: "com.facebook.react.rootproject" - -ext { - REACT_NATIVE_NODE_MODULES_DIR = file("$rootDir/../node_modules/react-native") -} diff --git a/packages/mobile/android/build.gradle.kts b/packages/mobile/android/build.gradle.kts new file mode 100644 index 0000000000..c52cb2c282 --- /dev/null +++ b/packages/mobile/android/build.gradle.kts @@ -0,0 +1,23 @@ +rootProject.extra["compileSdkVersion"] = 36 +rootProject.extra["minSdkVersion"] = 26 +rootProject.extra["targetSdkVersion"] = 35 +rootProject.extra["buildToolsVersion"] = "36.0.0" + +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.facebook.react.rootproject") apply false +} + + + +buildscript { + + dependencies { + classpath(libs.gradle) + classpath(libs.google.services) + classpath(libs.react.native.gradle.plugin) + classpath(libs.gradle.download.task) + classpath(libs.kotlin.gradle.plugin) + // put application dependnecies in app/build.gralde, not hee + } +} diff --git a/packages/mobile/android/settings.gradle b/packages/mobile/android/settings.gradle deleted file mode 100644 index 673cb1c4b5..0000000000 --- a/packages/mobile/android/settings.gradle +++ /dev/null @@ -1,14 +0,0 @@ -pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } -plugins { id("com.facebook.react.settings") } -extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> ex.autolinkLibrariesFromCommand() } - -rootProject.name = "QuietMobile" - -apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle") -applyNativeModulesSettingsGradle(settings) -include(":app") -include(":react-native-fs") -project(":react-native-fs").projectDir = new File(settingsDir, "../node_modules/react-native-fs/android") -includeBuild("../node_modules/@react-native/gradle-plugin") -include(":react-native-gesture-handler") -project(":react-native-gesture-handler").projectDir = new File(rootProject.projectDir, "../node_modules/react-native-gesture-handler/android") diff --git a/packages/mobile/android/settings.gradle.kts b/packages/mobile/android/settings.gradle.kts new file mode 100644 index 0000000000..e4212bf1a3 --- /dev/null +++ b/packages/mobile/android/settings.gradle.kts @@ -0,0 +1,32 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + maven { url = uri("$rootDir/../node_modules/react-native/android") } + maven { url = uri("$rootDir/../node_modules/detox/Detox-android") } + maven { url = uri("https://mvnrepository.com/artifact/commons-io/commons-io") } + maven { + url = uri("https://central.sonatype.com/repository/maven/") + // Only search this repository for the specific dependency + content { + includeModule("com.goterl", "lazysodium-java") + } + } + } +} + +rootProject.name = "QuietMobile" +apply(from = "../node_modules/@react-native-community/cli-platform-android/native_modules.gradle") +val applyNativeModulesSettingsGradle: groovy.lang.Closure by extra +applyNativeModulesSettingsGradle(settings) +include(":app", ":react-native-fs") +includeBuild("../node_modules/@react-native/gradle-plugin") \ No newline at end of file From 3be467843f23213c1b2f74dfc2f6ee3dc2f00ffc Mon Sep 17 00:00:00 2001 From: bitmold Date: Mon, 25 May 2026 11:39:41 -0400 Subject: [PATCH 10/10] no need to applynativemodules in app/build.gradle, since settings.gradle applies settings and we autolink react native pkgs into quiet --- packages/mobile/android/app/build.gradle | 2 -- packages/mobile/android/gradle/libs.versions.toml | 3 --- packages/mobile/android/settings.gradle.kts | 4 +++- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index a222279cc7..5979b6c8f9 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -419,5 +419,3 @@ tasks.register("GenerateNodeNativeAssetsLists${abi_name}") { } project.android.sourceSets.main.assets.srcDirs += "${rootProject.layout.buildDirectory.get()}/nodejs-native-assets/" -apply from: "../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle" -applyNativeModulesAppBuildGradle(project) \ No newline at end of file diff --git a/packages/mobile/android/gradle/libs.versions.toml b/packages/mobile/android/gradle/libs.versions.toml index c632dd2c0f..b2d9263d06 100644 --- a/packages/mobile/android/gradle/libs.versions.toml +++ b/packages/mobile/android/gradle/libs.versions.toml @@ -53,7 +53,4 @@ react-native-gradle-plugin = { module = "com.facebook.react:react-native-gradle- socket-io-client = { module = "io.socket:socket.io-client", version.ref = "socketIoClient" } jsc-android-intl = { module = "io.github.react-native-community:jsc-android-intl", version.ref = "jsc-android-intl" } - - [plugins] - diff --git a/packages/mobile/android/settings.gradle.kts b/packages/mobile/android/settings.gradle.kts index e4212bf1a3..6528429ea0 100644 --- a/packages/mobile/android/settings.gradle.kts +++ b/packages/mobile/android/settings.gradle.kts @@ -1,3 +1,5 @@ +import groovy.lang.Closure + pluginManagement { repositories { gradlePluginPortal() @@ -26,7 +28,7 @@ dependencyResolutionManagement { rootProject.name = "QuietMobile" apply(from = "../node_modules/@react-native-community/cli-platform-android/native_modules.gradle") -val applyNativeModulesSettingsGradle: groovy.lang.Closure by extra +val applyNativeModulesSettingsGradle: Closure by extra applyNativeModulesSettingsGradle(settings) include(":app", ":react-native-fs") includeBuild("../node_modules/@react-native/gradle-plugin") \ No newline at end of file