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