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

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    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}\""
    }

    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 fast local development.  If you have an API 21 device, you should
        // use this.
        local {
            // 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
                multiDexEnabled true
            }
        }
        // For local development on older devices.  Use this only if you only
        // have a pre-API 21 device, or if you want to test API-specific things.
        localOld {
        }
        // Automation builds.
        automation {
        }
    }

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

            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"
                }

                if (!mozconfig.substs.MOZ_CRASHREPORTER) {
                    exclude 'org/mozilla/gecko/CrashReporter.java'
                }

                if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
                    exclude 'org/mozilla/gecko/ChromeCast.java'
                    exclude 'org/mozilla/gecko/GeckoMediaPlayer.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'
                }
            }
        }

        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:support-v4:23.0.1'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:recyclerview-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'

    if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
        compile 'com.android.support:mediarouter-v7:23.0.1'
        compile 'com.google.android.gms:play-services-basement:8.1.0'
        compile 'com.google.android.gms:play-services-base:8.1.0'
        compile 'com.google.android.gms:play-services-cast:8.1.0'
    }

    if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
        compile 'com.google.android.gms:play-services-ads:8.1.0'
        compile 'com.google.android.gms:play-services-analytics:8.1.0'
        compile 'com.google.android.gms:play-services-appindexing:8.1.0'
        compile 'com.google.android.gms:play-services-basement:8.1.0'
    }

    if (mozconfig.substs.MOZ_ANDROID_GCM) {
        compile 'com.google.android.gms:play-services-basement:8.1.0'
        compile 'com.google.android.gms:play-services-base:8.1.0'
        compile 'com.google.android.gms:play-services-gcm:8.1.0'
    }

    // Gradle based builds include LeakCanary.  Gradle based tests include the no-op version.  Mach
    // based builds only include the no-op version of this library.
    compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'

    compile project(':thirdparty')

    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:3.0'
    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:4.3.1'
}

task syncOmnijarFromDistDir(type: Sync) {
    into("${project.buildDir}/generated/omnijar")
    from("${topobjdir}/dist/fennec/assets") {
        include 'omni.ja'
    }
}

task checkLibsExistInDistDir<< {
    if (syncLibsFromDistDir.source.empty) {
        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
    }
}

task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
    into("${project.buildDir}/generated/jniLibs")
    from("${topobjdir}/dist/fennec/lib")
}

task checkAssetsExistInDistDir<< {
    if (syncAssetsFromDistDir.source.empty) {
        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
    }
}

task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
    into("${project.buildDir}/generated/assets")
    from("${topobjdir}/dist/fennec/assets") {
        exclude 'omni.ja'
    }
}

task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
    into("${project.buildDir}/generated/source/preprocessed_code")
    from("${topobjdir}/mobile/android/base/generated/preprocessed")
}

// 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)
    }
}

// The omnijar inputs are listed as resource directory inputs to a dummy JAR.
// That arrangement labels them nicely in IntelliJ.  See the comment in the
// :omnijar project for more context.
evaluationDependsOn(':omnijar')

task buildOmnijar(type:Exec) {
    dependsOn rootProject.generateCodeAndResources

    // See comment in :omnijar project regarding interface mismatches here.
    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs

    // Produce a single output file.
    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"

    workingDir "${topobjdir}"

    commandLine mozconfig.substs.GMAKE
    args '-C'
    args "${topobjdir}/mobile/android/base"
    args 'gradle-omnijar'

    // Only show the output if something went wrong.
    ignoreExitValue = true
    standardOutput = new ByteArrayOutputStream()
    errorOutput = standardOutput
    doLast {
        if (execResult.exitValue != 0) {
            throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
        }
    }
}

// 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"') }
}

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

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

    // We insert omni.ja and the .so libraries into all local builds.
    if (!productFlavor.startsWith('local')) {
        return
    }

    syncOmnijarFromDistDir.dependsOn buildOmnijar
    def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
    generateAssetsTask.dependsOn syncOmnijarFromDistDir
    generateAssetsTask.dependsOn syncLibsFromDistDir
    generateAssetsTask.dependsOn syncAssetsFromDistDir

    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncOmnijarFromDistDir.destinationDir
    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncAssetsFromDistDir.destinationDir
    android.sourceSets."${productFlavor}${buildType.capitalize()}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
}

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"]
    }
}
