build.gradle.kts 46 KB

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