Revision control

Copy as Markdown

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import org.mozilla.gradle.tasks.ValidateAndroidAppReleaseConfiguration
plugins {
id "com.jetbrains.python.envs" version "0.0.26"
}
apply plugin: 'com.android.application'
apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'findbugs'
apply plugin: 'jacoco'
apply plugin: 'pmd'
apply plugin: 'checkstyle'
apply plugin: 'kotlin-android-extensions'
apply from: "$project.rootDir/tools/gradle/versionCode.gradle"
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "org.mozilla.tv.firefox"
minSdkVersion 22
targetSdkVersion 28
versionCode 11 // This versionCode is "frozen" for local builds. For "release" builds we
// override this with a generated versionCode at build time.
versionName "4.8"
testInstrumentationRunner "org.mozilla.tv.firefox.FirefoxOnDeviceTestRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
multiDexEnabled true
ndk {
// This strips unused architectures out of our build, lowering out APK size.
// At this time, all FFTV devices run ARM v7, and most emulators use x86. See
// #2647 for details
// TODO improve APK size further in #2743
abiFilters 'armeabi-v7a', 'x86'
}
}
dexOptions {
preDexLibraries true
}
lintOptions {
lintConfig file("lint.xml")
baseline file("lint-baseline.xml")
// We want to fail the build if there is a lint error, otherwise we'll never fix them.
// This is the most efficient way to make Lint fail: the lint tool will add additional
// checks in the future so this ensures they will fail our build as soon as they are added,
// unlike a whitelist.
warningsAsErrors true
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
}
flavorDimensions "engine"
productFlavors {
system {
dimension "engine"
}
gecko {
dimension "engine"
// Move the suffix to the `system` product flavor when the shipping
// version is based on Gecko.
applicationIdSuffix ".gecko"
}
}
testOptions {
unitTests.includeAndroidResources true // for robolectric.
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
sourceSets {
test {
resources {
// Make the default asset folder available as test resource folder. This is only
// necessary for SearchEngineParserTest.getBasePath, which access the test resources
// before RuntimeEnvironment.application is available.
srcDir "${projectDir}/src/main/assets/"
}
}
}
// Needed until WorkManager from a-c gets upgraded
packagingOptions {
exclude 'META-INF/proguard/androidx-annotations.pro'
exclude 'META-INF/atomicfu.kotlin_module'
}
// Required for GeckoARM configuration
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
allWarningsAsErrors = true
jvmTarget = JavaVersion.VERSION_1_8
}
}
configurations {
systemImplementation {}
geckoImplementation {}
}
// Generate markdown docs for the collected metrics.
ext.gleanGenerateMarkdownDocs = true
ext.gleanDocsDirectory = "$rootDir/docs/glean"
apply from: 'https://github.com/mozilla-mobile/android-components/raw/v' + rootProject.ext.moz_components_version + '/components/service/glean/scripts/sdk_generator.gradle'
dependencies {
implementation 'io.sentry:sentry-android:1.7.21'
implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.legacy:legacy-support-v4:$androidx_version"
implementation "androidx.cardview:cardview:$androidx_version"
implementation "androidx.browser:browser:$androidx_version"
implementation "com.google.android.material:material:$androidx_version"
implementation "androidx.leanback:leanback:$androidx_version"
implementation "androidx.recyclerview:recyclerview:$androidx_version"
implementation "androidx.core:core-ktx:1.0.2"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.work:work-runtime-ktx:$androidx_work_version"
implementation 'com.squareup.picasso:picasso:2.71828'
implementation ("com.google.code.findbugs:annotations:3.0.1") {
// We really only need the SuppressFBWarnings annotation, everything else can be ignored.
// Without this we get weird failures due to dependencies.
transitive = false
}
implementation "org.mozilla.components:concept-fetch:$moz_components_version"
implementation "org.mozilla.components:concept-push:$moz_components_version"
implementation "org.mozilla.components:lib-push-amazon:$moz_components_version"
implementation "org.mozilla.components:lib-fetch-okhttp:$moz_components_version"
implementation "org.mozilla.components:lib-fetch-httpurlconnection:$moz_components_version"
implementation "org.mozilla.components:browser-session:$moz_components_version"
implementation "org.mozilla.components:browser-search:$moz_components_version"
implementation "org.mozilla.components:browser-domains:$moz_components_version"
implementation "org.mozilla.components:concept-engine:$moz_components_version"
implementation "org.mozilla.components:service-telemetry:$moz_components_version"
implementation "org.mozilla.components:ui-colors:$moz_components_version"
implementation "org.mozilla.components:ui-fonts:$moz_components_version"
implementation "org.mozilla.components:feature-session:$moz_components_version"
implementation "org.mozilla.components:feature-push:$moz_components_version"
implementation "org.mozilla.components:feature-sendtab:$moz_components_version"
implementation "org.mozilla.components:ui-icons:$moz_components_version"
implementation "org.mozilla.components:service-fretboard:$moz_components_version"
implementation "org.mozilla.components:service-firefox-accounts:$moz_components_version"
implementation "org.mozilla.components:support-ktx:$moz_components_version"
implementation "org.mozilla.components:support-utils:$moz_components_version"
implementation "org.mozilla.components:support-rusthttp:$moz_components_version"
implementation "org.mozilla.components:service-glean:$moz_components_version"
systemImplementation "org.mozilla.components:browser-engine-system:$moz_components_version"
// Required to override android.support lib in GeckoView dependency (uses palette-v7:26.1.0)
geckoImplementation "androidx.palette:palette:$androidx_version"
geckoImplementation "org.mozilla.components:browser-engine-gecko-nightly:$moz_components_version"
geckoImplementation "org.mozilla.geckoview:geckoview-nightly"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "io.reactivex.rxjava2:rxjava:2.2.6"
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "androidx.lifecycle:lifecycle-extensions:$architecture_components_version"
implementation "androidx.lifecycle:lifecycle-viewmodel:$architecture_components_version"
// TODO lifecycle-reactivestreams bridges between LiveData and Rx. Remove it
// when we fully migrate off of LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams:$architecture_components_version"
testImplementation "androidx.arch.core:core-testing:$architecture_components_version"
testImplementation "androidx.test.ext:junit:$junit"
testImplementation "org.robolectric:robolectric:$robolectric_version"
testImplementation 'org.mockito:mockito-core:2.24.5'
testImplementation "io.mockk:mockk:1.9.1"
testImplementation "androidx.test:core:$test_core"
testImplementation "androidx.work:work-testing:2.0.1" // Required by Glean testing
androidTestImplementation "androidx.test:core:$test_core"
androidTestImplementation "androidx.test.uiautomator:uiautomator:$uiautomator_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espresso_version")
androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-web:$espresso_version"
androidTestImplementation "androidx.test:runner:$test_runner"
androidTestImplementation "androidx.test:rules:$test_rules"
androidTestUtil "androidx.test:orchestrator:$test_orchestrator"
androidTestImplementation "org.mozilla.components:support-android-test:$moz_components_version"
testImplementation "org.mozilla.components:support-test:$moz_components_version"
testImplementation "org.mozilla.components:support-test-appservices:$moz_components_version"
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
// 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-forUnitTests:21.3.0"
androidTestImplementation "tools.fastlane:screengrab:1.2.0", {
// This Fastlane dependency causes our builds to fail due to having two different
// versions of UiAutomator. Fastlane is not on the most recent version of
// com.android.support.test.uiautomator, which presumably prevents Jetifier from
// fixing this
//
// TODO try removing this then running Espresso every time Fastlane is updated
exclude group: 'com.android.support.test.uiautomator', module: 'uiautomator-v18'
}
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}"
// This library has not been updated since 2013, so it might be unlikely that the version will change
compileOnly files("libs/amazon-device-messaging-${adm_version}.jar")
testImplementation files("libs/amazon-device-messaging-${adm_version}.jar")
implementation "com.google.android.gms:play-services-oss-licenses:$oss_licenses_version"
}
// task validateReleaseConfiguration(type: ValidateAndroidAppReleaseConfiguration)
// -------------------------------------------------------------------------------------------------
// Dynamically set versionCode (See tools/build/versionCode.gradle
// -------------------------------------------------------------------------------------------------
android.applicationVariants.all { variant ->
def buildType = variant.buildType.name
if (buildType == "release") {
variant.outputs.all { output ->
setVersionCodeOverride(generatedVersionCode)
}
}
println("Build type: " + buildType + " (versionCode = " + variant.mergedFlavor.versionCode + ")")
}
// Disable unit tests for gecko since we don't actively support gecko variant for FFTV
gradle.taskGraph.useFilter { task ->
if (task.name.startsWith("test") && task.name.contains("Gecko")) {
return false;
}
return true;
}
// -------------------------------------------------------------------------------------------------
// Secrets: add API secrets from config files into the BuildConfig instance.
// We use files because they're easier to manage than environment variables.
// -------------------------------------------------------------------------------------------------
def addSecretToBuildConfig(variant, fieldName, fileBaseName) {
def variantName = variant.getName()
print("${fieldName} (" + variantName + "): ")
// We separate the debug files from the release files so that we don't
// accidentally ship a release using the debug key or vice versa.
def fileSuffix
if (variantName.toLowerCase().contains("debug")) {
fileSuffix = "debug"
} else if (variantName.toLowerCase().contains("release")) {
fileSuffix = "release"
} else {
throw new IllegalStateException("Unhandled variant $variantName")
}
def filePath = "${rootDir}/${fileBaseName}_${fileSuffix}"
def file = new File(filePath)
if (file.exists()) {
def token = file.text.trim()
variant.buildConfigField 'String', fieldName, '"' + token + '"'
println "Added from $filePath"
} else {
variant.buildConfigField 'String', fieldName, 'null'
println("X_X")
}
}
android.applicationVariants.all { variant ->
addSecretToBuildConfig(variant, 'SENTRY_DSN', ".sentry_dsn")
}
// -------------------------------------------------------------------------------------------------
// L10N: Generate list of locales
// Firefox TV 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 }
}
def generatedLocaleListDir = 'src/main/java/org/mozilla/tv/firefox/generated'
def generatedLocaleListFilename = 'LocaleList.java'
task generateLocaleList {
doLast {
def dir = file(generatedLocaleListDir)
dir.mkdir()
def localeList = file(new File(dir, generatedLocaleListFilename))
localeList.delete()
localeList.createNewFile()
localeList << "package org.mozilla.tv.firefox.generated;" << "\n" << "\n"
localeList << "import java.util.Arrays;" << "\n"
localeList << "import java.util.Collections;" << "\n"
localeList << "import java.util.List;" << "\n"
localeList << "\n"
localeList << "public class LocaleList {" << "\n"
// findbugs doesn't like "public static final String[]", see http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_ARRAY
localeList << " public static final List<String> BUNDLED_LOCALES = Collections.unmodifiableList(Arrays.asList(new String[] { "
localeList << getEnabledLocales().join(", ") + " }));" << "\n"
localeList << "}" << "\n"
}
}
tasks.whenTaskAdded { task ->
if (name.contains("compile")) {
task.dependsOn generateLocaleList
}
}
clean.doLast {
file(generatedLocaleListDir).deleteDir()
}
// -------------------------------------------------------------------------------------------------
// Static Analysis: findbugs and pmd
// -------------------------------------------------------------------------------------------------
findbugs {
ignoreFailures = false
effort = "max"
// This selects what level of bugs to report: low means low priority issues will be reported
// (in addition to medium+high), which corresponds to warning about everything.
// TODO: boost this to low once low priority issues are fixed.
reportLevel = "medium"
excludeFilter = new File("${project.rootDir}/quality/findbugs-exclude.xml")
}
task findbugs(type: FindBugs, dependsOn: "assemble", group: 'verification') {
classes = files("$projectDir.absolutePath/build/intermediates/classes")
source = fileTree('src/main/java')
classpath = files()
// Only one report format is supported. Html is easier to read, so let's use that
// (xml is the one that's enabled by default).
reports {
xml.enabled = false
html.enabled = true
}
}
pmd {
toolVersion = '5.5.2'
ignoreFailures = true
ruleSetFiles = files("${project.rootDir}/quality/pmd-rules.xml")
ruleSets = []
}
task pmd(type: Pmd, group: 'verification') {
source 'src'
include '**/*.java'
exclude('**/gen/**',
'**/debug/**',
'**/androidTest/**',
'**/test**/**')
reports {
xml.enabled = false
html.enabled = true
html {
destination file("$projectDir.absolutePath/build/reports/pmd/pmd.html")
}
}
}
task checkstyle(type: Checkstyle) {
configFile file("${project.rootDir}/quality/checkstyle.xml")
source 'src'
include '**/*.java'
exclude '**/gen/**'
classpath = files()
}
// Set up jacoco for code coverage. We originally used the jacoco-android plugin
// but it doesn't support kotlin so we're configuring it manually.
if (project.hasProperty("coverage")) {
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
android.applicationVariants.all { variant ->
// We dynamically generate our jacoco tasks to avoid creating too many build variants,
// which takes up space in AS despite not being run by most users and it can accidentally
// be run during CI with commands like `./gradlew test`, taking up more build time.
task "jacoco${variant.name.capitalize()}TestReport"(type: JacocoReport,
dependsOn: ["test${variant.name.capitalize()}UnitTest"]) {
reports {
html.enabled true
xml.enabled true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
'**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
def kotlinTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/${variant.name}", excludes: fileFilter)
def javaTree = fileTree(dir: "$project.buildDir/intermediates/classes/${variant.flavorName}/${variant.buildType.name}",
excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([kotlinTree, javaTree])
executionData = fileTree(dir: project.buildDir, includes: [
"jacoco/test${variant.name.capitalize()}UnitTest.exec", 'outputs/code-coverage/connected/*coverage.ec'
])
}
}
android {
buildTypes {
debug {
testCoverageEnabled true
applicationIdSuffix ".coverage"
}
}
}
}
afterEvaluate {
check.dependsOn 'findbugs', 'pmd', 'checkstyle'
}