build.gradle 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. plugins {
  2. id 'org.sonarqube'
  3. }
  4. apply plugin: 'com.android.application'
  5. apply plugin: 'kotlin-android'
  6. // only apply the plugin if we are dealing with a AppGallery build
  7. if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
  8. println "enabling hms plugin"
  9. apply plugin: 'com.huawei.agconnect'
  10. }
  11. // version codes
  12. def app_version = "4.7"
  13. def beta_suffix = "" // with leading dash
  14. /**
  15. * Return the git hash, if git is installed.
  16. */
  17. def getGitHash = { ->
  18. def stdout = new ByteArrayOutputStream()
  19. def stderr = new ByteArrayOutputStream()
  20. try {
  21. exec {
  22. commandLine 'git', 'rev-parse', '--short', 'HEAD'
  23. standardOutput = stdout
  24. errorOutput = stderr
  25. ignoreExitValue true
  26. }
  27. } catch (ignored) { /* If git binary is not found, carry on */ }
  28. def hash = stdout.toString().trim()
  29. return (hash.isEmpty()) ? "?" : hash
  30. }
  31. /**
  32. * Look up the keystore with the specified name in a `keystore` directory
  33. * adjacent to this project directory. If it exists, return a signing config.
  34. * Otherwise, return null.
  35. */
  36. def findKeystore = { name ->
  37. def basePath = "${projectDir.getAbsolutePath()}/../../keystore"
  38. def storePath = "${basePath}/${name}.keystore"
  39. def storeFile = new File(storePath)
  40. if (storeFile.exists() && storeFile.isFile()) {
  41. def propertiesPath = "${basePath}/${name}.properties"
  42. def propertiesFile = new File(propertiesPath)
  43. if (propertiesFile.exists() && propertiesFile.isFile()) {
  44. Properties props = new Properties()
  45. propertiesFile.withInputStream { props.load(it) }
  46. return [
  47. storeFile: storePath,
  48. storePassword: props.storePassword,
  49. keyAlias: props.keyAlias,
  50. keyPassword: props.keyPassword,
  51. ]
  52. } else {
  53. return [
  54. storeFile: storePath,
  55. storePassword: null,
  56. keyAlias: null,
  57. keyPassword: null,
  58. ]
  59. }
  60. }
  61. }
  62. /**
  63. * Map with keystore paths (if found).
  64. */
  65. def keystores = [
  66. debug: findKeystore("debug"),
  67. release: findKeystore("threema"),
  68. hms_release: findKeystore("threema_hms"),
  69. onprem_release: findKeystore("onprem"),
  70. red_release: findKeystore("red"),
  71. fdroid_release: findKeystore("threema_fdroid"),
  72. ]
  73. android {
  74. // NOTE: When adjusting compileSdkVersion, buildToolsVersion or ndkVersion,
  75. // make sure to adjust them in `scripts/Dockerfile` and
  76. // `.gitlab-ci.yml` as well!
  77. compileSdkVersion 31
  78. buildToolsVersion '31.0.0'
  79. ndkVersion '21.1.6352462'
  80. defaultConfig {
  81. minSdkVersion 21
  82. //noinspection OldTargetApi
  83. targetSdkVersion 30
  84. vectorDrawables.useSupportLibrary = true
  85. applicationId "ch.threema.app"
  86. testApplicationId 'ch.threema.app.test'
  87. versionCode 735
  88. versionName "${app_version}${beta_suffix}"
  89. resValue "string", "app_name", "Threema"
  90. // package name used for sync adapter - needs to match mime types below
  91. resValue "string", "package_name", applicationId
  92. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.profile"
  93. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.call"
  94. buildConfigField "int", "MAX_GROUP_SIZE", "256"
  95. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"g-\""
  96. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.g-\""
  97. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.threema.ch\""
  98. buildConfigField "int[]", "CHAT_SERVER_PORTS", "{5222, 443}"
  99. buildConfigField "String", "MEDIA_PATH", "\"Threema\""
  100. buildConfigField "boolean", "CHAT_SERVER_GROUPS", "true"
  101. buildConfigField "boolean", "DISABLE_CERT_PINNING", "false"
  102. buildConfigField "boolean", "VIDEO_CALLS_ENABLED", "true"
  103. 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 }"
  104. 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 }"
  105. buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\""
  106. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.threema.ch/\""
  107. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.threema.ch/\""
  108. buildConfigField "String", "WORK_SERVER_URL", "null"
  109. buildConfigField "String", "WORK_SERVER_IPV6_URL", "null"
  110. buildConfigField "String", "BLOB_SERVER_DOWNLOAD_URL", "\"https://blobp-{blobIdPrefix}.threema.ch/{blobId}\""
  111. buildConfigField "String", "BLOB_SERVER_DOWNLOAD_IPV6_URL", "\"https://ds-blobp-{blobIdPrefix}.threema.ch/{blobId}\""
  112. buildConfigField "String", "BLOB_SERVER_DONE_URL", "\"https://blobp-{blobIdPrefix}.threema.ch/{blobId}/done\""
  113. buildConfigField "String", "BLOB_SERVER_DONE_IPV6_URL", "\"https://ds-blobp-{blobIdPrefix}.threema.ch/{blobId}/done\""
  114. buildConfigField "String", "BLOB_SERVER_UPLOAD_URL", "\"https://blobp-upload.threema.ch/upload\""
  115. buildConfigField "String", "BLOB_SERVER_UPLOAD_IPV6_URL", "\"https://ds-blobp-upload.threema.ch/upload\""
  116. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.threema.ch/\""
  117. buildConfigField "String", "SAFE_SERVER_URL", "\"https://safe-%h.threema.ch/\""
  118. buildConfigField "String", "WEB_SERVER_URL", "\"https://web.threema.ch/\""
  119. buildConfigField "String", "ONPREM_ID_PREFIX", "\"O\""
  120. buildConfigField "String[]", "ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", "null"
  121. buildConfigField "boolean", "SEND_CONSUMED_DELIVERY_RECEIPTS", "false"
  122. // config fields for action URLs / deep links
  123. buildConfigField "String", "uriScheme", "\"threema\""
  124. buildConfigField "String", "actionUrl", "\"go.threema.ch\""
  125. buildConfigField "String", "contactActionUrl", "\"threema.id\""
  126. buildConfigField "String", "groupLinkActionUrl", "\"threema.group\""
  127. // duplicated for manifest
  128. manifestPlaceholders = [
  129. uriScheme: "threema",
  130. contactActionUrl: "threema.id",
  131. groupLinkActionUrl: "threema.group",
  132. actionUrl: "go.threema.ch"
  133. ]
  134. ndk {
  135. abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
  136. }
  137. testInstrumentationRunner 'ch.threema.app.ThreemaTestRunner'
  138. testInstrumentationRunnerArgument 'notAnnotation', 'ch.threema.app.TestFastlaneOnly,ch.threema.app.DangerousTest'
  139. testInstrumentationRunnerArgument 'disableAnalytics', 'true' // https://developer.android.com/training/testing/espresso/setup#analytics
  140. }
  141. splits {
  142. abi {
  143. enable true
  144. reset()
  145. include 'armeabi-v7a', 'x86', "arm64-v8a", "x86_64"
  146. exclude 'armeabi', 'mips', 'mips64'
  147. universalApk true
  148. }
  149. }
  150. // Assign different version code for each output
  151. project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
  152. android.applicationVariants.all { variant ->
  153. variant.outputs.each { output ->
  154. output.versionCodeOverride =
  155. project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
  156. }
  157. }
  158. flavorDimensions "default"
  159. productFlavors {
  160. none { }
  161. store_google { }
  162. store_threema {
  163. resValue "string", "shop_download_filename", "Threema-update.apk"
  164. }
  165. store_google_work {
  166. versionName "${app_version}k${beta_suffix}"
  167. applicationId "ch.threema.app.work"
  168. testApplicationId 'ch.threema.app.work.test'
  169. resValue "string", "app_name", "Threema Work"
  170. resValue "string", "package_name", applicationId
  171. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.profile"
  172. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.call"
  173. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  174. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  175. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWork\""
  176. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.threema.ch/\""
  177. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.threema.ch/\""
  178. // config fields for action URLs / deep links
  179. buildConfigField "String", "uriScheme", "\"threemawork\""
  180. buildConfigField "String", "actionUrl", "\"work.threema.ch\""
  181. manifestPlaceholders = [
  182. uriScheme: "threemawork",
  183. actionUrl: "work.threema.ch",
  184. ]
  185. }
  186. sandbox {
  187. applicationId "ch.threema.app.sandbox"
  188. testApplicationId 'ch.threema.app.sandbox.test'
  189. resValue "string", "app_name", "Threema Sandbox"
  190. resValue "string", "package_name", applicationId
  191. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.profile"
  192. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.call"
  193. buildConfigField "String", "MEDIA_PATH", "\"ThreemaSandbox\""
  194. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  195. 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 }"
  196. 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 }"
  197. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.test.threema.ch/\""
  198. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.test.threema.ch/\""
  199. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  200. }
  201. sandbox_work {
  202. versionName "${app_version}k${beta_suffix}"
  203. applicationId "ch.threema.app.sandbox.work"
  204. testApplicationId 'ch.threema.app.sandbox.work.test'
  205. resValue "string", "app_name", "Threema Sandbox Work"
  206. resValue "string", "package_name", applicationId
  207. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.work.profile"
  208. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.sandbox.work.call"
  209. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  210. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  211. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  212. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWorkSandbox\""
  213. 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 }"
  214. 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 }"
  215. buildConfigField "String", "DIRECTORY_SERVER_URL", "\"https://apip.test.threema.ch/\""
  216. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "\"https://ds-apip.test.threema.ch/\""
  217. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.test.threema.ch/\""
  218. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.test.threema.ch/\""
  219. buildConfigField "String", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  220. // config fields for action URLs / deep links
  221. buildConfigField "String", "uriScheme", "\"threemawork\""
  222. buildConfigField "String", "actionUrl", "\"work.threema.ch\""
  223. manifestPlaceholders = [
  224. uriScheme : "threemawork",
  225. actionUrl : "work.threema.ch",
  226. ]
  227. }
  228. onprem {
  229. versionName "${app_version}o${beta_suffix}"
  230. applicationId "ch.threema.app.onprem"
  231. testApplicationId 'ch.threema.app.onprem.test'
  232. resValue "string", "app_name", "Threema OnPrem"
  233. resValue "string", "package_name", applicationId
  234. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.onprem.profile"
  235. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.onprem.call"
  236. buildConfigField "int", "MAX_GROUP_SIZE", "256"
  237. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"\""
  238. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"\""
  239. buildConfigField "String", "CHAT_SERVER_SUFFIX", "null"
  240. buildConfigField "String", "MEDIA_PATH", "\"ThreemaOnPrem\""
  241. buildConfigField "boolean", "CHAT_SERVER_GROUPS", "false"
  242. buildConfigField "byte[]", "SERVER_PUBKEY", "null"
  243. buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "null"
  244. buildConfigField "String", "DIRECTORY_SERVER_URL", "null"
  245. buildConfigField "String", "DIRECTORY_SERVER_IPV6_URL", "null"
  246. buildConfigField "String", "BLOB_SERVER_DOWNLOAD_URL", "null"
  247. buildConfigField "String", "BLOB_SERVER_DOWNLOAD_IPV6_URL", "null"
  248. buildConfigField "String", "BLOB_SERVER_DONE_URL", "null"
  249. buildConfigField "String", "BLOB_SERVER_DONE_IPV6_URL", "null"
  250. buildConfigField "String", "BLOB_SERVER_UPLOAD_URL", "null"
  251. buildConfigField "String", "BLOB_SERVER_UPLOAD_IPV6_URL", "null"
  252. buildConfigField "String[]", "ONPREM_CONFIG_TRUSTED_PUBLIC_KEYS", "new String[] {\"ek1qBp4DyRmLL9J5sCmsKSfwbsiGNB4veDAODjkwe/k=\", \"Hrk8aCjwKkXySubI7CZ3y9Sx+oToEHjNkGw98WSRneU=\", \"5pEn1T/5bhecNWrp9NgUQweRfgVtu/I8gRb3VxGP7k4=\"}"
  253. // config fields for action URLs / deep links
  254. buildConfigField "String", "uriScheme", "\"threemaonprem\""
  255. buildConfigField "String", "actionUrl", "\"onprem.threema.ch\""
  256. manifestPlaceholders = [
  257. uriScheme: "threemaonprem",
  258. actionUrl: "onprem.threema.ch",
  259. ]
  260. }
  261. red { // Essentially like sandbox work, but with a different icon and accent color, used for internal testing
  262. versionName "${app_version}r${beta_suffix}"
  263. applicationId "ch.threema.app.red"
  264. testApplicationId 'ch.threema.app.red.test'
  265. resValue "string", "app_name", "Threema Red"
  266. resValue "string", "package_name", applicationId
  267. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.red.profile"
  268. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.red.call"
  269. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  270. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  271. buildConfigField "String", "CHAT_SERVER_SUFFIX", "\".0.test.threema.ch\""
  272. buildConfigField "String", "MEDIA_PATH", "\"ThreemaRed\""
  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", "AVATAR_FETCH_URL", "\"https://avatar.test.threema.ch/\""
  280. buildConfigField "boolean", "SEND_CONSUMED_DELIVERY_RECEIPTS", "true"
  281. // config fields for action URLs / deep links
  282. buildConfigField "String", "uriScheme", "\"threemared\""
  283. buildConfigField "String", "actionUrl", "\"red.threema.ch\""
  284. manifestPlaceholders = [
  285. uriScheme: "threemared",
  286. actionUrl: "red.threema.ch",
  287. ]
  288. }
  289. hms {
  290. applicationId "ch.threema.app.hms"
  291. }
  292. hms_work {
  293. versionName "${app_version}k${beta_suffix}"
  294. applicationId "ch.threema.app.work.hms"
  295. testApplicationId 'ch.threema.app.work.test.hms'
  296. resValue "string", "app_name", "Threema Work"
  297. resValue "string", "package_name", "ch.threema.app.work"
  298. resValue "string", "contacts_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.profile"
  299. resValue "string", "call_mime_type", "vnd.android.cursor.item/vnd.ch.threema.app.work.call"
  300. buildConfigField "String", "CHAT_SERVER_PREFIX", "\"w-\""
  301. buildConfigField "String", "CHAT_SERVER_IPV6_PREFIX", "\"ds.w-\""
  302. buildConfigField "String", "MEDIA_PATH", "\"ThreemaWork\""
  303. buildConfigField "String", "WORK_SERVER_URL", "\"https://apip-work.threema.ch/\""
  304. buildConfigField "String", "WORK_SERVER_IPV6_URL", "\"https://ds-apip-work.threema.ch/\""
  305. // config fields for action URLs / deep links
  306. buildConfigField "String", "uriScheme", "\"threemawork\""
  307. buildConfigField "String", "actionUrl", "\"work.threema.ch\""
  308. manifestPlaceholders = [
  309. uriScheme: "threemawork",
  310. actionUrl: "work.threema.ch",
  311. ]
  312. }
  313. fdroid {
  314. versionName "${app_version}f${beta_suffix}"
  315. applicationId "ch.threema.app.fdroid"
  316. testApplicationId 'ch.threema.app.fdroid.test'
  317. resValue "string", "app_name", "Threema Libre"
  318. buildConfigField "String", "MEDIA_PATH", "\"ThreemaLibre\""
  319. }
  320. }
  321. signingConfigs {
  322. // Debug config
  323. if (keystores.debug != null) {
  324. debug {
  325. storeFile file(keystores.debug.storeFile)
  326. }
  327. } else {
  328. logger.warn("No debug keystore found. Falling back to locally generated keystore.")
  329. }
  330. // Release config
  331. if (keystores.release != null) {
  332. release {
  333. storeFile file(keystores.release.storeFile)
  334. storePassword keystores.release.storePassword
  335. keyAlias keystores.release.keyAlias
  336. keyPassword keystores.release.keyPassword
  337. }
  338. } else {
  339. logger.warn("No release keystore found. Falling back to locally generated keystore.")
  340. }
  341. // Release config
  342. if (keystores.hms_release != null) {
  343. hms_release {
  344. storeFile file(keystores.hms_release.storeFile)
  345. storePassword keystores.hms_release.storePassword
  346. keyAlias keystores.hms_release.keyAlias
  347. keyPassword keystores.hms_release.keyPassword
  348. }
  349. } else {
  350. logger.warn("No hms keystore found. Falling back to locally generated keystore.")
  351. }
  352. // Onprem release config
  353. if (keystores.onprem_release != null) {
  354. onprem_release {
  355. storeFile file(keystores.onprem_release.storeFile)
  356. storePassword keystores.onprem_release.storePassword
  357. keyAlias keystores.onprem_release.keyAlias
  358. keyPassword keystores.onprem_release.keyPassword
  359. }
  360. } else {
  361. logger.warn("No onprem keystore found. Falling back to locally generated keystore.")
  362. }
  363. // Red release config
  364. if (keystores.red_release != null) {
  365. red_release {
  366. storeFile file(keystores.red_release.storeFile)
  367. storePassword keystores.red_release.storePassword
  368. keyAlias keystores.red_release.keyAlias
  369. keyPassword keystores.red_release.keyPassword
  370. }
  371. } else {
  372. logger.warn("No red keystore found. Falling back to locally generated keystore.")
  373. }
  374. // F-Droid release config
  375. if (keystores.fdroid_release != null) {
  376. fdroid_release {
  377. storeFile file(keystores.fdroid_release.storeFile)
  378. storePassword keystores.fdroid_release.storePassword
  379. keyAlias keystores.fdroid_release.keyAlias
  380. keyPassword keystores.fdroid_release.keyPassword
  381. }
  382. } else {
  383. logger.warn("No fdroid keystore found. Falling back to locally generated keystore.")
  384. }
  385. }
  386. sourceSets {
  387. main {
  388. assets.srcDirs = ['assets']
  389. jniLibs.srcDirs = ['libs']
  390. }
  391. // Based on Google services
  392. none {
  393. java.srcDir 'src/google_services_based/java'
  394. }
  395. store_google {
  396. java.srcDir 'src/google_services_based/java'
  397. }
  398. store_google_work {
  399. java.srcDir 'src/google_services_based/java'
  400. }
  401. store_threema {
  402. java.srcDir 'src/google_services_based/java'
  403. }
  404. fdroid {
  405. assets.srcDirs = ['src/foss_based/assets']
  406. java.srcDir 'src/foss_based/java'
  407. }
  408. onprem {
  409. java.srcDir 'src/google_services_based/java'
  410. }
  411. sandbox {
  412. java.srcDir 'src/google_services_based/java'
  413. manifest.srcFile 'src/store_google/AndroidManifest.xml'
  414. }
  415. sandbox_work {
  416. java.srcDirs = ['src/store_google_work/java', 'src/google_services_based/java']
  417. res.srcDir 'src/store_google_work/res'
  418. manifest.srcFile 'src/store_google_work/AndroidManifest.xml'
  419. }
  420. red {
  421. java.srcDir 'src/google_services_based/java'
  422. res.srcDir 'src/red/res'
  423. }
  424. // Based on Huawei services
  425. hms {
  426. java.srcDir 'src/hms_services_based/java'
  427. }
  428. hms_work {
  429. java.srcDir 'src/hms_services_based/java'
  430. res.srcDir 'src/store_google_work/res'
  431. }
  432. // FOSS, no proprietary services
  433. fdroid {
  434. assets.srcDirs = ['src/foss_based/assets']
  435. java.srcDir 'src/foss_based/java'
  436. }
  437. }
  438. buildTypes {
  439. debug {
  440. debuggable true
  441. jniDebuggable false
  442. multiDexEnabled true
  443. multiDexKeepProguard file('multidex-keep.pro')
  444. testCoverageEnabled false
  445. if (keystores['debug'] != null) {
  446. signingConfig signingConfigs.debug
  447. }
  448. }
  449. release {
  450. debuggable false
  451. jniDebuggable false
  452. minifyEnabled true
  453. shrinkResources false // Caused inconsistencies between local and CI builds
  454. zipAlignEnabled true
  455. multiDexEnabled true
  456. multiDexKeepProguard file('multidex-keep.pro')
  457. proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-project.txt'
  458. if (keystores['release'] != null) {
  459. productFlavors.store_google.signingConfig signingConfigs.release
  460. productFlavors.store_google_work.signingConfig signingConfigs.release
  461. productFlavors.store_threema.signingConfig signingConfigs.release
  462. productFlavors.sandbox.signingConfig signingConfigs.release
  463. productFlavors.sandbox_work.signingConfig signingConfigs.release
  464. productFlavors.none.signingConfig signingConfigs.release
  465. }
  466. if (keystores['hms_release'] != null) {
  467. productFlavors.hms.signingConfig signingConfigs.hms_release
  468. productFlavors.hms_work.signingConfig signingConfigs.hms_release
  469. }
  470. if (keystores['onprem_release'] != null) {
  471. productFlavors.onprem.signingConfig signingConfigs.onprem_release
  472. }
  473. if (keystores['red_release'] != null) {
  474. productFlavors.red.signingConfig signingConfigs.red_release
  475. }
  476. if (keystores['fdroid_release'] != null) {
  477. productFlavors.fdroid.signingConfig signingConfigs.fdroid_release
  478. }
  479. }
  480. }
  481. // Only build relevant buildType / flavor combinations
  482. variantFilter { variant ->
  483. def names = variant.flavors*.name
  484. if (
  485. variant.buildType.name == "release" && (
  486. names.contains("sandbox") || names.contains("sandbox_work")
  487. )
  488. ) {
  489. setIgnore(true)
  490. }
  491. }
  492. externalNativeBuild {
  493. ndkBuild {
  494. path 'jni/Android.mk'
  495. }
  496. }
  497. lintOptions {
  498. // set to true to have all release builds run lint on issues with severity=fatal
  499. // and abort the build (controlled by abortOnError above) if fatal issues are found
  500. checkReleaseBuilds true
  501. // check dependencies
  502. checkDependencies true
  503. // set to true to turn off analysis progress reporting by lint
  504. // quiet true
  505. // if true, stop the gradle build if errors are found
  506. abortOnError true
  507. // if true, only report errors
  508. ignoreWarnings false
  509. // if true, emit full/absolute paths to files with errors (true by default)
  510. //absolutePaths true
  511. // if true, check all issues, including those that are off by default
  512. checkAllWarnings true
  513. // if true, treat all warnings as errors
  514. warningsAsErrors false
  515. // turn off checking the given issue id's
  516. disable 'TypographyFractions', 'TypographyQuotes'
  517. // turn on the given issue id's
  518. disable 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
  519. // check *only* the given issue id's
  520. // check 'NewApi', 'InlinedApi'
  521. // if true, don't include source code lines in the error output
  522. noLines false
  523. // if true, show all locations for an error, do not truncate lists, etc.
  524. showAll true
  525. // if true, generate an XML report for use by for example Jenkins
  526. xmlReport true
  527. // file to write report to (if not specified, defaults to lint-results.xml)
  528. xmlOutput file("lint-report.xml")
  529. // Set the severity of the given issues to fatal (which means they will be
  530. // checked during release builds (even if the lint target is not included)
  531. fatal 'NewApi', 'InlinedApi'
  532. // Set the severity of the given issues to error
  533. error 'Wakelock', 'TextViewEdits', 'ResourceAsColor'
  534. // Set the severity of the given issues to warning
  535. warning 'MissingTranslation'
  536. // Set the severity of the given issues to ignore (same as disabling the check)
  537. ignore 'TypographyQuotes'
  538. }
  539. packagingOptions {
  540. exclude 'META-INF/DEPENDENCIES.txt'
  541. exclude 'META-INF/LICENSE.txt'
  542. exclude 'META-INF/NOTICE.txt'
  543. exclude 'META-INF/NOTICE'
  544. exclude 'META-INF/LICENSE'
  545. exclude 'META-INF/DEPENDENCIES'
  546. exclude 'META-INF/notice.txt'
  547. exclude 'META-INF/license.txt'
  548. exclude 'META-INF/dependencies.txt'
  549. exclude 'META-INF/LGPL2.1'
  550. exclude '**/*.proto'
  551. // fix https://stackoverflow.com/questions/42739916/aarch64-linux-android-strip-file-missing
  552. doNotStrip '*/mips/*.so'
  553. doNotStrip '*/mips64/*.so'
  554. doNotStrip '*/armeabi/*.so'
  555. }
  556. testOptions {
  557. // Disable animations in instrumentation tests
  558. animationsDisabled true
  559. unitTests {
  560. all {
  561. // All the usual Gradle options.
  562. testLogging {
  563. events "passed", "skipped", "failed", "standardOut", "standardError"
  564. outputs.upToDateWhen { false }
  565. exceptionFormat = 'full'
  566. }
  567. }
  568. // By default, local unit tests throw an exception any time the code you are testing tries to access
  569. // Android platform APIs (unless you mock Android dependencies yourself or with a testing
  570. // framework like Mockito). However, you can enable the following property so that the test
  571. // returns either null or zero when accessing platform APIs, rather than throwing an exception.
  572. returnDefaultValues true
  573. }
  574. }
  575. compileOptions {
  576. sourceCompatibility JavaVersion.VERSION_11
  577. targetCompatibility JavaVersion.VERSION_11
  578. }
  579. kotlin {
  580. jvmToolchain {
  581. languageVersion.set(JavaLanguageVersion.of(11))
  582. }
  583. }
  584. aaptOptions {
  585. noCompress 'png'
  586. }
  587. }
  588. dependencies {
  589. configurations.all {
  590. // Prefer modules that are part of this build (multi-project or composite build)
  591. // over external modules
  592. resolutionStrategy.preferProjectModules()
  593. // Alternatively, we can fail eagerly on version conflict to see the conflicts
  594. //resolutionStrategy.failOnVersionConflict()
  595. }
  596. implementation project(':domain')
  597. implementation 'net.zetetic:android-database-sqlcipher:4.5.1'
  598. implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
  599. implementation 'net.sf.opencsv:opencsv:2.3'
  600. implementation 'net.lingala.zip4j:zip4j:2.9.1'
  601. implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
  602. implementation 'org.maplibre.gl:android-sdk:9.5.2'
  603. // commons-io >2.6 requires android 8
  604. implementation 'commons-io:commons-io:2.6'
  605. implementation "org.slf4j:slf4j-api:$slf4j_version"
  606. implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.24'
  607. implementation 'com.github.CanHub:Android-Image-Cropper:4.1.0'
  608. implementation 'com.datatheorem.android.trustkit:trustkit:1.1.5'
  609. implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
  610. implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3'
  611. // AndroidX / Jetpack support libraries
  612. implementation "androidx.core:core-ktx:1.7.0"
  613. implementation "androidx.preference:preference-ktx:1.2.0"
  614. implementation 'androidx.recyclerview:recyclerview:1.2.1'
  615. implementation 'androidx.palette:palette-ktx:1.0.0'
  616. implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
  617. implementation 'androidx.appcompat:appcompat:1.4.1'
  618. implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
  619. implementation 'androidx.biometric:biometric:1.1.0'
  620. implementation "androidx.work:work-runtime:2.7.1"
  621. implementation 'androidx.fragment:fragment-ktx:1.4.1'
  622. implementation 'androidx.activity:activity-ktx:1.4.0'
  623. implementation 'androidx.sqlite:sqlite:2.1.0'
  624. implementation "androidx.concurrent:concurrent-futures:1.1.0"
  625. implementation "androidx.camera:camera-camera2:1.1.0-beta02"
  626. implementation "androidx.camera:camera-lifecycle:1.1.0-beta02"
  627. implementation "androidx.camera:camera-view:1.1.0-beta02"
  628. implementation 'androidx.multidex:multidex:2.0.1'
  629. implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
  630. implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
  631. implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
  632. implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.1"
  633. implementation "androidx.lifecycle:lifecycle-service:2.4.1"
  634. implementation "androidx.lifecycle:lifecycle-process:2.4.1"
  635. implementation "androidx.lifecycle:lifecycle-common-java8:2.4.1"
  636. implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
  637. implementation "androidx.paging:paging-runtime:3.1.1"
  638. implementation "androidx.sharetarget:sharetarget:1.1.0"
  639. implementation 'com.google.android.material:material:1.5.0'
  640. implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1'
  641. implementation 'com.google.android.exoplayer:exoplayer-ui:2.17.1'
  642. implementation 'com.google.zxing:core:3.3.3' // zxing 3.4 crashes on API < 24
  643. implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.45' // make sure to update this in domain's build.gradle as well
  644. // webclient dependencies
  645. implementation 'org.msgpack:msgpack-core:0.8.22!!'
  646. implementation 'com.neovisionaries:nv-websocket-client:2.9'
  647. // Backport of Streams and CompletableFuture. Remove once API level 24 is supported.
  648. implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.2'
  649. implementation('org.saltyrtc:saltyrtc-client:0.14.2') {
  650. exclude group: 'org.json'
  651. }
  652. implementation 'org.saltyrtc:chunked-dc:1.0.1'
  653. implementation 'ch.threema:webrtc-android:94.0.0'
  654. implementation('org.saltyrtc:saltyrtc-task-webrtc:0.18.1') {
  655. exclude module: 'saltyrtc-client'
  656. }
  657. // Glide components
  658. implementation 'com.github.bumptech.glide:glide:4.12.0'
  659. annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
  660. // kotlin
  661. implementation "androidx.core:core-ktx:1.7.0"
  662. implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  663. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
  664. // use leak canary in debug builds
  665. // debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
  666. // test dependencies
  667. testImplementation "junit:junit:$junit_version"
  668. testImplementation(testFixtures(project(":domain")))
  669. // use powermock instead of mockito. it support mocking static classes.
  670. def mockitoVersion = '2.0.9'
  671. testImplementation "org.powermock:powermock-api-mockito2:${mockitoVersion}"
  672. testImplementation "org.powermock:powermock-module-junit4-rule-agent:${mockitoVersion}"
  673. testImplementation "org.powermock:powermock-module-junit4-rule:${mockitoVersion}"
  674. testImplementation "org.powermock:powermock-module-junit4:${mockitoVersion}"
  675. // add JSON support to tests without mocking
  676. testImplementation 'org.json:json:20190722'
  677. testImplementation 'com.tngtech.archunit:archunit-junit4:0.18.0'
  678. androidTestImplementation(testFixtures(project(":domain")))
  679. androidTestImplementation 'androidx.test:rules:1.4.0'
  680. androidTestImplementation 'tools.fastlane:screengrab:2.0.0', {
  681. exclude group: 'androidx.annotation', module: 'annotation'
  682. }
  683. androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0', {
  684. exclude group: 'androidx.annotation', module: 'annotation'
  685. }
  686. androidTestImplementation 'androidx.test:runner:1.1.0', {
  687. exclude group: 'androidx.annotation', module: 'annotation'
  688. }
  689. androidTestImplementation 'androidx.test.ext:junit:1.1.3'
  690. androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0', {
  691. exclude group: 'androidx.annotation', module: 'annotation'
  692. exclude group: 'androidx.appcompat', module: 'appcompat'
  693. exclude group: 'androidx.legacy', module: 'legacy-support-v4'
  694. exclude group: 'com.google.android.material', module: 'material'
  695. exclude group: 'androidx.recyclerview', module: 'recyclerview'
  696. }
  697. androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0', {
  698. exclude group: 'androidx.annotation', module: 'annotation'
  699. }
  700. androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
  701. // Google Play Services and related libraries
  702. def googleDependencies = [
  703. // Play services
  704. 'com.google.android.gms:play-services-base:16.1.0': [],
  705. // Firebase push
  706. //
  707. // Note: Do not upgrade to a higher version of firebase-messaging,
  708. // as we do not want the Firebase Installations API in our app
  709. 'com.google.firebase:firebase-messaging:20.1.0': [
  710. [group: 'com.google.firebase', module: 'firebase-core'],
  711. [group: 'com.google.firebase', module: 'firebase-analytics'],
  712. [group: 'com.google.firebase', module: 'firebase-measurement-connector'],
  713. ],
  714. ]
  715. googleDependencies.each {
  716. def dependency = it.key
  717. def excludes = it.value
  718. noneImplementation(dependency) { excludes.each { exclude it } }
  719. store_googleImplementation(dependency) { excludes.each { exclude it } }
  720. store_google_workImplementation(dependency) { excludes.each { exclude it } }
  721. store_threemaImplementation(dependency) { excludes.each { exclude it } }
  722. fdroidImplementation(dependency) { excludes.each { exclude it } }
  723. onpremImplementation(dependency) { excludes.each { exclude it } }
  724. sandboxImplementation(dependency) { excludes.each { exclude it } }
  725. sandbox_workImplementation(dependency) { excludes.each { exclude it } }
  726. redImplementation(dependency) { excludes.each { exclude it } }
  727. }
  728. // Google Assistant Voice Action verification library
  729. noneImplementation(name: 'libgsaverification-client', ext: 'aar')
  730. store_googleImplementation(name: 'libgsaverification-client', ext: 'aar')
  731. store_google_workImplementation(name: 'libgsaverification-client', ext: 'aar')
  732. onpremImplementation(name: 'libgsaverification-client', ext: 'aar')
  733. store_threemaImplementation(name: 'libgsaverification-client', ext: 'aar')
  734. fdroidImplementation(name: 'libgsaverification-client', ext: 'aar')
  735. sandboxImplementation(name: 'libgsaverification-client', ext: 'aar')
  736. sandbox_workImplementation(name: 'libgsaverification-client', ext: 'aar')
  737. redImplementation(name: 'libgsaverification-client', ext: 'aar')
  738. // Huawei related libraries (only for hms* build variants)
  739. def huaweiDependencies = [
  740. // HMS push
  741. 'com.huawei.hms:push:5.0.4.302': [
  742. // Exclude agconnect dependency, we'll replace it with the vendored version below
  743. [group: 'com.huawei.agconnect'],
  744. ],
  745. ]
  746. huaweiDependencies.each {
  747. def dependency = it.key
  748. def excludes = it.value
  749. hmsImplementation(dependency) { excludes.each { exclude it } }
  750. hms_workImplementation(dependency) { excludes.each { exclude it } }
  751. }
  752. hmsImplementation(name: 'agconnect-core-1.4.0.300', ext: 'aar')
  753. hms_workImplementation(name: 'agconnect-core-1.4.0.300', ext: 'aar')
  754. }
  755. sonarqube {
  756. properties {
  757. property "sonar.sources", "src/main/, ../scripts/, ../scripts-internal/"
  758. property "sonar.exclusions", "src/main/java/ch/threema/localcrypto/**, src/test/java/ch/threema/localcrypto/**"
  759. property "sonar.tests", "src/test/"
  760. property "sonar.sourceEncoding", "UTF-8"
  761. property "sonar.verbose", "true"
  762. property 'sonar.projectKey', 'android-client'
  763. property 'sonar.projectName', 'Threema for Android'
  764. }
  765. }
  766. // Set up Gradle tasks to fetch screenshots on UI test failures
  767. // See https://medium.com/stepstone-tech/how-to-capture-screenshots-for-failed-ui-tests-9927eea6e1e4
  768. def reportsDirectory = "$buildDir/reports/androidTests/connected"
  769. def screenshotsDirectory = "/sdcard/testfailures/screenshots/"
  770. def clearScreenshotsTask = task('clearScreenshots', type: Exec) {
  771. executable "${android.getAdbExe().toString()}"
  772. args 'shell', 'rm', '-r', screenshotsDirectory
  773. }
  774. def createScreenshotsDirectoryTask = task('createScreenshotsDirectory', type: Exec, group: 'reporting') {
  775. executable "${android.getAdbExe().toString()}"
  776. args 'shell', 'mkdir', '-p', screenshotsDirectory
  777. }
  778. def fetchScreenshotsTask = task('fetchScreenshots', type: Exec, group: 'reporting') {
  779. executable "${android.getAdbExe().toString()}"
  780. args 'pull', screenshotsDirectory + '.', reportsDirectory
  781. finalizedBy {
  782. clearScreenshotsTask
  783. }
  784. dependsOn {
  785. createScreenshotsDirectoryTask
  786. }
  787. doFirst {
  788. new File(reportsDirectory).mkdirs()
  789. }
  790. }
  791. tasks.whenTaskAdded { task ->
  792. if (task.name == 'connectedDebugAndroidTest') {
  793. task.finalizedBy {
  794. fetchScreenshotsTask
  795. }
  796. }
  797. }