diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 96297ad416..0000000000 --- a/app/build.gradle +++ /dev/null @@ -1,710 +0,0 @@ -import com.android.build.api.dsl.ManagedVirtualDevice - -plugins { - id 'com.android.application' - id 'kotlin-android' - id 'androidx.navigation.safeargs' - id 'org.jlleitschuh.gradle.ktlint' - id 'org.jetbrains.kotlin.android' - id 'app.cash.exhaustive' - id 'kotlin-parcelize' - id 'com.squareup.wire' - id 'translations' - id 'licenses' -} - -apply from: 'static-ips.gradle' - -wire { - kotlin { - javaInterop = true - } - - sourcePath { - srcDir 'src/main/protowire' - } - - protoPath { - srcDir "${project.rootDir}/libsignal-service/src/main/protowire" - } -} - -ktlint { - version = "0.49.1" -} - -def canonicalVersionCode = 1365 -def canonicalVersionName = "6.41.3" - -def postFixSize = 100 -def abiPostFix = ['universal' : 0, - 'armeabi-v7a' : 1, - 'arm64-v8a' : 2, - 'x86' : 3, - 'x86_64' : 4] - -def keystores = [ 'debug' : loadKeystoreProperties('keystore.debug.properties') ] - -def selectableVariants = [ - 'nightlyProdSpinner', - 'nightlyProdPerf', - 'nightlyProdRelease', - 'nightlyStagingRelease', - 'nightlyPnpPerf', - 'nightlyPnpRelease', - 'playProdDebug', - 'playProdSpinner', - 'playProdCanary', - 'playProdPerf', - 'playProdBenchmark', - 'playProdInstrumentation', - 'playProdRelease', - 'playStagingDebug', - 'playStagingCanary', - 'playStagingSpinner', - 'playStagingPerf', - 'playStagingInstrumentation', - 'playPnpDebug', - 'playPnpSpinner', - 'playStagingRelease', - 'websiteProdSpinner', - 'websiteProdRelease', -] - -android { - namespace 'org.thoughtcrime.securesms' - - buildToolsVersion = signalBuildToolsVersion - compileSdkVersion = signalCompileSdkVersion - - flavorDimensions 'distribution', 'environment' - useLibrary 'org.apache.http.legacy' - testBuildType 'instrumentation' - - kotlinOptions { - jvmTarget = signalKotlinJvmTarget - freeCompilerArgs = ["-Xallow-result-return-type"] - } - - signingConfigs { - if (keystores.debug != null) { - debug { - storeFile file("${project.rootDir}/${keystores.debug.storeFile}") - storePassword keystores.debug.storePassword - keyAlias keystores.debug.keyAlias - keyPassword keystores.debug.keyPassword - } - } - } - - testOptions { - execution 'ANDROIDX_TEST_ORCHESTRATOR' - - unitTests { - includeAndroidResources = true - } - - managedDevices { - devices { - pixel3api30 (ManagedVirtualDevice) { - device = "Pixel 3" - apiLevel = 30 - systemImageSource = "google-atd" - require64Bit = false - } - } - } - } - - - sourceSets { - test { - java.srcDirs += "$projectDir/src/testShared" - } - - androidTest { - java.srcDirs += "$projectDir/src/testShared" - } - } - - compileOptions { - coreLibraryDesugaringEnabled true - sourceCompatibility signalJavaVersion - targetCompatibility signalJavaVersion - } - - packagingOptions { - resources { - excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/LICENSE.md', 'META-INF/NOTICE', 'META-INF/LICENSE-notice.md', 'META-INF/proguard/androidx-annotations.pro', 'libsignal_jni.dylib', 'signal_jni.dll'] - } - } - - - buildFeatures { - viewBinding true - compose true - } - - composeOptions { - kotlinCompilerExtensionVersion = '1.4.4' - } - - defaultConfig { - versionCode canonicalVersionCode * postFixSize - versionName canonicalVersionName - - minSdkVersion signalMinSdkVersion - targetSdkVersion signalTargetSdkVersion - - multiDexEnabled true - - vectorDrawables.useSupportLibrary = true - project.ext.set("archivesBaseName", "Signal") - - manifestPlaceholders = [mapsKey:"AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"] - - buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L" - buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\"" - buildConfigField "String", "SIGNAL_URL", "\"https://chat.signal.org\"" - buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\"" - buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\"" - buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\"" - buildConfigField "String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"" - buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"" - buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"" - buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"" - buildConfigField "String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"" - buildConfigField "String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"" - buildConfigField "String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"" - buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}" - buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}" - buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"" - buildConfigField "int", "CONTENT_PROXY_PORT", "443" - buildConfigField "String[]", "SIGNAL_SERVICE_IPS", service_ips - buildConfigField "String[]", "SIGNAL_STORAGE_IPS", storage_ips - buildConfigField "String[]", "SIGNAL_CDN_IPS", cdn_ips - buildConfigField "String[]", "SIGNAL_CDN2_IPS", cdn2_ips - buildConfigField "String[]", "SIGNAL_CDN3_IPS", cdn3_ips - buildConfigField "String[]", "SIGNAL_SFU_IPS", sfu_ips - buildConfigField "String[]", "SIGNAL_CONTENT_PROXY_IPS", content_proxy_ips - buildConfigField "String[]", "SIGNAL_CDSI_IPS", cdsi_ips - buildConfigField "String[]", "SIGNAL_SVR2_IPS", svr2_ips - buildConfigField "String", "SIGNAL_AGENT", "\"OWA\"" - buildConfigField "String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"" - buildConfigField "String", "SVR2_MRENCLAVE", "\"6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094\"" - buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" - buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P\"" - buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"" - buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' - buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode" - buildConfigField "String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\"" - buildConfigField "String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"" - buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"" - buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"" - - buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"" - buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"" - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"unset\"" - buildConfigField "String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"" - buildConfigField "String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\"" - buildConfigField "boolean", "TRACING_ENABLED", "false" - - ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - } - resourceConfigurations += [] - - - splits { - abi { - enable !project.hasProperty('generateBaselineProfile') - reset() - include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - universalApk true - } - } - - testInstrumentationRunner "org.thoughtcrime.securesms.testing.SignalTestRunner" - testInstrumentationRunnerArguments clearPackageData: 'true' - } - - buildTypes { - debug { - if (keystores['debug'] != null) { - signingConfig signingConfigs.debug - } - isDefault true - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), - 'proguard/proguard-firebase-messaging.pro', - 'proguard/proguard-google-play-services.pro', - 'proguard/proguard-jackson.pro', - 'proguard/proguard-sqlite.pro', - 'proguard/proguard-appcompat-v7.pro', - 'proguard/proguard-square-okhttp.pro', - 'proguard/proguard-square-okio.pro', - 'proguard/proguard-rounded-image-view.pro', - 'proguard/proguard-glide.pro', - 'proguard/proguard-shortcutbadger.pro', - 'proguard/proguard-retrofit.pro', - 'proguard/proguard-webrtc.pro', - 'proguard/proguard-klinker.pro', - 'proguard/proguard-mobilecoin.pro', - 'proguard/proguard-retrolambda.pro', - 'proguard/proguard-okhttp.pro', - 'proguard/proguard-ez-vcard.pro', - 'proguard/proguard.cfg' - testProguardFiles 'proguard/proguard-automation.pro', - 'proguard/proguard.cfg' - - manifestPlaceholders = [mapsKey:getMapsKey()] - - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Debug\"" - } - - instrumentation { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - applicationIdSuffix ".instrumentation" - - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Instrumentation\"" - } - - spinner { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Spinner\"" - } - - release { - minifyEnabled true - proguardFiles = buildTypes.debug.proguardFiles - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Release\"" - } - - perf { - initWith debug - isDefault false - debuggable false - minifyEnabled true - matchingFallbacks = ['debug'] - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Perf\"" - buildConfigField "boolean", "TRACING_ENABLED", "true" - } - - benchmark { - initWith debug - isDefault false - debuggable false - minifyEnabled true - matchingFallbacks = ['debug'] - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Benchmark\"" - buildConfigField "boolean", "TRACING_ENABLED", "true" - } - - canary { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Canary\"" - } - } - - productFlavors { - play { - dimension 'distribution' - isDefault true - buildConfigField "boolean", "MANAGES_APP_UPDATES", "false" - buildConfigField "String", "APK_UPDATE_MANIFEST_URL", "null" - buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"play\"" - } - - website { - dimension 'distribution' - buildConfigField "boolean", "MANAGES_APP_UPDATES", "true" - buildConfigField "String", "APK_UPDATE_MANIFEST_URL", "\"https://updates.signal.org/android/latest.json\"" - buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"website\"" - } - - nightly { - def apkUpdateManifestUrl = "" - if (file("${project.rootDir}/nightly-url.txt").exists()) { - apkUpdateManifestUrl = file("${project.rootDir}/nightly-url.txt").text.trim() - } - dimension 'distribution' - versionNameSuffix "-nightly-untagged-${getDateSuffix()}" - buildConfigField "boolean", "MANAGES_APP_UPDATES", "true" - buildConfigField "String", "APK_UPDATE_MANIFEST_URL", "\"${apkUpdateManifestUrl}\"" - buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"nightly\"" - } - - prod { - dimension 'environment' - - isDefault true - - buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\"" - buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Prod\"" - } - - staging { - dimension 'environment' - - applicationIdSuffix ".staging" - - buildConfigField "String", "SIGNAL_URL", "\"https://chat.staging.signal.org\"" - buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"" - buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"" - buildConfigField "String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"" - buildConfigField "String", "SVR2_MRENCLAVE", "\"a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95\"" - buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"" - buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj\"" - buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\"" - buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"" - buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"" - buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"" - - buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"" - buildConfigField "String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"" - } - - pnp { - dimension 'environment' - - initWith staging - applicationIdSuffix ".pnp" - - buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Pnp\"" - } - } - - lint { - abortOnError true - baseline file('lint-baseline.xml') - checkReleaseBuilds false - disable 'LintError' - } - - android.applicationVariants.all { variant -> - variant.outputs.each { output -> - if (output.baseName.contains('nightly')) { - output.versionCodeOverride = canonicalVersionCode * postFixSize + 5 - def tag = getCurrentGitTag() - if (tag != null && tag.length() > 0) { - if (tag.startsWith("v")) { - tag = tag.substring(1) - } - output.versionNameOverride = tag - output.outputFileName = output.outputFileName.replace(".apk", "-${output.versionNameOverride}.apk") - } else { - output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") - } - } else { - output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") - def abiName = output.getFilter("ABI") ?: 'universal' - def postFix = abiPostFix.get(abiName, 0) - - if (postFix >= postFixSize) throw new AssertionError("postFix is too large") - - output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix - } - } - } - - android.variantFilter { variant -> - def distribution = variant.getFlavors().get(0).name - def environment = variant.getFlavors().get(1).name - def buildType = variant.buildType.name - def fullName = distribution + environment.capitalize() + buildType.capitalize() - - if (!selectableVariants.contains(fullName)) { - variant.setIgnore(true) - } - } - - android.buildTypes.each { - if (it.name != 'release') { - sourceSets.findByName(it.name).java.srcDirs += "$projectDir/src/debug/java" - } else { - sourceSets.findByName(it.name).java.srcDirs += "$projectDir/src/release/java" - } - } -} - -dependencies { - implementation libs.androidx.fragment.ktx - lintChecks project(':lintchecks') - - coreLibraryDesugaring libs.android.tools.desugar - - implementation (libs.androidx.appcompat) { - version { - strictly '1.6.1' - } - } - implementation libs.androidx.window.window - implementation libs.androidx.window.java - implementation libs.androidx.recyclerview - implementation libs.material.material - implementation libs.androidx.legacy.support - implementation libs.androidx.preference - implementation libs.androidx.legacy.preference - implementation libs.androidx.gridlayout - implementation libs.androidx.exifinterface - implementation libs.androidx.compose.rxjava3 - implementation libs.androidx.compose.runtime.livedata - implementation libs.androidx.constraintlayout - implementation libs.androidx.multidex - implementation libs.androidx.navigation.fragment.ktx - implementation libs.androidx.navigation.ui.ktx - implementation libs.androidx.lifecycle.viewmodel.ktx - implementation libs.androidx.lifecycle.livedata.ktx - implementation libs.androidx.lifecycle.process - implementation libs.androidx.lifecycle.viewmodel.savedstate - implementation libs.androidx.lifecycle.common.java8 - implementation libs.androidx.lifecycle.reactivestreams.ktx - implementation libs.androidx.camera.core - implementation libs.androidx.camera.camera2 - implementation libs.androidx.camera.lifecycle - implementation libs.androidx.camera.view - implementation libs.androidx.concurrent.futures - implementation libs.androidx.autofill - implementation libs.androidx.biometric - implementation libs.androidx.sharetarget - implementation libs.androidx.profileinstaller - implementation libs.androidx.asynclayoutinflater - implementation libs.androidx.asynclayoutinflater.appcompat - - implementation (libs.firebase.messaging) { - 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' - } - - implementation libs.google.play.services.maps - implementation libs.google.play.services.auth - - implementation libs.bundles.media3 - - implementation libs.conscrypt.android - implementation libs.signal.aesgcmprovider - - implementation project(':libsignal-service') - implementation project(':paging') - implementation project(':core-util') - implementation project(':glide-config') - implementation project(':video') - implementation project(':device-transfer') - implementation project(':image-editor') - implementation project(':donations') - implementation project(':contacts') - implementation project(':qr') - implementation project(':sms-exporter') - implementation project(':sticky-header-grid') - implementation project(':photoview') - implementation project(':glide-webp') - - implementation libs.libsignal.android - - implementation libs.mobilecoin - - implementation libs.signal.ringrtc - - implementation libs.leolin.shortcutbadger - implementation libs.emilsjolander.stickylistheaders - implementation libs.apache.httpclient.android - implementation libs.glide.glide - implementation libs.roundedimageview - implementation libs.materialish.progress - implementation libs.greenrobot.eventbus - implementation libs.google.zxing.android.integration - implementation libs.google.zxing.core - implementation libs.google.flexbox - implementation (libs.subsampling.scale.image.view) { - exclude group: 'com.android.support', module: 'support-annotations' - } - implementation (libs.android.tooltips) { - exclude group: 'com.android.support', module: 'appcompat-v7' - } - implementation (libs.android.smsmms) { - exclude group: 'com.squareup.okhttp', module: 'okhttp' - exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection' - } - implementation libs.stream - - implementation libs.lottie - - implementation libs.signal.android.database.sqlcipher - implementation libs.androidx.sqlite - - implementation (libs.google.ez.vcard) { - exclude group: 'com.fasterxml.jackson.core' - exclude group: 'org.freemarker' - } - implementation libs.dnsjava - implementation libs.kotlinx.collections.immutable - implementation libs.accompanist.permissions - - spinnerImplementation project(":spinner") - - canaryImplementation libs.square.leakcanary - - testImplementation testLibs.junit.junit - testImplementation testLibs.assertj.core - testImplementation testLibs.mockito.core - testImplementation testLibs.mockito.kotlin - - testImplementation testLibs.androidx.test.core - testImplementation (testLibs.robolectric.robolectric) { - exclude group: 'com.google.protobuf', module: 'protobuf-java' - } - testImplementation testLibs.robolectric.shadows.multidex - testImplementation (testLibs.bouncycastle.bcprov.jdk15on) { version { strictly "1.70" } } // Used by roboelectric - testImplementation (testLibs.bouncycastle.bcpkix.jdk15on) { version { strictly "1.70" } } // Used by roboelectric - testImplementation testLibs.conscrypt.openjdk.uber // Used by robolectric - testImplementation testLibs.hamcrest.hamcrest - testImplementation testLibs.mockk - - testImplementation(testFixtures(project(":libsignal-service"))) - - androidTestImplementation testLibs.androidx.test.ext.junit - androidTestImplementation testLibs.espresso.core - androidTestImplementation testLibs.androidx.test.core - androidTestImplementation testLibs.androidx.test.core.ktx - androidTestImplementation testLibs.androidx.test.ext.junit.ktx - androidTestImplementation testLibs.mockito.android - androidTestImplementation testLibs.mockito.kotlin - androidTestImplementation testLibs.mockk.android - androidTestImplementation testLibs.square.okhttp.mockserver - - instrumentationImplementation (libs.androidx.fragment.testing) { - exclude group: 'androidx.test', module: 'core' - } - - testImplementation testLibs.espresso.core - - implementation libs.kotlin.stdlib.jdk8 - implementation libs.kotlin.reflect - implementation libs.jackson.module.kotlin - - implementation libs.rxjava3.rxandroid - implementation libs.rxjava3.rxkotlin - implementation libs.rxdogtag - - androidTestUtil testLibs.androidx.test.orchestrator - - implementation project(':core-ui') - ktlintRuleset libs.ktlint.twitter.compose -} - -def getLastCommitTimestamp() { - if (!(new File('.git').exists())) { - return System.currentTimeMillis().toString() - } - - new ByteArrayOutputStream().withStream { os -> - exec { - executable = 'git' - args = ['log', '-1', '--pretty=format:%ct'] - standardOutput = os - } - - return os.toString() + "000" - } -} - -def getGitHash() { - if (!(new File('.git').exists())) { - throw new IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)") - } - - def stdout = new ByteArrayOutputStream() - exec { - commandLine 'git', 'rev-parse', 'HEAD' - standardOutput = stdout - } - return stdout.toString().trim().substring(0, 12) -} - -def getCurrentGitTag() { - if (!(new File('.git').exists())) { - throw new IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)") - } - - def stdout = new ByteArrayOutputStream() - exec { - commandLine 'git', 'tag', '--points-at', 'HEAD' - standardOutput = stdout - } - - def output = stdout.toString().trim() - - if (output != null && output.size() > 0) { - def tags = output.split('\n').toList() - return tags.stream().filter(t -> t.contains('nightly')).findFirst().orElse(tags.get(0)) - } else { - return null - } -} - -tasks.withType(Test) { - testLogging { - events "failed" - exceptionFormat "full" - showCauses true - showExceptions true - showStackTraces true - } -} - -project.tasks.configureEach { task -> - if (task.name.toLowerCase().contains("nightly") && task.name != 'checkNightlyParams') { - task.dependsOn checkNightlyParams - } -} - -tasks.register('checkNightlyParams') { - doFirst { - if (project.gradle.startParameter.taskNames.any { it.toLowerCase().contains("nightly") }) { - - if (!file("${project.rootDir}/nightly-url.txt").exists()) { - throw new GradleException("Cannot fine 'nightly-url.txt' for nightly build! It must exist in the root of this project and contain the location of the nightly manifest.") - } - } - } -} - - -def loadKeystoreProperties(filename) { - def keystorePropertiesFile = file("${project.rootDir}/${filename}") - if (keystorePropertiesFile.exists()) { - def keystoreProperties = new Properties() - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) - return keystoreProperties - } else { - return null - } -} - -static def getDateSuffix() { - def date = new Date() - def formattedDate = date.format('yyyy-MM-dd-HH:mm') - return formattedDate -} - -def getMapsKey() { - def mapKey = file("${project.rootDir}/maps.key") - if (mapKey.exists()) { - return mapKey.readLines()[0] - } - return "AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U" -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000000..b566223d71 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,739 @@ +import com.android.build.api.dsl.ManagedVirtualDevice +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import java.io.ByteArrayOutputStream +import java.io.FileInputStream +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Properties + +plugins { + id("com.android.application") + id("kotlin-android") + id("androidx.navigation.safeargs") + id("org.jlleitschuh.gradle.ktlint") + id("org.jetbrains.kotlin.android") + id("app.cash.exhaustive") + id("kotlin-parcelize") + id("com.squareup.wire") + id("translations") + id("licenses") +} + +apply(from = "static-ips.gradle.kts") + +val canonicalVersionCode = 1365 +val canonicalVersionName = "6.41.3" + +val postFixSize = 100 +val abiPostFix: Map = mapOf( + "universal" to 0, + "armeabi-v7a" to 1, + "arm64-v8a" to 2, + "x86" to 3, + "x86_64" to 4 +) + +val keystores: Map = mapOf("debug" to loadKeystoreProperties("keystore.debug.properties")) + +val selectableVariants = listOf( + "nightlyProdSpinner", + "nightlyProdPerf", + "nightlyProdRelease", + "nightlyStagingRelease", + "nightlyPnpPerf", + "nightlyPnpRelease", + "playProdDebug", + "playProdSpinner", + "playProdCanary", + "playProdPerf", + "playProdBenchmark", + "playProdInstrumentation", + "playProdRelease", + "playStagingDebug", + "playStagingCanary", + "playStagingSpinner", + "playStagingPerf", + "playStagingInstrumentation", + "playPnpDebug", + "playPnpSpinner", + "playStagingRelease", + "websiteProdSpinner", + "websiteProdRelease" +) + +val signalBuildToolsVersion: String by rootProject.extra +val signalCompileSdkVersion: String by rootProject.extra +val signalTargetSdkVersion: Int by rootProject.extra +val signalMinSdkVersion: Int by rootProject.extra +val signalJavaVersion: JavaVersion by rootProject.extra +val signalKotlinJvmTarget: String by rootProject.extra + +wire { + kotlin { + javaInterop = true + } + + sourcePath { + srcDir("src/main/protowire") + } + + protoPath { + srcDir("${project.rootDir}/libsignal-service/src/main/protowire") + } +} + +ktlint { + version.set("0.49.1") +} + +android { + namespace = "org.thoughtcrime.securesms" + + buildToolsVersion = signalBuildToolsVersion + compileSdkVersion = signalCompileSdkVersion + + flavorDimensions += listOf("distribution", "environment") + useLibrary("org.apache.http.legacy") + testBuildType = "instrumentation" + + kotlinOptions { + jvmTarget = signalKotlinJvmTarget + freeCompilerArgs = listOf("-Xallow-result-return-type") + } + + keystores["debug"]?.let { properties -> + signingConfigs.getByName("debug").apply { + storeFile = file("${project.rootDir}/${properties.getProperty("storeFile")}") + storePassword = properties.getProperty("storePassword") + keyAlias = properties.getProperty("keyAlias") + keyPassword = properties.getProperty("keyPassword") + } + } + + testOptions { + execution = "ANDROIDX_TEST_ORCHESTRATOR" + + unitTests { + isIncludeAndroidResources = true + } + + managedDevices { + devices { + create("pixel3api30") { + device = "Pixel 3" + apiLevel = 30 + systemImageSource = "google-atd" + require64Bit = false + } + } + } + } + + sourceSets { + getByName("test") { + java.srcDir("$projectDir/src/testShared") + } + + getByName("androidTest") { + java.srcDir("$projectDir/src/testShared") + } + } + + compileOptions { + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = signalJavaVersion + targetCompatibility = signalJavaVersion + } + + packagingOptions { + resources { + excludes += setOf("LICENSE.txt", "LICENSE", "NOTICE", "asm-license.txt", "META-INF/LICENSE", "META-INF/LICENSE.md", "META-INF/NOTICE", "META-INF/LICENSE-notice.md", "META-INF/proguard/androidx-annotations.pro", "libsignal_jni.dylib", "signal_jni.dll") + } + } + + buildFeatures { + viewBinding = true + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.4.4" + } + + defaultConfig { + versionCode = canonicalVersionCode * postFixSize + versionName = canonicalVersionName + + minSdkVersion(signalMinSdkVersion) + targetSdkVersion(signalTargetSdkVersion) + + multiDexEnabled = true + + vectorDrawables.useSupportLibrary = true + project.ext.set("archivesBaseName", "Signal") + + manifestPlaceholders["mapsKey"] = "AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U" + + buildConfigField("long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L") + buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"") + buildConfigField("String", "SIGNAL_URL", "\"https://chat.signal.org\"") + buildConfigField("String", "STORAGE_URL", "\"https://storage.signal.org\"") + buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\"") + buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\"") + buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"") + buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"") + buildConfigField("String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"") + buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"") + buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"") + buildConfigField("String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"") + buildConfigField("String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"") + buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}") + buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}") + buildConfigField("String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"") + buildConfigField("int", "CONTENT_PROXY_PORT", "443") + buildConfigField("String[]", "SIGNAL_SERVICE_IPS", rootProject.extra["service_ips"] as String) + buildConfigField("String[]", "SIGNAL_STORAGE_IPS", rootProject.extra["storage_ips"] as String) + buildConfigField("String[]", "SIGNAL_CDN_IPS", rootProject.extra["cdn_ips"] as String) + buildConfigField("String[]", "SIGNAL_CDN2_IPS", rootProject.extra["cdn2_ips"] as String) + buildConfigField("String[]", "SIGNAL_CDN3_IPS", rootProject.extra["cdn3_ips"] as String) + buildConfigField("String[]", "SIGNAL_SFU_IPS", rootProject.extra["sfu_ips"] as String) + buildConfigField("String[]", "SIGNAL_CONTENT_PROXY_IPS", rootProject.extra["content_proxy_ips"] as String) + buildConfigField("String[]", "SIGNAL_CDSI_IPS", rootProject.extra["cdsi_ips"] as String) + buildConfigField("String[]", "SIGNAL_SVR2_IPS", rootProject.extra["svr2_ips"] as String) + buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"") + buildConfigField("String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"") + buildConfigField("String", "SVR2_MRENCLAVE", "\"6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094\"") + buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"") + buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P\"") + buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"") + buildConfigField("String[]", "LANGUAGES", "new String[]{ ${languageList().map { "\"$it\"" }.joinToString(separator = ", ")} }") + buildConfigField("int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode") + buildConfigField("String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\"") + buildConfigField("String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"") + buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"") + buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"") + + buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"") + buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"") + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"unset\"") + buildConfigField("String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"") + buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\"") + buildConfigField("boolean", "TRACING_ENABLED", "false") + + ndk { + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + } + resourceConfigurations += listOf() + + splits { + abi { + isEnable = !project.hasProperty("generateBaselineProfile") + reset() + include("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + isUniversalApk = true + } + } + + testInstrumentationRunner = "org.thoughtcrime.securesms.testing.SignalTestRunner" + testInstrumentationRunnerArguments["clearPackageData"] = "true" + } + + buildTypes { + getByName("debug") { + if (keystores["debug"] != null) { + signingConfig = signingConfigs["debug"] + } + isDefault = true + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard/proguard-firebase-messaging.pro", + "proguard/proguard-google-play-services.pro", + "proguard/proguard-jackson.pro", + "proguard/proguard-sqlite.pro", + "proguard/proguard-appcompat-v7.pro", + "proguard/proguard-square-okhttp.pro", + "proguard/proguard-square-okio.pro", + "proguard/proguard-rounded-image-view.pro", + "proguard/proguard-glide.pro", + "proguard/proguard-shortcutbadger.pro", + "proguard/proguard-retrofit.pro", + "proguard/proguard-webrtc.pro", + "proguard/proguard-klinker.pro", + "proguard/proguard-mobilecoin.pro", + "proguard/proguard-retrolambda.pro", + "proguard/proguard-okhttp.pro", + "proguard/proguard-ez-vcard.pro", + "proguard/proguard.cfg" + ) + testProguardFiles( + "proguard/proguard-automation.pro", + "proguard/proguard.cfg" + ) + + manifestPlaceholders["mapsKey"] = getMapsKey() + + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Debug\"") + } + + getByName("release") { + isMinifyEnabled = true + proguardFiles(*buildTypes["debug"].proguardFiles.toTypedArray()) + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Release\"") + } + + create("instrumentation") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + applicationIdSuffix = ".instrumentation" + + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Instrumentation\"") + } + + create("spinner") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Spinner\"") + } + + create("perf") { + initWith(getByName("debug")) + isDefault = false + isDebuggable = false + isMinifyEnabled = true + matchingFallbacks += "debug" + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Perf\"") + buildConfigField("boolean", "TRACING_ENABLED", "true") + } + + create("benchmark") { + initWith(getByName("debug")) + isDefault = false + isDebuggable = false + isMinifyEnabled = true + matchingFallbacks += "debug" + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Benchmark\"") + buildConfigField("boolean", "TRACING_ENABLED", "true") + } + + create("canary") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Canary\"") + } + } + + productFlavors { + create("play") { + dimension = "distribution" + isDefault = true + buildConfigField("boolean", "MANAGES_APP_UPDATES", "false") + buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "null") + buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"play\"") + } + + create("website") { + dimension = "distribution" + buildConfigField("boolean", "MANAGES_APP_UPDATES", "true") + buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "\"https://updates.signal.org/android/latest.json\"") + buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"website\"") + } + + create("nightly") { + val apkUpdateManifestUrl = if (file("${project.rootDir}/nightly-url.txt").exists()) { + file("${project.rootDir}/nightly-url.txt").readText().trim() + } else { + "" + } + + dimension = "distribution" + versionNameSuffix = "-nightly-untagged-${getDateSuffix()}" + buildConfigField("boolean", "MANAGES_APP_UPDATES", "true") + buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "\"${apkUpdateManifestUrl}\"") + buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"nightly\"") + } + + create("prod") { + dimension = "environment" + + isDefault = true + + buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\"") + buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Prod\"") + } + + create("staging") { + dimension = "environment" + + applicationIdSuffix = ".staging" + + buildConfigField("String", "SIGNAL_URL", "\"https://chat.staging.signal.org\"") + buildConfigField("String", "STORAGE_URL", "\"https://storage-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"") + buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"") + buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"") + buildConfigField("String", "SVR2_MRENCLAVE", "\"a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95\"") + buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"") + buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj\"") + buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\"") + buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"") + buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"") + buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"") + + buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"") + buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"") + } + + create("pnp") { + dimension = "environment" + + initWith(getByName("staging")) + applicationIdSuffix = ".pnp" + + buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Pnp\"") + } + } + + lint { + abortOnError = true + baseline = file("lint-baseline.xml") + checkReleaseBuilds = false + disable += "LintError" + } + + applicationVariants.all { + val variant = this + + variant.outputs + .map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl } + .forEach { output -> + if (output.baseName.contains("nightly")) { + output.versionCodeOverride = canonicalVersionCode * postFixSize + 5 + var tag = getCurrentGitTag() + if (!tag.isNullOrEmpty()) { + if (tag.startsWith("v")) { + tag = tag.substring(1) + } + output.versionNameOverride = tag + output.outputFileName = output.outputFileName.replace(".apk", "-${output.versionNameOverride}.apk") + } else { + output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") + } + } else { + output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") + + val abiName: String = output.getFilter("ABI") ?: "universal" + val postFix: Int = abiPostFix[abiName]!! + + if (postFix >= postFixSize) { + throw AssertionError("postFix is too large") + } + + output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix + } + } + } + + android.variantFilter { + val distribution: String = flavors[0].name + val environment: String = flavors[1].name + val buildType: String = buildType.name + val fullName: String = distribution + environment.capitalize() + buildType.capitalize() + + if (!selectableVariants.contains(fullName)) { + ignore = true + } + } + + android.buildTypes.forEach { + val path: String = if (it.name == "release") { + "$projectDir/src/release/java" + } else { + "$projectDir/src/debug/java" + } + + sourceSets.findByName(it.name)!!.java.srcDir(path) + } +} + +dependencies { + lintChecks(project(":lintchecks")) + ktlintRuleset(libs.ktlint.twitter.compose) + coreLibraryDesugaring(libs.android.tools.desugar) + + implementation(project(":libsignal-service")) + implementation(project(":paging")) + implementation(project(":core-util")) + implementation(project(":glide-config")) + implementation(project(":video")) + implementation(project(":device-transfer")) + implementation(project(":image-editor")) + implementation(project(":donations")) + implementation(project(":contacts")) + implementation(project(":qr")) + implementation(project(":sms-exporter")) + implementation(project(":sticky-header-grid")) + implementation(project(":photoview")) + implementation(project(":glide-webp")) + implementation(project(":core-ui")) + + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.appcompat) { + version { + strictly("1.6.1") + } + } + implementation(libs.androidx.window.window) + implementation(libs.androidx.window.java) + implementation(libs.androidx.recyclerview) + implementation(libs.material.material) + implementation(libs.androidx.legacy.support) + implementation(libs.androidx.preference) + implementation(libs.androidx.legacy.preference) + implementation(libs.androidx.gridlayout) + implementation(libs.androidx.exifinterface) + implementation(libs.androidx.compose.rxjava3) + implementation(libs.androidx.compose.runtime.livedata) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.multidex) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.process) + implementation(libs.androidx.lifecycle.viewmodel.savedstate) + implementation(libs.androidx.lifecycle.common.java8) + implementation(libs.androidx.lifecycle.reactivestreams.ktx) + implementation(libs.androidx.camera.core) + implementation(libs.androidx.camera.camera2) + implementation(libs.androidx.camera.lifecycle) + implementation(libs.androidx.camera.view) + implementation(libs.androidx.concurrent.futures) + implementation(libs.androidx.autofill) + implementation(libs.androidx.biometric) + implementation(libs.androidx.sharetarget) + implementation(libs.androidx.profileinstaller) + implementation(libs.androidx.asynclayoutinflater) + implementation(libs.androidx.asynclayoutinflater.appcompat) + implementation(libs.firebase.messaging) { + 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") + } + implementation(libs.google.play.services.maps) + implementation(libs.google.play.services.auth) + implementation(libs.bundles.media3) + implementation(libs.conscrypt.android) + implementation(libs.signal.aesgcmprovider) + implementation(libs.libsignal.android) + implementation(libs.mobilecoin) + implementation(libs.signal.ringrtc) + implementation(libs.leolin.shortcutbadger) + implementation(libs.emilsjolander.stickylistheaders) + implementation(libs.apache.httpclient.android) + implementation(libs.glide.glide) + implementation(libs.roundedimageview) + implementation(libs.materialish.progress) + implementation(libs.greenrobot.eventbus) + implementation(libs.google.zxing.android.integration) + implementation(libs.google.zxing.core) + implementation(libs.google.flexbox) + implementation(libs.subsampling.scale.image.view) { + exclude(group = "com.android.support", module = "support-annotations") + } + implementation(libs.android.tooltips) { + exclude(group = "com.android.support", module = "appcompat-v7") + } + implementation(libs.android.smsmms) { + exclude(group = "com.squareup.okhttp", module = "okhttp") + exclude(group = "com.squareup.okhttp", module = "okhttp-urlconnection") + } + implementation(libs.stream) + implementation(libs.lottie) + implementation(libs.signal.android.database.sqlcipher) + implementation(libs.androidx.sqlite) + implementation(libs.google.ez.vcard) { + exclude(group = "com.fasterxml.jackson.core") + exclude(group = "org.freemarker") + } + implementation(libs.dnsjava) + implementation(libs.kotlinx.collections.immutable) + implementation(libs.accompanist.permissions) + implementation(libs.kotlin.stdlib.jdk8) + implementation(libs.kotlin.reflect) + implementation(libs.jackson.module.kotlin) + implementation(libs.rxjava3.rxandroid) + implementation(libs.rxjava3.rxkotlin) + implementation(libs.rxdogtag) + + "spinnerImplementation"(project(":spinner")) + + "canaryImplementation"(libs.square.leakcanary) + + "instrumentationImplementation"(libs.androidx.fragment.testing) { + exclude(group = "androidx.test", module = "core") + } + + testImplementation(testLibs.junit.junit) + testImplementation(testLibs.assertj.core) + testImplementation(testLibs.mockito.core) + testImplementation(testLibs.mockito.kotlin) + testImplementation(testLibs.androidx.test.core) + testImplementation(testLibs.robolectric.robolectric) { + exclude(group = "com.google.protobuf", module = "protobuf-java") + } + testImplementation(testLibs.robolectric.shadows.multidex) + testImplementation(testLibs.bouncycastle.bcprov.jdk15on) { + version { + strictly("1.70") + } + } + testImplementation(testLibs.bouncycastle.bcpkix.jdk15on) { + version { + strictly("1.70") + } + } + testImplementation(testLibs.conscrypt.openjdk.uber) + testImplementation(testLibs.hamcrest.hamcrest) + testImplementation(testLibs.mockk) + testImplementation(testFixtures(project(":libsignal-service"))) + testImplementation(testLibs.espresso.core) + + androidTestImplementation(testLibs.androidx.test.ext.junit) + androidTestImplementation(testLibs.espresso.core) + androidTestImplementation(testLibs.androidx.test.core) + androidTestImplementation(testLibs.androidx.test.core.ktx) + androidTestImplementation(testLibs.androidx.test.ext.junit.ktx) + androidTestImplementation(testLibs.mockito.android) + androidTestImplementation(testLibs.mockito.kotlin) + androidTestImplementation(testLibs.mockk.android) + androidTestImplementation(testLibs.square.okhttp.mockserver) + + androidTestUtil(testLibs.androidx.test.orchestrator) +} + +fun assertIsGitRepo() { + if (!file("${project.rootDir}/.git").exists()) { + throw IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)") + } +} + +fun getLastCommitTimestamp(): String { + assertIsGitRepo() + + ByteArrayOutputStream().use { os -> + exec { + executable = "git" + args = listOf("log", "-1", "--pretty=format:%ct") + standardOutput = os + } + + return os.toString() + "000" + } +} + +fun getGitHash(): String { + assertIsGitRepo() + + val stdout = ByteArrayOutputStream() + exec { + commandLine = listOf("git", "rev-parse", "HEAD") + standardOutput = stdout + } + + return stdout.toString().trim().substring(0, 12) +} + +fun getCurrentGitTag(): String? { + assertIsGitRepo() + + val stdout = ByteArrayOutputStream() + exec { + commandLine = listOf("git", "tag", "--points-at", "HEAD") + standardOutput = stdout + } + + val output: String = stdout.toString().trim() + + return if (output.isNotEmpty()) { + val tags = output.split("\n").toList() + tags.firstOrNull { it.contains("nightly") } ?: tags[0] + } else { + null + } +} + +tasks.withType().configureEach { + testLogging { + events("failed") + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showExceptions = true + showStackTraces = true + } +} + +project.tasks.configureEach { + if (name.lowercase().contains("nightly") && name != "checkNightlyParams") { + dependsOn(tasks.getByName("checkNightlyParams")) + } +} + +tasks.register("checkNightlyParams") { + doFirst { + if (project.gradle.startParameter.taskNames.any { it.lowercase().contains("nightly") }) { + + if (!file("${project.rootDir}/nightly-url.txt").exists()) { + throw GradleException("Cannot find 'nightly-url.txt' for nightly build! It must exist in the root of this project and contain the location of the nightly manifest.") + } + } + } +} + +fun loadKeystoreProperties(filename: String): Properties? { + val keystorePropertiesFile = file("${project.rootDir}/$filename") + + return if (keystorePropertiesFile.exists()) { + val keystoreProperties = Properties() + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) + keystoreProperties + } else { + null + } +} + +fun getDateSuffix(): String { + return SimpleDateFormat("yyyy-MM-dd-HH:mm").format(Date()) +} + +fun getMapsKey(): String { + val mapKey = file("${project.rootDir}/maps.key") + + return if (mapKey.exists()) { + mapKey.readLines()[0] + } else { + "AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U" + } +} + +fun Project.languageList(): List { + return fileTree("src/main/res") { include("**/strings.xml") } + .map { stringFile -> stringFile.parentFile.name } + .map { valuesFolderName -> valuesFolderName.replace("values-", "") } + .filter { valuesFolderName -> valuesFolderName != "values" } + .map { languageCode -> languageCode.replace("-r", "_") } + .distinct() + "en" +} + +fun String.capitalize(): String { + return this.replaceFirstChar { it.uppercase() } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt index 73b0a6c0ab..a26beb226b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt @@ -383,7 +383,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att if (currentState.downloadClickedListener == null) { Log.w(TAG, "No click listener set for retry!") } - + binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView), listOf(binding.primaryProgressView, binding.playVideoButton)) showAllViews( diff --git a/app/static-ips.gradle b/app/static-ips.gradle deleted file mode 100644 index a2e98de8e0..0000000000 --- a/app/static-ips.gradle +++ /dev/null @@ -1,9 +0,0 @@ -ext.service_ips='new String[]{"13.248.212.111","76.223.92.165"}' -ext.storage_ips='new String[]{"142.251.41.19"}' -ext.cdn_ips='new String[]{"18.238.55.2","18.238.55.54","18.238.55.7","18.238.55.78"}' -ext.cdn2_ips='new String[]{"104.18.10.47","104.18.11.47"}' -ext.cdn3_ips='new String[]{"104.18.10.47","104.18.11.47"}' -ext.sfu_ips='new String[]{"35.186.192.249"}' -ext.content_proxy_ips='new String[]{"107.178.250.75"}' -ext.svr2_ips='new String[]{"20.119.62.85"}' -ext.cdsi_ips='new String[]{"40.122.45.194"}' \ No newline at end of file diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts new file mode 100644 index 0000000000..51f0a067e4 --- /dev/null +++ b/app/static-ips.gradle.kts @@ -0,0 +1,9 @@ +rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.211"}""" +rootProject.extra["cdn_ips"] = """new String[]{"18.238.49.106","18.238.49.6","18.238.49.66","18.238.49.90"}""" +rootProject.extra["cdn2_ips"] = """new String[]{"104.18.10.47","104.18.11.47"}""" +rootProject.extra["cdn3_ips"] = """new String[]{"104.18.10.47","104.18.11.47"}""" +rootProject.extra["sfu_ips"] = """new String[]{"35.186.192.249"}""" +rootProject.extra["content_proxy_ips"] = """new String[]{"107.178.250.75"}""" +rootProject.extra["svr2_ips"] = """new String[]{"20.119.62.85"}""" +rootProject.extra["cdsi_ips"] = """new String[]{"40.122.45.194"}""" diff --git a/build-logic/plugins/src/main/java/translations.gradle b/build-logic/plugins/src/main/java/translations.gradle index 27d4d0d689..06a3658ebb 100644 --- a/build-logic/plugins/src/main/java/translations.gradle +++ b/build-logic/plugins/src/main/java/translations.gradle @@ -3,10 +3,6 @@ import groovy.transform.stc.ClosureParams import groovy.transform.stc.SimpleType import org.signal.buildtools.StaticIpResolver -ext { - autoResConfig = this.&autoResConfig -} - def allStringsResourceFiles(@ClosureParams(value = SimpleType.class, options = ['java.io.File']) Closure c) { file('src/main/res').eachFileRecurse(FileType.FILES) { f -> if (f.name == 'strings.xml') { @@ -15,20 +11,6 @@ def allStringsResourceFiles(@ClosureParams(value = SimpleType.class, options = [ } } -/** - * Discovers supported languages listed as under the res/values- directory. - */ -def autoResConfig() { - def files = [] - allStringsResourceFiles { f -> - files.add(f.parentFile.name) - } - ['en'] + files.collect { f -> f =~ /^values-([a-z]{2,3}(-r[A-Z]{2})?)$/ } - .findAll { matcher -> matcher.find() } - .collect { matcher -> matcher.group(1) } - .sort() -} - task replaceEllipsis { group 'Static Files' description 'Process strings for ellipsis characters.' @@ -125,17 +107,17 @@ task resolveStaticIps { description 'Fetches static IPs for core hosts and writes them to static-ips.gradle' doLast { def staticIpResolver = new StaticIpResolver() - new File(projectDir, "static-ips.gradle").text = """ - ext.service_ips='${staticIpResolver.resolveToBuildConfig("chat.signal.org")}' - ext.storage_ips='${staticIpResolver.resolveToBuildConfig("storage.signal.org")}' - ext.cdn_ips='${staticIpResolver.resolveToBuildConfig("cdn.signal.org")}' - ext.cdn2_ips='${staticIpResolver.resolveToBuildConfig("cdn2.signal.org")}' - ext.cdn3_ips='${staticIpResolver.resolveToBuildConfig("cdn3.signal.org")}' - ext.sfu_ips='${staticIpResolver.resolveToBuildConfig("sfu.voip.signal.org")}' - ext.content_proxy_ips='${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}' - ext.svr2_ips='${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}' - ext.cdsi_ips='${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}' - """.stripIndent().trim() + new File(projectDir, "static-ips.gradle.kts").text = """ + rootProject.extra["service_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("chat.signal.org")}\"\"\" + rootProject.extra["storage_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("storage.signal.org")}\"\"\" + rootProject.extra["cdn_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn.signal.org")}\"\"\" + rootProject.extra["cdn2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn2.signal.org")}\"\"\" + rootProject.extra["cdn3_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn3.signal.org")}\"\"\" + rootProject.extra["sfu_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("sfu.voip.signal.org")}\"\"\" + rootProject.extra["content_proxy_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}\"\"\" + rootProject.extra["svr2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}\"\"\" + rootProject.extra["cdsi_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}\"\"\" + """.stripIndent().trim() + "\n" } }