Bug 1845354 - Refactor ApkSizeTask into a plugin using composite build
[gecko.git] / mobile / android / fenix / app / build.gradle
blobfa0dadbc7acb1a655075a04a3f5b1fa3b5e350ab
1 import org.apache.tools.ant.util.StringUtils
3 plugins {
4     id "com.jetbrains.python.envs" version "$python_envs_plugin"
5     id "com.google.protobuf" version "$protobuf_plugin"
8 apply plugin: 'com.android.application'
9 apply plugin: 'kotlin-android'
10 apply plugin: 'kotlin-parcelize'
11 apply plugin: 'jacoco'
12 apply plugin: 'androidx.navigation.safeargs.kotlin'
13 apply plugin: 'com.google.android.gms.oss-licenses-plugin'
15 import com.android.build.OutputFile
16 import groovy.json.JsonOutput
17 import org.gradle.internal.logging.text.StyledTextOutput.Style
18 import org.gradle.internal.logging.text.StyledTextOutputFactory
20 import static org.gradle.api.tasks.testing.TestResult.ResultType
22 apply from: 'benchmark.gradle'
24 android {
25     compileSdkVersion config.compileSdkVersion
27     project.maybeConfigForJetpackBenchmark(it)
28     if (project.hasProperty("testBuildType")) {
29         // Allowing to configure the test build type via command line flag (./gradlew -PtestBuildType=beta ..)
30         // in order to run UI tests against other build variants than debug in automation.
31         testBuildType project.property("testBuildType")
32     }
34     defaultConfig {
35         applicationId "org.mozilla"
36         minSdkVersion config.minSdkVersion
37         targetSdkVersion config.targetSdkVersion
38         versionCode 1
39         versionName Config.generateDebugVersionName()
40         vectorDrawables.useSupportLibrary = true
41         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
42         testInstrumentationRunnerArguments clearPackageData: 'true'
43         resValue "bool", "IS_DEBUG", "false"
44         buildConfigField "boolean", "USE_RELEASE_VERSIONING", "false"
45         buildConfigField "String", "GIT_HASH", "\"\"" // see override in release builds for why it's blank.
46         // This should be the "public" base URL of AMO.
47         buildConfigField "String", "AMO_BASE_URL", "\"https://addons.mozilla.org\""
48         buildConfigField "String", "AMO_COLLECTION_NAME", "\"Extensions-for-Android\""
49         buildConfigField "String", "AMO_COLLECTION_USER", "\"mozilla\""
50         // These add-ons should be excluded for Mozilla Online builds.
51         buildConfigField "String[]", "MOZILLA_ONLINE_ADDON_EXCLUSIONS",
52                 "{" +
53                         "\"uBlock0@raymondhill.net\"," +
54                         "\"firefox@ghostery.com\"," +
55                         "\"jid1-MnnxcxisBPnSXQ@jetpack\"," +
56                         "\"adguardadblocker@adguard.com\"," +
57                         "\"foxyproxy@eric.h.jung\"," +
58                         "\"{73a6fe31-595d-460b-a920-fcc0f8843232}\"," +
59                         "\"jid1-BoFifL9Vbdl2zQ@jetpack\"," +
60                         "\"woop-NoopscooPsnSXQ@jetpack\"," +
61                         "\"adnauseam@rednoise.org\"" +
62                 "}"
63         // This should be the base URL used to call the AMO API.
64         buildConfigField "String", "AMO_SERVER_URL", "\"https://services.addons.mozilla.org\""
66         def deepLinkSchemeValue = "fenix-dev"
67         buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
69         // This allows overriding the target activity for MozillaOnline builds, which happens
70         // as part of the defaultConfig below.
71         def targetActivity = "HomeActivity"
73         // Build flag for "Mozilla Online" variants. See `Config.isMozillaOnline`.
74         if (project.hasProperty("mozillaOnline") || gradle.hasProperty("localProperties.mozillaOnline")) {
75             buildConfigField "boolean", "MOZILLA_ONLINE", "true"
76             targetActivity = "MozillaOnlineHomeActivity"
77         } else {
78             buildConfigField "boolean", "MOZILLA_ONLINE", "false"
79         }
81         manifestPlaceholders = [
82                 "targetActivity": targetActivity,
83                 "deepLinkScheme": deepLinkSchemeValue
84         ]
85     }
87     def releaseTemplate = {
88         // We allow disabling optimization by passing `-PdisableOptimization` to gradle. This is used
89         // in automation for UI testing non-debug builds.
90         shrinkResources !project.hasProperty("disableOptimization")
91         minifyEnabled !project.hasProperty("disableOptimization")
92         proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
93         matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs)
95         // Changing the build config can cause files that depend on BuildConfig.java to recompile
96         // so we only set the git hash in release builds to avoid possible recompilation in debug builds.
97         buildConfigField "String", "GIT_HASH", "\"${Config.getGitHash()}\""
99         if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey")) {
100             signingConfig signingConfigs.debug
101         }
103         if (gradle.hasProperty("localProperties.debuggable")) {
104             debuggable true
105         }
106     }
108     buildTypes {
109         debug {
110             shrinkResources false
111             minifyEnabled false
112             applicationIdSuffix ".fenix.debug"
113             resValue "bool", "IS_DEBUG", "true"
114             pseudoLocalesEnabled true
115         }
116         nightly releaseTemplate >> {
117             applicationIdSuffix ".fenix"
118             buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
119             def deepLinkSchemeValue = "fenix-nightly"
120             buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
121             manifestPlaceholders.putAll([
122                     "deepLinkScheme": deepLinkSchemeValue
123             ])
124         }
125         beta releaseTemplate >> {
126             buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
127             applicationIdSuffix ".firefox_beta"
128             def deepLinkSchemeValue = "fenix-beta"
129             buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
130             manifestPlaceholders.putAll([
131                     // This release type is meant to replace Firefox (Beta channel) and therefore needs to inherit
132                     // its sharedUserId for all eternity. See:
133                     // https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false&regexp=false&path=
134                     // Shipping an app update without sharedUserId can have
135                     // fatal consequences. For example see:
136                     //  - https://issuetracker.google.com/issues/36924841
137                     //  - https://issuetracker.google.com/issues/36905922
138                     "sharedUserId": "org.mozilla.firefox.sharedID",
139                     "deepLinkScheme": deepLinkSchemeValue,
140             ])
141         }
142         release releaseTemplate >> {
143             buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
144             applicationIdSuffix ".firefox"
145             def deepLinkSchemeValue = "fenix"
146             buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
147             manifestPlaceholders.putAll([
148                     // This release type is meant to replace Firefox (Release channel) and therefore needs to inherit
149                     // its sharedUserId for all eternity. See:
150                     // https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false&regexp=false&path=
151                     // Shipping an app update without sharedUserId can have
152                     // fatal consequences. For example see:
153                     //  - https://issuetracker.google.com/issues/36924841
154                     //  - https://issuetracker.google.com/issues/36905922
155                     "sharedUserId": "org.mozilla.firefox.sharedID",
156                     "deepLinkScheme": deepLinkSchemeValue,
157             ])
158         }
159         benchmark releaseTemplate >> {
160             initWith buildTypes.nightly
161             applicationIdSuffix ".fenix"
162             debuggable false
163         }
164     }
166     buildFeatures {
167         viewBinding true
168     }
170     androidResources {
171         // All JavaScript code used internally by GeckoView is packaged in a
172         // file called omni.ja. If this file is compressed in the APK,
173         // GeckoView must uncompress it before it can do anything else which
174         // causes a significant delay on startup.
175         noCompress 'ja'
177         // manifest.template.json is converted to manifest.json at build time.
178         // No need to package the template in the APK.
179         ignoreAssetsPattern "manifest.template.json"
180     }
182     testOptions {
183         execution 'ANDROIDX_TEST_ORCHESTRATOR'
184         unitTests.includeAndroidResources = true
185         animationsDisabled = true
186     }
188     flavorDimensions "engine"
190     flavorDimensions "product"
192     productFlavors {
193         fenix {
194             dimension "product"
195         }
196     }
198     sourceSets {
199         androidTest {
200             resources.srcDirs += ['src/androidTest/resources']
201         }
202     }
204     splits {
205         abi {
206             enable true
208             reset()
210             include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
211         }
212     }
214     compileOptions {
215         sourceCompatibility JavaVersion.VERSION_17
216         targetCompatibility JavaVersion.VERSION_17
217     }
219     lint {
220         lintConfig file("lint.xml")
221         baseline file("lint-baseline.xml")
222     }
223     packagingOptions {
224         resources {
225             excludes += ['META-INF/atomicfu.kotlin_module', 'META-INF/AL2.0', 'META-INF/LGPL2.1',
226                          'META-INF/LICENSE.md', 'META-INF/LICENSE-notice.md']
227         }
228     }
231     testOptions {
232         unitTests.returnDefaultValues = true
234         unitTests.all {
235             // We keep running into memory issues when running our tests. With this config we
236             // reserve more memory and also create a new process after every 80 test classes. This
237             // is a band-aid solution and eventually we should try to find and fix the leaks
238             // instead. :)
239             forkEvery = 80
240             maxHeapSize = "3072m"
241             minHeapSize = "1024m"
242         }
243     }
245     buildFeatures {
246         compose true
247     }
249     composeOptions {
250         kotlinCompilerExtensionVersion = Versions.compose_compiler
251     }
253     namespace 'org.mozilla.fenix'
256 android.applicationVariants.all { variant ->
258 // -------------------------------------------------------------------------------------------------
259 // Generate version codes for builds
260 // -------------------------------------------------------------------------------------------------
262     def isDebug = variant.buildType.resValues['bool/IS_DEBUG']?.value ?: false
263     def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false
265     println("----------------------------------------------")
266     println("Variant name:      " + variant.name)
267     println("Application ID:    " + [variant.applicationId, variant.buildType.applicationIdSuffix].findAll().join())
268     println("Build type:        " + variant.buildType.name)
269     println("Flavor:            " + variant.flavorName)
270     println("Telemetry enabled: " + !isDebug)
272     if (useReleaseVersioning) {
273         // The Google Play Store does not allow multiple APKs for the same app that all have the
274         // same version code. Therefore we need to have different version codes for our ARM and x86
275         // builds.
277         def versionName = variant.buildType.name == 'nightly' ? Config.nightlyVersionName() : Config.releaseVersionName(project)
278         println("versionName override: $versionName")
280         variant.outputs.each { output ->
281             def isMozillaOnline = project.hasProperty("mozillaOnline") || gradle.hasProperty("localProperties.mozillaOnline")
282             def abi = output.getFilter(OutputFile.ABI)
283             // If it is a Mozilla Online build, use a unified version code of armeabi-v7a
284             def arch = (isMozillaOnline) ? "armeabi-v7a" : abi
285             // We use the same version code generator, that we inherited from Fennec, across all channels - even on
286             // channels that never shipped a Fennec build.
287             def versionCodeOverride = Config.generateFennecVersionCode(arch)
289             println("versionCode for $abi = $versionCodeOverride, isMozillaOnline = $isMozillaOnline")
291             if (versionName != null) {
292                 output.versionNameOverride = versionName
293             }
294             output.versionCodeOverride = versionCodeOverride
295         }
296     } else if (gradle.hasProperty("localProperties.branchBuild.fenix.version")) {
297         def versionName = gradle.getProperty("localProperties.branchBuild.fenix.version")
298         println("versionName override: $versionName")
299         variant.outputs.each { output ->
300             output.versionNameOverride = versionName
301         }
302     }
304 // -------------------------------------------------------------------------------------------------
305 // BuildConfig: Set variables for Sentry, Crash Reporting, and Telemetry
306 // -------------------------------------------------------------------------------------------------
308     buildConfigField 'String', 'SENTRY_TOKEN', 'null'
309     if (!isDebug) {
310         buildConfigField 'boolean', 'CRASH_REPORTING', 'true'
311         // Reading sentry token from local file (if it exists). In a release task on taskcluster it will be available.
312         try {
313             def token = new File("${rootDir}/.sentry_token").text.trim()
314             buildConfigField 'String', 'SENTRY_TOKEN', '"' + token + '"'
315         } catch (FileNotFoundException ignored) {}
316     } else {
317         buildConfigField 'boolean', 'CRASH_REPORTING', 'false'
318     }
320     if (!isDebug) {
321         buildConfigField 'boolean', 'TELEMETRY', 'true'
322     } else {
323         buildConfigField 'boolean', 'TELEMETRY', 'false'
324     }
326     def buildDate = Config.generateBuildDate()
327     // Setting buildDate with every build changes the generated BuildConfig, which slows down the
328     // build. Only do this for non-debug builds, to speed-up builds produced during local development.
329     if (isDebug) {
330         buildConfigField 'String', 'BUILD_DATE', '"debug build"'
331     } else {
332         buildConfigField 'String', 'BUILD_DATE', '"' + buildDate + '"'
333     }
335 // -------------------------------------------------------------------------------------------------
336 // Adjust: Read token from local file if it exists (Only release builds)
337 // -------------------------------------------------------------------------------------------------
339     print("Adjust token: ")
341     if (!isDebug) {
342         try {
343             def token = new File("${rootDir}/.adjust_token").text.trim()
344             buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"'
345             println "(Added from .adjust_token file)"
346         } catch (FileNotFoundException ignored) {
347             buildConfigField 'String', 'ADJUST_TOKEN', 'null'
348             println("X_X")
349         }
350     } else {
351         buildConfigField 'String', 'ADJUST_TOKEN', 'null'
352         println("--")
353     }
355 // -------------------------------------------------------------------------------------------------
356 // MLS: Read token from local file if it exists
357 // -------------------------------------------------------------------------------------------------
359     print("MLS token: ")
361     try {
362         def token = new File("${rootDir}/.mls_token").text.trim()
363         buildConfigField 'String', 'MLS_TOKEN', '"' + token + '"'
364         println "(Added from .mls_token file)"
365     } catch (FileNotFoundException ignored) {
366         buildConfigField 'String', 'MLS_TOKEN', '""'
367         println("X_X")
368     }
370 // -------------------------------------------------------------------------------------------------
371 // Nimbus: Read endpoint from local.properties of a local file if it exists
372 // -------------------------------------------------------------------------------------------------
374     print("Nimbus endpoint: ")
376     if (!isDebug) {
377         try {
378             def url = new File("${rootDir}/.nimbus").text.trim()
379             buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"'
380             println "(Added from .nimbus file)"
381         } catch (FileNotFoundException ignored) {
382             buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
383             println("X_X")
384         }
385     } else if (gradle.hasProperty("localProperties.nimbus.remote-settings.url")) {
386         def url=gradle.getProperty("localProperties.nimbus.remote-settings.url")
387         buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"'
388         println "(Added from local.properties file)"
389     } else {
390         buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
391         println("--")
392     }
394 // -------------------------------------------------------------------------------------------------
395 // Glean: Read custom server URL from local.properties of a local file if it exists
396 // -------------------------------------------------------------------------------------------------
398     print("Glean custom server URL: ")
400     if (gradle.hasProperty("localProperties.glean.custom.server.url")) {
401         def url=gradle.getProperty("localProperties.glean.custom.server.url")
402         buildConfigField 'String', 'GLEAN_CUSTOM_URL', url
403         println "(Added from local.properties file)"
404     } else {
405         buildConfigField 'String', 'GLEAN_CUSTOM_URL', 'null'
406         println("--")
407     }
409 // -------------------------------------------------------------------------------------------------
410 // BuildConfig: Set flag for official builds; similar to MOZILLA_OFFICIAL in mozilla-central.
411 // -------------------------------------------------------------------------------------------------
413     if (project.hasProperty("official") || gradle.hasProperty("localProperties.official")) {
414         buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'true'
415     } else {
416         buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'false'
417     }
419 // -------------------------------------------------------------------------------------------------
420 // BuildConfig: Set remote wallpaper URL using local file if it exists
421 // -------------------------------------------------------------------------------------------------
423     print("Wallpaper URL: ")
425     try {
426         def token = new File("${rootDir}/.wallpaper_url").text.trim()
427         buildConfigField 'String', 'WALLPAPER_URL', '"' + token + '"'
428         println "(Added from .wallpaper_url file)"
429     } catch (FileNotFoundException ignored) {
430         buildConfigField 'String', 'WALLPAPER_URL', '""'
431         println("--")
432     }
434 // -------------------------------------------------------------------------------------------------
435 // BuildConfig: Set the Pocket consumer key from a local file if it exists
436 // -------------------------------------------------------------------------------------------------
438     print("Pocket consumer key: ")
440     try {
441         def token = new File("${rootDir}/.pocket_consumer_key").text.trim()
442         buildConfigField 'String', 'POCKET_CONSUMER_KEY', '"' + token + '"'
443         println "(Added from .pocket_consumer_key file)"
444     } catch (FileNotFoundException ignored) {
445         buildConfigField 'String', 'POCKET_CONSUMER_KEY', '""'
446         println("--")
447     }
450 // Generate Kotlin code for the Fenix Glean metrics.
451 apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
452 apply plugin: "org.mozilla.appservices.nimbus-gradle-plugin"
454 nimbus {
455     // The path to the Nimbus feature manifest file
456     manifestFile = "nimbus.fml.yaml"
457     // The fully qualified class name for the generated features.
458     // Map from the variant name to the channel as experimenter and nimbus understand it.
459     // If nimbus's channels were accurately set up well for this project, then this
460     // shouldn't be needed.
461     channels = [
462             fenixDebug: "developer",
463             fenixNightly: "nightly",
464             fenixBeta: "beta",
465             fenixRelease: "release",
466             fenixBenchmark: "developer",
467     ]
468     // This is generated by the FML and should be checked into git.
469     // It will be fetched by Experimenter (the Nimbus experiment website)
470     // and used to inform experiment configuration.
471     experimenterManifest = ".experimenter.yaml"
474 tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
475     kotlinOptions {
476         freeCompilerArgs += [
477                 "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
478         ]
479     }
482 dependencies {
483     implementation project(':browser-engine-gecko')
485     implementation ComponentsDependencies.kotlin_coroutines
486     testImplementation ComponentsDependencies.testing_coroutines
487     implementation ComponentsDependencies.androidx_appcompat
488     implementation ComponentsDependencies.androidx_constraintlayout
489     implementation ComponentsDependencies.androidx_coordinatorlayout
490     implementation FenixDependencies.google_accompanist_drawablepainter
492     implementation ComponentsDependencies.thirdparty_sentry_latest
494     implementation project(':compose-awesomebar')
495     implementation project(':compose-cfr')
497     implementation project(':concept-awesomebar')
498     implementation project(':concept-base')
499     implementation project(':concept-engine')
500     implementation project(':concept-menu')
501     implementation project(':concept-push')
502     implementation project(':concept-storage')
503     implementation project(':concept-sync')
504     implementation project(':concept-toolbar')
505     implementation project(':concept-tabstray')
507     implementation project(':browser-domains')
508     implementation project(':browser-icons')
509     implementation project(':browser-menu')
510     implementation project(':browser-menu2')
511     implementation project(':browser-session-storage')
512     implementation project(':browser-state')
513     implementation project(':browser-storage-sync')
514     implementation project(':browser-tabstray')
515     implementation project(':browser-thumbnails')
516     implementation project(':browser-toolbar')
518     implementation project(':feature-addons')
519     implementation project(':feature-accounts')
520     implementation project(':feature-app-links')
521     implementation project(':feature-autofill')
522     implementation project(':feature-awesomebar')
523     implementation project(':feature-contextmenu')
524     implementation project(':feature-customtabs')
525     implementation project(':feature-downloads')
526     implementation project(':feature-intent')
527     implementation project(':feature-media')
528     implementation project(':feature-prompts')
529     implementation project(':feature-push')
530     implementation project(':feature-privatemode')
531     implementation project(':feature-pwa')
532     implementation project(':feature-qr')
533     implementation project(':feature-search')
534     implementation project(':feature-session')
535     implementation project(':feature-syncedtabs')
536     implementation project(':feature-toolbar')
537     implementation project(':feature-tabs')
538     implementation project(':feature-findinpage')
539     implementation project(':feature-logins')
540     implementation project(':feature-sitepermissions')
541     implementation project(':feature-readerview')
542     implementation project(':feature-tab-collections')
543     implementation project(':feature-recentlyclosed')
544     implementation project(':feature-top-sites')
545     implementation project(':feature-share')
546     implementation project(':feature-accounts-push')
547     implementation project(':feature-webauthn')
548     implementation project(':feature-webcompat')
549     implementation project(':feature-webnotifications')
550     implementation project(':feature-webcompat-reporter')
552     implementation project(':service-pocket')
553     implementation project(':service-contile')
554     implementation project(':service-digitalassetlinks')
555     implementation project(':service-sync-autofill')
556     implementation project(':service-sync-logins')
557     implementation project(':service-firefox-accounts')
558     implementation project(':service-glean')
559     implementation project(':service-location')
560     implementation project(':service-nimbus')
562     implementation project(':support-webextensions')
563     implementation project(':support-base')
564     implementation project(':support-rusterrors')
565     implementation project(':support-images')
566     implementation project(':support-ktx')
567     implementation project(':support-rustlog')
568     implementation project(':support-utils')
569     implementation project(':support-locale')
571     implementation project(':ui-colors')
572     implementation project(':ui-icons')
573     implementation project(':lib-publicsuffixlist')
574     implementation project(':ui-widgets')
575     implementation project(':ui-tabcounter')
577     implementation project(':lib-crash')
578     implementation project(':lib-crash-sentry')
579     implementation project(':lib-push-firebase')
580     implementation project(':lib-state')
581     implementation project(':lib-dataprotect')
583     debugImplementation ComponentsDependencies.leakcanary
584     debugImplementation ComponentsDependencies.androidx_compose_ui_tooling
586     implementation ComponentsDependencies.androidx_activity_compose
587     implementation FenixDependencies.androidx_activity_ktx
588     implementation ComponentsDependencies.androidx_annotation
589     implementation ComponentsDependencies.androidx_compose_ui
590     implementation ComponentsDependencies.androidx_compose_ui_tooling_preview
591     implementation ComponentsDependencies.androidx_compose_foundation
592     implementation ComponentsDependencies.androidx_compose_material
593     implementation FenixDependencies.androidx_legacy
594     implementation ComponentsDependencies.androidx_biometric
595     implementation FenixDependencies.androidx_paging
596     implementation ComponentsDependencies.androidx_preferences
597     implementation ComponentsDependencies.androidx_fragment
598     implementation FenixDependencies.androidx_navigation_fragment
599     implementation FenixDependencies.androidx_navigation_ui
600     implementation ComponentsDependencies.androidx_recyclerview
601     implementation FenixDependencies.androidx_lifecycle_common
602     implementation ComponentsDependencies.androidx_lifecycle_livedata
603     implementation ComponentsDependencies.androidx_lifecycle_process
604     implementation ComponentsDependencies.androidx_lifecycle_runtime
606     implementation ComponentsDependencies.androidx_lifecycle_viewmodel
607     implementation ComponentsDependencies.androidx_core
608     implementation ComponentsDependencies.androidx_core_ktx
609     implementation FenixDependencies.androidx_core_splashscreen
610     implementation FenixDependencies.androidx_transition
611     implementation ComponentsDependencies.androidx_work_runtime
612     implementation FenixDependencies.androidx_datastore
613     implementation ComponentsDependencies.androidx_data_store_preferences
614     implementation FenixDependencies.protobuf_javalite
615     implementation ComponentsDependencies.google_material
617     implementation FenixDependencies.adjust
618     implementation FenixDependencies.installreferrer // Required by Adjust
620     implementation FenixDependencies.google_ads_id // Required for the Google Advertising ID
622     // Required for in-app reviews
623     implementation FenixDependencies.google_play_review
624     implementation FenixDependencies.google_play_review_ktx
626     implementation FenixDependencies.androidx_profileinstaller
628     androidTestImplementation ComponentsDependencies.androidx_test_uiautomator
629     androidTestImplementation FenixDependencies.fastlane
630     // This Falcon version is added to maven central now required for Screengrab
631     androidTestImplementation FenixDependencies.falcon
633     androidTestImplementation ComponentsDependencies.androidx_compose_ui_test
635     androidTestImplementation ComponentsDependencies.androidx_espresso_core, {
636         exclude group: 'com.android.support', module: 'support-annotations'
637     }
639     androidTestImplementation(FenixDependencies.espresso_contrib) {
640         exclude module: 'appcompat-v7'
641         exclude module: 'support-v4'
642         exclude module: 'support-annotations'
643         exclude module: 'recyclerview-v7'
644         exclude module: 'design'
645         exclude module: 'espresso-core'
646         exclude module: 'protobuf-lite'
647     }
649     androidTestImplementation ComponentsDependencies.androidx_test_core
650     androidTestImplementation FenixDependencies.espresso_idling_resources
651     androidTestImplementation FenixDependencies.espresso_intents
653     androidTestImplementation ComponentsDependencies.androidx_test_runner
654     androidTestImplementation ComponentsDependencies.androidx_test_rules
655     androidTestUtil FenixDependencies.orchestrator
656     androidTestImplementation ComponentsDependencies.androidx_espresso_core, {
657         exclude group: 'com.android.support', module: 'support-annotations'
658     }
660     androidTestImplementation ComponentsDependencies.androidx_test_junit
661     androidTestImplementation ComponentsDependencies.androidx_work_testing
662     androidTestImplementation FenixDependencies.androidx_benchmark_junit4
663     androidTestImplementation FenixDependencies.mockwebserver
664     testImplementation project(':support-test')
665     testImplementation project(':support-test-libstate')
666     testImplementation ComponentsDependencies.androidx_test_junit
667     testImplementation ComponentsDependencies.androidx_work_testing
668     testImplementation (ComponentsDependencies.testing_robolectric) {
669         exclude group: 'org.apache.maven'
670     }
672     testImplementation ComponentsDependencies.testing_maven_ant_tasks
673     implementation project(':support-rusthttp')
675     androidTestImplementation FenixDependencies.mockk_android
676     testImplementation FenixDependencies.mockk
678     // For the initial release of Glean 19, we require consumer applications to
679     // depend on a separate library for unit tests. This will be removed in future releases.
680     testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:${project.ext.glean_version}"
682     lintChecks project(":mozilla-lint-rules")
685 protobuf {
686     protoc {
687         artifact = FenixDependencies.protobuf_compiler
688     }
690     // Generates the java Protobuf-lite code for the Protobufs in this project. See
691     // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
692     // for more information.
693     generateProtoTasks {
694         all().each { task ->
695             task.builtins {
696                 java {
697                     option 'lite'
698                 }
699             }
700         }
701     }
704 if (project.hasProperty("coverage")) {
705     tasks.withType(Test).configureEach {
706         jacoco.includeNoLocationClasses = true
707         jacoco.excludes = ['jdk.internal.*']
708     }
710     jacoco {
711         toolVersion = Versions.jacoco
712     }
714     android.applicationVariants.all { variant ->
715         tasks.register("jacoco${variant.name.capitalize()}TestReport", JacocoReport) {
716             dependsOn "test${variant.name.capitalize()}UnitTest"
718             reports {
719                 xml.enabled = true
720                 html.enabled = true
721             }
723             def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
724                               '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
725             def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/${variant.name}", excludes: fileFilter)
726             def javaDebugTree = fileTree(dir: "$project.buildDir/intermediates/classes/${variant.flavorName}/${variant.buildType.name}",
727                     excludes: fileFilter)
728             def mainSrc = "$project.projectDir/src/main/java"
730             sourceDirectories.setFrom(files([mainSrc]))
731             classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree]))
732             executionData.setFrom(fileTree(dir: project.buildDir, includes: [
733                 "jacoco/test${variant.name.capitalize()}UnitTest.exec",
734                 'outputs/code-coverage/connected/*coverage.ec'
735             ]))
736         }
737     }
739     android {
740         buildTypes {
741             debug {
742                 testCoverageEnabled true
743             }
744         }
745     }
748 // -------------------------------------------------------------------------------------------------
749 // Task for printing APK information for the requested variant
750 // Usage: "./gradlew printVariants
751 // -------------------------------------------------------------------------------------------------
752 tasks.register('printVariants') {
753     doLast {
754         def variants = android.applicationVariants.collect { variant -> [
755             apks: variant.outputs.collect { output -> [
756                 abi: output.getFilter(com.android.build.VariantOutput.FilterType.ABI),
757                 fileName: output.outputFile.name
758             ]},
759             build_type: variant.buildType.name,
760             name: variant.name,
761         ]}
762         // AndroidTest is a special case not included above
763         variants.add([
764             apks: [[
765                 abi: 'noarch',
766                 fileName: 'app-debug-androidTest.apk',
767             ]],
768             build_type: 'androidTest',
769             name: 'androidTest',
770         ])
771         println 'variants: ' + JsonOutput.toJson(variants)
772     }
775 task buildTranslationArray {
776     // This isn't running as a task, instead the array is build when the gradle file is parsed.
777     // https://github.com/mozilla-mobile/fenix/issues/14175
778     def foundLocales = new StringBuilder()
779     foundLocales.append("new String[]{")
781     fileTree("src/main/res").visit { FileVisitDetails details ->
782         if(details.file.path.endsWith("${File.separator}strings.xml")){
783             def languageCode = details.file.parent.tokenize(File.separator).last().replaceAll('values-','').replaceAll('-r','-')
784             languageCode = (languageCode == "values") ? "en-US" : languageCode
785             foundLocales.append("\"").append(languageCode).append("\"").append(",")
786         }
787     }
789     foundLocales.append("}")
790     def foundLocalesString = foundLocales.toString().replaceAll(',}','}')
791     android.defaultConfig.buildConfigField "String[]", "SUPPORTED_LOCALE_ARRAY", foundLocalesString
794 afterEvaluate {
796     // Format test output. Ported from AC #2401
797     tasks.withType(Test).configureEach {
798         systemProperty "robolectric.logging", "stdout"
799         systemProperty "logging.test-mode", "true"
801         testLogging.events = []
803         def out = services.get(StyledTextOutputFactory).create("tests")
805         beforeSuite { descriptor ->
806             if (descriptor.getClassName() != null) {
807                 out.style(Style.Header).println("\nSUITE: " + descriptor.getClassName())
808             }
809         }
811         beforeTest { descriptor ->
812             out.style(Style.Description).println("  TEST: " + descriptor.getName())
813         }
815         onOutput { descriptor, event ->
816             logger.lifecycle("    " + event.message.trim())
817         }
819         afterTest { descriptor, result ->
820             switch (result.getResultType()) {
821                 case ResultType.SUCCESS:
822                     out.style(Style.Success).println("  SUCCESS")
823                     break
825                 case ResultType.FAILURE:
826                     out.style(Style.Failure).println("  FAILURE")
827                     logger.lifecycle("", result.getException())
828                     break
830                 case ResultType.SKIPPED:
831                     out.style(Style.Info).println("  SKIPPED")
832                     break
833             }
834             logger.lifecycle("")
835         }
836     }
839 if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) {
840     if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) {
841         ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir"
842     }
843     ext.topsrcdir = StringUtils.removeSuffix(gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir", File.separator)
844     apply from: "${topsrcdir}/substitute-local-geckoview.gradle"
847 if (gradle.hasProperty('localProperties.autoPublish.glean.dir')) {
848     ext.gleanSrcDir = gradle."localProperties.autoPublish.glean.dir"
849     apply from: "../${gleanSrcDir}/build-scripts/substitute-local-glean.gradle"
852 android.applicationVariants.all { variant ->
853     tasks.register("apkSize${variant.name.capitalize()}", ApkSizeTask) {
854         variantName = variant.name
855         apks = variant.outputs.collect { output -> output.outputFile.name }
856         dependsOn "package${variant.name.capitalize()}"
857     }
860 // Enable expiration by major version.
861 ext.gleanExpireByVersion = Config.majorVersion()