build.gradle.kts 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. /* _____ _
  2. * |_ _| |_ _ _ ___ ___ _ __ __ _
  3. * | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. * |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. *
  6. * Threema for Android
  7. * Copyright (c) 2014-2025 Threema GmbH
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. import com.android.build.gradle.internal.api.ApkVariantOutputImpl
  22. import com.android.build.gradle.internal.tasks.factory.dependsOn
  23. import config.BuildFeatureFlags
  24. import config.PublicKeys
  25. import config.setProductNames
  26. import org.gradle.api.tasks.testing.logging.TestExceptionFormat
  27. import org.gradle.kotlin.dsl.lintChecks
  28. import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
  29. import utils.*
  30. plugins {
  31. alias(libs.plugins.sonarqube)
  32. alias(libs.plugins.kotlin.serialization)
  33. alias(libs.plugins.rust.android)
  34. id("com.android.application")
  35. id("kotlin-android")
  36. alias(libs.plugins.ksp)
  37. alias(libs.plugins.compose.compiler)
  38. alias(libs.plugins.stem)
  39. }
  40. // only apply the plugin if we are dealing with an AppGallery build
  41. if (gradle.startParameter.taskRequests.toString().contains("Hms")) {
  42. logger.info("enabling hms plugin")
  43. apply {
  44. plugin("com.huawei.agconnect")
  45. }
  46. }
  47. /**
  48. * Only use the scheme "<major>.<minor>.<patch>" for the appVersion
  49. */
  50. val appVersion = "6.3.0"
  51. /**
  52. * betaSuffix with leading dash (e.g. `-beta1`).
  53. * Should be one of (alpha|beta|rc) and an increasing number, or empty for a regular release.
  54. * Note: in nightly builds this will be overwritten with a nightly version "-n12345"
  55. */
  56. val betaSuffix = ""
  57. val defaultVersionCode = 1113
  58. /**
  59. * Map with keystore paths (if found).
  60. */
  61. val keystores: Map<String, KeystoreConfig?> = mapOf(
  62. "debug" to findKeystore(projectDir, "debug"),
  63. "release" to findKeystore(projectDir, "threema"),
  64. "hms_release" to findKeystore(projectDir, "threema_hms"),
  65. "onprem_release" to findKeystore(projectDir, "onprem"),
  66. "blue_release" to findKeystore(projectDir, "threema_blue"),
  67. )
  68. android {
  69. // NOTE: When adjusting compileSdkVersion, buildToolsVersion or ndkVersion,
  70. // make sure to adjust them in `scripts/Dockerfile` as well!
  71. compileSdk = 35
  72. buildToolsVersion = "35.0.0"
  73. ndkVersion = "28.2.13676358"
  74. defaultConfig {
  75. // https://developer.android.com/training/testing/espresso/setup#analytics
  76. with(testInstrumentationRunnerArguments) {
  77. put("notAnnotation", "ch.threema.app.DangerousTest")
  78. put("disableAnalytics", "true")
  79. }
  80. minSdk = 24
  81. targetSdk = 35
  82. vectorDrawables.useSupportLibrary = true
  83. applicationId = "ch.threema.app"
  84. testApplicationId = "$applicationId.test"
  85. versionCode = defaultVersionCode
  86. versionName = "$appVersion$betaSuffix"
  87. setProductNames(
  88. appName = "Threema",
  89. )
  90. intBuildConfigField("DEFAULT_VERSION_CODE", defaultVersionCode)
  91. // package name used for sync adapter - needs to match mime types below
  92. stringResValue("package_name", applicationId!!)
  93. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.$applicationId.profile")
  94. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.$applicationId.call")
  95. intBuildConfigField("MAX_GROUP_SIZE", 256)
  96. stringBuildConfigField("CHAT_SERVER_PREFIX", "g-")
  97. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "ds.g-")
  98. stringBuildConfigField("CHAT_SERVER_SUFFIX", ".0.threema.ch")
  99. intArrayBuildConfigField("CHAT_SERVER_PORTS", intArrayOf(5222, 443))
  100. stringBuildConfigField("MEDIA_PATH", "Threema")
  101. booleanBuildConfigField("CHAT_SERVER_GROUPS", true)
  102. booleanBuildConfigField("DISABLE_CERT_PINNING", false)
  103. booleanBuildConfigField("VIDEO_CALLS_ENABLED", true)
  104. // This public key is pinned for the chat server protocol.
  105. byteArrayBuildConfigField("SERVER_PUBKEY", PublicKeys.prodServer)
  106. byteArrayBuildConfigField("SERVER_PUBKEY_ALT", PublicKeys.prodServerAlt)
  107. stringBuildConfigField("GIT_HASH", getGitHash())
  108. stringBuildConfigField("GIT_BRANCH", getGitBranch())
  109. stringBuildConfigField("DIRECTORY_SERVER_URL", "https://apip.threema.ch/")
  110. stringBuildConfigField("DIRECTORY_SERVER_IPV6_URL", "https://ds-apip.threema.ch/")
  111. stringBuildConfigField("WORK_SERVER_URL", null)
  112. stringBuildConfigField("WORK_SERVER_IPV6_URL", null)
  113. stringBuildConfigField("MEDIATOR_SERVER_URL", "wss://mediator-{deviceGroupIdPrefix4}.threema.ch/{deviceGroupIdPrefix8}")
  114. // Base blob url used for "download" and "done" calls
  115. stringBuildConfigField("BLOB_SERVER_URL", "https://blobp-{blobIdPrefix}.threema.ch")
  116. stringBuildConfigField("BLOB_SERVER_IPV6_URL", "https://ds-blobp-{blobIdPrefix}.threema.ch")
  117. // Specific blob url used for "upload" calls
  118. stringBuildConfigField("BLOB_SERVER_URL_UPLOAD", "https://blobp-upload.threema.ch/upload")
  119. stringBuildConfigField("BLOB_SERVER_IPV6_URL_UPLOAD", "https://ds-blobp-upload.threema.ch/upload")
  120. // Base blob mirror url used for "download", "upload", "done"
  121. stringBuildConfigField("BLOB_MIRROR_SERVER_URL", "https://blob-mirror-{deviceGroupIdPrefix4}.threema.ch/{deviceGroupIdPrefix8}")
  122. stringBuildConfigField("AVATAR_FETCH_URL", "https://avatar.threema.ch/")
  123. stringBuildConfigField("SAFE_SERVER_URL", "https://safe-{backupIdPrefix8}.threema.ch/")
  124. stringBuildConfigField("WEB_SERVER_URL", "https://web.threema.ch/")
  125. stringBuildConfigField("APP_RATING_URL", "https://threema.com/app-rating/android/{rating}")
  126. stringBuildConfigField("MAP_STYLES_URL", "https://map.threema.ch/styles/threema/style.json")
  127. stringBuildConfigField("MAP_POI_AROUND_URL", "https://poi.threema.ch/around/{latitude}/{longitude}/{radius}/")
  128. stringBuildConfigField("MAP_POI_NAMES_URL", "https://poi.threema.ch/names/{latitude}/{longitude}/{query}/")
  129. byteArrayBuildConfigField("THREEMA_PUSH_PUBLIC_KEY", PublicKeys.threemaPush)
  130. stringBuildConfigField("ONPREM_ID_PREFIX", "O")
  131. stringBuildConfigField("LOG_TAG", "3ma")
  132. stringBuildConfigField("DEFAULT_APP_THEME", "2")
  133. stringArrayBuildConfigField("ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", emptyArray())
  134. booleanBuildConfigField("MD_SYNC_DISTRIBUTION_LISTS", false)
  135. booleanBuildConfigField("AVAILABILITY_STATUS_ENABLED", BuildFeatureFlags["availability_status"] ?: false)
  136. booleanBuildConfigField("CRASH_REPORTING_SUPPORTED", BuildFeatureFlags["crash_reporting"] ?: false)
  137. // TODO(ANDR-4376): Remove this build flag
  138. booleanBuildConfigField("REFERRAL_PROGRAM_AVAILABLE", BuildFeatureFlags["referral_program_available"] ?: false)
  139. // config fields for action URLs / deep links
  140. stringBuildConfigField("uriScheme", "threema")
  141. stringBuildConfigField("actionUrl", "go.threema.ch")
  142. stringBuildConfigField("contactActionUrl", "threema.id")
  143. // The OPPF url must be null in the default config. Do not change this.
  144. stringBuildConfigField("PRESET_OPPF_URL", null)
  145. with(manifestPlaceholders) {
  146. put("uriScheme", "threema")
  147. put("contactActionUrl", "threema.id")
  148. put("actionUrl", "go.threema.ch")
  149. put("callMimeType", "vnd.android.cursor.item/vnd.$applicationId.call")
  150. }
  151. testInstrumentationRunner = "$applicationId.ThreemaTestRunner"
  152. // Only include language resources for those languages
  153. androidResources.localeFilters.addAll(
  154. setOf(
  155. "en",
  156. "be-rBY",
  157. "bg",
  158. "ca",
  159. "cs",
  160. "de",
  161. "es",
  162. "fr",
  163. "gsw",
  164. "hu",
  165. "it",
  166. "ja",
  167. "nl-rNL",
  168. "no",
  169. "pl",
  170. "pt-rBR",
  171. "ru",
  172. "sk",
  173. "tr",
  174. "uk",
  175. "zh-rCN",
  176. "zh-rTW",
  177. ),
  178. )
  179. }
  180. splits {
  181. abi {
  182. isEnable = true
  183. reset()
  184. if (project.hasProperty("noAbiSplits")) {
  185. isUniversalApk = true
  186. } else {
  187. include("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
  188. isUniversalApk = project.hasProperty("buildUniversalApk")
  189. }
  190. }
  191. }
  192. // Assign different version code for each output
  193. android.applicationVariants.all {
  194. outputs.all {
  195. if (this is ApkVariantOutputImpl) {
  196. val abi = getFilter("ABI")
  197. val abiVersionCode = when (abi) {
  198. "armeabi-v7a" -> 2
  199. "arm64-v8a" -> 3
  200. "x86" -> 8
  201. "x86_64" -> 9
  202. else -> 0
  203. }
  204. versionCodeOverride = abiVersionCode * 1_000_000 + defaultVersionCode
  205. }
  206. }
  207. }
  208. namespace = "ch.threema.app"
  209. flavorDimensions.add("default")
  210. productFlavors {
  211. create("none")
  212. create("store_google")
  213. create("store_threema") {
  214. stringResValue("shop_download_filename", "Threema-update.apk")
  215. }
  216. create("store_google_work") {
  217. versionName = "${appVersion}k$betaSuffix"
  218. applicationId = "ch.threema.app.work"
  219. testApplicationId = "$applicationId.test"
  220. setProductNames(appName = "Threema Work")
  221. stringResValue("package_name", applicationId!!)
  222. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.$applicationId.profile")
  223. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.$applicationId.call")
  224. stringBuildConfigField("CHAT_SERVER_PREFIX", "w-")
  225. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "ds.w-")
  226. stringBuildConfigField("MEDIA_PATH", "ThreemaWork")
  227. stringBuildConfigField("WORK_SERVER_URL", "https://apip-work.threema.ch/")
  228. stringBuildConfigField("WORK_SERVER_IPV6_URL", "https://ds-apip-work.threema.ch/")
  229. stringBuildConfigField("APP_RATING_URL", "https://threema.com/app-rating/android-work/{rating}")
  230. stringBuildConfigField("LOG_TAG", "3mawrk")
  231. stringBuildConfigField("DEFAULT_APP_THEME", "2")
  232. // config fields for action URLs / deep links
  233. stringBuildConfigField("uriScheme", "threemawork")
  234. stringBuildConfigField("actionUrl", "work.threema.ch")
  235. with(manifestPlaceholders) {
  236. put("uriScheme", "threemawork")
  237. put("actionUrl", "work.threema.ch")
  238. put("callMimeType", "vnd.android.cursor.item/vnd.$applicationId.call")
  239. }
  240. }
  241. create("green") {
  242. applicationId = "ch.threema.app.green"
  243. testApplicationId = "$applicationId.test"
  244. setProductNames(appName = "Threema Green")
  245. stringResValue("package_name", applicationId!!)
  246. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.$applicationId.profile")
  247. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.$applicationId.call")
  248. stringBuildConfigField("MEDIA_PATH", "ThreemaGreen")
  249. stringBuildConfigField("CHAT_SERVER_SUFFIX", ".0.test.threema.ch")
  250. // This public key is pinned for the chat server protocol.
  251. byteArrayBuildConfigField("SERVER_PUBKEY", PublicKeys.sandboxServer)
  252. byteArrayBuildConfigField("SERVER_PUBKEY_ALT", PublicKeys.sandboxServer)
  253. stringBuildConfigField("DIRECTORY_SERVER_URL", "https://apip.test.threema.ch/")
  254. stringBuildConfigField("DIRECTORY_SERVER_IPV6_URL", "https://ds-apip.test.threema.ch/")
  255. stringBuildConfigField("MEDIATOR_SERVER_URL", "wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  256. stringBuildConfigField("BLOB_SERVER_URL", "https://blobp-{blobIdPrefix}.test.threema.ch")
  257. stringBuildConfigField("BLOB_SERVER_IPV6_URL", "https://ds-blobp-{blobIdPrefix}.test.threema.ch")
  258. stringBuildConfigField("BLOB_SERVER_URL_UPLOAD", "https://blobp-upload.test.threema.ch/upload")
  259. stringBuildConfigField("BLOB_SERVER_IPV6_URL_UPLOAD", "https://ds-blobp-upload.test.threema.ch/upload")
  260. stringBuildConfigField("AVATAR_FETCH_URL", "https://avatar.test.threema.ch/")
  261. stringBuildConfigField("APP_RATING_URL", "https://test.threema.com/app-rating/android/{rating}")
  262. stringBuildConfigField("MAP_STYLES_URL", "https://map.test.threema.ch/styles/threema/style.json")
  263. stringBuildConfigField("MAP_POI_AROUND_URL", "https://poi.test.threema.ch/around/{latitude}/{longitude}/{radius}/")
  264. stringBuildConfigField("MAP_POI_NAMES_URL", "https://poi.test.threema.ch/names/{latitude}/{longitude}/{query}/")
  265. stringBuildConfigField("BLOB_MIRROR_SERVER_URL", "https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  266. booleanBuildConfigField("CRASH_REPORTING_SUPPORTED", true)
  267. // TODO(ANDR-4376): Remove this build flag
  268. booleanBuildConfigField("REFERRAL_PROGRAM_AVAILABLE", true)
  269. }
  270. create("sandbox_work") {
  271. versionName = "${appVersion}k$betaSuffix"
  272. applicationId = "ch.threema.app.sandbox.work"
  273. testApplicationId = "$applicationId.test"
  274. setProductNames(
  275. appName = "Threema Sandbox Work",
  276. appNameDesktop = "Threema Blue",
  277. )
  278. stringResValue("package_name", applicationId!!)
  279. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.$applicationId.profile")
  280. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.$applicationId.call")
  281. stringBuildConfigField("CHAT_SERVER_PREFIX", "w-")
  282. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "ds.w-")
  283. stringBuildConfigField("CHAT_SERVER_SUFFIX", ".0.test.threema.ch")
  284. stringBuildConfigField("MEDIA_PATH", "ThreemaWorkSandbox")
  285. // This public key is pinned for the chat server protocol.
  286. byteArrayBuildConfigField("SERVER_PUBKEY", PublicKeys.sandboxServer)
  287. byteArrayBuildConfigField("SERVER_PUBKEY_ALT", PublicKeys.sandboxServer)
  288. stringBuildConfigField("DIRECTORY_SERVER_URL", "https://apip.test.threema.ch/")
  289. stringBuildConfigField("DIRECTORY_SERVER_IPV6_URL", "https://ds-apip.test.threema.ch/")
  290. stringBuildConfigField("WORK_SERVER_URL", "https://apip-work.test.threema.ch/")
  291. stringBuildConfigField("WORK_SERVER_IPV6_URL", "https://ds-apip-work.test.threema.ch/")
  292. stringBuildConfigField("MEDIATOR_SERVER_URL", "wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  293. stringBuildConfigField("BLOB_SERVER_URL", "https://blobp-{blobIdPrefix}.test.threema.ch")
  294. stringBuildConfigField("BLOB_SERVER_IPV6_URL", "https://ds-blobp-{blobIdPrefix}.test.threema.ch")
  295. stringBuildConfigField("BLOB_SERVER_URL_UPLOAD", "https://blobp-upload.test.threema.ch/upload")
  296. stringBuildConfigField("BLOB_SERVER_IPV6_URL_UPLOAD", "https://ds-blobp-upload.test.threema.ch/upload")
  297. stringBuildConfigField("AVATAR_FETCH_URL", "https://avatar.test.threema.ch/")
  298. stringBuildConfigField("APP_RATING_URL", "https://test.threema.com/app-rating/android-work/{rating}")
  299. stringBuildConfigField("MAP_STYLES_URL", "https://map.test.threema.ch/styles/threema/style.json")
  300. stringBuildConfigField("MAP_POI_AROUND_URL", "https://poi.test.threema.ch/around/{latitude}/{longitude}/{radius}/")
  301. stringBuildConfigField("MAP_POI_NAMES_URL", "https://poi.test.threema.ch/names/{latitude}/{longitude}/{query}/")
  302. stringBuildConfigField("LOG_TAG", "3mawrk")
  303. stringBuildConfigField("DEFAULT_APP_THEME", "2")
  304. stringBuildConfigField("BLOB_MIRROR_SERVER_URL", "https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  305. // config fields for action URLs / deep links
  306. stringBuildConfigField("uriScheme", "threemawork")
  307. stringBuildConfigField("actionUrl", "work.test.threema.ch")
  308. booleanBuildConfigField("CRASH_REPORTING_SUPPORTED", true)
  309. stringBuildConfigField("MD_CLIENT_DOWNLOAD_URL", "https://three.ma/mdw")
  310. with(manifestPlaceholders) {
  311. put("uriScheme", "threemawork")
  312. put("actionUrl", "work.test.threema.ch")
  313. }
  314. }
  315. create("onprem") {
  316. versionName = "${appVersion}o$betaSuffix"
  317. applicationId = "ch.threema.app.onprem"
  318. testApplicationId = "$applicationId.test"
  319. setProductNames(
  320. appName = "Threema OnPrem",
  321. shortAppName = "Threema",
  322. companyName = "Threema",
  323. )
  324. stringResValue("package_name", applicationId!!)
  325. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.$applicationId.profile")
  326. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.$applicationId.call")
  327. intBuildConfigField("MAX_GROUP_SIZE", 256)
  328. stringBuildConfigField("CHAT_SERVER_PREFIX", "")
  329. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "")
  330. stringBuildConfigField("CHAT_SERVER_SUFFIX", null)
  331. stringBuildConfigField("MEDIA_PATH", "ThreemaOnPrem")
  332. booleanBuildConfigField("CHAT_SERVER_GROUPS", false)
  333. byteArrayBuildConfigField("SERVER_PUBKEY", null)
  334. byteArrayBuildConfigField("SERVER_PUBKEY_ALT", null)
  335. stringBuildConfigField("DIRECTORY_SERVER_URL", null)
  336. stringBuildConfigField("DIRECTORY_SERVER_IPV6_URL", null)
  337. stringBuildConfigField("BLOB_SERVER_URL", null)
  338. stringBuildConfigField("BLOB_SERVER_IPV6_URL", null)
  339. stringBuildConfigField("BLOB_SERVER_URL_UPLOAD", null)
  340. stringBuildConfigField("BLOB_SERVER_IPV6_URL_UPLOAD", null)
  341. stringBuildConfigField("BLOB_MIRROR_SERVER_URL", null)
  342. stringArrayBuildConfigField("ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", PublicKeys.onPremTrusted)
  343. stringBuildConfigField("LOG_TAG", "3maop")
  344. // config fields for action URLs / deep links
  345. val uriScheme = "threemaonprem"
  346. val actionUrl = "onprem.threema.ch"
  347. stringBuildConfigField("uriScheme", uriScheme)
  348. stringBuildConfigField("actionUrl", actionUrl)
  349. stringBuildConfigField("PRESET_OPPF_URL", null)
  350. stringBuildConfigField("MD_CLIENT_DOWNLOAD_URL", "https://three.ma/mdo")
  351. with(manifestPlaceholders) {
  352. put("uriScheme", uriScheme)
  353. put("actionUrl", actionUrl)
  354. put("callMimeType", "vnd.android.cursor.item/vnd.$applicationId.call")
  355. }
  356. }
  357. create("blue") {
  358. // Essentially like sandbox work, but with a different icon and application id, used for internal testing
  359. versionName = "${appVersion}b$betaSuffix"
  360. // The app was previously named `red`. The app id remains unchanged to still be able to install updates.
  361. applicationId = "ch.threema.app.red"
  362. testApplicationId = "ch.threema.app.blue.test"
  363. setProductNames(appName = "Threema Blue")
  364. stringResValue("package_name", applicationId!!)
  365. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.blue.profile")
  366. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.blue.call")
  367. stringBuildConfigField("CHAT_SERVER_PREFIX", "w-")
  368. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "ds.w-")
  369. stringBuildConfigField("CHAT_SERVER_SUFFIX", ".0.test.threema.ch")
  370. stringBuildConfigField("MEDIA_PATH", "ThreemaBlue")
  371. // This public key is pinned for the chat server protocol.
  372. byteArrayBuildConfigField("SERVER_PUBKEY", PublicKeys.sandboxServer)
  373. byteArrayBuildConfigField("SERVER_PUBKEY_ALT", PublicKeys.sandboxServer)
  374. stringBuildConfigField("DIRECTORY_SERVER_URL", "https://apip.test.threema.ch/")
  375. stringBuildConfigField("DIRECTORY_SERVER_IPV6_URL", "https://ds-apip.test.threema.ch/")
  376. stringBuildConfigField("WORK_SERVER_URL", "https://apip-work.test.threema.ch/")
  377. stringBuildConfigField("WORK_SERVER_IPV6_URL", "https://ds-apip-work.test.threema.ch/")
  378. stringBuildConfigField("MEDIATOR_SERVER_URL", "wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  379. stringBuildConfigField("BLOB_SERVER_URL", "https://blobp-{blobIdPrefix}.test.threema.ch")
  380. stringBuildConfigField("BLOB_SERVER_IPV6_URL", "https://ds-blobp-{blobIdPrefix}.test.threema.ch")
  381. stringBuildConfigField("BLOB_SERVER_URL_UPLOAD", "https://blobp-upload.test.threema.ch/upload")
  382. stringBuildConfigField("BLOB_SERVER_IPV6_URL_UPLOAD", "https://ds-blobp-upload.test.threema.ch/upload")
  383. stringBuildConfigField("AVATAR_FETCH_URL", "https://avatar.test.threema.ch/")
  384. stringBuildConfigField("APP_RATING_URL", "https://test.threema.com/app-rating/android-work/{rating}")
  385. stringBuildConfigField("MAP_STYLES_URL", "https://map.test.threema.ch/styles/threema/style.json")
  386. stringBuildConfigField("MAP_POI_AROUND_URL", "https://poi.test.threema.ch/around/{latitude}/{longitude}/{radius}/")
  387. stringBuildConfigField("MAP_POI_NAMES_URL", "https://poi.test.threema.ch/names/{latitude}/{longitude}/{query}/")
  388. stringBuildConfigField("LOG_TAG", "3mablue")
  389. stringBuildConfigField("BLOB_MIRROR_SERVER_URL", "https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}")
  390. booleanBuildConfigField("CRASH_REPORTING_SUPPORTED", true)
  391. // config fields for action URLs / deep links
  392. stringBuildConfigField("uriScheme", "threemablue")
  393. stringBuildConfigField("actionUrl", "blue.threema.ch")
  394. with(manifestPlaceholders) {
  395. put("uriScheme", "threemablue")
  396. put("actionUrl", "blue.threema.ch")
  397. put("callMimeType", "vnd.android.cursor.item/vnd.ch.threema.app.blue.call")
  398. }
  399. }
  400. create("hms") {
  401. applicationId = "ch.threema.app.hms"
  402. }
  403. create("hms_work") {
  404. versionName = "${appVersion}k$betaSuffix"
  405. applicationId = "ch.threema.app.work.hms"
  406. testApplicationId = "ch.threema.app.work.test.hms"
  407. setProductNames(appName = "Threema Work")
  408. stringResValue("package_name", "ch.threema.app.work")
  409. stringResValue("contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.profile")
  410. stringResValue("call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.call")
  411. stringBuildConfigField("CHAT_SERVER_PREFIX", "w-")
  412. stringBuildConfigField("CHAT_SERVER_IPV6_PREFIX", "ds.w-")
  413. stringBuildConfigField("MEDIA_PATH", "ThreemaWork")
  414. stringBuildConfigField("WORK_SERVER_URL", "https://apip-work.threema.ch/")
  415. stringBuildConfigField("WORK_SERVER_IPV6_URL", "https://ds-apip-work.threema.ch/")
  416. stringBuildConfigField("APP_RATING_URL", "https://threema.com/app-rating/android-work/{rating}")
  417. stringBuildConfigField("LOG_TAG", "3mawrk")
  418. stringBuildConfigField("DEFAULT_APP_THEME", "2")
  419. // config fields for action URLs / deep links
  420. stringBuildConfigField("uriScheme", "threemawork")
  421. stringBuildConfigField("actionUrl", "work.threema.ch")
  422. with(manifestPlaceholders) {
  423. put("uriScheme", "threemawork")
  424. put("actionUrl", "work.threema.ch")
  425. put("callMimeType", "vnd.android.cursor.item/vnd.ch.threema.app.work.call")
  426. }
  427. }
  428. create("libre") {
  429. versionName = "${appVersion}l$betaSuffix"
  430. applicationId = "ch.threema.app.libre"
  431. testApplicationId = "$applicationId.test"
  432. stringResValue("package_name", applicationId!!)
  433. setProductNames(
  434. appName = "Threema Libre",
  435. appNameDesktop = "Threema",
  436. )
  437. stringBuildConfigField("MEDIA_PATH", "ThreemaLibre")
  438. }
  439. }
  440. signingConfigs {
  441. // Debug config
  442. keystores["debug"]
  443. ?.let { keystore ->
  444. getByName("debug") {
  445. storeFile = keystore.storeFile
  446. }
  447. }
  448. ?: run {
  449. logger.warn("No debug keystore found. Falling back to locally generated keystore.")
  450. }
  451. // Release config
  452. keystores["release"]
  453. ?.let { keystore ->
  454. create("release") {
  455. apply(keystore)
  456. }
  457. }
  458. ?: run {
  459. logger.warn("No release keystore found. Falling back to locally generated keystore.")
  460. }
  461. // Release config
  462. keystores["hms_release"]
  463. ?.let { keystore ->
  464. create("hms_release") {
  465. apply(keystore)
  466. }
  467. }
  468. ?: run {
  469. logger.warn("No hms keystore found. Falling back to locally generated keystore.")
  470. }
  471. // Onprem release config
  472. keystores["onprem_release"]
  473. ?.let { keystore ->
  474. create("onprem_release") {
  475. apply(keystore)
  476. }
  477. }
  478. ?: run {
  479. logger.warn("No onprem keystore found. Falling back to locally generated keystore.")
  480. }
  481. // Blue release config
  482. keystores["blue_release"]
  483. ?.let { keystore ->
  484. create("blue_release") {
  485. apply(keystore)
  486. }
  487. }
  488. ?: run {
  489. logger.warn("No blue keystore found. Falling back to locally generated keystore.")
  490. }
  491. // Note: Libre release is signed with HSM, no config here
  492. }
  493. sourceSets {
  494. getByName("main") {
  495. assets.srcDirs("assets")
  496. jniLibs.srcDirs("libs")
  497. res.srcDir("src/main/res-rendezvous")
  498. java.srcDir("./build/generated/source/protobuf/main/java")
  499. java.srcDir("./build/generated/source/protobuf/main/kotlin")
  500. }
  501. // Based on Google services
  502. getByName("none") {
  503. java.srcDir("src/google_services_based/java")
  504. }
  505. getByName("store_google") {
  506. java.srcDir("src/google_services_based/java")
  507. }
  508. getByName("store_google_work") {
  509. java.srcDir("src/google_services_based/java")
  510. }
  511. getByName("store_threema") {
  512. java.srcDir("src/google_services_based/java")
  513. }
  514. getByName("libre") {
  515. assets.srcDirs("src/foss_based/assets")
  516. java.srcDir("src/foss_based/java")
  517. }
  518. getByName("onprem") {
  519. java.srcDir("src/google_services_based/java")
  520. }
  521. getByName("green") {
  522. java.srcDir("src/google_services_based/java")
  523. manifest.srcFile("src/store_google/AndroidManifest.xml")
  524. }
  525. getByName("sandbox_work") {
  526. java.srcDir("src/google_services_based/java")
  527. res.srcDir("src/store_google_work/res")
  528. manifest.srcFile("src/store_google_work/AndroidManifest.xml")
  529. }
  530. getByName("blue") {
  531. java.srcDir("src/google_services_based/java")
  532. res.srcDir("src/blue/res")
  533. }
  534. // Based on Huawei services
  535. getByName("hms") {
  536. java.srcDir("src/hms_services_based/java")
  537. }
  538. getByName("hms_work") {
  539. java.srcDir("src/hms_services_based/java")
  540. res.srcDir("src/store_google_work/res")
  541. }
  542. // FOSS, no proprietary services
  543. getByName("libre") {
  544. assets.srcDirs("src/foss_based/assets")
  545. java.srcDir("src/foss_based/java")
  546. }
  547. }
  548. buildTypes {
  549. debug {
  550. isDebuggable = true
  551. ndk {
  552. debugSymbolLevel = "FULL"
  553. }
  554. enableUnitTestCoverage = false
  555. enableAndroidTestCoverage = false
  556. if (keystores["debug"] != null) {
  557. signingConfig = signingConfigs["debug"]
  558. }
  559. }
  560. release {
  561. isDebuggable = false
  562. isMinifyEnabled = true
  563. isShrinkResources = false // Caused inconsistencies between local and CI builds
  564. vcsInfo.include = false // For reproducible builds independent from git history
  565. proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-project.txt")
  566. ndk {
  567. debugSymbolLevel = "FULL" // 'SYMBOL_TABLE'
  568. }
  569. if (keystores["release"] != null) {
  570. val releaseSigningConfig = signingConfigs["release"]
  571. productFlavors["store_google"].signingConfig = releaseSigningConfig
  572. productFlavors["store_google_work"].signingConfig = releaseSigningConfig
  573. productFlavors["store_threema"].signingConfig = releaseSigningConfig
  574. productFlavors["green"].signingConfig = releaseSigningConfig
  575. productFlavors["sandbox_work"].signingConfig = releaseSigningConfig
  576. productFlavors["none"].signingConfig = releaseSigningConfig
  577. }
  578. if (keystores["hms_release"] != null) {
  579. val hmsReleaseSigningConfig = signingConfigs["hms_release"]
  580. productFlavors["hms"].signingConfig = hmsReleaseSigningConfig
  581. productFlavors["hms_work"].signingConfig = hmsReleaseSigningConfig
  582. }
  583. if (keystores["onprem_release"] != null) {
  584. productFlavors["onprem"].signingConfig = signingConfigs["onprem_release"]
  585. }
  586. if (keystores["blue_release"] != null) {
  587. productFlavors["blue"].signingConfig = signingConfigs["blue_release"]
  588. }
  589. // Note: Libre release is signed with HSM, no config here
  590. }
  591. }
  592. packaging {
  593. jniLibs {
  594. // replacement for extractNativeLibs in AndroidManifest
  595. useLegacyPackaging = true
  596. }
  597. resources {
  598. excludes.addAll(
  599. setOf(
  600. "META-INF/DEPENDENCIES.txt",
  601. "META-INF/LICENSE.txt",
  602. "META-INF/LICENSE.md",
  603. "META-INF/LICENSE-notice.md",
  604. "META-INF/NOTICE.txt",
  605. "META-INF/NOTICE",
  606. "META-INF/LICENSE",
  607. "META-INF/DEPENDENCIES",
  608. "META-INF/notice.txt",
  609. "META-INF/license.txt",
  610. "META-INF/dependencies.txt",
  611. "META-INF/LGPL2.1",
  612. "**/*.proto",
  613. "DebugProbesKt.bin",
  614. ),
  615. )
  616. }
  617. }
  618. testOptions {
  619. // Disable animations in instrumentation tests
  620. animationsDisabled = true
  621. unitTests {
  622. all { test ->
  623. test.outputs.upToDateWhen { false }
  624. test.testLogging {
  625. events("passed", "skipped", "failed", "standardOut", "standardError")
  626. exceptionFormat = TestExceptionFormat.FULL
  627. }
  628. test.jvmArgs = test.jvmArgs!! + listOf(
  629. "--add-opens=java.base/java.util=ALL-UNNAMED",
  630. "--add-opens=java.base/java.util.stream=ALL-UNNAMED",
  631. "--add-opens=java.base/java.lang=ALL-UNNAMED",
  632. "-Xmx4096m",
  633. )
  634. }
  635. // By default, local unit tests throw an exception any time the code you are testing tries to access
  636. // Android platform APIs (unless you mock Android dependencies yourself or with a testing
  637. // framework like Mockk). However, you can enable the following property so that the test
  638. // returns either null or zero when accessing platform APIs, rather than throwing an exception.
  639. isReturnDefaultValues = true
  640. }
  641. }
  642. compileOptions {
  643. isCoreLibraryDesugaringEnabled = true
  644. sourceCompatibility = JavaVersion.VERSION_11
  645. targetCompatibility = JavaVersion.VERSION_11
  646. }
  647. java {
  648. toolchain {
  649. languageVersion.set(JavaLanguageVersion.of(17))
  650. }
  651. }
  652. kotlin {
  653. jvmToolchain(17)
  654. }
  655. androidResources {
  656. noCompress.add("png")
  657. }
  658. lint {
  659. // if true, stop the gradle build if errors are found
  660. abortOnError = true
  661. // if true, check all issues, including those that are off by default
  662. checkAllWarnings = true
  663. // check dependencies
  664. checkDependencies = true
  665. // set to true to have all release builds run lint on issues with severity=fatal
  666. // and abort the build (controlled by abortOnError above) if fatal issues are found
  667. checkReleaseBuilds = true
  668. // turn off checking the given issue id's
  669. disable.addAll(setOf("TypographyFractions", "TypographyQuotes", "RtlHardcoded", "RtlCompat", "RtlEnabled"))
  670. // Set the severity of the given issues to error
  671. error.addAll(setOf("Wakelock", "TextViewEdits", "ResourceAsColor"))
  672. // Set the severity of the given issues to fatal (which means they will be
  673. // checked during release builds (even if the lint target is not included)
  674. fatal.addAll(setOf("NewApi", "InlinedApi", "LoggerName"))
  675. ignoreWarnings = false
  676. // if true, don't include source code lines in the error output
  677. noLines = false
  678. // if true, show all locations for an error, do not truncate lists, etc.
  679. showAll = true
  680. // Set the severity of the given issues to warning
  681. warning.add("MissingTranslation")
  682. // if true, treat all warnings as errors
  683. warningsAsErrors = false
  684. // file to write report to (if not specified, defaults to lint-results.xml)
  685. xmlOutput = file("lint-report.xml")
  686. // if true, generate an XML report for use by for example Jenkins
  687. xmlReport = true
  688. }
  689. buildFeatures {
  690. compose = true
  691. buildConfig = true
  692. }
  693. }
  694. composeCompiler {
  695. includeSourceInformation = true
  696. stabilityConfigurationFiles.add(rootProject.layout.projectDirectory.file("stability_config.conf"))
  697. }
  698. // Only build relevant buildType / flavor combinations
  699. androidComponents {
  700. beforeVariants { variant ->
  701. val name = variant.name
  702. if (variant.buildType == "release" && ("green" in name || "sandbox_work" in name)) {
  703. variant.enable = false
  704. }
  705. }
  706. }
  707. dependencies {
  708. configurations.all {
  709. // Prefer modules that are part of this build (multi-project or composite build)
  710. // over external modules
  711. resolutionStrategy.preferProjectModules()
  712. // Alternatively, we can fail eagerly on version conflict to see the conflicts
  713. // resolutionStrategy.failOnVersionConflict()
  714. }
  715. coreLibraryDesugaring(libs.desugarJdkLibs)
  716. implementation(project(":domain"))
  717. implementation(project("::commonAndroid"))
  718. implementation(project(":common"))
  719. lintChecks(project(":lint-rules"))
  720. // Dependency Injection
  721. implementation(libs.koin.android)
  722. implementation(libs.koin.androidCompat)
  723. implementation(libs.koin.compose)
  724. testImplementation(libs.koin.test)
  725. testImplementation(libs.koin.test.junit4)
  726. implementation(libs.sqlcipher.android)
  727. implementation(libs.subsamplingScaleImageView)
  728. implementation(libs.opencsv)
  729. implementation(libs.zip4j)
  730. implementation(libs.taptargetview)
  731. implementation(libs.commonsIo)
  732. implementation(libs.slf4j.api)
  733. implementation(libs.androidImageCropper)
  734. implementation(libs.fastscroll)
  735. implementation(libs.ezVcard)
  736. implementation(libs.gestureViews)
  737. // AndroidX / Jetpack support libraries
  738. implementation(libs.androidx.preference)
  739. implementation(libs.androidx.recyclerview)
  740. implementation(libs.androidx.palette)
  741. implementation(libs.androidx.swiperefreshlayout)
  742. implementation(libs.androidx.core)
  743. implementation(libs.androidx.appcompat)
  744. implementation(libs.androidx.constraintlayout)
  745. implementation(libs.androidx.biometric)
  746. implementation(libs.androidx.work.runtime)
  747. implementation(libs.androidx.fragment)
  748. implementation(libs.androidx.activity)
  749. implementation(libs.androidx.sqlite)
  750. implementation(libs.androidx.concurrent.futures)
  751. implementation(libs.androidx.camera2)
  752. implementation(libs.androidx.camera.lifecycle)
  753. implementation(libs.androidx.camera.view)
  754. implementation(libs.androidx.camera.video)
  755. implementation(libs.androidx.media)
  756. implementation(libs.androidx.media3.exoplayer)
  757. implementation(libs.androidx.media3.ui)
  758. implementation(libs.androidx.media3.session)
  759. implementation(libs.androidx.lifecycle.viewmodel)
  760. implementation(libs.androidx.lifecycle.livedata)
  761. implementation(libs.androidx.lifecycle.runtime)
  762. implementation(libs.androidx.lifecycle.viewmodel.savedstate)
  763. implementation(libs.androidx.lifecycle.service)
  764. implementation(libs.androidx.lifecycle.process)
  765. implementation(libs.androidx.lifecycle.commonJava8)
  766. implementation(libs.androidx.lifecycle.extensions)
  767. implementation(libs.androidx.paging.runtime)
  768. implementation(libs.androidx.sharetarget)
  769. implementation(libs.androidx.room.runtime)
  770. implementation(libs.androidx.window)
  771. implementation(libs.androidx.splashscreen)
  772. ksp(libs.androidx.room.compiler)
  773. // Jetpack Compose
  774. implementation(platform(libs.compose.bom))
  775. implementation(libs.androidx.material3)
  776. implementation(libs.androidx.ui.tooling.preview)
  777. implementation(libs.androidx.activity.compose)
  778. implementation(libs.androidx.lifecycle.viewmodel.compose)
  779. implementation(libs.androidx.lifecycle.runtime.compose)
  780. debugImplementation(libs.androidx.ui.tooling)
  781. androidTestImplementation(platform(libs.compose.bom))
  782. implementation(libs.bcprov.jdk15to18)
  783. implementation(libs.material)
  784. implementation(libs.zxing)
  785. implementation(libs.libphonenumber)
  786. // webclient dependencies
  787. implementation(libs.msgpack.core)
  788. implementation(libs.jackson.core)
  789. implementation(libs.nvWebsocket.client)
  790. implementation(libs.streamsupport.cfuture)
  791. implementation(libs.saltyrtc.client) {
  792. exclude(group = "org.json")
  793. }
  794. implementation(libs.chunkedDc)
  795. implementation(libs.webrtcAndroid)
  796. implementation(libs.saltyrtc.taskWebrtc) {
  797. exclude(module = "saltyrtc-client")
  798. }
  799. // Glide components
  800. implementation(libs.glide)
  801. ksp(libs.glide.ksp)
  802. // Kotlin
  803. implementation(libs.kotlin.stdlib)
  804. implementation(libs.kotlinx.coroutines.android)
  805. implementation(libs.kotlinx.serialization.json)
  806. testImplementation(libs.kotlin.test)
  807. androidTestImplementation(libs.kotlin.test)
  808. // use leak canary in debug builds if requested
  809. if (project.hasProperty("leakCanary")) {
  810. debugImplementation(libs.leakcanary)
  811. }
  812. // test dependencies
  813. testImplementation(libs.junit)
  814. testImplementation(testFixtures(project(":domain")))
  815. // custom test helpers, shared between unit test and android tests
  816. testImplementation(project(":test-helpers"))
  817. androidTestImplementation(project(":test-helpers"))
  818. testImplementation(libs.mockk)
  819. androidTestImplementation(libs.mockkAndroid)
  820. // add JSON support to tests without mocking
  821. testImplementation(libs.json)
  822. testImplementation(libs.archunit.junit4)
  823. androidTestImplementation(testFixtures(project(":domain")))
  824. androidTestImplementation(libs.androidx.test.rules)
  825. androidTestImplementation(libs.fastlane.screengrab) {
  826. exclude(group = "androidx.annotation", module = "annotation")
  827. }
  828. androidTestImplementation(libs.androidx.espresso.core) {
  829. exclude(group = "androidx.annotation", module = "annotation")
  830. }
  831. androidTestImplementation(libs.androidx.test.runner) {
  832. exclude(group = "androidx.annotation", module = "annotation")
  833. }
  834. androidTestImplementation(libs.androidx.junit)
  835. androidTestImplementation(libs.androidx.espresso.contrib) {
  836. exclude(group = "androidx.annotation", module = "annotation")
  837. exclude(group = "androidx.appcompat", module = "appcompat")
  838. exclude(group = "androidx.legacy", module = "legacy-support-v4")
  839. exclude(group = "com.google.android.material", module = "material")
  840. exclude(group = "androidx.recyclerview", module = "recyclerview")
  841. exclude(group = "org.checkerframework", module = "checker")
  842. exclude(module = "protobuf-lite")
  843. }
  844. androidTestImplementation(libs.androidx.espresso.intents) {
  845. exclude(group = "androidx.annotation", module = "annotation")
  846. }
  847. androidTestImplementation(libs.androidx.test.uiautomator)
  848. androidTestImplementation(libs.androidx.test.core)
  849. androidTestImplementation(libs.kotlinx.coroutines.test)
  850. testImplementation(libs.kotlinx.coroutines.test)
  851. // Google Play Services and related libraries
  852. "noneImplementation"(libs.playServices.base)
  853. "store_googleImplementation"(libs.playServices.base)
  854. "store_google_workImplementation"(libs.playServices.base)
  855. "store_threemaImplementation"(libs.playServices.base)
  856. "onpremImplementation"(libs.playServices.base)
  857. "greenImplementation"(libs.playServices.base)
  858. "sandbox_workImplementation"(libs.playServices.base)
  859. "blueImplementation"(libs.playServices.base)
  860. fun ExternalModuleDependency.excludeFirebaseDependencies() {
  861. exclude(group = "com.google.firebase", module = "firebase-core")
  862. exclude(group = "com.google.firebase", module = "firebase-analytics")
  863. exclude(group = "com.google.firebase", module = "firebase-measurement-connector")
  864. }
  865. "noneImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  866. "store_googleImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  867. "store_google_workImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  868. "store_threemaImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  869. "onpremImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  870. "greenImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  871. "sandbox_workImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  872. "blueImplementation"(libs.firebase.messaging) { excludeFirebaseDependencies() }
  873. // Google Assistant Voice Action verification library
  874. "noneImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  875. "store_googleImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  876. "store_google_workImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  877. "onpremImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  878. "store_threemaImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  879. "greenImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  880. "sandbox_workImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  881. "blueImplementation"(group = "", name = "libgsaverification-client", ext = "aar")
  882. // Maplibre (may have transitive dependencies on Google location services)
  883. "noneImplementation"(libs.maplibre)
  884. "store_googleImplementation"(libs.maplibre)
  885. "store_google_workImplementation"(libs.maplibre)
  886. "store_threemaImplementation"(libs.maplibre)
  887. "libreImplementation"(libs.maplibre) {
  888. exclude(group = "com.google.android.gms")
  889. }
  890. "onpremImplementation"(libs.maplibre)
  891. "greenImplementation"(libs.maplibre)
  892. "sandbox_workImplementation"(libs.maplibre)
  893. "blueImplementation"(libs.maplibre)
  894. "hmsImplementation"(libs.maplibre)
  895. "hms_workImplementation"(libs.maplibre)
  896. // Huawei related libraries (only for hms* build variants)
  897. // Exclude agconnect dependency, we'll replace it with the vendored version below
  898. "hmsImplementation"(libs.hmsPush) {
  899. exclude(group = "com.huawei.agconnect")
  900. }
  901. "hms_workImplementation"(libs.hmsPush) {
  902. exclude(group = "com.huawei.agconnect")
  903. }
  904. "hmsImplementation"(group = "", name = "agconnect-core-1.9.1.301", ext = "aar")
  905. "hms_workImplementation"(group = "", name = "agconnect-core-1.9.1.301", ext = "aar")
  906. }
  907. // Define the cargo attributes. These will be used by the rust-android plugin that will create the
  908. // 'cargoBuild' task that builds native libraries that will be added to the apk. Note that the
  909. // kotlin bindings are created in the domain module. Building native libraries with rust-android
  910. // cannot be done in any other module than 'app'.
  911. cargo {
  912. prebuiltToolchains = true
  913. targetDirectory = "$projectDir/build/generated/source/libthreema"
  914. module = "$projectDir/../domain/libthreema" // must contain Cargo.toml
  915. libname = "libthreema" // must match the Cargo.toml's package name
  916. profile = "release"
  917. pythonCommand = "python3"
  918. targets = listOf("x86_64", "arm64", "arm", "x86")
  919. features {
  920. defaultAnd(arrayOf("uniffi"))
  921. }
  922. extraCargoBuildArguments = listOf("--lib", "--target-dir", "$projectDir/build/generated/source/libthreema", "--locked")
  923. verbose = false
  924. }
  925. afterEvaluate {
  926. // The `cargoBuild` task isn't available until after evaluation.
  927. android.applicationVariants.configureEach {
  928. val variantName = name.replaceFirstChar { it.uppercase() }
  929. // Set the dependency so that cargoBuild is executed before the native libs are merged
  930. tasks["merge${variantName}NativeLibs"].dependsOn(tasks["cargoBuild"])
  931. }
  932. }
  933. sonarqube {
  934. properties {
  935. property(
  936. "sonar.sources",
  937. listOf(
  938. "src/main/",
  939. "../scripts/",
  940. "../scripts-internal/",
  941. )
  942. .joinToString(separator = ", "),
  943. )
  944. property(
  945. "sonar.exclusions",
  946. listOf(
  947. "src/**/res/",
  948. "src/**/res-rendezvous/",
  949. "**/emojis/EmojiParser.kt",
  950. "**/emojis/EmojiSpritemap.kt",
  951. )
  952. .joinToString(separator = ", "),
  953. )
  954. property("sonar.tests", "src/test/")
  955. property("sonar.sourceEncoding", "UTF-8")
  956. property("sonar.verbose", "true")
  957. property("sonar.projectKey", "android-client")
  958. property("sonar.projectName", "Threema for Android")
  959. }
  960. }
  961. androidStem {
  962. includeLocalizedOnlyTemplates = true
  963. }
  964. tasks.register<Exec>("compileProto") {
  965. group = "build"
  966. description = "generate class bindings from protobuf files in the 'protobuf' directory"
  967. workingDir(project.projectDir)
  968. commandLine("./compile-proto.sh")
  969. }
  970. project.tasks.preBuild.dependsOn("compileProto")
  971. tasks.withType<Test> {
  972. // Necessary to load the dynamic libthreema library in unit tests
  973. systemProperty("jna.library.path", "${project.projectDir}/../domain/libthreema/target/release")
  974. }
  975. // Set up Gradle tasks to fetch screenshots on UI test failures
  976. // See https://medium.com/stepstone-tech/how-to-capture-screenshots-for-failed-ui-tests-9927eea6e1e4
  977. val reportsDirectory = "${layout.buildDirectory}/reports/androidTests/connected"
  978. val screenshotsDirectory = "/sdcard/testfailures/screenshots/"
  979. val clearScreenshotsTask = tasks.register<Exec>("clearScreenshots") {
  980. executable = android.adbExecutable.toString()
  981. args("shell", "rm", "-r", screenshotsDirectory)
  982. }
  983. val createScreenshotsDirectoryTask = tasks.register<Exec>("createScreenshotsDirectory") {
  984. group = "reporting"
  985. executable = android.adbExecutable.toString()
  986. args("shell", "mkdir", "-p", screenshotsDirectory)
  987. }
  988. val fetchScreenshotsTask = tasks.register<Exec>("fetchScreenshots") {
  989. group = "reporting"
  990. executable = android.adbExecutable.toString()
  991. args("pull", "$screenshotsDirectory.", reportsDirectory)
  992. finalizedBy(clearScreenshotsTask)
  993. dependsOn(createScreenshotsDirectoryTask)
  994. doFirst {
  995. file(reportsDirectory).mkdirs()
  996. }
  997. }
  998. tasks.whenTaskAdded {
  999. if (name == "connectedDebugAndroidTest") {
  1000. finalizedBy(fetchScreenshotsTask)
  1001. }
  1002. }
  1003. // Let the compose compiler generate stability reports
  1004. tasks.withType<KotlinCompile>().configureEach {
  1005. compilerOptions {
  1006. val composeCompilerReportsPath = "${project.layout.buildDirectory.get().dir("compose_conpiler").asFile.absolutePath}/reports"
  1007. freeCompilerArgs.addAll(
  1008. listOf(
  1009. "-P",
  1010. "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$composeCompilerReportsPath",
  1011. ),
  1012. )
  1013. }
  1014. }