build.gradle 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs
  2. plugins {
  3. id 'org.sonarqube'
  4. id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
  5. id 'org.mozilla.rust-android-gradle.rust-android' version "0.9.3"
  6. }
  7. apply plugin: 'com.android.application'
  8. apply plugin: 'kotlin-android'
  9. apply plugin: 'kotlin-kapt'
  10. // only apply the plugin if we are dealing with a AppGallery build
  11. if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
  12. println "enabling hms plugin"
  13. apply plugin: 'com.huawei.agconnect'
  14. }
  15. // version codes
  16. // Only use the scheme "<major>.<minor>.<patch>" for the app_version
  17. def app_version = "5.8.0"
  18. // beta_suffix with leading dash (e.g. `-beta1`)
  19. // should be one of (alpha|beta|rc) and an increasing number or empty for a regular release.
  20. // Note: in nightly builds this will be overwritten with a nightly version "-n12345"
  21. def beta_suffix = ""
  22. def defaultVersionCode = 1043
  23. /**
  24. * Return the git hash, if git is installed.
  25. */
  26. def getGitHash = { ->
  27. def stdout = new ByteArrayOutputStream()
  28. def stderr = new ByteArrayOutputStream()
  29. try {
  30. exec {
  31. commandLine 'git', 'rev-parse', '--short', 'HEAD'
  32. standardOutput = stdout
  33. errorOutput = stderr
  34. ignoreExitValue true
  35. }
  36. } catch (ignored) { /* If git binary is not found, carry on */
  37. }
  38. def hash = stdout.toString().trim()
  39. return (hash.isEmpty()) ? "?" : hash
  40. }
  41. /**
  42. * Look up the keystore with the specified name in a `keystore` directory
  43. * adjacent to this project directory. If it exists, return a signing config.
  44. * Otherwise, return null.
  45. */
  46. def findKeystore = { name ->
  47. def basePath = "${projectDir.getAbsolutePath()}/../../keystore"
  48. def storePath = "${basePath}/${name}.keystore"
  49. def storeFile = new File(storePath)
  50. if (storeFile.exists() && storeFile.isFile()) {
  51. def propertiesPath = "${basePath}/${name}.properties"
  52. def propertiesFile = new File(propertiesPath)
  53. if (propertiesFile.exists() && propertiesFile.isFile()) {
  54. Properties props = new Properties()
  55. propertiesFile.withInputStream { props.load(it) }
  56. return [
  57. storeFile : storePath,
  58. storePassword: props.storePassword,
  59. keyAlias : props.keyAlias,
  60. keyPassword : props.keyPassword,
  61. ]
  62. } else {
  63. return [
  64. storeFile : storePath,
  65. storePassword: null,
  66. keyAlias : null,
  67. keyPassword : null,
  68. ]
  69. }
  70. }
  71. }
  72. /**
  73. * Map with keystore paths (if found).
  74. */
  75. def keystores = [
  76. debug : findKeystore("debug"),
  77. release : findKeystore("threema"),
  78. hms_release : findKeystore("threema_hms"),
  79. onprem_release: findKeystore("onprem"),
  80. blue_release : findKeystore("threema_blue"),
  81. ]
  82. android {
  83. // NOTE: When adjusting compileSdkVersion, buildToolsVersion or ndkVersion,
  84. // make sure to adjust them in `scripts/Dockerfile` and
  85. // `.gitlab-ci.yml` as well!
  86. compileSdk 34
  87. buildToolsVersion = '34.0.0'
  88. ndkVersion '25.2.9519653'
  89. defaultConfig {
  90. // https://developer.android.com/training/testing/espresso/setup#analytics
  91. testInstrumentationRunnerArguments notAnnotation: 'ch.threema.app.TestFastlaneOnly,ch.threema.app.DangerousTest', disableAnalytics: 'true'
  92. minSdkVersion 21
  93. //noinspection OldTargetApi
  94. targetSdkVersion 34
  95. vectorDrawables.useSupportLibrary = true
  96. applicationId "ch.threema.app"
  97. testApplicationId 'ch.threema.app.test'
  98. versionCode defaultVersionCode
  99. versionName "${app_version}${beta_suffix}"
  100. resValue "string", "app_name", "Threema"
  101. // package name used for sync adapter - needs to match mime types below
  102. resValue "string", "package_name", applicationId
  103. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.profile"
  104. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.call"
  105. buildConfigField "int", "MAX_GROUP_SIZE", "256"
  106. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"g-\""
  107. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.g-\""
  108. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.threema.ch\""
  109. buildConfigField "int[]", "CHAT_SERVER_PORTS", "{5222, 443}"
  110. buildConfigField "String", "MEDIA_PATH", "\"Threema\""
  111. buildConfigField "boolean", "CHAT_SERVER_GROUPS", "true"
  112. buildConfigField "boolean", "DISABLE_CERT_PINNING", "false"
  113. buildConfigField "boolean", "VIDEO_CALLS_ENABLED", "true"
  114. // This public key is pinned for the chat server protocol.
  115. buildConfigField "byte[]", "SERVER_PUBKEY", "new byte[] {(byte) 0x45, (byte) 0x0b, (byte) 0x97, (byte) 0x57, (byte) 0x35, (byte) 0x27, (byte) 0x9f, (byte) 0xde, (byte) 0xcb, (byte) 0x33, (byte) 0x13, (byte) 0x64, (byte) 0x8f, (byte) 0x5f, (byte) 0xc6, (byte) 0xee, (byte) 0x9f, (byte) 0xf4, (byte) 0x36, (byte) 0x0e, (byte) 0xa9, (byte) 0x2a, (byte) 0x8c, (byte) 0x17, (byte) 0x51, (byte) 0xc6, (byte) 0x61, (byte) 0xe4, (byte) 0xc0, (byte) 0xd8, (byte) 0xc9, (byte) 0x09 }"
  116. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "new byte[] {(byte) 0xda, (byte) 0x7c, (byte) 0x73, (byte) 0x79, (byte) 0x8f, (byte) 0x97, (byte) 0xd5, (byte) 0x87, (byte) 0xc3, (byte) 0xa2, (byte) 0x5e, (byte) 0xbe, (byte) 0x0a, (byte) 0x91, (byte) 0x41, (byte) 0x7f, (byte) 0x76, (byte) 0xdb, (byte) 0xcc, (byte) 0xcd, (byte) 0xda, (byte) 0x29, (byte) 0x30, (byte) 0xe6, (byte) 0xa9, (byte) 0x09, (byte) 0x0a, (byte) 0xf6, (byte) 0x2e, (byte) 0xba, (byte) 0x6f, (byte) 0x15 }"
  117. buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\""
  118. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.threema.ch/\""
  119. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.threema.ch/\""
  120. buildConfigField "String", "WORK_SERVER_URL", "null"
  121. buildConfigField "String", "WORK_SERVER_IPV6_URL", "null"
  122. buildConfigField "String", "MEDIATOR_SERVER_URL", "\"wss://mediator-{deviceGroupIdPrefix4}.threema.ch/{deviceGroupIdPrefix8}\""
  123. // Base blob url used for "download" and "done" calls
  124. buildConfigField "String", "BLOB_SERVER_URL", "\"https://blobp-{blobIdPrefix8}.threema.ch\""
  125. buildConfigField "String", "BLOB_SERVER_IPV6_URL", "\"https://ds-blobp-{blobIdPrefix8}.threema.ch\""
  126. // Specific blob url used for "upload" calls
  127. buildConfigField "String", "BLOB_SERVER_URL_UPLOAD", "\"https://blobp-upload.threema.ch/upload\""
  128. buildConfigField "String", "BLOB_SERVER_IPV6_URL_UPLOAD", "\"https://ds-blobp-upload.threema.ch/upload\""
  129. // Base blob mirror url used for "download", "upload", "done"
  130. buildConfigField "String", "BLOB_MIRROR_SERVER_URL", "\"https://blob-mirror-{deviceGroupIdPrefix4}.threema.ch/{deviceGroupIdPrefix8}\""
  131. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.threema.ch/\""
  132. buildConfigField "String", "SAFE_SERVER_URL", "\"https://safe-{backupIdPrefix8}.threema.ch/\""
  133. buildConfigField "String", "WEB_SERVER_URL", "\"https://web.threema.ch/\""
  134. buildConfigField "String", "APP_RATING_URL", "\"https://threema.ch/app-rating/android/{rating}\""
  135. buildConfigField "byte[]", "THREEMA_PUSH_PUBLIC_KEY", "new byte[] {(byte) 0xfd, (byte) 0x71, (byte) 0x1e, (byte) 0x1a, (byte) 0x0d, (byte) 0xb0, (byte) 0xe2, (byte) 0xf0, (byte) 0x3f, (byte) 0xca, (byte) 0xab, (byte) 0x6c, (byte) 0x43, (byte) 0xda, (byte) 0x25, (byte) 0x75, (byte) 0xb9, (byte) 0x51, (byte) 0x36, (byte) 0x64, (byte) 0xa6, (byte) 0x2a, (byte) 0x12, (byte) 0xbd, (byte) 0x07, (byte) 0x28, (byte) 0xd8, (byte) 0x7f, (byte) 0x71, (byte) 0x25, (byte) 0xcc, (byte) 0x24}"
  136. buildConfigField "String", "ONPREM_ID_PREFIX", "\"O\""
  137. buildConfigField "String", "LOG_TAG", "\"3ma\""
  138. buildConfigField "String", "DEFAULT_APP_THEME", "\"2\""
  139. buildConfigField "String[]", "ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", "null"
  140. buildConfigField "boolean", "MD_ENABLED", "false"
  141. buildConfigField "boolean", "MD_SYNC_DISTRIBUTION_LISTS", "false"
  142. buildConfigField "boolean", "EDIT_MESSAGES_ENABLED", "true"
  143. buildConfigField "boolean", "DELETE_MESSAGES_ENABLED", "true"
  144. buildConfigField "boolean", "EMOJI_REACTIONS_ENABLED", "false"
  145. buildConfigField "boolean", "EMOJI_REACTIONS_WEB_ENABLED", "false"
  146. // config fields for action URLs / deep links
  147. buildConfigField "String", "uriScheme", "\"threema\""
  148. buildConfigField "String", "actionUrl", "\"go.threema.ch\""
  149. buildConfigField "String", "contactActionUrl", "\"threema.id\""
  150. buildConfigField "String", "groupLinkActionUrl", "\"threema.group\""
  151. // duplicated for manifest
  152. manifestPlaceholders = [
  153. uriScheme : "threema",
  154. contactActionUrl : "threema.id",
  155. groupLinkActionUrl: "threema.group",
  156. actionUrl : "go.threema.ch",
  157. callMimeType : "vnd.android.cursor.item/vnd.ch.threema.app.call",
  158. ]
  159. testInstrumentationRunner 'ch.threema.app.ThreemaTestRunner'
  160. // Only include language resources for those languages
  161. resourceConfigurations += [
  162. "en",
  163. "be-rBY",
  164. "ca",
  165. "cs",
  166. "de",
  167. "es",
  168. "fr",
  169. "gsw",
  170. "hu",
  171. "it",
  172. "ja",
  173. "nl-rNL",
  174. "no",
  175. "pl",
  176. "pt-rBR",
  177. "rm",
  178. "ru",
  179. "sk",
  180. "tr",
  181. "uk",
  182. "zh-rCN",
  183. "zh-rTW"
  184. ]
  185. }
  186. splits {
  187. abi {
  188. enable true
  189. reset()
  190. include 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'
  191. exclude 'armeabi', 'mips', 'mips64'
  192. universalApk project.hasProperty("buildUniversalApk")
  193. }
  194. }
  195. // Assign different version code for each output
  196. def abiVersionCodes = ['armeabi-v7a': 2, 'arm64-v8a': 3, 'x86': 8, 'x86_64': 9]
  197. android.applicationVariants.all { variant ->
  198. variant.outputs.each { output ->
  199. def abi = output.getFilter("ABI")
  200. output.versionCodeOverride =
  201. abiVersionCodes.get(abi, 0) * 1000000 + defaultVersionCode
  202. }
  203. }
  204. namespace 'ch.threema.app'
  205. flavorDimensions = ["default"]
  206. productFlavors {
  207. none {}
  208. store_google {}
  209. store_threema {
  210. resValue "string", "shop_download_filename", "Threema-update.apk"
  211. }
  212. store_google_work {
  213. versionName "${app_version}k${beta_suffix}"
  214. applicationId "ch.threema.app.work"
  215. testApplicationId 'ch.threema.app.work.test'
  216. resValue "string", "app_name", "Threema Work"
  217. resValue "string", "package_name", applicationId
  218. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.profile"
  219. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.call"
  220. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  221. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  222. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWork\""
  223. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.threema.ch/\""
  224. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.threema.ch/\""
  225. buildConfigField "String", "APP_RATING_URL", "\"https://threema.ch/app-rating/android-work/{rating}\""
  226. buildConfigField "String", "LOG_TAG", "\"3mawrk\""
  227. buildConfigField "String", "DEFAULT_APP_THEME", "\"2\""
  228. // config fields for action URLs / deep links
  229. buildConfigField "String", "uriScheme", "\"threemawork\""
  230. buildConfigField "String", "actionUrl", "\"work.threema.ch\""
  231. manifestPlaceholders = [
  232. uriScheme : "threemawork",
  233. actionUrl : "work.threema.ch",
  234. callMimeType: "vnd.android.cursor.item/vnd.ch.threema.app.work.call",
  235. ]
  236. }
  237. green {
  238. // The app was previously named `sandbox`. The app id remains unchanged to still be able to install updates.
  239. applicationId "ch.threema.app.sandbox"
  240. testApplicationId 'ch.threema.app.sandbox.test'
  241. resValue "string", "app_name", "Threema Green"
  242. resValue "string", "package_name", applicationId
  243. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.profile"
  244. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.call"
  245. buildConfigField "String", "MEDIA_PATH", "\"ThreemaGreen\""
  246. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  247. // This public key is pinned for the chat server protocol.
  248. buildConfigField "byte[]", "SERVER_PUBKEY", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  249. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  250. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.test.threema.ch/\""
  251. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.test.threema.ch/\""
  252. buildConfigField "String", "MEDIATOR_SERVER_URL", "\"wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  253. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  254. buildConfigField "String", "APP_RATING_URL", "\"https://test.threema.ch/app-rating/android/{rating}\""
  255. buildConfigField "boolean", "MD_ENABLED", "true"
  256. buildConfigField "String", "BLOB_MIRROR_SERVER_URL", "\"https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  257. buildConfigField "boolean", "EMOJI_REACTIONS_ENABLED", "true"
  258. buildConfigField "boolean", "EMOJI_REACTIONS_WEB_ENABLED", "true"
  259. }
  260. sandbox_work {
  261. versionName "${app_version}k${beta_suffix}"
  262. applicationId "ch.threema.app.sandbox.work"
  263. testApplicationId 'ch.threema.app.sandbox.work.test'
  264. resValue "string", "app_name", "Threema Sandbox Work"
  265. resValue "string", "package_name", applicationId
  266. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.work.profile"
  267. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.work.call"
  268. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  269. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  270. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  271. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWorkSandbox\""
  272. // This public key is pinned for the chat server protocol.
  273. buildConfigField "byte[]", "SERVER_PUBKEY", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  274. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  275. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.test.threema.ch/\""
  276. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.test.threema.ch/\""
  277. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.test.threema.ch/\""
  278. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.test.threema.ch/\""
  279. buildConfigField "String", "MEDIATOR_SERVER_URL", "\"wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  280. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  281. buildConfigField "String", "APP_RATING_URL", "\"https://test.threema.ch/app-rating/android-work/{rating}\""
  282. buildConfigField "String", "LOG_TAG", "\"3mawrk\""
  283. buildConfigField "String", "DEFAULT_APP_THEME", "\"2\""
  284. buildConfigField "String", "BLOB_MIRROR_SERVER_URL", "\"https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  285. // config fields for action URLs / deep links
  286. buildConfigField "String", "uriScheme", "\"threemawork\""
  287. buildConfigField "String", "actionUrl", "\"work.test.threema.ch\""
  288. buildConfigField "boolean", "MD_ENABLED", "true"
  289. buildConfigField "boolean", "EMOJI_REACTIONS_ENABLED", "true"
  290. buildConfigField "boolean", "EMOJI_REACTIONS_WEB_ENABLED", "true"
  291. manifestPlaceholders = [
  292. uriScheme: "threemawork",
  293. actionUrl: "work.test.threema.ch",
  294. ]
  295. }
  296. onprem {
  297. versionName "${app_version}o${beta_suffix}"
  298. applicationId "ch.threema.app.onprem"
  299. testApplicationId 'ch.threema.app.onprem.test'
  300. resValue "string", "app_name", "Threema OnPrem"
  301. resValue "string", "package_name", applicationId
  302. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.onprem.profile"
  303. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.onprem.call"
  304. buildConfigField "int", "MAX_GROUP_SIZE", "256"
  305. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"\""
  306. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"\""
  307. buildConfigField "String", "CHAT_SERVER_SUFFIX", "null"
  308. buildConfigField "String", "MEDIA_PATH", "\"ThreemaOnPrem\""
  309. buildConfigField "boolean", "CHAT_SERVER_GROUPS", "false"
  310. buildConfigField "byte[]", "SERVER_PUBKEY", "null"
  311. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "null"
  312. buildConfigField "String", "DIRECTORY_SERVER_URL", "null"
  313. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "null"
  314. buildConfigField "String", "BLOB_SERVER_URL", "null"
  315. buildConfigField "String", "BLOB_SERVER_IPV6_URL", "null"
  316. buildConfigField "String", "BLOB_SERVER_URL_UPLOAD", "null"
  317. buildConfigField "String", "BLOB_SERVER_IPV6_URL_UPLOAD", "null"
  318. buildConfigField "String", "BLOB_MIRROR_SERVER_URL", "null"
  319. buildConfigField "String[]", "ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", "new String[] {\"ek1qBp4DyRmLL9J5sCmsKSfwbsiGNB4veDAODjkwe/k=\", \"Hrk8aCjwKkXySubI7CZ3y9Sx+oToEHjNkGw98WSRneU=\", \"5pEn1T/5bhecNWrp9NgUQweRfgVtu/I8gRb3VxGP7k4=\"}"
  320. buildConfigField "String", "LOG_TAG", "\"3maop\""
  321. // config fields for action URLs / deep links
  322. buildConfigField "String", "uriScheme", "\"threemaonprem\""
  323. buildConfigField "String", "actionUrl", "\"onprem.threema.ch\""
  324. manifestPlaceholders = [
  325. uriScheme : "threemaonprem",
  326. actionUrl : "onprem.threema.ch",
  327. callMimeType: "vnd.android.cursor.item/vnd.ch.threema.app.onprem.call",
  328. ]
  329. }
  330. blue {
  331. // Essentially like sandbox work, but with a different icon and application id, used for internal testing
  332. versionName "${app_version}b${beta_suffix}"
  333. // The app was previously named `red`. The app id remains unchanged to still be able to install updates.
  334. applicationId "ch.threema.app.red"
  335. testApplicationId 'ch.threema.app.blue.test'
  336. resValue "string", "app_name", "Threema Blue"
  337. resValue "string", "package_name", applicationId
  338. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.blue.profile"
  339. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.blue.call"
  340. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  341. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  342. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  343. buildConfigField "String", "MEDIA_PATH", "\"ThreemaBlue\""
  344. // This public key is pinned for the chat server protocol.
  345. buildConfigField "byte[]", "SERVER_PUBKEY", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  346. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
  347. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.test.threema.ch/\""
  348. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.test.threema.ch/\""
  349. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.test.threema.ch/\""
  350. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.test.threema.ch/\""
  351. buildConfigField "String", "MEDIATOR_SERVER_URL", "\"wss://mediator-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  352. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  353. buildConfigField "String", "APP_RATING_URL", "\"https://test.threema.ch/app-rating/android-work/{rating}\""
  354. buildConfigField "String", "LOG_TAG", "\"3mablue\""
  355. buildConfigField "String", "BLOB_MIRROR_SERVER_URL", "\"https://blob-mirror-{deviceGroupIdPrefix4}.test.threema.ch/{deviceGroupIdPrefix8}\""
  356. buildConfigField "boolean", "EMOJI_REACTIONS_ENABLED", "true"
  357. buildConfigField "boolean", "EMOJI_REACTIONS_WEB_ENABLED", "true"
  358. // config fields for action URLs / deep links
  359. buildConfigField "String", "uriScheme", "\"threemablue\""
  360. buildConfigField "String", "actionUrl", "\"blue.threema.ch\""
  361. manifestPlaceholders = [
  362. uriScheme : "threemablue",
  363. actionUrl : "blue.threema.ch",
  364. callMimeType: "vnd.android.cursor.item/vnd.ch.threema.app.blue.call",
  365. ]
  366. }
  367. hms {
  368. applicationId "ch.threema.app.hms"
  369. }
  370. hms_work {
  371. versionName "${app_version}k${beta_suffix}"
  372. applicationId "ch.threema.app.work.hms"
  373. testApplicationId 'ch.threema.app.work.test.hms'
  374. resValue "string", "app_name", "Threema Work"
  375. resValue "string", "package_name", "ch.threema.app.work"
  376. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.profile"
  377. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.call"
  378. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  379. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  380. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWork\""
  381. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.threema.ch/\""
  382. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.threema.ch/\""
  383. buildConfigField "String", "APP_RATING_URL", "\"https://threema.ch/app-rating/android-work/{rating}\""
  384. buildConfigField "String", "LOG_TAG", "\"3mawrk\""
  385. buildConfigField "String", "DEFAULT_APP_THEME", "\"2\""
  386. // config fields for action URLs / deep links
  387. buildConfigField "String", "uriScheme", "\"threemawork\""
  388. buildConfigField "String", "actionUrl", "\"work.threema.ch\""
  389. manifestPlaceholders = [
  390. uriScheme : "threemawork",
  391. actionUrl : "work.threema.ch",
  392. callMimeType: "vnd.android.cursor.item/vnd.ch.threema.app.work.call",
  393. ]
  394. }
  395. libre {
  396. versionName "${app_version}l${beta_suffix}"
  397. applicationId "ch.threema.app.libre"
  398. testApplicationId 'ch.threema.app.libre.test'
  399. resValue "string", "package_name", applicationId
  400. resValue "string", "app_name", "Threema Libre"
  401. buildConfigField "String", "MEDIA_PATH", "\"ThreemaLibre\""
  402. }
  403. }
  404. signingConfigs {
  405. // Debug config
  406. if (keystores.debug != null) {
  407. debug {
  408. storeFile file(keystores.debug.storeFile)
  409. }
  410. } else {
  411. logger.warn("No debug keystore found. Falling back to locally generated keystore.")
  412. }
  413. // Release config
  414. if (keystores.release != null) {
  415. release {
  416. storeFile file(keystores.release.storeFile)
  417. storePassword keystores.release.storePassword
  418. keyAlias keystores.release.keyAlias
  419. keyPassword keystores.release.keyPassword
  420. }
  421. } else {
  422. logger.warn("No release keystore found. Falling back to locally generated keystore.")
  423. }
  424. // Release config
  425. if (keystores.hms_release != null) {
  426. hms_release {
  427. storeFile file(keystores.hms_release.storeFile)
  428. storePassword keystores.hms_release.storePassword
  429. keyAlias keystores.hms_release.keyAlias
  430. keyPassword keystores.hms_release.keyPassword
  431. }
  432. } else {
  433. logger.warn("No hms keystore found. Falling back to locally generated keystore.")
  434. }
  435. // Onprem release config
  436. if (keystores.onprem_release != null) {
  437. onprem_release {
  438. storeFile file(keystores.onprem_release.storeFile)
  439. storePassword keystores.onprem_release.storePassword
  440. keyAlias keystores.onprem_release.keyAlias
  441. keyPassword keystores.onprem_release.keyPassword
  442. }
  443. } else {
  444. logger.warn("No onprem keystore found. Falling back to locally generated keystore.")
  445. }
  446. // Blue release config
  447. if (keystores.blue_release != null) {
  448. blue_release {
  449. storeFile file(keystores.blue_release.storeFile)
  450. storePassword keystores.blue_release.storePassword
  451. keyAlias keystores.blue_release.keyAlias
  452. keyPassword keystores.blue_release.keyPassword
  453. }
  454. } else {
  455. logger.warn("No blue keystore found. Falling back to locally generated keystore.")
  456. }
  457. // Note: Libre release is signed with HSM, no config here
  458. }
  459. sourceSets {
  460. main {
  461. assets.srcDirs = ['assets']
  462. jniLibs.srcDirs = ['libs']
  463. res.srcDir 'src/main/res-rendezvous'
  464. }
  465. // Based on Google services
  466. none {
  467. java.srcDir 'src/google_services_based/java'
  468. }
  469. store_google {
  470. java.srcDir 'src/google_services_based/java'
  471. }
  472. store_google_work {
  473. java.srcDir 'src/google_services_based/java'
  474. }
  475. store_threema {
  476. java.srcDir 'src/google_services_based/java'
  477. }
  478. libre {
  479. assets.srcDirs = ['src/foss_based/assets']
  480. java.srcDir 'src/foss_based/java'
  481. }
  482. onprem {
  483. java.srcDir 'src/google_services_based/java'
  484. }
  485. green {
  486. java.srcDir 'src/google_services_based/java'
  487. manifest.srcFile 'src/store_google/AndroidManifest.xml'
  488. }
  489. sandbox_work {
  490. java.srcDir 'src/google_services_based/java'
  491. res.srcDir 'src/store_google_work/res'
  492. manifest.srcFile 'src/store_google_work/AndroidManifest.xml'
  493. }
  494. blue {
  495. java.srcDir 'src/google_services_based/java'
  496. res.srcDir 'src/blue/res'
  497. }
  498. // Based on Huawei services
  499. hms {
  500. java.srcDir 'src/hms_services_based/java'
  501. }
  502. hms_work {
  503. java.srcDir 'src/hms_services_based/java'
  504. res.srcDir 'src/store_google_work/res'
  505. }
  506. // FOSS, no proprietary services
  507. libre {
  508. assets.srcDirs = ['src/foss_based/assets']
  509. java.srcDir 'src/foss_based/java'
  510. }
  511. }
  512. buildTypes {
  513. debug {
  514. debuggable true
  515. jniDebuggable false
  516. ndk {
  517. debugSymbolLevel 'FULL'
  518. }
  519. enableUnitTestCoverage false
  520. enableAndroidTestCoverage false
  521. if (keystores['debug'] != null) {
  522. signingConfig signingConfigs.debug
  523. }
  524. }
  525. release {
  526. debuggable false
  527. jniDebuggable false
  528. minifyEnabled true
  529. shrinkResources false // Caused inconsistencies between local and CI builds
  530. vcsInfo.include false // For reproducible builds independent from git history
  531. proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-project.txt'
  532. ndk {
  533. debugSymbolLevel 'FULL' // 'SYMBOL_TABLE'
  534. }
  535. if (keystores['release'] != null) {
  536. productFlavors.store_google.signingConfig signingConfigs.release
  537. productFlavors.store_google_work.signingConfig signingConfigs.release
  538. productFlavors.store_threema.signingConfig signingConfigs.release
  539. productFlavors.green.signingConfig signingConfigs.release
  540. productFlavors.sandbox_work.signingConfig signingConfigs.release
  541. productFlavors.none.signingConfig signingConfigs.release
  542. }
  543. if (keystores['hms_release'] != null) {
  544. productFlavors.hms.signingConfig signingConfigs.hms_release
  545. productFlavors.hms_work.signingConfig signingConfigs.hms_release
  546. }
  547. if (keystores['onprem_release'] != null) {
  548. productFlavors.onprem.signingConfig signingConfigs.onprem_release
  549. }
  550. if (keystores['blue_release'] != null) {
  551. productFlavors.blue.signingConfig signingConfigs.blue_release
  552. }
  553. // Note: Libre release is signed with HSM, no config here
  554. }
  555. }
  556. // Only build relevant buildType / flavor combinations
  557. variantFilter { variant ->
  558. def names = variant.flavors*.name
  559. if (
  560. variant.buildType.name == "release" && (
  561. names.contains("green") || names.contains("sandbox_work")
  562. )
  563. ) {
  564. setIgnore(true)
  565. }
  566. }
  567. externalNativeBuild {
  568. ndkBuild {
  569. path 'jni/Android.mk'
  570. }
  571. }
  572. packagingOptions {
  573. jniLibs {
  574. // replacement for extractNativeLibs in AndroidManifest
  575. useLegacyPackaging = true
  576. }
  577. resources {
  578. excludes += [
  579. 'META-INF/DEPENDENCIES.txt',
  580. 'META-INF/LICENSE.txt',
  581. 'META-INF/NOTICE.txt',
  582. 'META-INF/NOTICE',
  583. 'META-INF/LICENSE',
  584. 'META-INF/DEPENDENCIES',
  585. 'META-INF/notice.txt',
  586. 'META-INF/license.txt',
  587. 'META-INF/dependencies.txt',
  588. 'META-INF/LGPL2.1',
  589. '**/*.proto',
  590. 'DebugProbesKt.bin'
  591. ]
  592. }
  593. }
  594. testOptions {
  595. // Disable animations in instrumentation tests
  596. animationsDisabled true
  597. unitTests {
  598. all {
  599. // All the usual Gradle options.
  600. testLogging {
  601. events "passed", "skipped", "failed", "standardOut", "standardError"
  602. outputs.upToDateWhen { false }
  603. exceptionFormat = 'full'
  604. }
  605. jvmArgs = jvmArgs + ['--add-opens=java.base/java.util=ALL-UNNAMED']
  606. jvmArgs = jvmArgs + ['--add-opens=java.base/java.util.stream=ALL-UNNAMED']
  607. jvmArgs = jvmArgs + ['--add-opens=java.base/java.lang=ALL-UNNAMED']
  608. jvmArgs = jvmArgs + ['-Xmx4096m']
  609. }
  610. // By default, local unit tests throw an exception any time the code you are testing tries to access
  611. // Android platform APIs (unless you mock Android dependencies yourself or with a testing
  612. // framework like Mockito). However, you can enable the following property so that the test
  613. // returns either null or zero when accessing platform APIs, rather than throwing an exception.
  614. returnDefaultValues true
  615. }
  616. }
  617. compileOptions {
  618. coreLibraryDesugaringEnabled true
  619. sourceCompatibility JavaVersion.VERSION_11
  620. targetCompatibility JavaVersion.VERSION_11
  621. }
  622. java {
  623. toolchain {
  624. languageVersion.set(JavaLanguageVersion.of(17))
  625. }
  626. }
  627. kotlin {
  628. jvmToolchain(17)
  629. }
  630. androidResources {
  631. noCompress 'png'
  632. }
  633. lint {
  634. // if true, stop the gradle build if errors are found
  635. abortOnError true
  636. // if true, check all issues, including those that are off by default
  637. checkAllWarnings true
  638. // check dependencies
  639. checkDependencies true
  640. // set to true to have all release builds run lint on issues with severity=fatal
  641. // and abort the build (controlled by abortOnError above) if fatal issues are found
  642. checkReleaseBuilds true
  643. // turn off checking the given issue id's
  644. disable 'TypographyFractions', 'TypographyQuotes', 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
  645. // Set the severity of the given issues to error
  646. error 'Wakelock', 'TextViewEdits', 'ResourceAsColor'
  647. // Set the severity of the given issues to fatal (which means they will be
  648. // checked during release builds (even if the lint target is not included)
  649. fatal 'NewApi', 'InlinedApi'
  650. // Set the severity of the given issues to ignore (same as disabling the check)
  651. ignore 'TypographyQuotes'
  652. ignoreWarnings false
  653. // if true, don't include source code lines in the error output
  654. noLines false
  655. // if true, show all locations for an error, do not truncate lists, etc.
  656. showAll true
  657. // Set the severity of the given issues to warning
  658. warning 'MissingTranslation'
  659. // if true, treat all warnings as errors
  660. warningsAsErrors false
  661. // file to write report to (if not specified, defaults to lint-results.xml)
  662. xmlOutput file('lint-report.xml')
  663. // if true, generate an XML report for use by for example Jenkins
  664. xmlReport true
  665. }
  666. buildFeatures {
  667. compose true
  668. buildConfig true
  669. }
  670. composeOptions {
  671. kotlinCompilerExtensionVersion = "1.5.7"
  672. }
  673. }
  674. dependencies {
  675. configurations.all {
  676. // Prefer modules that are part of this build (multi-project or composite build)
  677. // over external modules
  678. resolutionStrategy.preferProjectModules()
  679. // Alternatively, we can fail eagerly on version conflict to see the conflicts
  680. //resolutionStrategy.failOnVersionConflict()
  681. }
  682. coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
  683. implementation project(':domain')
  684. implementation 'net.zetetic:sqlcipher-android:4.5.7@aar'
  685. implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
  686. implementation 'net.sf.opencsv:opencsv:2.3'
  687. implementation 'net.lingala.zip4j:zip4j:2.11.5'
  688. implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
  689. // commons-io >2.6 requires android 8
  690. implementation 'commons-io:commons-io:2.6'
  691. implementation 'org.apache.commons:commons-text:1.10.0'
  692. implementation "org.slf4j:slf4j-api:$slf4j_version"
  693. implementation 'com.vanniktech:android-image-cropper:4.5.0'
  694. implementation 'com.datatheorem.android.trustkit:trustkit:1.1.5'
  695. implementation 'me.zhanghai.android.fastscroll:library:1.3.0'
  696. implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3'
  697. implementation 'com.alexvasilkov:gesture-views:2.8.3'
  698. // AndroidX / Jetpack support libraries
  699. implementation "androidx.preference:preference-ktx:1.2.1"
  700. implementation 'androidx.recyclerview:recyclerview:1.3.2'
  701. implementation 'androidx.palette:palette-ktx:1.0.0'
  702. implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
  703. implementation 'androidx.core:core-ktx:1.13.1'
  704. implementation 'androidx.appcompat:appcompat:1.7.0'
  705. implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
  706. implementation 'androidx.biometric:biometric:1.1.0'
  707. implementation 'androidx.work:work-runtime-ktx:2.9.0'
  708. implementation 'androidx.fragment:fragment-ktx:1.8.0'
  709. implementation 'androidx.activity:activity-ktx:1.9.0'
  710. implementation 'androidx.sqlite:sqlite:2.2.2'
  711. implementation "androidx.concurrent:concurrent-futures:1.2.0"
  712. implementation "androidx.camera:camera-camera2:1.3.4"
  713. implementation "androidx.camera:camera-lifecycle:1.3.4"
  714. implementation "androidx.camera:camera-view:1.3.4"
  715. implementation 'androidx.camera:camera-video:1.3.4'
  716. implementation "androidx.media:media:1.7.0"
  717. implementation 'androidx.media3:media3-exoplayer:1.3.1'
  718. implementation 'androidx.media3:media3-ui:1.3.1'
  719. implementation "androidx.media3:media3-session:1.3.1"
  720. implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2"
  721. implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.2"
  722. implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.2"
  723. implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.2"
  724. implementation "androidx.lifecycle:lifecycle-service:2.8.2"
  725. implementation "androidx.lifecycle:lifecycle-process:2.8.2"
  726. implementation "androidx.lifecycle:lifecycle-common-java8:2.8.2"
  727. implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
  728. implementation "androidx.paging:paging-runtime-ktx:3.3.0"
  729. implementation "androidx.sharetarget:sharetarget:1.2.0"
  730. implementation 'androidx.room:room-runtime:2.6.1'
  731. implementation 'androidx.window:window:1.3.0'
  732. kapt 'androidx.room:room-compiler:2.6.1'
  733. // Jetpack Compose
  734. def composeBom = platform('androidx.compose:compose-bom:2024.06.00')
  735. implementation composeBom
  736. implementation 'androidx.compose.material3:material3'
  737. implementation 'androidx.compose.ui:ui-tooling-preview'
  738. implementation 'androidx.activity:activity-compose:1.9.0'
  739. implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6'
  740. implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.6'
  741. debugImplementation 'androidx.compose.ui:ui-tooling'
  742. androidTestImplementation composeBom
  743. implementation 'org.bouncycastle:bcprov-jdk15to18:1.78.1'
  744. implementation 'com.google.android.material:material:1.12.0'
  745. implementation 'com.google.zxing:core:3.3.3' // zxing 3.4 crashes on API < 24
  746. implementation 'com.googlecode.libphonenumber:libphonenumber:8.13.39'
  747. // make sure to update this in domain's build.gradle as well
  748. // webclient dependencies
  749. implementation 'org.msgpack:msgpack-core:0.8.24!!'
  750. implementation 'com.fasterxml.jackson.core:jackson-core:2.12.5!!'
  751. implementation 'com.neovisionaries:nv-websocket-client:2.9'
  752. // Backport of Streams and CompletableFuture. Remove once API level 24 is supported.
  753. implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.4'
  754. implementation('org.saltyrtc:saltyrtc-client:0.14.2') {
  755. exclude group: 'org.json'
  756. }
  757. implementation 'org.saltyrtc:chunked-dc:1.0.1'
  758. implementation 'ch.threema:webrtc-android:123.0.0'
  759. implementation('org.saltyrtc:saltyrtc-task-webrtc:0.18.1') {
  760. exclude module: 'saltyrtc-client'
  761. }
  762. // Glide components
  763. // Glide 4.15+ does not work on API 21
  764. implementation 'com.github.bumptech.glide:glide:4.16.0'
  765. kapt 'com.github.bumptech.glide:compiler:4.16.0'
  766. annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
  767. // Kotlin
  768. implementation 'androidx.core:core-ktx:1.13.1'
  769. implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  770. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
  771. implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
  772. testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
  773. androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
  774. // use leak canary in debug builds
  775. if (!project.hasProperty("noLeakCanary")) {
  776. debugImplementation('com.squareup.leakcanary:leakcanary-android:2.13')
  777. }
  778. // test dependencies
  779. testImplementation "junit:junit:$junit_version"
  780. testImplementation(testFixtures(project(":domain")))
  781. // custom test helpers, shared between unit test and android tests
  782. testImplementation(project(":test-helpers"))
  783. androidTestImplementation(project(":test-helpers"))
  784. // use powermock instead of mockito. it support mocking static classes.
  785. def mockitoVersion = '2.0.9'
  786. testImplementation "org.powermock:powermock-api-mockito2:${mockitoVersion}"
  787. testImplementation "org.powermock:powermock-module-junit4-rule-agent:${mockitoVersion}"
  788. testImplementation "org.powermock:powermock-module-junit4-rule:${mockitoVersion}"
  789. testImplementation "org.powermock:powermock-module-junit4:${mockitoVersion}"
  790. // add JSON support to tests without mocking
  791. testImplementation 'org.json:json:20220924'
  792. testImplementation 'com.tngtech.archunit:archunit-junit4:0.18.0'
  793. androidTestImplementation(testFixtures(project(":domain")))
  794. androidTestImplementation 'androidx.test:rules:1.6.0'
  795. androidTestImplementation 'tools.fastlane:screengrab:2.1.1', {
  796. exclude group: 'androidx.annotation', module: 'annotation'
  797. }
  798. androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0', {
  799. exclude group: 'androidx.annotation', module: 'annotation'
  800. }
  801. androidTestImplementation 'androidx.test:runner:1.4.0', {
  802. exclude group: 'androidx.annotation', module: 'annotation'
  803. }
  804. androidTestImplementation 'androidx.test.ext:junit-ktx:1.2.0'
  805. androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0', {
  806. exclude group: 'androidx.annotation', module: 'annotation'
  807. exclude group: 'androidx.appcompat', module: 'appcompat'
  808. exclude group: 'androidx.legacy', module: 'legacy-support-v4'
  809. exclude group: 'com.google.android.material', module: 'material'
  810. exclude group: 'androidx.recyclerview', module: 'recyclerview'
  811. exclude(group: 'org.checkerframework', module: 'checker')
  812. exclude module: "protobuf-lite"
  813. }
  814. androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0', {
  815. exclude group: 'androidx.annotation', module: 'annotation'
  816. }
  817. androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.3.0'
  818. androidTestImplementation 'androidx.test:core-ktx:1.6.0'
  819. androidTestImplementation "org.mockito:mockito-core:4.8.1"
  820. androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
  821. // Google Play Services and related libraries
  822. def googleDependencies = [
  823. // Play services
  824. 'com.google.android.gms:play-services-base:18.0.1': [],
  825. // Firebase push
  826. 'com.google.firebase:firebase-messaging:23.1.2' : [
  827. [group: 'com.google.firebase', module: 'firebase-core'],
  828. [group: 'com.google.firebase', module: 'firebase-analytics'],
  829. [group: 'com.google.firebase', module: 'firebase-measurement-connector'],
  830. ],
  831. ]
  832. googleDependencies.each {
  833. def dependency = it.key
  834. def excludes = it.value
  835. noneImplementation(dependency) { excludes.each { exclude it } }
  836. store_googleImplementation(dependency) { excludes.each { exclude it } }
  837. store_google_workImplementation(dependency) { excludes.each { exclude it } }
  838. store_threemaImplementation(dependency) { excludes.each { exclude it } }
  839. onpremImplementation(dependency) { excludes.each { exclude it } }
  840. greenImplementation(dependency) { excludes.each { exclude it } }
  841. sandbox_workImplementation(dependency) { excludes.each { exclude it } }
  842. blueImplementation(dependency) { excludes.each { exclude it } }
  843. }
  844. // Google Assistant Voice Action verification library
  845. noneImplementation(name: 'libgsaverification-client', ext: 'aar')
  846. store_googleImplementation(name: 'libgsaverification-client', ext: 'aar')
  847. store_google_workImplementation(name: 'libgsaverification-client', ext: 'aar')
  848. onpremImplementation(name: 'libgsaverification-client', ext: 'aar')
  849. store_threemaImplementation(name: 'libgsaverification-client', ext: 'aar')
  850. greenImplementation(name: 'libgsaverification-client', ext: 'aar')
  851. sandbox_workImplementation(name: 'libgsaverification-client', ext: 'aar')
  852. blueImplementation(name: 'libgsaverification-client', ext: 'aar')
  853. // Maplibre (may have transitive dependencies on Google location services)
  854. def maplibreDependency = 'org.maplibre.gl:android-sdk:11.0.1'
  855. noneImplementation maplibreDependency
  856. store_googleImplementation maplibreDependency
  857. store_google_workImplementation maplibreDependency
  858. store_threemaImplementation maplibreDependency
  859. libreImplementation maplibreDependency, { exclude group: 'com.google.android.gms' }
  860. onpremImplementation maplibreDependency
  861. greenImplementation maplibreDependency
  862. sandbox_workImplementation maplibreDependency
  863. blueImplementation maplibreDependency
  864. hmsImplementation maplibreDependency
  865. hms_workImplementation maplibreDependency
  866. // Huawei related libraries (only for hms* build variants)
  867. def huaweiDependencies = [
  868. // HMS push
  869. 'com.huawei.hms:push:6.3.0.304': [
  870. // Exclude agconnect dependency, we'll replace it with the vendored version below
  871. [group: 'com.huawei.agconnect'],
  872. ],
  873. ]
  874. huaweiDependencies.each {
  875. def dependency = it.key
  876. def excludes = it.value
  877. hmsImplementation(dependency) { excludes.each { exclude it } }
  878. hms_workImplementation(dependency) { excludes.each { exclude it } }
  879. }
  880. hmsImplementation(name: 'agconnect-core-1.9.1.301', ext: 'aar')
  881. hms_workImplementation(name: 'agconnect-core-1.9.1.301', ext: 'aar')
  882. }
  883. tasks.withType(KaptGenerateStubs).configureEach {
  884. kotlinOptions {
  885. jvmTarget = JavaVersion.VERSION_11.toString()
  886. }
  887. }
  888. // Define the cargo attributes. These will be used by the rust-android plugin that will create the
  889. // 'cargoBuild' task that builds native libraries that will be added to the apk. Note that the
  890. // kotlin bindings are created in the domain module. Building native libraries with rust-android
  891. // cannot be done in any other module than 'app'.
  892. cargo {
  893. prebuiltToolchains = true
  894. targetDirectory = "$projectDir/build/generated/source/libthreema"
  895. module = "$projectDir/../domain/libthreema" // must contain Cargo.toml
  896. libname = "libthreema" // must match the Cargo.toml's package name
  897. profile = 'release'
  898. pythonCommand = 'python3'
  899. targets = ["x86_64", "arm64", "arm", "x86"]
  900. features {
  901. defaultAnd("uniffi")
  902. }
  903. extraCargoBuildArguments = ["--lib", "--target-dir", "$projectDir/build/generated/source/libthreema"]
  904. verbose = false
  905. }
  906. afterEvaluate {
  907. // The `cargoBuild` task isn't available until after evaluation.
  908. android.applicationVariants.configureEach { variant ->
  909. def variantName = "${variant.name.capitalize()}"
  910. // Set the dependency so that cargoBuild is executed before the native libs are merged
  911. tasks["merge${variantName}NativeLibs"].dependsOn(tasks["cargoBuild"])
  912. }
  913. }
  914. sonarqube {
  915. properties {
  916. property "sonar.sources", "src/main/, ../scripts/, ../scripts-internal/"
  917. property "sonar.exclusions", "src/main/java/ch/threema/localcrypto/**, src/test/java/ch/threema/localcrypto/**"
  918. property "sonar.tests", "src/test/"
  919. property "sonar.sourceEncoding", "UTF-8"
  920. property "sonar.verbose", "true"
  921. property 'sonar.projectKey', 'android-client'
  922. property 'sonar.projectName', 'Threema for Android'
  923. }
  924. }
  925. // Set up Gradle tasks to fetch screenshots on UI test failures
  926. // See https://medium.com/stepstone-tech/how-to-capture-screenshots-for-failed-ui-tests-9927eea6e1e4
  927. def reportsDirectory = "$buildDir/reports/androidTests/connected"
  928. def screenshotsDirectory = "/sdcard/testfailures/screenshots/"
  929. def clearScreenshotsTask = task('clearScreenshots', type: Exec) {
  930. executable "${android.getAdbExe().toString()}"
  931. args 'shell', 'rm', '-r', screenshotsDirectory
  932. }
  933. def createScreenshotsDirectoryTask = task('createScreenshotsDirectory', type: Exec, group: 'reporting') {
  934. executable "${android.getAdbExe().toString()}"
  935. args 'shell', 'mkdir', '-p', screenshotsDirectory
  936. }
  937. def fetchScreenshotsTask = task('fetchScreenshots', type: Exec, group: 'reporting') {
  938. executable "${android.getAdbExe().toString()}"
  939. args 'pull', screenshotsDirectory + '.', reportsDirectory
  940. finalizedBy {
  941. clearScreenshotsTask
  942. }
  943. dependsOn {
  944. createScreenshotsDirectoryTask
  945. }
  946. doFirst {
  947. new File(reportsDirectory).mkdirs()
  948. }
  949. }
  950. tasks.whenTaskAdded { task ->
  951. if (task.name == 'connectedDebugAndroidTest') {
  952. task.finalizedBy {
  953. fetchScreenshotsTask
  954. }
  955. }
  956. }