summaryrefslogtreecommitdiffstats
path: root/mobile/android/focus-android/app/build.gradle
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/focus-android/app/build.gradle')
-rw-r--r--mobile/android/focus-android/app/build.gradle788
1 files changed, 788 insertions, 0 deletions
diff --git a/mobile/android/focus-android/app/build.gradle b/mobile/android/focus-android/app/build.gradle
new file mode 100644
index 0000000000..3a82291ce9
--- /dev/null
+++ b/mobile/android/focus-android/app/build.gradle
@@ -0,0 +1,788 @@
+plugins {
+ id "com.jetbrains.python.envs" version "$python_envs_plugin"
+}
+
+if (findProject(":geckoview") != null) {
+ buildDir "${topobjdir}/gradle/build/mobile/android/focus-android"
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
+apply plugin: 'jacoco'
+apply plugin: 'com.google.android.gms.oss-licenses-plugin'
+
+def versionCodeGradle = "$project.rootDir/tools/gradle/versionCode.gradle"
+if (findProject(":geckoview") != null) {
+ versionCodeGradle = "$project.rootDir/mobile/android/focus-android/tools/gradle/versionCode.gradle"
+}
+apply from: versionCodeGradle
+
+if (findProject(":geckoview") != null) {
+ apply from: "${topsrcdir}/mobile/android/gradle/product_flavors.gradle"
+}
+
+import com.android.build.api.variant.FilterConfiguration
+import groovy.json.JsonOutput
+import org.gradle.internal.logging.text.StyledTextOutput.Style
+import org.gradle.internal.logging.text.StyledTextOutputFactory
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static org.gradle.api.tasks.testing.TestResult.ResultType
+
+android {
+ 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 11 // This versionCode is "frozen" for local builds. For "release" builds we
+ // override this with a generated versionCode at build time.
+ // The versionName is dynamically overridden for all the build variants at build time.
+ versionName Config.generateDebugVersionName()
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArguments clearPackageData: 'true'
+ // See override in release builds for why it's blank.
+ buildConfigField "String", "VCS_HASH", "\"\""
+
+ vectorDrawables.useSupportLibrary = true
+ }
+
+ bundle {
+ 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")
+ }
+
+ // We have a three dimensional build configuration:
+ // BUILD TYPE (debug, release) X PRODUCT FLAVOR (focus, klar)
+
+ buildTypes {
+ release {
+ // 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']
+ buildConfigField "String", "VCS_HASH", "\"${Config.getVcsHash()}\""
+
+ if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey")) {
+ println ("All builds will be automatically signed with the debug key")
+ signingConfig signingConfigs.debug
+ }
+
+ if (gradle.hasProperty("localProperties.debuggable")) {
+ println ("All builds will be debuggable")
+ debuggable true
+ }
+ }
+ debug {
+ applicationIdSuffix ".debug"
+ matchingFallbacks = ['debug']
+ }
+ beta {
+ initWith release
+ applicationIdSuffix ".beta"
+ // This is used when the user selects text in other third-party apps. See https://github.com/mozilla-mobile/focus-android/issues/6478
+ manifestPlaceholders = [textSelectionSearchAction: "@string/text_selection_search_action_focus_beta"]
+ }
+ nightly {
+ initWith release
+ applicationIdSuffix ".nightly"
+ // This is used when the user selects text in other third-party apps. See https://github.com/mozilla-mobile/focus-android/issues/6478
+ manifestPlaceholders = [textSelectionSearchAction: "@string/text_selection_search_action_focus_nightly"]
+ }
+ }
+ testOptions {
+ execution 'ANDROIDX_TEST_ORCHESTRATOR'
+ animationsDisabled = true
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+
+ buildFeatures {
+ compose true
+ viewBinding true
+ buildConfig true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = Versions.compose_compiler
+ }
+
+ if (findProject(":geckoview") != null) {
+ project.configureProductFlavors.delegate = it
+ project.configureProductFlavors()
+ }
+
+ flavorDimensions.add("product")
+
+ productFlavors {
+ // In most countries we are Firefox Focus - but in some we need to be Firefox Klar
+ focus {
+ dimension "product"
+
+ applicationIdSuffix ".focus"
+
+ // This is used when the user selects text in other third-party apps. See https://github.com/mozilla-mobile/focus-android/issues/6478
+ manifestPlaceholders = [textSelectionSearchAction: "@string/text_selection_search_action_focus"]
+ }
+ klar {
+ dimension "product"
+
+ applicationIdSuffix ".klar"
+
+ // This is used when the user selects text in other third-party apps. See https://github.com/mozilla-mobile/focus-android/issues/6478
+ manifestPlaceholders = [textSelectionSearchAction: "@string/text_selection_search_action_klar"]
+ }
+ }
+
+ splits {
+ abi {
+ enable true
+
+ reset()
+
+ include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
+ }
+ }
+
+ sourceSets {
+ test {
+ resources {
+ // Make the default asset folder available as test resource folder. Robolectric seems
+ // to fail to read assets for our setup. With this we can just read the files directly
+ // and do not need to rely on Robolectric.
+ srcDir "${projectDir}/src/main/assets/"
+ }
+ }
+
+ if (findProject(":geckoview") != null) {
+ // Release
+ withGeckoBinariesFocusRelease.root = 'src/focusRelease'
+ withGeckoBinariesKlarRelease.root = 'src/klarRelease'
+ withoutGeckoBinariesFocusRelease.root = 'src/focusRelease'
+ withoutGeckoBinariesKlarRelease.root = 'src/klarRelease'
+
+ // Debug
+ withGeckoBinariesFocusDebug.root = 'src/focusDebug'
+ withGeckoBinariesKlarDebug.root = 'src/klarDebug'
+ withoutGeckoBinariesFocusDebug.root = 'src/focusDebug'
+ withoutGeckoBinariesKlarDebug.root = 'src/klarDebug'
+
+ // Nightly
+ withGeckoBinariesFocusNightly.root = 'src/focusNightly'
+ withGeckoBinariesKlarNightly.root = 'src/klarNightly'
+ withoutGeckoBinariesFocusNightly.root = 'src/focusNightly'
+ withoutGeckoBinariesKlarNightly.root = 'src/klarNightly'
+ } else {
+ // Release
+ focusRelease.root = 'src/focusRelease'
+ klarRelease.root = 'src/klarRelease'
+
+ // Debug
+ focusDebug.root = 'src/focusDebug'
+ klarDebug.root = 'src/klarDebug'
+
+ // Nightly
+ focusNightly.root = 'src/focusNightly'
+ klarNightly.root = 'src/klarNightly'
+ }
+ }
+ packagingOptions {
+ resources {
+ pickFirsts += ['META-INF/atomicfu.kotlin_module', 'META-INF/proguard/coroutines.pro']
+ }
+ jniLibs {
+ useLegacyPackaging true
+ }
+ }
+
+ namespace 'org.mozilla.focus'
+}
+
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions.allWarningsAsErrors = true
+ kotlinOptions.freeCompilerArgs += [
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.RequiresOptIn",
+ "-Xjvm-default=all"
+ ]
+}
+
+// -------------------------------------------------------------------------------------------------
+// Generate Kotlin code for the Focus Glean metrics.
+// -------------------------------------------------------------------------------------------------
+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"
+ // 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 = [
+ focusDebug: "debug",
+ focusNightly: "nightly",
+ focusBeta: "beta",
+ focusRelease: "release",
+ klarDebug: "debug",
+ klarNightly: "nightly",
+ klarBeta: "beta",
+ klarRelease: "release",
+ withGeckoBinariesFocusDebug: "debug",
+ withGeckoBinariesFocusNightly: "nightly",
+ withGeckoBinariesFocusBeta: "beta",
+ withGeckoBinariesFocusRelease: "release",
+ withGeckoBinariesKlarDebug: "debug",
+ withGeckoBinariesKlarNightly: "nightly",
+ withGeckoBinariesKlarBeta: "beta",
+ withGeckoBinariesKlarRelease: "release",
+ withoutGeckoBinariesFocusDebug: "debug",
+ withoutGeckoBinariesFocusNightly: "nightly",
+ withoutGeckoBinariesFocusBeta: "beta",
+ withoutGeckoBinariesFocusRelease: "release",
+ withoutGeckoBinariesKlarDebug: "debug",
+ withoutGeckoBinariesKlarNightly: "nightly",
+ withoutGeckoBinariesKlarBeta: "beta",
+ withoutGeckoBinariesKlarRelease: "release",
+ ]
+ // 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"
+}
+
+dependencies {
+ implementation platform(ComponentsDependencies.androidx_compose_bom)
+ androidTestImplementation platform(ComponentsDependencies.androidx_compose_bom)
+
+ implementation ComponentsDependencies.androidx_appcompat
+ implementation ComponentsDependencies.androidx_browser
+ implementation ComponentsDependencies.androidx_cardview
+ implementation ComponentsDependencies.androidx_compose_ui
+ implementation ComponentsDependencies.androidx_compose_ui_tooling
+ implementation ComponentsDependencies.androidx_compose_foundation
+ implementation ComponentsDependencies.androidx_compose_material
+ implementation ComponentsDependencies.androidx_compose_runtime_livedata
+ implementation ComponentsDependencies.androidx_constraintlayout
+ implementation FocusDependencies.androidx_constraint_layout_compose
+ implementation ComponentsDependencies.androidx_core_ktx
+ implementation ComponentsDependencies.androidx_fragment
+ implementation ComponentsDependencies.androidx_lifecycle_process
+ implementation ComponentsDependencies.androidx_lifecycle_viewmodel
+ implementation ComponentsDependencies.androidx_palette
+ implementation ComponentsDependencies.androidx_preferences
+ implementation ComponentsDependencies.androidx_recyclerview
+ implementation ComponentsDependencies.androidx_savedstate
+ implementation FocusDependencies.androidx_splashscreen
+ implementation FocusDependencies.androidx_transition
+ implementation ComponentsDependencies.androidx_work_runtime
+ implementation ComponentsDependencies.androidx_data_store_preferences
+
+ implementation FocusDependencies.google_play
+
+ implementation ComponentsDependencies.google_material
+
+ implementation ComponentsDependencies.thirdparty_sentry
+
+ implementation project(':browser-engine-gecko')
+ implementation project(':browser-domains')
+ implementation project(':browser-errorpages')
+ implementation project(':browser-icons')
+ implementation project(':browser-menu')
+ implementation project(':browser-state')
+ implementation project(':browser-toolbar')
+
+ implementation project(':concept-awesomebar')
+ implementation project(':concept-engine')
+ implementation project(':concept-fetch')
+ implementation project(':concept-menu')
+
+ implementation project(':compose-awesomebar')
+
+ implementation project(':feature-awesomebar')
+ implementation project(':feature-app-links')
+ implementation project(':feature-customtabs')
+ implementation project(':feature-contextmenu')
+ implementation project(':feature-downloads')
+ implementation project(':feature-findinpage')
+ implementation project(':feature-intent')
+ implementation project(':feature-prompts')
+ implementation project(':feature-session')
+ implementation project(':feature-search')
+ implementation project(':feature-tabs')
+ implementation project(':feature-toolbar')
+ implementation project(':feature-top-sites')
+ implementation project(':feature-sitepermissions')
+ implementation project(':lib-crash')
+ implementation project(':lib-crash-sentry')
+ implementation project(':lib-state')
+ implementation project(':feature-media')
+ implementation project(':lib-auth')
+ implementation project(':lib-publicsuffixlist')
+
+ implementation project(':service-glean'), {
+ exclude group: 'org.mozilla.telemetry', module: 'glean-native'
+ }
+ implementation project(':service-location')
+ implementation project(':service-nimbus')
+
+ implementation project(':support-ktx')
+ implementation project(':support-utils')
+ implementation project(':support-rusthttp')
+ implementation project(':support-rustlog')
+ implementation project(':support-license')
+
+ implementation project(':ui-autocomplete')
+ implementation project(':ui-colors')
+ implementation project(':ui-icons')
+ implementation project(':ui-tabcounter')
+ implementation project(':ui-widgets')
+ implementation project(':feature-webcompat')
+ implementation project(':feature-webcompat-reporter')
+ implementation project(':support-webextensions')
+ implementation project(':support-locale')
+ implementation project(':compose-cfr')
+
+ implementation ComponentsDependencies.kotlin_coroutines
+ debugImplementation ComponentsDependencies.leakcanary
+
+ focusImplementation FocusDependencies.adjust
+ focusImplementation FocusDependencies.install_referrer // Required by Adjust
+
+ testImplementation "org.mozilla.telemetry:glean-native-forUnitTests:${project.ext.glean_version}"
+
+ testImplementation FocusDependencies.testing_junit_api
+ testRuntimeOnly FocusDependencies.testing_junit_engine
+ testImplementation FocusDependencies.testing_junit_params
+ testImplementation ComponentsDependencies.testing_robolectric
+ testImplementation ComponentsDependencies.testing_mockito
+ testImplementation ComponentsDependencies.testing_coroutines
+ testImplementation ComponentsDependencies.androidx_work_testing
+ testImplementation ComponentsDependencies.androidx_arch_core_testing
+ testImplementation project(':support-test')
+ testImplementation project(':support-test-libstate')
+ androidTestImplementation ComponentsDependencies.androidx_espresso_core, {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ }
+ androidTestImplementation FocusDependencies.espresso_idling_resource
+ androidTestImplementation FocusDependencies.espresso_web, {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ }
+ androidTestImplementation FocusDependencies.espresso_intents
+
+
+ androidTestImplementation ComponentsDependencies.testing_mockwebserver
+ testImplementation ComponentsDependencies.testing_mockwebserver
+ testImplementation project(':lib-fetch-okhttp')
+
+ androidTestImplementation FocusDependencies.fastlane
+ androidTestImplementation FocusDependencies.falcon // Required by fastlane
+
+ androidTestImplementation FocusDependencies.espresso_contrib, {
+ exclude module: 'appcompat-v7'
+ exclude module: 'support-v4'
+ exclude module: 'support-annotations'
+ exclude module: 'recyclerview-v7'
+ exclude module: 'design'
+ exclude module: 'espresso-core'
+ }
+ testImplementation ComponentsDependencies.androidx_test_core
+ testImplementation ComponentsDependencies.androidx_test_runner
+ testImplementation ComponentsDependencies.androidx_test_rules
+
+ androidTestImplementation ComponentsDependencies.androidx_test_core
+ androidTestImplementation ComponentsDependencies.androidx_test_junit
+ androidTestImplementation ComponentsDependencies.androidx_test_uiautomator
+ androidTestImplementation ComponentsDependencies.androidx_test_runner
+ androidTestUtil FocusDependencies.androidx_orchestrator
+
+ lintChecks project(':tooling-lint')
+}
+// -------------------------------------------------------------------------------------------------
+// Dynamically set versionCode (See tools/build/versionCode.gradle
+// -------------------------------------------------------------------------------------------------
+
+android.applicationVariants.configureEach { variant ->
+ def buildType = variant.buildType.name
+
+ println("----------------------------------------------")
+ println("Variant name: " + variant.name)
+ println("Application ID: " + [variant.applicationId, variant.buildType.applicationIdSuffix].findAll().join())
+ println("Build type: " + variant.buildType.name)
+ println("Flavor: " + variant.flavorName)
+
+ if (buildType == "release" || buildType == "nightly" || buildType == "beta") {
+ def baseVersionCode = generatedVersionCode
+ def versionName = buildType == "nightly" ?
+ "${Config.nightlyVersionName(project)}" :
+ "${Config.releaseVersionName(project)}"
+ println("versionName override: $versionName")
+
+ // 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. See https://developer.android.com/studio/publish/versioning
+
+ // Our generated version code now has a length of 9 (See tools/gradle/versionCode.gradle).
+ // Our x86 builds need a higher version code to avoid installing ARM builds on an x86 device
+ // with ARM compatibility mode.
+
+ // AAB builds need a version code that is distinct from any APK builds. Since AAB and APK
+ // builds may run in parallel, AAB and APK version codes might be based on the same
+ // (minute granularity) time of day. To avoid conflicts, we ensure the minute portion
+ // of the version code is even for APKs and odd for AABs.
+
+ variant.outputs.each { output ->
+ def abi = output.getFilter(FilterConfiguration.FilterType.ABI.name())
+ def aab = project.hasProperty("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.
+
+ // ensure baseVersionCode is an even number
+ if (baseVersionCode % 2) {
+ baseVersionCode = baseVersionCode + 1
+ }
+
+ def versionCodeOverride = baseVersionCode
+
+ if (aab) {
+ // AAB version code is odd
+ versionCodeOverride = versionCodeOverride + 1
+ println("versionCode for AAB = $versionCodeOverride")
+ } else {
+ if (abi == "x86_64") {
+ versionCodeOverride = versionCodeOverride + 6
+ } else if (abi == "x86") {
+ versionCodeOverride = versionCodeOverride + 4
+ } else if (abi == "arm64-v8a") {
+ versionCodeOverride = versionCodeOverride + 2
+ } else if (abi == "armeabi-v7a") {
+ versionCodeOverride = versionCodeOverride + 0
+ } else {
+ throw new RuntimeException("Unknown ABI: " + abi)
+ }
+ println("versionCode for $abi = $versionCodeOverride")
+ }
+
+ if (versionName != null) {
+ output.versionNameOverride = versionName
+ }
+ output.versionCodeOverride = versionCodeOverride
+
+ }
+
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// MLS: Read token from local file if it exists (Only release builds)
+// -------------------------------------------------------------------------------------------------
+
+android.applicationVariants.configureEach {
+ print("MLS token: ")
+ try {
+ def token = new File("${rootDir}/.mls_token").text.trim()
+ buildConfigField 'String', 'MLS_TOKEN', '"' + token + '"'
+ println "(Added from .mls_token file)"
+ } catch (FileNotFoundException ignored) {
+ buildConfigField 'String', 'MLS_TOKEN', '""'
+ println("X_X")
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// Adjust: Read token from local file if it exists (Only release builds)
+// -------------------------------------------------------------------------------------------------
+
+android.applicationVariants.configureEach { variant ->
+ def variantName = variant.getName()
+
+ print("Adjust token: ")
+
+ if (variantName.contains("Release") && variantName.contains("focus")) {
+ try {
+ def token = new File("${rootDir}/.adjust_token").text.trim()
+ buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"'
+ println "(Added from .adjust_token file)"
+ } catch (FileNotFoundException ignored) {
+ if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey")) {
+ buildConfigField 'String', 'ADJUST_TOKEN', '"fake"'
+ println("fake - only for local development")
+ } else {
+ buildConfigField 'String', 'ADJUST_TOKEN', 'null'
+ println("X_X")
+ }
+ }
+ } else {
+ buildConfigField 'String', 'ADJUST_TOKEN', 'null'
+ println("--")
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// Sentry: Read token from local file if it exists (Only release builds)
+// -------------------------------------------------------------------------------------------------
+
+android.applicationVariants.configureEach {
+ print("Sentry token: ")
+ try {
+ def token = new File("${rootDir}/.sentry_token").text.trim()
+ buildConfigField 'String', 'SENTRY_TOKEN', '"' + token + '"'
+ println "(Added from .sentry_token file)"
+ } catch (FileNotFoundException ignored) {
+ buildConfigField 'String', 'SENTRY_TOKEN', '""'
+ println("X_X")
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// L10N: Generate list of locales
+// Focus provides its own (Android independent) locale switcher. That switcher requires a list
+// of locale codes. We generate that list here to avoid having to manually maintain a list of locales:
+// -------------------------------------------------------------------------------------------------
+
+def getEnabledLocales() {
+ def resDir = file('src/main/res')
+
+ def potentialLanguageDirs = resDir.listFiles(new FilenameFilter() {
+ @Override
+ boolean accept(File dir, String name) {
+ return name.startsWith("values-")
+ }
+ })
+
+ def langs = potentialLanguageDirs.findAll {
+ // Only select locales where strings.xml exists
+ // Some locales might only contain e.g. sumo URLS in urls.xml, and should be skipped (see es vs es-ES/es-MX/etc)
+ return file(new File(it, "strings.xml")).exists()
+ } .collect {
+ // And reduce down to actual values-* names
+ return it.name
+ } .collect {
+ return it.substring("values-".length())
+ } .collect {
+ if (it.length() > 3 && it.contains("-r")) {
+ // Android resource dirs add an "r" prefix to the region - we need to strip that for java usage
+ // Add 1 to have the index of the r, without the dash
+ def regionPrefixPosition = it.indexOf("-r") + 1
+
+ return it.substring(0, regionPrefixPosition) + it.substring(regionPrefixPosition + 1)
+ } else {
+ return it
+ }
+ }.collect {
+ return '"' + it + '"'
+ }
+
+ // en-US is the default language (in "values") and therefore needs to be added separately
+ langs << "\"en-US\""
+
+ return langs.sort { it }
+}
+
+// -------------------------------------------------------------------------------------------------
+// Nimbus: Read endpoint from local.properties of a local file if it exists
+// -------------------------------------------------------------------------------------------------
+
+print("Nimbus endpoint: ")
+android.applicationVariants.configureEach { variant ->
+ def variantName = variant.getName()
+
+ if (!variantName.contains("Debug")) {
+ try {
+ def url = new File("${rootDir}/.nimbus").text.trim()
+ buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"'
+ println "(Added from .nimbus file)"
+ } catch (FileNotFoundException ignored) {
+ buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
+ println("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 + '"'
+ println "(Added from local.properties file)"
+ } else {
+ buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null'
+ println("--")
+ }
+}
+
+def generatedLocaleListDir = 'src/main/java/org/mozilla/focus/generated'
+def generatedLocaleListFilename = 'LocalesList.kt'
+
+tasks.register('generateLocaleList') {
+ doLast {
+ def dir = file(generatedLocaleListDir)
+ dir.mkdir()
+ def localeList = file(new File(dir, generatedLocaleListFilename))
+
+ localeList.delete()
+ localeList.createNewFile()
+ localeList << "package org.mozilla.focus.generated" << "\n" << "\n"
+ localeList << "import java.util.Collections" << "\n"
+ localeList << "\n"
+ localeList << "/**"
+ localeList << "\n"
+ localeList << " * Provides a list of bundled locales based on the language files in the res folder."
+ localeList << "\n"
+ localeList << " */"
+ localeList << "\n"
+ localeList << "object LocalesList {" << "\n"
+ localeList << " " << "val BUNDLED_LOCALES: List<String> = Collections.unmodifiableList("
+ localeList << "\n"
+ localeList << " " << "listOf("
+ localeList << "\n"
+ localeList << " "
+ localeList << getEnabledLocales().join(",\n" + " ")
+ localeList << ",\n"
+ localeList << " )," << "\n"
+ localeList << " )" << "\n"
+ localeList << "}" << "\n"
+ }
+}
+
+tasks.configureEach { task ->
+ if (name.contains("compile")) {
+ task.dependsOn generateLocaleList
+ }
+}
+
+clean.doLast {
+ file(generatedLocaleListDir).deleteDir()
+}
+
+if (project.hasProperty("coverage")) {
+ tasks.withType(Test).configureEach {
+ jacoco.includeNoLocationClasses = true
+ jacoco.excludes = ['jdk.internal.*']
+ }
+
+ android.applicationVariants.configureEach { variant ->
+ tasks.register("jacoco${variant.name.capitalize()}TestReport", JacocoReport) {
+
+ dependsOn(["test${variant.name.capitalize()}UnitTest"])
+ reports {
+ html.required = true
+ xml.required = true
+ }
+
+ def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
+ '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
+ def kotlinTree = fileTree(dir: "$project.layout.buildDirectory/tmp/kotlin-classes/${variant.name}", excludes: fileFilter)
+ def javaTree = 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([kotlinTree, javaTree]))
+ 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
+ applicationIdSuffix ".coverage"
+ }
+ }
+ }
+}
+
+if (gradle.hasProperty('localProperties.autoPublish.glean.dir')) {
+ ext.gleanSrcDir = gradle."localProperties.autoPublish.glean.dir"
+ apply from: "../${gleanSrcDir}/build-scripts/substitute-local-glean.gradle"
+}
+
+// -------------------------------------------------------------------------------------------------
+// Task for printing APK information for the requested variant
+// Taskgraph Usage: "./gradlew printVariants
+// -------------------------------------------------------------------------------------------------
+tasks.register('printVariants') {
+ doLast {
+ def variants = 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,
+ ]}
+ // AndroidTest is a special case not included above
+ variants.add([
+ apks: [[
+ abi: 'noarch',
+ fileName: 'app-debug-androidTest.apk',
+ ]],
+ build_type: 'androidTest',
+ name: 'androidTest',
+ ])
+ println 'variants: ' + JsonOutput.toJson(variants)
+ }
+}
+
+// Enable expiration by major version.
+ext.gleanExpireByVersion = 1
+
+afterEvaluate {
+
+ // Format test output. Copied from Fenix, which was ported from AC #2401
+ tasks.withType(Test).configureEach {
+ systemProperty "robolectric.logging", "stdout"
+ systemProperty "logging.test-mode", "true"
+
+ testLogging.events = []
+
+ def out = services.get(StyledTextOutputFactory).create("tests")
+
+ beforeSuite { descriptor ->
+ if (descriptor.getClassName() != null) {
+ out.style(Style.Header).println("\nSUITE: " + descriptor.getClassName())
+ }
+ }
+
+ beforeTest { descriptor ->
+ out.style(Style.Description).println(" TEST: " + descriptor.getName())
+ }
+
+ onOutput { descriptor, event ->
+ logger.lifecycle(" " + event.message.trim())
+ }
+
+ afterTest { descriptor, result ->
+ switch (result.getResultType()) {
+ case ResultType.SUCCESS:
+ out.style(Style.Success).println(" SUCCESS")
+ break
+
+ case ResultType.FAILURE:
+ def testId = descriptor.getClassName() + "." + descriptor.getName()
+ out.style(Style.Failure).println(" TEST-UNEXPECTED-FAIL | " + testId + " | " + result.getException())
+ break
+
+ case ResultType.SKIPPED:
+ out.style(Style.Info).println(" SKIPPED")
+ break
+ }
+ logger.lifecycle("")
+ }
+ }
+}