plugins { id 'com.android.application' version '8.11.1' id 'com.google.gms.google-services' version '4.4.1' id 'com.diffplug.spotless' version '8.3.0' } repositories { google() mavenCentral() maven { url "https://www.jitpack.io" name 'JitPack Github wrapper' } } android { dependenciesInfo { // Disables dependency metadata when building APKs. includeInApk = false // Disables dependency metadata when building Android App Bundles. includeInBundle = false } namespace "org.thoughtcrime.securesms" flavorDimensions "none" compileSdk 36 // Set NDK version to strip native libraries. // Even though we compile our libraries outside Gradle with `scripts/ndk-make.sh`, // without ndkVersion `./gradlew clean` followed by `./gradlew assembleDebug --warning-mode=all` emits the following warning: // > Task :stripFatDebugDebugSymbols // Unable to strip the following libraries, packaging them as they are: libanimation-decoder-gif.so, libnative-utils.so. // See for details. ndkVersion "27.0.12077973" useLibrary 'org.apache.http.legacy' defaultConfig { versionCode 30000742 versionName "2.49.0" applicationId "chat.delta.lite" multiDexEnabled true minSdkVersion 21 targetSdkVersion 36 vectorDrawables.useSupportLibrary = true // base name of the generated apk project.ext.set("archivesBaseName", "deltachat"); buildConfigField "boolean", "DEV_BUILD", "false" ndk { if(project.hasProperty("ABI_FILTER")) { abiFilters ABI_FILTER } else { abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "TEST_ADDR", buildConfigProperty("TEST_ADDR")) buildConfigField("String", "TEST_MAIL_PW", buildConfigProperty("TEST_MAIL_PW")) buildConfigField("String", "NDK_ARCH", getNdkArch()) } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } packagingOptions { jniLibs { doNotStrip '**/*.so' keepDebugSymbols += [ '*/mips/*.so', '*/mips64/*.so' ] } resources { excludes += [ 'LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/NOTICE' ] } } signingConfigs { debug { // add `DC_DEBUG_STORE_FILE=/path/to/debug.keystore` to `~/.gradle/gradle.properties` if(project.hasProperty("DC_DEBUG_STORE_FILE" )) { storeFile file(DC_DEBUG_STORE_FILE ) } } releaseFdroid { // can be defined at `~/.gradle/gradle.properties` or at "Build/Generate signed APK" if(project.hasProperty("DC_RELEASE_STORE_FILE")) { storeFile file(DC_RELEASE_STORE_FILE) storePassword DC_RELEASE_STORE_PASSWORD keyAlias DC_RELEASE_KEY_ALIAS_FDROID keyPassword DC_RELEASE_KEY_PASSWORD } } releaseApk { // can be defined at `~/.gradle/gradle.properties` or at "Build/Generate signed APK" if(project.hasProperty("DC_RELEASE_STORE_FILE")) { storeFile file(DC_RELEASE_STORE_FILE) storePassword DC_RELEASE_STORE_PASSWORD keyAlias DC_RELEASE_KEY_ALIAS_GPLAY keyPassword DC_RELEASE_KEY_PASSWORD } } releaseBundle { if(project.hasProperty("DC_BUNDLE_STORE_FILE")) { storeFile file(DC_BUNDLE_STORE_FILE) storePassword DC_BUNDLE_STORE_PASSWORD keyAlias DC_BUNDLE_KEY_ALIAS keyPassword DC_BUNDLE_STORE_PASSWORD } } } productFlavors { foss { dimension "none" buildConfigField "boolean", "USE_PLAY_SERVICES", "false" } gplay { dimension "none" apply plugin: "com.google.gms.google-services" buildConfigField "boolean", "USE_PLAY_SERVICES", "true" applicationId "com.github.arcanechat" } } buildTypes { debug { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' testProguardFiles 'test-proguard-rules.pro' applicationIdSuffix ".beta" } release { // minification and proguard disabled for now. // // when enabled, it can cut down apk size about 6%, // however this also has the potential to break things. // so exceptions are needed and have to be maintained. // (see git-history and https://github.com/deltachat/deltachat-android/issues/905 ) // // nb: it is highly recommended to use the same settings in debug+release - // otherwise problems might be noticed delayed only minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' testProguardFiles 'test-proguard-rules.pro' productFlavors.foss.signingConfig signingConfigs.releaseFdroid productFlavors.gplay.signingConfig signingConfigs.releaseApk } } if(!project.hasProperty("ABI_FILTER")) { splits { abi { enable true reset() include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" universalApk true } } } project.ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4] android.applicationVariants.all { variant -> variant.outputs.all { output -> output.outputFileName = output.outputFileName .replace("android", "ArcaneChat") .replace("-release", "") .replace(".apk", "-${variant.versionName}.apk") if(project.hasProperty("ABI_FILTER")) { output.versionCodeOverride = variant.versionCode * 10 + project.ext.versionCodes.get(ABI_FILTER) } else { output.versionCodeOverride = variant.versionCode * 10 + project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 4) } } } sourceSets { main { jniLibs.srcDirs = ['libs'] } } androidResources { generateLocaleConfig true } lint { abortOnError false } buildFeatures { renderScript true aidl true } } spotless { java { target 'src/*/java/**/*.java' targetExclude 'src/main/java/chat/delta/**' // ignore auto-generated code googleJavaFormat() // Google style = 2-space indent, matches Android Studio defaults removeUnusedImports() trimTrailingWhitespace() endWithNewline() } format 'xml', { target 'src/*/res/**/*.xml', 'src/*/AndroidManifest.xml' targetExclude 'src/*/res/values*/strings*.xml' // line-break changes invalidate translations eclipseWtp('xml').configFile('spotless/eclipse-wtp-xml.prefs') trimTrailingWhitespace() endWithNewline() } groovyGradle { target '*.gradle' greclipse() leadingTabsToSpaces(4) trimTrailingWhitespace() endWithNewline() } } final def markwon_version = '4.6.2' dependencies { // ArcaneChat-only dependencies: implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:ext-strikethrough:$markwon_version" implementation "io.noties.markwon:inline-parser:$markwon_version" implementation 'com.airbnb.android:lottie:4.2.2' // Lottie animations support. def media3_version = "1.8.0" // 1.9.0 need minSdkVersion 23 implementation 'androidx.concurrent:concurrent-futures:1.3.0' implementation 'androidx.core:core:1.17.0' implementation 'androidx.core:core-telecom:1.0.1' implementation 'im.conversations.webrtc:webrtc-android:129.0.0' // For Kotlin->Java interpolation implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.9.4' implementation 'androidx.sharetarget:sharetarget:1.2.0' implementation 'androidx.webkit:webkit:1.14.0' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.appcompat:appcompat:1.7.1' implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation ('androidx.preference:preference:1.2.1') { exclude group: 'androidx.lifecycle', module:'lifecycle-viewmodel' exclude group: 'androidx.lifecycle', module:'lifecycle-viewmodel-ktx' } implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.exifinterface:exifinterface:1.4.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.2' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2' implementation 'androidx.work:work-runtime:2.9.1' implementation 'androidx.emoji2:emoji2-emojipicker:1.5.0' implementation 'com.google.guava:guava:31.1-android' implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' // FIXME: exoplayer dependencies kept for Video, but we shall migrate them at some point implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1' implementation "androidx.media3:media3-exoplayer:$media3_version" implementation "androidx.media3:media3-session:$media3_version" implementation "androidx.media3:media3-ui:$media3_version" implementation 'androidx.constraintlayout:constraintlayout:2.2.0' implementation 'com.google.zxing:core:3.3.0' // fixed version to support SDK<24 implementation ('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } // QR Code scanner implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1' // used as JSON library implementation 'com.github.Baseflow:PhotoView:2.3.0' // does the zooming on photos / media implementation 'com.caverock:androidsvg-aar:1.4' // SVG support. implementation 'com.github.bumptech.glide:glide:4.16.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' annotationProcessor 'androidx.annotation:annotation:1.9.1' implementation 'com.makeramen:roundedimageview:2.3.0' // crops the avatars to circles implementation 'com.github.amulyakhare:TextDrawable:558677ea31' // number of unread messages, // the one-letter circle for the contacts (when there is not avatar) and a white background. implementation 'com.googlecode.mp4parser:isoparser:1.0.6' // MP4 recoding; upgrading eg. to 1.1.22 breaks recoding, however, i have not investigated further, just reset to 1.0.6 implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.10.0') { // for the zooming on photos / media exclude group: 'com.android.support', module: 'support-annotations' } // Replacement for ContentResolver // that protects against the Surreptitious Sharing attack. // implementation 'de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0' gplayImplementation('com.google.firebase:firebase-messaging:24.1.2') { // for PUSH notifications, don't upgrade: v25.0.0 requires minSdk>=23 exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' } gplayImplementation 'com.google.android.gms:play-services-location:21.3.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.assertj:assertj-core:3.27.3' testImplementation 'org.mockito:mockito-core:5.18.0' testImplementation 'org.powermock:powermock-api-mockito:1.7.4' testImplementation 'org.powermock:powermock-module-junit4:2.0.9' testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.9' testImplementation 'org.powermock:powermock-classloading-xstream:2.0.9' androidTestImplementation 'androidx.test:runner:1.7.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.7.0' androidTestImplementation 'androidx.test:rules:1.7.0' androidTestImplementation 'androidx.test.ext:junit:1.3.0' androidTestImplementation 'com.android.support:support-annotations:28.0.0' androidTestImplementation ('org.assertj:assertj-core:3.27.3') { exclude group: 'org.hamcrest', module: 'hamcrest-core' } } String buildConfigProperty(String name) { return "\"${propertyOrEmpty(name)}\"" } String propertyOrEmpty(String name) { Object p = findProperty(name) if (p == null) return environmentVariable(name) return (String) p } static String environmentVariable(String name) { String env = System.getenv(name) if (env == null) return "" return env } String getNdkArch() { Properties properties = new Properties() def file = project.rootProject.file('ndkArch') if (!file.exists()) return "\"\"" properties.load(file.newDataInputStream()) def arch = properties.getProperty('NDK_ARCH') if (arch == null) return "\"\"" return "\"$arch\"" }