build.gradle.kts 50 KB

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