1
0
Fork 0
firefox/mobile/android/fenix/app/build.gradle
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

924 lines
37 KiB
Groovy

import com.android.build.api.variant.FilterConfiguration
import java.time.format.DateTimeFormatter
buildscript {
repositories {
gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
maven {
url = repository
if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
allowInsecureProtocol = true
}
}
}
}
dependencies {
classpath libs.plugin.serialization
}
}
plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.python.envs.plugin)
alias(libs.plugins.protobuf.plugin)
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-parcelize'
apply plugin: 'jacoco'
apply plugin: 'androidx.navigation.safeargs.kotlin'
apply plugin: 'kotlinx-serialization'
if (gradle.mozconfig.substs.MOZILLA_OFFICIAL) {
apply plugin: 'com.google.android.gms.oss-licenses-plugin'
}
import groovy.json.JsonOutput
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import static org.gradle.api.tasks.testing.TestResult.ResultType
apply from: 'benchmark.gradle'
def isAppBundle = gradle.startParameter.taskNames.any { it.toLowerCase().contains("bundle") }
// Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
def getBuildId() {
if (System.env.MOZ_BUILD_DATE) {
if (System.env.MOZ_BUILD_DATE.length() == 14) {
return System.env.MOZ_BUILD_DATE
}
logger.warn("Ignoring invalid MOZ_BUILD_DATE: ${System.env.MOZ_BUILD_DATE}")
}
return file("${gradle.mozconfig.topobjdir}/buildid.h").getText('utf-8').split()[2]
}
android {
project.maybeConfigForJetpackBenchmark(it)
if (project.hasProperty("testBuildType")) {
// Allowing to configure the test build type via command line flag (./gradlew -PtestBuildType=beta ..)
// in order to run UI tests against other build variants than debug in automation.
testBuildType project.property("testBuildType")
}
defaultConfig {
applicationId "org.mozilla"
minSdkVersion config.minSdkVersion
compileSdk config.compileSdkVersion
targetSdkVersion config.targetSdkVersion
versionCode 1
versionName Config.generateDebugVersionName()
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
testInstrumentationRunnerArguments "detect-leaks": 'true'
resValue "bool", "IS_DEBUG", "false"
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "false"
// Blank in debug builds so that tests are deterministic.
buildConfigField "String", "VCS_HASH", "\"\""
// This should be the "public" base URL of AMO.
buildConfigField "String", "AMO_BASE_URL", "\"https://addons.mozilla.org\""
buildConfigField "String", "AMO_COLLECTION_NAME", "\"Extensions-for-Android\""
buildConfigField "String", "AMO_COLLECTION_USER", "\"mozilla\""
// This should be the base URL used to call the AMO API.
buildConfigField "String", "AMO_SERVER_URL", "\"https://services.addons.mozilla.org\""
def deepLinkSchemeValue = "fenix-dev"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders = [
"deepLinkScheme": deepLinkSchemeValue
]
buildConfigField "String[]", "SUPPORTED_LOCALE_ARRAY", getSupportedLocales()
}
def releaseTemplate = {
// We allow disabling optimization by passing `-PdisableOptimization` to gradle. This is used
// in automation for UI testing non-debug builds.
shrinkResources = !project.hasProperty("disableOptimization")
minifyEnabled = !project.hasProperty("disableOptimization")
proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro']
matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs)
// Present in release builds.
if (gradle.mozconfig.substs.MOZ_INCLUDE_SOURCE_INFO) {
buildConfigField("String", "VCS_HASH", "\"" + gradle.mozconfig.source_repo.MOZ_SOURCE_STAMP + "\"")
}
if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey") ||
gradle.mozconfig.substs.DEVELOPER_OPTIONS) {
signingConfig signingConfigs.debug
}
if (gradle.hasProperty("localProperties.debuggable") ||
gradle.mozconfig.substs.MOZ_ANDROID_DEBUGGABLE) {
debuggable true
}
}
buildTypes {
debug {
shrinkResources = false
minifyEnabled = false
applicationIdSuffix ".fenix.debug"
resValue "bool", "IS_DEBUG", "true"
pseudoLocalesEnabled = true
}
nightly releaseTemplate >> {
applicationIdSuffix ".fenix"
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
def deepLinkSchemeValue = "fenix-nightly"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders.putAll([
"deepLinkScheme": deepLinkSchemeValue
])
}
beta releaseTemplate >> {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
applicationIdSuffix ".firefox_beta"
def deepLinkSchemeValue = "fenix-beta"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders.putAll([
// This release type is meant to replace Firefox (Beta channel) and therefore needs to inherit
// its sharedUserId for all eternity. See:
// https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false&regexp=false&path=
// Shipping an app update without sharedUserId can have
// fatal consequences. For example see:
// - https://issuetracker.google.com/issues/36924841
// - https://issuetracker.google.com/issues/36905922
"sharedUserId": "org.mozilla.firefox.sharedID",
"deepLinkScheme": deepLinkSchemeValue,
])
}
release releaseTemplate >> {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
applicationIdSuffix ".firefox"
def deepLinkSchemeValue = "fenix"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders.putAll([
// This release type is meant to replace Firefox (Release channel) and therefore needs to inherit
// its sharedUserId for all eternity. See:
// https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false&regexp=false&path=
// Shipping an app update without sharedUserId can have
// fatal consequences. For example see:
// - https://issuetracker.google.com/issues/36924841
// - https://issuetracker.google.com/issues/36905922
"sharedUserId": "org.mozilla.firefox.sharedID",
"deepLinkScheme": deepLinkSchemeValue,
])
}
benchmark releaseTemplate >> {
initWith buildTypes.nightly
applicationIdSuffix ".fenix"
signingConfig = signingConfigs.debug
debuggable false
}
}
buildFeatures {
viewBinding = true
buildConfig = true
}
androidResources {
// All JavaScript code used internally by GeckoView is packaged in a
// file called omni.ja. If this file is compressed in the APK,
// GeckoView must uncompress it before it can do anything else which
// causes a significant delay on startup.
noCompress 'ja'
// manifest.template.json is converted to manifest.json at build time.
// No need to package the template in the APK.
ignoreAssetsPattern = "manifest.template.json"
}
testOptions {
execution = 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests.includeAndroidResources = true
animationsDisabled = true
}
flavorDimensions.add("product")
productFlavors {
fenix {
dimension "product"
}
}
sourceSets {
androidTest {
resources.srcDirs += ['src/androidTest/resources']
}
if (project.hasProperty('baselineProfilePath')) {
main {
baselineProfiles.srcDirs(project.property('baselineProfilePath'))
}
}
}
splits {
abi {
enable = !isAppBundle
reset()
// As gradle is unable to pick the right apk to install when multiple apks are generated
// while running benchmark tests or generating baseline profiles. To circumvent this,
// this flag is passed to make sure only one apk is generated so gradle can pick that one.
if (project.hasProperty("benchmarkTest")) {
include "arm64-v8a"
} else {
include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
}
}
}
bundle {
// Profiler issues require us to temporarily package native code compressed to
// match the previous APK packaging.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1865634
packaging {
jniLibs {
it.useLegacyPackaging = true
}
}
language {
// Because we have runtime language selection we will keep all strings and languages
// in the base APKs.
enableSplit = false
}
}
lint {
lintConfig = file("lint.xml")
baseline = file("lint-baseline.xml")
sarifReport = true
sarifOutput = file("../build/reports/lint/lint-report.sarif.json")
abortOnError = false
}
// https://issuetracker.google.com/issues/379732901
packaging {
resources {
excludes += ["META-INF/LICENSE.md", "META-INF/LICENSE-notice.md",
"META-INF/versions/9/OSGI-INF/MANIFEST.MF"]
}
jniLibs {
useLegacyPackaging = true
}
}
testOptions {
unitTests.returnDefaultValues = true
unitTests.all {
// We keep running into memory issues when running our tests. With this config we
// reserve more memory and also create a new process after every 80 test classes. This
// is a band-aid solution and eventually we should try to find and fix the leaks
// instead. :)
forkEvery = 80
maxHeapSize = "3072m"
minHeapSize = "1024m"
}
}
buildFeatures {
compose = true
}
compileOptions {
coreLibraryDesugaringEnabled = true
}
namespace = 'org.mozilla.fenix'
}
android.applicationVariants.configureEach { variant ->
// -------------------------------------------------------------------------------------------------
// Generate version codes for builds
// -------------------------------------------------------------------------------------------------
def isDebug = variant.buildType.resValues['bool/IS_DEBUG']?.value ?: false
def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false
project.logger.debug("----------------------------------------------")
project.logger.debug("Variant name: " + variant.name)
project.logger.debug("Application ID: " + [variant.applicationId, variant.buildType.applicationIdSuffix].findAll().join())
project.logger.debug("Build type: " + variant.buildType.name)
project.logger.debug("Flavor: " + variant.flavorName)
project.logger.debug("Telemetry enabled: " + !isDebug)
if (useReleaseVersioning) {
// The Google Play Store does not allow multiple APKs for the same app that all have the
// same version code. Therefore we need to have different version codes for our ARM and x86
// builds.
def versionName = variant.buildType.name == 'nightly' ?
"${Config.nightlyVersionName(project)}" :
"${Config.releaseVersionName(project)}"
project.logger.debug("versionName override: $versionName")
variant.outputs.each { output ->
def abi = output.getFilter(FilterConfiguration.FilterType.ABI.name())
if (isAppBundle) {
abi = "AAB"
}
// We use the same version code generator, that we inherited from Fennec, across all channels - even on
// channels that never shipped a Fennec build.
def versionCodeOverride = Config.generateFennecVersionCode(abi, isAppBundle )
project.logger.debug("versionCode for $abi = $versionCodeOverride")
if (versionName != null) {
output.versionNameOverride = versionName
}
output.versionCodeOverride = versionCodeOverride
}
} else if (gradle.hasProperty("localProperties.branchBuild.fenix.version")) {
def versionName = gradle.getProperty("localProperties.branchBuild.fenix.version")
project.logger.debug("versionName override: $versionName")
variant.outputs.each { output ->
output.versionNameOverride = versionName
}
}
// -------------------------------------------------------------------------------------------------
// BuildConfig: Set variables for Sentry, Crash Reporting, and Telemetry
// -------------------------------------------------------------------------------------------------
buildConfigField 'String', 'SENTRY_TOKEN', 'null'
if (!isDebug) {
buildConfigField 'boolean', 'CRASH_REPORTING', 'true'
// Reading sentry token from local file (if it exists). In a release task on taskcluster it will be available.
try {
def token = new File("${rootDir}/.sentry_token").text.trim()
buildConfigField 'String', 'SENTRY_TOKEN', '"' + token + '"'
} catch (FileNotFoundException ignored) {}
} else {
buildConfigField 'boolean', 'CRASH_REPORTING', 'false'
}
if (!isDebug) {
buildConfigField 'boolean', 'TELEMETRY', 'true'
} else {
buildConfigField 'boolean', 'TELEMETRY', 'false'
}
// Setting buildDate with every build ID changes the generated BuildConfig, which slows down the
// build. Only do this for non-debug builds, to speed-up builds produced during local development.
if (isDebug) {
buildConfigField 'String', 'BUILD_DATE', '"debug build"'
} else {
def buildDate = LocalDateTime.parse(getBuildId(), DateTimeFormatter.ofPattern("yyyyMMddHHmmss")).toString()
buildConfigField 'String', 'BUILD_DATE', '"' + buildDate + '"'
}
// -------------------------------------------------------------------------------------------------
// Adjust: Read token from local file if it exists (Only release builds)
// -------------------------------------------------------------------------------------------------
project.logger.debug("Adjust token: ")
if (!isDebug) {
try {
def token = new File("${rootDir}/.adjust_token").text.trim()
buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"'
project.logger.debug("(Added from .adjust_token file)")
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'ADJUST_TOKEN', 'null'
project.logger.debug("X_X")
}
} else {
buildConfigField 'String', 'ADJUST_TOKEN', 'null'
project.logger.debug("--")
}
// -------------------------------------------------------------------------------------------------
// MLS: Read token from local file if it exists
// -------------------------------------------------------------------------------------------------
project.logger.debug("MLS token: ")
try {
def token = new File("${rootDir}/.mls_token").text.trim()
buildConfigField 'String', 'MLS_TOKEN', '"' + token + '"'
project.logger.debug("(Added from .mls_token file)")
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'MLS_TOKEN', '""'
project.logger.debug("X_X")
}
// -------------------------------------------------------------------------------------------------
// Nimbus: Read endpoint from local.properties of a local file if it exists
// -------------------------------------------------------------------------------------------------
project.logger.debug("Nimbus endpoint: ")
if (!isDebug) {
try {
def url = new File("${rootDir}/.nimbus").text.trim()
buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"'
project.logger.debug("(Added from .nimbus file)")
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
project.logger.debug("X_X")
}
} else if (gradle.hasProperty("localProperties.nimbus.remote-settings.url")) {
def url=gradle.getProperty("localProperties.nimbus.remote-settings.url")
buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"'
project.logger.debug("(Added from local.properties file)")
} else {
buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
project.logger.debug("--")
}
// -------------------------------------------------------------------------------------------------
// Glean: Read custom server URL from local.properties of a local file if it exists
// -------------------------------------------------------------------------------------------------
project.logger.debug("Glean custom server URL: ")
if (gradle.hasProperty("localProperties.glean.custom.server.url")) {
def url=gradle.getProperty("localProperties.glean.custom.server.url")
buildConfigField 'String', 'GLEAN_CUSTOM_URL', url
project.logger.debug("(Added from local.properties file)")
} else {
buildConfigField 'String', 'GLEAN_CUSTOM_URL', 'null'
project.logger.debug("--")
}
// -------------------------------------------------------------------------------------------------
// BuildConfig: Set flag for official builds; similar to MOZILLA_OFFICIAL in mozilla-central.
// -------------------------------------------------------------------------------------------------
if (project.hasProperty("official") || gradle.hasProperty("localProperties.official")) {
buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'true'
} else {
buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'false'
}
// -------------------------------------------------------------------------------------------------
// BuildConfig: Set remote wallpaper URL using local file if it exists
// -------------------------------------------------------------------------------------------------
project.logger.debug("Wallpaper URL: ")
try {
def token = new File("${rootDir}/.wallpaper_url").text.trim()
buildConfigField 'String', 'WALLPAPER_URL', '"' + token + '"'
project.logger.debug("(Added from .wallpaper_url file)")
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'WALLPAPER_URL', '""'
project.logger.debug("--")
}
// -------------------------------------------------------------------------------------------------
// BuildConfig: Set the Pocket consumer key from a local file if it exists
// -------------------------------------------------------------------------------------------------
project.logger.debug("Pocket consumer key: ")
try {
def token = new File("${rootDir}/.pocket_consumer_key").text.trim()
buildConfigField 'String', 'POCKET_CONSUMER_KEY', '"' + token + '"'
project.logger.debug("(Added from .pocket_consumer_key file)")
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'POCKET_CONSUMER_KEY', '""'
project.logger.debug("--")
}
// -------------------------------------------------------------------------------------------------
// BuildConfig: Set flag to disable LeakCanary in debug (on CI builds)
// -------------------------------------------------------------------------------------------------
if (isDebug) {
if (project.hasProperty("disableLeakCanary") || gradle.hasProperty("localProperties.disableLeakCanary")) {
buildConfigField "boolean", "LEAKCANARY", "false"
project.logger.debug("LeakCanary enabled in debug: false")
} else {
buildConfigField "boolean", "LEAKCANARY", "true"
project.logger.debug("LeakCanary enabled in debug: true")
}
} else {
buildConfigField "boolean", "LEAKCANARY", "false"
}
}
// Generate Kotlin code for the Fenix Glean metrics.
ext {
// Enable expiration by major version.
gleanExpireByVersion = Config.majorVersion(project)
gleanNamespace = "mozilla.telemetry.glean"
gleanPythonEnvDir = gradle.mozconfig.substs.GRADLE_GLEAN_PARSER_VENV
}
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
apply plugin: "org.mozilla.appservices.nimbus-gradle-plugin"
nimbus {
// The path to the Nimbus feature manifest file
manifestFile = "nimbus.fml.yaml"
// The fully qualified class name for the generated features.
// Map from the variant name to the channel as experimenter and nimbus understand it.
// If nimbus's channels were accurately set up well for this project, then this
// shouldn't be needed.
channels = [
fenixDebug: "developer",
fenixNightly: "nightly",
fenixBeta: "beta",
fenixRelease: "release",
fenixBenchmark: "developer",
]
// This is generated by the FML and should be checked into git.
// It will be fetched by Experimenter (the Nimbus experiment website)
// and used to inform experiment configuration.
experimenterManifest = ".experimenter.yaml"
applicationServicesDir = gradle.hasProperty('localProperties.autoPublish.application-services.dir')
? gradle.getProperty('localProperties.autoPublish.application-services.dir') : null
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
}
dependencies {
coreLibraryDesugaring libs.desugar.jdk.libs
implementation libs.kotlin.json
implementation project(':browser-engine-gecko')
implementation libs.kotlin.coroutines
testImplementation libs.testing.coroutines
implementation libs.accompanist.drawablepainter
implementation libs.thirdparty.sentry
implementation project(':compose-awesomebar')
implementation project(':compose-base')
implementation project(':compose-browser-toolbar')
implementation project(':compose-cfr')
implementation project(':concept-awesomebar')
implementation project(':concept-base')
implementation project(':concept-engine')
implementation project(':concept-menu')
implementation project(':concept-push')
implementation project(':concept-storage')
implementation project(':concept-sync')
implementation project(':concept-toolbar')
implementation project(':concept-tabstray')
implementation project(':browser-domains')
implementation project(':browser-icons')
implementation project(':browser-menu')
implementation project(':browser-menu2')
implementation project(':browser-session-storage')
implementation project(':browser-state')
implementation project(':browser-storage-sync')
implementation project(':browser-tabstray')
implementation project(':browser-thumbnails')
implementation project(':browser-toolbar')
implementation project(':feature-addons')
implementation project(':feature-accounts')
implementation project(':feature-app-links')
implementation project(':feature-autofill')
implementation project(':feature-awesomebar')
implementation project(':feature-contextmenu')
implementation project(':feature-customtabs')
implementation project(':feature-downloads')
implementation project(':feature-fxsuggest')
implementation project(':feature-intent')
implementation project(':feature-media')
implementation project(':feature-prompts')
implementation project(':feature-push')
implementation project(':feature-privatemode')
implementation project(':feature-pwa')
implementation project(':feature-qr')
implementation project(':feature-search')
implementation project(':feature-session')
implementation project(':feature-syncedtabs')
implementation project(':feature-toolbar')
implementation project(':feature-tabs')
implementation project(':feature-findinpage')
implementation project(':feature-logins')
implementation project(':feature-sitepermissions')
implementation project(':feature-readerview')
implementation project(':feature-tab-collections')
implementation project(':feature-recentlyclosed')
implementation project(':feature-top-sites')
implementation project(':feature-share')
implementation project(':feature-accounts-push')
implementation project(':feature-webauthn')
implementation project(':feature-webcompat')
implementation project(':feature-webnotifications')
implementation project(':feature-webcompat-reporter')
implementation project(':service-pocket')
implementation project(':service-mars')
implementation project(':service-digitalassetlinks')
implementation project(':service-sync-autofill')
implementation project(':service-sync-logins')
implementation project(':service-firefox-accounts')
implementation project(':service-glean')
implementation libs.mozilla.glean
implementation project(':service-location')
implementation project(':service-nimbus')
implementation project(':support-webextensions')
implementation project(':support-base')
implementation project(':support-remotesettings')
implementation project(':support-rusterrors')
implementation project(':support-images')
implementation project(':support-ktx')
implementation project(':support-rustlog')
implementation project(':support-utils')
implementation project(':support-locale')
implementation project(':ui-colors')
implementation project(':ui-icons')
implementation project(':lib-publicsuffixlist')
implementation project(':ui-widgets')
implementation project(':ui-tabcounter')
implementation project(':lib-crash')
implementation project(':lib-crash-sentry')
implementation project(':lib-push-firebase')
implementation project(':lib-state')
implementation project(':lib-dataprotect')
testImplementation project(':support-test-fakes')
debugImplementation libs.leakcanary
debugImplementation libs.androidx.compose.ui.tooling
implementation libs.androidx.activity
implementation libs.androidx.activity.ktx
implementation libs.androidx.annotation
implementation libs.androidx.appcompat
implementation libs.androidx.biometric
implementation platform(libs.androidx.compose.bom)
androidTestImplementation platform(libs.androidx.compose.bom)
implementation libs.androidx.compose.animation
implementation libs.androidx.compose.foundation
implementation libs.androidx.compose.material
implementation libs.androidx.compose.ui
implementation libs.androidx.compose.ui.tooling.preview
implementation libs.androidx.constraintlayout
implementation libs.androidx.coordinatorlayout
implementation libs.androidx.core
implementation libs.androidx.core.ktx
implementation libs.androidx.core.splashscreen
implementation libs.androidx.datastore
implementation libs.androidx.datastore.preferences
implementation libs.androidx.fragment
implementation libs.androidx.lifecycle.common
implementation libs.androidx.lifecycle.livedata
implementation libs.androidx.lifecycle.process
implementation libs.androidx.lifecycle.runtime
implementation libs.androidx.lifecycle.service
implementation libs.androidx.lifecycle.viewmodel
implementation libs.androidx.localbroadcastmanager
implementation libs.androidx.navigation.compose
implementation libs.androidx.navigation.fragment
implementation libs.androidx.navigation.ui
implementation libs.androidx.paging
implementation libs.androidx.preferences
implementation libs.androidx.profileinstaller
implementation libs.androidx.recyclerview
implementation libs.androidx.swiperefreshlayout
implementation libs.androidx.transition
implementation libs.androidx.viewpager2
implementation libs.androidx.work.runtime
implementation libs.protobuf.javalite
implementation libs.google.material
implementation(libs.adjust)
implementation(libs.installreferrer)
// Required for the Google Advertising ID
implementation libs.play.services.ads.id
// Required for in-app reviews
implementation libs.play.review
implementation libs.play.review.ktx
implementation ComponentsDependencies.mozilla_appservices_init_rust_components
androidTestImplementation(libs.androidx.espresso.contrib) {
exclude module: 'protobuf-lite'
}
androidTestImplementation libs.androidx.espresso.core
androidTestImplementation libs.androidx.espresso.idling.resource
androidTestImplementation libs.androidx.espresso.intents
androidTestImplementation libs.androidx.test.core
androidTestImplementation libs.androidx.test.junit
androidTestImplementation libs.androidx.test.monitor
androidTestImplementation libs.androidx.test.rules
androidTestImplementation libs.androidx.test.runner
androidTestImplementation libs.androidx.test.uiautomator
androidTestUtil libs.androidx.test.orchestrator
androidTestImplementation libs.testing.leakcanary
androidTestImplementation libs.androidx.benchmark.junit4
androidTestImplementation libs.androidx.compose.ui.test
androidTestImplementation libs.androidx.work.testing
androidTestImplementation libs.testing.mockwebserver
testImplementation project(':support-test')
testImplementation project(':support-test-libstate')
testImplementation libs.androidx.test.junit
testImplementation libs.androidx.work.testing
testImplementation ComponentsDependencies.mozilla_appservices_full_megazord_libsForTests
testImplementation (libs.testing.robolectric) {
exclude group: 'org.apache.maven'
}
testImplementation libs.testing.maven.ant.tasks
implementation project(':support-rusthttp')
androidTestImplementation libs.testing.mockk.android
testImplementation libs.testing.mockk
// For the initial release of Glean 19, we require consumer applications to
// depend on a separate library for unit tests. This will be removed in future releases.
testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:${project.ext.glean_version}"
lintChecks project(':tooling-lint')
}
protobuf {
protoc {
artifact = libs.protobuf.compiler.get()
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
if (project.hasProperty("coverage")) {
tasks.withType(Test).configureEach {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
jacoco {
toolVersion = libs.versions.jacoco.get()
}
android.applicationVariants.configureEach { variant ->
tasks.register("jacoco${variant.name.capitalize()}TestReport", JacocoReport) {
dependsOn "test${variant.name.capitalize()}UnitTest"
reports {
xml.required = true
html.required = true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
'**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
def kotlinDebugTree = fileTree(dir: "$project.layout.buildDirectory/tmp/kotlin-classes/${variant.name}", excludes: fileFilter)
def javaDebugTree = fileTree(dir: "$project.layout.buildDirectory/intermediates/classes/${variant.flavorName}/${variant.buildType.name}",
excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories.setFrom(files([mainSrc]))
classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree]))
executionData.setFrom(fileTree(dir: project.layout.buildDirectory, includes: [
"jacoco/test${variant.name.capitalize()}UnitTest.exec",
'outputs/code-coverage/connected/*coverage.ec'
]))
}
}
android {
buildTypes {
debug {
testCoverageEnabled true
}
}
}
}
// -------------------------------------------------------------------------------------------------
// Task for printing APK information for the requested variant
// Usage: "./gradlew printVariants
// -------------------------------------------------------------------------------------------------
tasks.register('printVariants') {
def variants = project.provider {
android.applicationVariants.collect { variant -> [
apks: variant.outputs.collect { output -> [
abi: output.getFilter(FilterConfiguration.FilterType.ABI.name()),
fileName: output.outputFile.name
]},
build_type: variant.buildType.name,
name: variant.name,
]}}
doLast {
// AndroidTest is a special case not included above
variants.get().add([
apks: [[
abi: 'noarch',
fileName: 'app-debug-androidTest.apk',
]],
build_type: 'androidTest',
name: 'androidTest',
])
println 'variants: ' + JsonOutput.toJson(variants.get())
}
}
afterEvaluate {
// Format test output. Ported from AC #2401
tasks.withType(Test).configureEach {
systemProperty "robolectric.logging", "stdout"
systemProperty "logging.test-mode", "true"
testLogging.events = []
beforeSuite { descriptor ->
if (descriptor.getClassName() != null) {
println("\nSUITE: " + descriptor.getClassName())
}
}
beforeTest { descriptor ->
println(" TEST: " + descriptor.getName())
}
onOutput { descriptor, event ->
it.logger.lifecycle(" " + event.message.trim())
}
afterTest { descriptor, result ->
switch (result.getResultType()) {
case ResultType.SUCCESS:
println(" SUCCESS")
break
case ResultType.FAILURE:
def testId = descriptor.getClassName() + "." + descriptor.getName()
println(" TEST-UNEXPECTED-FAIL | " + testId + " | " + result.getException())
break
case ResultType.SKIPPED:
println(" SKIPPED")
break
}
it.logger.lifecycle("")
}
}
}
if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) {
if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) {
ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir"
}
ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir"
apply from: "${topsrcdir}/substitute-local-geckoview.gradle"
}
android.applicationVariants.configureEach { variant ->
tasks.register("apkSize${variant.name.capitalize()}", ApkSizeTask) {
variantName = variant.name
apks = variant.outputs.collect { output -> output.outputFile.name }
dependsOn "package${variant.name.capitalize()}"
}
}
def getSupportedLocales() {
// This isn't running as a task, instead the array is build when the gradle file is parsed.
// https://github.com/mozilla-mobile/fenix/issues/14175
def foundLocales = new StringBuilder()
foundLocales.append("new String[]{")
fileTree("src/main/res").visit { FileVisitDetails details ->
if (details.file.path.endsWith("${File.separator}strings.xml")) {
def languageCode = details.file.parent.tokenize(File.separator).last().replaceAll('values-', '').replaceAll('-r', '-')
languageCode = (languageCode == "values") ? "en-US" : languageCode
foundLocales.append("\"").append(languageCode).append("\"").append(",")
}
}
foundLocales.append("}")
def foundLocalesString = foundLocales.toString().replaceAll(',}', '}')
return foundLocalesString
}