| 1 | buildscript { |
| 2 | repositories { |
| 3 | google() |
| 4 | mavenCentral() |
| 5 | } |
| 6 | dependencies { |
| 7 | classpath 'com.android.tools.build:gradle:9.2.1' |
| 8 | classpath 'com.diffplug.spotless:spotless-plugin-gradle:8.10.2' |
| 9 | } |
| 10 | } |
| 11 | |
| 12 | apply plugin: 'com.android.application' |
| 13 | apply plugin: 'com.diffplug.spotless' |
| 14 | |
| 15 | repositories { |
| 16 | google() |
| 17 | mavenCentral() |
| 18 | maven { url = 'https://jitpack.io' } |
| 19 | } |
| 20 | |
| 21 | spotless { |
| 22 | ratchetFrom '2.17.4' |
| 23 | java { |
| 24 | target '**/*.java' |
| 25 | googleJavaFormat().aosp().reflowLongStrings() |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | def abiCodes = ['armeabi-v7a': 1, 'x86': 2, 'x86_64': 3, 'arm64-v8a': 4] |
| 30 | |
| 31 | configurations.named('implementation') { |
| 32 | exclude group: 'org.jetbrains', module: 'annotations' |
| 33 | } |
| 34 | |
| 35 | configurations { |
| 36 | freeImplementation {} |
| 37 | conversationsPlaystoreImplementation {} |
| 38 | quicksyImplementation {} |
| 39 | quicksyPlaystoreImplementation {} |
| 40 | } |
| 41 | |
| 42 | dependencies { |
| 43 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' |
| 44 | |
| 45 | annotationProcessor 'org.immutables:value:2.12.2' |
| 46 | implementation 'org.immutables:value-annotations:2.12.2' |
| 47 | annotationProcessor project(':libs:annotation-processor') |
| 48 | implementation project(':libs:annotation') |
| 49 | implementation 'androidx.appcompat:appcompat:1.8.0' |
| 50 | implementation 'androidx.cardview:cardview:1.0.0' |
| 51 | implementation 'androidx.concurrent:concurrent-futures:1.3.0' |
| 52 | implementation 'androidx.documentfile:documentfile:1.1.0' |
| 53 | implementation 'androidx.emoji2:emoji2:1.6.0' |
| 54 | freeImplementation 'androidx.emoji2:emoji2-bundled:1.6.0' |
| 55 | implementation 'androidx.emoji2:emoji2-emojipicker:1.6.0' |
| 56 | implementation 'androidx.exifinterface:exifinterface:1.4.2' |
| 57 | implementation 'androidx.heifwriter:heifwriter:1.1.0' |
| 58 | implementation 'androidx.preference:preference:1.2.1' |
| 59 | implementation 'androidx.biometric:biometric:1.1.0' |
| 60 | implementation 'androidx.sharetarget:sharetarget:1.2.0' |
| 61 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0' |
| 62 | implementation 'androidx.viewpager:viewpager:1.1.0' |
| 63 | implementation 'androidx.work:work-runtime:2.11.2' |
| 64 | implementation 'com.github.open-keychain.open-keychain:openpgp-api:v5.7.1' |
| 65 | implementation 'com.google.android.material:material:1.14.0' |
| 66 | implementation 'com.google.guava:guava:33.7.1-android' |
| 67 | implementation 'com.google.zxing:core:3.5.4' |
| 68 | implementation 'com.leinardi.android:speed-dial:3.3.0' |
| 69 | implementation 'com.squareup.okhttp3:okhttp:5.5.0' |
| 70 | implementation 'com.squareup.retrofit2:converter-gson:3.0.0' |
| 71 | implementation 'com.squareup.retrofit2:retrofit:3.0.0' |
| 72 | implementation 'com.vanniktech:android-image-cropper:4.7.0' |
| 73 | implementation 'im.conversations.webrtc:webrtc-android:149.0.0' |
| 74 | implementation 'io.deepmedia.community:transcoder-android:0.11.2' |
| 75 | implementation 'me.leolin:ShortcutBadger:1.1.22@aar' |
| 76 | //noinspection NewerVersionAvailable |
| 77 | implementation 'net.fellbaum:jemoji:1.7.6' //1.7.6 is the latest with Java 8 support |
| 78 | implementation 'org.bouncycastle:bcmail-jdk18on:1.86' |
| 79 | implementation 'org.conscrypt:conscrypt-android:2.7.0' |
| 80 | implementation 'org.hsluv:hsluv:1.0.2' |
| 81 | implementation 'org.jxmpp:jxmpp-jid:1.1.0' |
| 82 | implementation 'org.jxmpp:jxmpp-stringprep-libidn:1.1.0' |
| 83 | implementation 'org.minidns:minidns-client:1.1.1' |
| 84 | implementation 'org.minidns:minidns-dnssec:1.1.1' |
| 85 | implementation 'org.osmdroid:osmdroid-android:6.1.20' |
| 86 | //noinspection NewerVersionAvailable |
| 87 | implementation 'org.whispersystems:signal-protocol-java:2.6.2' |
| 88 | conversationsPlaystoreImplementation 'com.android.installreferrer:installreferrer:2.2' |
| 89 | quicksyImplementation 'io.michaelrocks:libphonenumber-android:9.0.38' |
| 90 | quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.3.1' |
| 91 | |
| 92 | testImplementation 'junit:junit:4.13.2' |
| 93 | testImplementation 'org.robolectric:robolectric:4.17' |
| 94 | androidTestImplementation 'androidx.test.ext:junit:1.3.0' |
| 95 | } |
| 96 | |
| 97 | // Load local.properties so GIPHY_API_KEY etc. don't need to be passed on the command line |
| 98 | def localProps = new Properties() |
| 99 | def localPropsFile = rootProject.file('local.properties') |
| 100 | if (localPropsFile.exists()) localProps.load(new FileInputStream(localPropsFile)) |
| 101 | |
| 102 | android { |
| 103 | namespace = 'tel.xmpp.jabjab' |
| 104 | compileSdk = 37 |
| 105 | |
| 106 | defaultConfig { |
| 107 | minSdk = 23 |
| 108 | versionCode = 42315 |
| 109 | versionName = '1.0.8' |
| 110 | applicationId = 'tel.xmpp.jabjab' |
| 111 | resValue('string', 'applicationId', applicationId) |
| 112 | def appName = "JabJab" |
| 113 | resValue('string', 'app_name', appName) |
| 114 | buildConfigField('String', 'APP_NAME', "\"${appName}\"") |
| 115 | // Giphy GIF search — override with GIPHY_API_KEY gradle property |
| 116 | def giphyKey = localProps.getProperty('GIPHY_API_KEY') ?: project.findProperty('GIPHY_API_KEY') ?: "" |
| 117 | buildConfigField('String', 'GIPHY_API_KEY', "\"${giphyKey}\"") |
| 118 | def updateUrl = project.findProperty('UPDATE_SERVER_URL') ?: "" |
| 119 | buildConfigField('String', 'UPDATE_SERVER_URL', "\"${updateUrl}\"") |
| 120 | // Conversations upstream version — read from conversations.doap at build time |
| 121 | def conversationsVersion = "unknown" |
| 122 | def doapFile = file("conversations.doap") |
| 123 | if (doapFile.exists()) { |
| 124 | def m = doapFile.text =~ /<revision>([^<]+)<\/revision>/ |
| 125 | if (m.find()) conversationsVersion = m.group(1).trim() |
| 126 | } |
| 127 | buildConfigField('String', 'CONVERSATIONS_BASE_VERSION', "\"${conversationsVersion}\"") |
| 128 | base { |
| 129 | archivesName = "jabjab-${android.defaultConfig.versionName}-${android.defaultConfig.versionCode}" |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | splits { |
| 134 | abi { |
| 135 | universalApk = true |
| 136 | enable = true |
| 137 | reset() |
| 138 | //noinspection ChromeOsAbiSupport |
| 139 | include(abiCodes.keySet() as String[]) |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | compileOptions { |
| 144 | coreLibraryDesugaringEnabled = true |
| 145 | sourceCompatibility = JavaVersion.VERSION_21 |
| 146 | targetCompatibility = JavaVersion.VERSION_21 |
| 147 | } |
| 148 | |
| 149 | flavorDimensions += 'mode' |
| 150 | flavorDimensions += 'distribution' |
| 151 | |
| 152 | productFlavors { |
| 153 | quicksy { |
| 154 | dimension = 'mode' |
| 155 | applicationId = 'im.quicksy.client' |
| 156 | resValue('string', 'applicationId', applicationId) |
| 157 | def appName = "Quicksy" |
| 158 | resValue('string', 'app_name', appName) |
| 159 | buildConfigField('String', 'APP_NAME', "\"${appName}\"") |
| 160 | buildConfigField('String', 'PRIVACY_POLICY', '"https://quicksy.im/privacy.htm"') |
| 161 | |
| 162 | def propsFile = rootProject.file('quicksy-auth-secret.properties') |
| 163 | def key = null |
| 164 | if (propsFile.exists()) { |
| 165 | def props = new Properties() |
| 166 | propsFile.withInputStream { props.load(it) } |
| 167 | key = props.getProperty('QUICKSY_AUTHENTICATION_SECRET') |
| 168 | } |
| 169 | buildConfigField('String', 'QUICKSY_AUTHENTICATION_SECRET', key != null ? "\"${key}\"" : 'null') |
| 170 | } |
| 171 | |
| 172 | conversations { |
| 173 | dimension = 'mode' |
| 174 | buildConfigField('String', 'PRIVACY_POLICY', '"https://conversations.im/privacy.html"') |
| 175 | } |
| 176 | |
| 177 | playstore { |
| 178 | dimension = 'distribution' |
| 179 | } |
| 180 | free { |
| 181 | dimension = 'distribution' |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | sourceSets { |
| 186 | quicksyFree { |
| 187 | java.srcDir('src/quicksyFree/java') |
| 188 | } |
| 189 | quicksyPlaystore { |
| 190 | java.srcDir('src/quicksyPlaystore/java') |
| 191 | res.srcDir('src/quicksyPlaystore/res') |
| 192 | } |
| 193 | conversationsFree { |
| 194 | java.srcDir('src/conversationsFree/java') |
| 195 | } |
| 196 | conversationsPlaystore { |
| 197 | java.srcDir('src/conversationsPlaystore/java') |
| 198 | res.srcDir('src/conversationsPlaystore/res') |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | def signingPropsFile = ["signing.properties", |
| 203 | "${System.getProperty('user.home')}/.keys/android/jabjab/signing.properties"] |
| 204 | .collect { new File(it) } |
| 205 | .find { it.exists() } |
| 206 | |
| 207 | if (signingPropsFile != null) { |
| 208 | Properties props = new Properties() |
| 209 | props.load(new FileInputStream(signingPropsFile)) |
| 210 | |
| 211 | // Password is never stored in signing.properties — read from env var set by build-release.sh |
| 212 | def ksPassword = props['keystore.password'] ?: "" |
| 213 | |
| 214 | signingConfigs { |
| 215 | release { |
| 216 | storeFile = file(props['keystore']) |
| 217 | storePassword = ksPassword |
| 218 | keyAlias = props['keystore.alias'] |
| 219 | keyPassword = ksPassword |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | buildTypes { |
| 225 | release { |
| 226 | shrinkResources = true |
| 227 | minifyEnabled = true |
| 228 | proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro') |
| 229 | if (signingPropsFile != null) { |
| 230 | signingConfig = signingConfigs.getByName('release') |
| 231 | } |
| 232 | } |
| 233 | debug { |
| 234 | if (signingPropsFile != null) { |
| 235 | signingConfig = signingConfigs.getByName('release') |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | packaging { |
| 241 | resources { |
| 242 | excludes += ['META-INF/BCKEY.DSA', 'META-INF/BCKEY.SF', 'META-INF/LICENSE.md', |
| 243 | 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'] |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | lint { |
| 248 | disable += ['MissingTranslation', 'InvalidPackage', 'AppCompatResource'] |
| 249 | } |
| 250 | |
| 251 | buildFeatures { |
| 252 | buildConfig = true |
| 253 | resValues = true |
| 254 | dataBinding = true |
| 255 | aidl = true |
| 256 | } |
| 257 | |
| 258 | dependenciesInfo { |
| 259 | includeInApk = false |
| 260 | includeInBundle = false |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | subprojects { |
| 265 | plugins.withId('com.android.library') { |
| 266 | android { |
| 267 | lint { |
| 268 | disable += ['AndroidGradlePluginVersion', 'MissingTranslation'] |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | plugins.withId('com.android.application') { |
| 273 | android { |
| 274 | lint { |
| 275 | disable += ['AndroidGradlePluginVersion', 'MissingTranslation'] |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | } |
Built with passion for open-source software, XMPP, privacy, and security —
the belief that people deserve communication tools that serve them, not surveil them.
Dedicated to every brave man and woman who stands up against tyranny and oppression across the world.
And in memory of those who were silenced before they could speak freely.