plugins { id "com.jetbrains.python.envs" version "0.0.26" } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply from: "$project.rootDir/automation/gradle/versionCode.gradle" apply plugin: 'androidx.navigation.safeargs.kotlin' import com.android.build.gradle.internal.tasks.AppPreBuildTask android { compileSdkVersion 28 defaultConfig { applicationId "org.mozilla.fenix" minSdkVersion Config.minSdkVersion targetSdkVersion Config.targetSdkVersion versionCode 1 versionName Config.generateDebugVersionName() testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments clearPackageData: 'true' manifestPlaceholders.isRaptorEnabled = "false" resValue "bool", "IS_DEBUG", "false" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "false" } def releaseTemplate = { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs) } buildTypes { debug { shrinkResources false minifyEnabled false applicationIdSuffix ".debug" manifestPlaceholders.isRaptorEnabled = "true" resValue "bool", "IS_DEBUG", "true" } forPerformanceTest releaseTemplate >> { // the ">>" concatenates the raptor-specific options with the template manifestPlaceholders.isRaptorEnabled = "true" applicationIdSuffix ".performancetest" debuggable true } nightly releaseTemplate >> { buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" } beta releaseTemplate >> { applicationIdSuffix ".beta" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" } production releaseTemplate >> { buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" } } variantFilter { // There's a "release" build type that exists by default that we don't use (it's replaced by "nightly" and "beta") if (buildType.name == 'release') { setIgnore true } } testOptions { execution 'ANDROIDX_TEST_ORCHESTRATOR' unitTests.includeAndroidResources = true } flavorDimensions "abi" productFlavors { arm { dimension "abi" ndk { abiFilter "armeabi-v7a" } } aarch64 { dimension "abi" ndk { abiFilter "arm64-v8a" } } x86 { dimension "abi" ndk { abiFilter "x86" } } x86_64 { dimension "abi" ndk { abiFilter "x86_64" } } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { lintConfig file("lint.xml") } packagingOptions { exclude 'META-INF/atomicfu.kotlin_module' } } android.applicationVariants.all { variant -> // ------------------------------------------------------------------------------------------------- // Set up kotlin-allopen plugin for writing tests // ------------------------------------------------------------------------------------------------- boolean hasTest = gradle.startParameter.taskNames.find { it.contains("test") || it.contains("Test") } != null if (hasTest) { apply plugin: 'kotlin-allopen' allOpen { annotation("org.mozilla.fenix.test.OpenClass") } } // ------------------------------------------------------------------------------------------------- // Generate version codes for builds // ------------------------------------------------------------------------------------------------- def buildType = variant.buildType.name def versionCode = null def isDebug = variant.buildType.resValues['IS_DEBUG']?.value ?: false def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false if (useReleaseVersioning) { versionCode = generatedVersionCode // 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. // Our generated version code now has a length of 9 (See automation/gradle/versionCode.gradle). // Our x86 builds need a higher version code to avoid installing ARM builds on an x86 device // with ARM compatibility mode. if (variant.flavorName.contains("x86_64")) { versionCode = versionCode + 3 } else if (variant.flavorName.contains("x86")) { versionCode = versionCode + 2 } else if (variant.flavorName.contains("aarch64")) { versionCode = versionCode + 1 }// else variant.flavorName.contains("Arm")) use generated version code variant.outputs.all { versionCodeOverride = versionCode versionNameOverride = Config.releaseVersionName(project) } // If this is a release build, validate that "versionName" is set tasks.withType(AppPreBuildTask) { prebuildTask -> // You can't add a closure to a variant, so we need to look for an early variant-specific type // of task (AppPreBuildTask is the first) and filter to make sure we're looking at the task for // this variant that we're currently configuring if (prebuildTask.variantName != variant.name) { return } // Append to the task so the first thing it does is run our validation prebuildTask.doFirst { if (!project.hasProperty('versionName')) { throw new RuntimeException("Release builds require the 'versionName' property, e.g.: './gradlew -PversionName=<...> assembleNightly'") } } } } println("----------------------------------------------") println("Variant name: " + variant.name) println("Build type: " + variant.buildType.name) println("Flavor: " + variant.flavorName) println("Version code: " + (versionCode ?: variant.mergedFlavor.versionCode)) println("Telemetry enabled: " + !isDebug) // ------------------------------------------------------------------------------------------------- // 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' } def buildDate = Config.generateBuildDate() buildConfigField 'String', 'BUILD_DATE', '"' + buildDate + '"' def variantName = variant.getName() // ------------------------------------------------------------------------------------------------- // Adjust: Read token from local file if it exists (Only release builds) // ------------------------------------------------------------------------------------------------- print("Adjust token: ") if (!isDebug) { try { def token = new File("${rootDir}/.adjust_token").text.trim() buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"' println "(Added from .adjust_token file)" } catch (FileNotFoundException ignored) { buildConfigField 'String', 'ADJUST_TOKEN', 'null' println("X_X") } } else { buildConfigField 'String', 'ADJUST_TOKEN', 'null' println("--") } // ------------------------------------------------------------------------------------------------- // Leanplum: Read token from local file if it exists // ------------------------------------------------------------------------------------------------- print("Leanplum token: ") try { def parts = new File("${rootDir}/.leanplum_token").text.trim().split(":") def id = parts[0] def key = parts[1] buildConfigField 'String', 'LEANPLUM_ID', '"' + id + '"' buildConfigField 'String', 'LEANPLUM_TOKEN', '"' + key + '"' println "(Added from .leanplum_token file)" } catch (FileNotFoundException ignored) { buildConfigField 'String', 'LEANPLUM_ID', 'null' buildConfigField 'String', 'LEANPLUM_TOKEN', 'null' println("X_X") } // ------------------------------------------------------------------------------------------------- // Feature build flags // ------------------------------------------------------------------------------------------------- buildConfigField 'Boolean', 'SEND_TAB_ENABLED', (buildType == "nightly" || isDebug).toString() buildConfigField 'Boolean', 'PULL_TO_REFRESH_ENABLED', (false).toString() } androidExtensions { experimental = true } dependencies { implementation project(':architecture') implementation Deps.kotlin_stdlib implementation Deps.kotlin_coroutines implementation Deps.androidx_appcompat implementation Deps.androidx_constraintlayout implementation Deps.rxAndroid implementation Deps.rxKotlin implementation Deps.rxBindings implementation Deps.autodispose implementation Deps.autodispose_android implementation Deps.autodispose_android_aac implementation Deps.anko_commons implementation Deps.anko_sdk implementation Deps.anko_constraintlayout implementation Deps.sentry implementation Deps.leanplum implementation Deps.mozilla_concept_engine implementation Deps.mozilla_concept_storage implementation Deps.mozilla_concept_toolbar implementation Deps.mozilla_concept_sync implementation Deps.mozilla_browser_awesomebar implementation Deps.mozilla_feature_downloads implementation Deps.mozilla_browser_domains implementation Deps.mozilla_browser_icons implementation Deps.mozilla_browser_engine_gecko_beta implementation Deps.mozilla_browser_session implementation Deps.mozilla_browser_storage_sync implementation Deps.mozilla_browser_toolbar implementation Deps.mozilla_feature_accounts implementation Deps.mozilla_feature_app_links implementation Deps.mozilla_feature_awesomebar implementation Deps.mozilla_feature_contextmenu implementation Deps.mozilla_feature_customtabs implementation Deps.mozilla_feature_downloads implementation Deps.mozilla_feature_intent implementation Deps.mozilla_feature_prompts implementation Deps.mozilla_feature_qr implementation Deps.mozilla_feature_session implementation Deps.mozilla_feature_sync implementation Deps.mozilla_feature_toolbar implementation Deps.mozilla_feature_tabs implementation Deps.mozilla_feature_findinpage implementation Deps.mozilla_feature_site_permissions implementation Deps.mozilla_feature_readerview implementation Deps.mozilla_feature_tab_collections implementation Deps.mozilla_service_firefox_accounts implementation Deps.mozilla_service_fretboard implementation Deps.mozilla_service_glean implementation Deps.mozilla_support_ktx implementation Deps.mozilla_support_rustlog implementation Deps.mozilla_ui_colors implementation Deps.mozilla_ui_icons implementation Deps.mozilla_lib_crash debugImplementation Deps.leakcanary releaseImplementation Deps.leakcanary_noop implementation Deps.mozilla_lib_fetch_httpurlconnection armImplementation Gecko.geckoview_beta_arm aarch64Implementation Gecko.geckoview_beta_aarch64 x86Implementation Gecko.geckoview_beta_x86 x86_64Implementation Gecko.geckoview_beta_x86_64 implementation Deps.androidx_legacy implementation Deps.androidx_preference implementation Deps.androidx_fragment implementation Deps.androidx_navigation_fragment implementation Deps.androidx_navigation_ui implementation Deps.androidx_recyclerview implementation Deps.androidx_lifecycle_viewmodel_ktx implementation Deps.androidx_lifecycle_viewmodel_ss implementation Deps.androidx_core implementation Deps.androidx_transition implementation Deps.google_material implementation Deps.autodispose implementation Deps.adjust implementation Deps.installreferrer // Required by Adjust implementation Deps.google_ads_id // Required for the Google Advertising ID // androidTestImplementation Deps.tools_test_runner // androidTestImplementation Deps.tools_espresso_core androidTestImplementation Deps.uiautomator androidTestImplementation Deps.espresso_core, { exclude group: 'com.android.support', module: 'support-annotations' } androidTestImplementation(Deps.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' } androidTestImplementation Deps.espresso_idling_resources androidTestImplementation Deps.tools_test_runner androidTestImplementation Deps.tools_test_rules androidTestUtil Deps.orchestrator androidTestImplementation Deps.espresso_core, { exclude group: 'com.android.support', module: 'support-annotations' } testImplementation Deps.junit testImplementation Deps.robolectric implementation Deps.fragment_testing testImplementation Deps.places_forUnitTests testImplementation Deps.mockito_core androidTestImplementation Deps.mockito_android testImplementation Deps.mockk implementation Deps.glide annotationProcessor Deps.glideAnnotationProcessor debugImplementation Deps.flipper debugImplementation Deps.soLoader releaseImplementation Deps.flipper_noop } // ------------------------------------------------------------------------------------------------- // Task for printing all build variants to build variants in parallel in automation // ------------------------------------------------------------------------------------------------- task printBuildVariants { doLast { def variantData = android.applicationVariants.collect { variant -> [ name: variant.name, buildType: variant.buildType.name, abi: variant.productFlavors.find { it.dimension == 'abi' }.name, isSigned: variant.signingReady, ]} println "variants: " + groovy.json.JsonOutput.toJson(variantData) } } task printGeckoviewVersions { doLast { println "nightly: " + groovy.json.JsonOutput.toJson(GeckoVersions.nightly_version) } } // Normally this should use the same version as the glean dependency. But since we are currently using AC snapshots we // can't reference a git tag with a specific version here. So we are just using "master" and hoping for the best. apply from: 'https://github.com/mozilla-mobile/android-components/raw/master/components/service/glean/scripts/sdk_generator.gradle' // For production builds, the native code for all `org.mozilla.appservices` dependencies gets compiled together // into a single "megazord" build, and different megazords are published for different subsets of features. // Ref https://mozilla.github.io/application-services/docs/applications/consuming-megazord-libraries.html // Substitute all appservices dependencies with an appropriate megazord. afterEvaluate { def megazord = "fenix-megazord" def appServicesGroup = "org.mozilla.appservices" def appServicesVersion = null configurations.each { configuration -> configuration.resolutionStrategy.eachDependency { DependencyResolveDetails dependency -> if (dependency.requested.group == appServicesGroup) { // Ensure that we only depend on a single, consistent version of appservices. if (appServicesVersion == null) { appServicesVersion = dependency.requested.version } else { if (dependency.requested.version != appServicesVersion) { logger.lifecycle("In ${configuration}: mismatched '${appServicesGroup}` dependency version:" + " '${dependency.requested.group}:${dependency.requested.name}:${dependency.requested.version}'" + " does not have expected version ${appServicesVersion}") throw new RuntimeException("mismatched '${appServicesGroup}` dependency version") } } // Check whether it was already megazorded. if (! dependency.requested.name.endsWith("-withoutLib")) { // Use either the -forUnitTests megazord, or the default one. def substitution if (dependency.requested.name.endsWith("-forUnitTests")) { substitution = "org.mozilla.appservices:${megazord}-forUnitTests:${appServicesVersion}" } else { substitution = "org.mozilla.appservices:${megazord}:${appServicesVersion}" } logger.lifecycle("In ${configuration}: substituting megazord module '$substitution' for component module" + " '${dependency.requested.group}:${dependency.requested.name}:${dependency.requested.version}'") dependency.useTarget(substitution) } } } } }