buildDir "${topobjdir}/gradle/build/mobile/android/app"

apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
apply plugin: 'com.android.application'
apply plugin: 'checkstyle'

android {
    compileSdkVersion 23
    buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION

    defaultConfig {
        targetSdkVersion 23
        minSdkVersion 15
        applicationId mozconfig.substs.ANDROID_PACKAGE_NAME
        testApplicationId 'org.mozilla.roboexample.test'
        testInstrumentationRunner 'org.mozilla.gecko.FennecInstrumentationTestRunner'
        manifestPlaceholders = [
            ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
            MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
            MOZ_ANDROID_SHARED_ID: "${mozconfig.substs.ANDROID_PACKAGE_NAME}.sharedID",
        ]
        // Used by Robolectric based tests; see TestRunner.
        buildConfigField 'String', 'BUILD_DIR', "\"${project.buildDir}\""

        vectorDrawables.useSupportLibrary = true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    dexOptions {
        javaMaxHeapSize "2g"
    }

    lintOptions {
        abortOnError true
    }

    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFile "${topsrcdir}/mobile/android/config/proguard/proguard.cfg"
        }
    }

    productFlavors {
        // For API 21+ - with multi dex, this will be faster for local development.
        local {
            // For multi dex, setting `minSdkVersion 21` allows the Android gradle plugin to
            // pre-DEX each module and produce an APK that can be tested on
            // Android Lollipop without time consuming DEX merging processes.
            minSdkVersion 21
            dexOptions {
                preDexLibraries true
                // We only call `MultiDex.install()` for the automation build flavor
                // so this may not work. However, I don't think the multidex support
                // library is necessary for 21+, so I expect that it will work.
                multiDexEnabled true
            }
        }
        // For API < 21 - does not support multi dex because local development
        // is slow in that case. Most builds will not require multi dex so this
        // should not be an issue.
        localOld {
        }
        // Automation builds.
        automation {
            dexOptions {
                // As of FF48 on beta, the "test", "lint", etc. treeherder jobs fail because they
                // exceed the method limit. Beta includes Adjust and its GPS dependencies, which
                // increase the method count & explain the failures. Furthermore, this error only
                // occurs on debug builds because we don't proguard.
                //
                // We enable multidex as an easy, quick-fix with minimal side effects but before we
                // move to gradle for our production builds, we should re-evaluate this decision
                // (bug 1286677).
                multiDexEnabled true
            }
        }
    }

    sourceSets {
        main {
            manifest.srcFile "${project.buildDir}/generated/source/preprocessed_manifest/AndroidManifest.xml"

            aidl {
                srcDir "${topsrcdir}/mobile/android/base/aidl"
            }

            java {
                srcDir "${topsrcdir}/mobile/android/base/java"
                srcDir "${topsrcdir}/mobile/android/search/java"
                srcDir "${topsrcdir}/mobile/android/javaaddons/java"
                srcDir "${topsrcdir}/mobile/android/services/src/main/java"

                if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                    srcDir "${topsrcdir}/mobile/android/stumbler/java"
                }

                exclude 'org/mozilla/gecko/CrashReporter.java'

                if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
                    exclude 'org/mozilla/gecko/ChromeCastDisplay.java'
                    exclude 'org/mozilla/gecko/ChromeCastPlayer.java'
                    exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
                    exclude 'org/mozilla/gecko/GeckoPresentationDisplay.java'
                    exclude 'org/mozilla/gecko/MediaPlayerManager.java'
                }

                if (mozconfig.substs.MOZ_WEBRTC) {
                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
                    srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_render/android/java/src"
                }

                if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
                    exclude 'org/mozilla/gecko/adjust/StubAdjustHelper.java'
                } else {
                    exclude 'org/mozilla/gecko/adjust/AdjustHelper.java'
                }

                if (!mozconfig.substs.MOZ_ANDROID_GCM) {
                    exclude 'org/mozilla/gecko/gcm/**/*.java'
                    exclude 'org/mozilla/gecko/push/**/*.java'
                }

                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
            }

            res {
                srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res"
                srcDir "${project.buildDir}/generated/source/preprocessed_resources" // See syncPreprocessedResources.
                srcDir "${topsrcdir}/mobile/android/base/resources"
                srcDir "${topsrcdir}/mobile/android/services/src/main/res"
                if (mozconfig.substs.MOZ_CRASHREPORTER) {
                    srcDir "${topsrcdir}/mobile/android/base/crashreporter/res"
                }
            }

            assets {
                if (mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY && !mozconfig.substs.MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER) {
                    // If we are packaging the bouncer, it will have the distribution, so don't put
                    // it in the main APK as well.
                    srcDir "${mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY}/assets"
                }
                srcDir "${topsrcdir}/mobile/android/app/assets"
            }
        }

        test {
            java {
                srcDir "${topsrcdir}/mobile/android/tests/background/junit4/src"

                if (!mozconfig.substs.MOZ_ANDROID_GCM) {
                    exclude 'org/mozilla/gecko/gcm/**/*.java'
                    exclude 'org/mozilla/gecko/push/**/*.java'
                }
            }
            resources {
                srcDir "${topsrcdir}/mobile/android/tests/background/junit4/resources"
            }
        }

        androidTest {
            java {
                srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/src"
                srcDir "${topsrcdir}/mobile/android/tests/background/junit3/src"
                srcDir "${topsrcdir}/mobile/android/tests/browser/junit3/src"
                srcDir "${topsrcdir}/mobile/android/tests/javaddons/src"
            }
            res {
                srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/res"
            }
            assets {
                srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/assets"
            }
        }
    }

    testOptions {
        unitTests.all {
            // We'd like to use (Runtime.runtime.availableProcessors()/2), but
            // we have tests that start test servers and the bound ports
            // collide.  We'll fix this soon to have much faster test cycles.
            maxParallelForks 1
        }
    }
}

dependencies {
    compile 'com.android.support:multidex:1.0.0'

    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
    compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"

    if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
        compile "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
    }

    if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
        compile "com.google.android.gms:play-services-ads:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
    }

    if (mozconfig.substs.MOZ_ANDROID_GCM) {
        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
        compile "com.google.android.gms:play-services-measurement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
    }

    // Include LeakCanary in most gradle based builds. LeakCanary adds about 5k methods, so we disable
    // it for the (non-proguarded, non-multidex) localOld builds to allow space for other libraries.
    // Gradle based tests include the no-op version.  Mach based builds only include the no-op version
    // of this library.
    // It doesn't seem like there is a non-trivial way to be conditional on 'localOld', so instead we explicitly
    // define a version of leakcanary for every flavor:
    localCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
    localOldCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
    automationCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'

    compile project(':geckoview')
    compile project(':thirdparty')

    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:3.1.2'
    testCompile 'org.simpleframework:simple-http:6.0.1'
    testCompile 'org.mockito:mockito-core:1.10.19'

    // Including the Robotium JAR directly can cause issues with dexing.
    androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.5.4'
}

// TODO: (bug 1261486): This impl is not robust -
// we just wanted to land something.
task checkstyle(type: Checkstyle) {
    configFile file("checkstyle.xml")
    // TODO: should use sourceSets from project instead of hard-coded str.
    source '../base/java/'
    // TODO: This ignores our pre-processed resources.
    include '**/*.java'
    // TODO: classpath should probably be something.
    classpath = files()
}

task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
    into("${project.buildDir}/generated/source/preprocessed_code")
    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
        // All other preprocessed code is included in the geckoview project.
        include '**/AdjustConstants.java'
    }
}

// The localization system uses the moz.build preprocessor to interpolate a .dtd
// file of XML entity definitions into an XML file of elements referencing those
// entities.  (Each locale produces its own .dtd file, backstopped by the en-US
// .dtd file in tree.)  Android Studio (and IntelliJ) don't handle these inline
// entities smoothly.  This filter merely expands the entities in place, making
// them appear properly throughout the IDE.  Be aware that this assumes that the
// JVM's file.encoding is utf-8.  See comments in
// mobile/android/mach_commands.py.
class ExpandXMLEntitiesFilter extends FilterReader {
    ExpandXMLEntitiesFilter(Reader input) {
        // Extremely inefficient, but whatever.
        super(new StringReader(groovy.xml.XmlUtil.serialize(new XmlParser(false, false, true).parse(input))))
    }
}

task syncPreprocessedResources(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
    into("${project.buildDir}/generated/source/preprocessed_resources")
    from("${topobjdir}/mobile/android/base/res")
    filesMatching('**/strings.xml') {
        filter(ExpandXMLEntitiesFilter)
    }
}

// It's not easy -- see the backout in Bug 1242213 -- to change the <manifest>
// package for Fennec.  Gradle has grown a mechanism to achieve what we want for
// Fennec, however, with applicationId.  To use the same manifest as moz.build,
// we replace the package with org.mozilla.gecko (the eventual package) here.
task rewriteManifestPackage(type: Copy, dependsOn: rootProject.generateCodeAndResources) {
    into("${project.buildDir}/generated/source/preprocessed_manifest")
    from("${topobjdir}/mobile/android/base/AndroidManifest.xml")
    filter { it.replaceFirst(/package=".*?"/, 'package="org.mozilla.gecko"') }
}

apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"

android.applicationVariants.all { variant ->
    variant.preBuild.dependsOn rewriteManifestPackage
    variant.preBuild.dependsOn syncPreprocessedCode
    variant.preBuild.dependsOn syncPreprocessedResources

    // Automation builds don't include Gecko binaries, since those binaries are
    // not produced until after build time (at package time).  Therefore,
    // automation builds include the Gecko binaries into the APK at package
    // time.  The "withGeckoBinaries" variant of the :geckoview project also
    // does this.  (It does what it says on the tin!)  For notes on this
    // approach, see mobile/android/gradle/with_gecko_binaries.gradle.

    // Like 'local' or 'localOld'.
    def productFlavor = variant.productFlavors[0].name

    // :app uses :geckoview:release and handles it's own Gecko binary inclusion,
    // even though this would be most naturally done in the :geckoview project.
    if (!productFlavor.equals('automation')) {
        configureVariantWithGeckoBinaries(variant)
    }
}

apply plugin: 'spoon'

spoon {
    // For now, let's be verbose.
    debug = true
    // It's not helpful to pass when we don't have a device connected.
    failIfNoDeviceConnected = true

    def spoonPackageName
    if (gradle.startParameter.taskNames.contains('runBrowserTests')) {
        spoonPackageName = 'org.mozilla.tests.browser.junit3'
    }
    if (gradle.startParameter.taskNames.contains('runBackgroundTests')) {
        spoonPackageName = 'org.mozilla.gecko.background'
    }
    if (project.hasProperty('spoonPackageName')) {
        // Command line overrides everything.
        spoonPackageName = project.spoonPackageName
    }
    if (spoonPackageName) {
        instrumentationArgs = ['-e', "package=${spoonPackageName}".toString()]
    }
}

// See discussion at https://github.com/stanfy/spoon-gradle-plugin/issues/9.
afterEvaluate {
    tasks["spoonLocal${android.testBuildType.capitalize()}AndroidTest"].outputs.upToDateWhen { false }

    // This is an awkward way to define different sets of instrumentation tests.
    // The task name itself is fished at runtime and the package name configured
    // in the spoon configuration.
    task runBrowserTests {
        dependsOn tasks["spoonLocalOldDebugAndroidTest"]
    }
    task runBackgroundTests {
        dependsOn tasks["spoonLocalOldDebugAndroidTest"]
    }
}

// Bug 1299015: Complain to treeherder if checkstyle, lint, or unittest fails.  It's not obvious
// how to listen to individual errors in most cases, so we just link to the reports for now.
def makeTaskExecutionListener(artifactRootUrl) {
    return new TaskExecutionListener() {
        void beforeExecute(Task task) {
            // Do nothing.
        }

        void afterExecute(Task task, TaskState state) {
            if (!state.failure) {
                return
            }

            // Link to the failing report.  The task path and the report path
            // depend on the android-lint task in
            // taskcluster/ci/android-stuff/kind.yml.  It's not possible to link
            // directly, so for now consumers will need to copy-paste the URL.
            switch (task.path) {
            case ':app:checkstyle':
                def url = "${artifactRootUrl}/public/android/checkstyle/checkstyle.xml"
                println "TEST-UNEXPECTED-FAIL | android-checkstyle | Checkstyle rule violations were found. See the report at: $url"
                break

            case ':app:lintAutomationDebug':
                def url = "${artifactRootUrl}/public/android/lint/lint-results-automationDebug.html"
                println "TEST-UNEXPECTED-FAIL | android-lint | Lint found errors in the project; aborting build. See the report at: $url"
                break

            case ':app:testAutomationDebugUnitTest':
                def url = "${artifactRootUrl}/public/android/unittest/automationDebug/index.html"
                println "TEST-UNEXPECTED-FAIL | android-test | There were failing tests. See the report at: $url"
                break
            }
        }
    }
}

// TASK_ID and RUN_ID are provided by docker-worker; see
// https://docs.taskcluster.net/manual/execution/workers/docker-worker.
if (System.env.TASK_ID && System.env.RUN_ID) {
    def artifactRootUrl = "https://queue.taskcluster.net/v1/task/${System.env.TASK_ID}/runs/${System.env.RUN_ID}/artifacts"
    gradle.addListener(makeTaskExecutionListener(artifactRootUrl))
}