--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -84,17 +84,16 @@ android {
main {
manifest.srcFile "${project.buildDir}/generated/source/preprocessed_manifest/AndroidManifest.xml"
aidl {
srcDir "${topsrcdir}/mobile/android/base/aidl"
}
java {
- srcDir "${topsrcdir}/mobile/android/geckoview/src/main/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"
}
@@ -219,16 +218,17 @@ dependencies {
compile "com.google.android.gms:play-services-measurement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
}
// 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(':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.
@@ -242,50 +242,22 @@ task checkstyle(type: Checkstyle) {
// 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 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")
+ 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
@@ -301,82 +273,48 @@ class ExpandXMLEntitiesFilter extends Fi
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"') }
}
+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
- // 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
+ // :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)
}
-
- 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.
@@ -415,37 +353,37 @@ afterEvaluate {
// 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
}
}
}
}
--- a/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
@@ -30,26 +30,26 @@
"filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
"unpack": true
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "dotgradle.tar.xz",
"unpack": true,
"digest": "9f082ccd71ad18991eb71fcad355c6990f50a72a09ab9b79696521485656083a72faf5a8d4714de9c4b901ee2319b6786a51964846bb7075061642a8505501c2",
"size": 512
--- a/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -45,26 +45,26 @@
"filename": "gcc.tar.xz",
"unpack": true
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
},
{
"size": 30899096,
"visibility": "public",
"digest": "ac9f5f95d11580d3dbeff87e80a585fe4d324b270dabb91b1165686acab47d99fa6651074ab0be09420239a5d6af38bb2c539506962a7b44e0ed4d080bba2953",
"algorithm": "sha512",
"filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
"unpack": true
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -55,26 +55,26 @@
"filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
"unpack": true
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
},
{
"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
"size": 97552448,
"digest": "272438c1692a46998dc44f22bd1fe18da1be7af2e7fdcf6c52709366c80c73e30637f0c3864f45c64edf46ce6a905538c14b2313983be973f9f29a2f191ec89b",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/build.gradle
@@ -0,0 +1,83 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview"
+
+apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+ defaultConfig {
+ targetSdkVersion 23
+ minSdkVersion 15
+ }
+
+ buildTypes {
+ withGeckoBinaries {
+ initWith release
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+
+ dexOptions {
+ javaMaxHeapSize "2g"
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ sourceSets {
+ main {
+ java {
+ srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
+
+ // TODO: support WebRTC.
+ // 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"
+ // }
+
+ // TODO: don't use AppConstants.
+ srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
+ }
+
+ assets {
+ }
+ }
+ }
+}
+
+dependencies {
+ compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+}
+
+task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
+ into("${project.buildDir}/generated/source/preprocessed_code")
+ from("${topobjdir}/mobile/android/base/generated/preprocessed") {
+ // AdjustConstants is included in the main app project.
+ exclude '**/AdjustConstants.java'
+ }
+}
+
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
+android.libraryVariants.all { variant ->
+ variant.preBuild.dependsOn syncPreprocessedCode
+
+ // Like 'debug', 'release', or 'withGeckoBinaries'.
+ def buildType = variant.buildType.name
+
+ // It would be most natural for :geckoview to always include the Gecko
+ // binaries, but that's difficult; see the notes in
+ // mobile/android/gradle/with_gecko_binaries.gradle. Instead :app uses
+ // :geckoview:release and handles it's own Gecko binary inclusion.
+ if (buildType.equals('withGeckoBinaries')) {
+ configureVariantWithGeckoBinaries(variant)
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.mozilla.geckoview">
+
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <!-- READ_EXTERNAL_STORAGE was added in API 16, and is only enforced in API
+ 19+. We declare it so that the bouncer APK and the main APK have the
+ same set of permissions. -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+ <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
+
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+
+ <uses-feature android:name="android.hardware.location" android:required="false"/>
+ <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+ <uses-feature android:name="android.hardware.touchscreen"/>
+
+ <!--#ifdef MOZ_WEBRTC-->
+ <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
+ <!--<uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
+ <!--<uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
+ <!--<uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
+ <!--#endif-->
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-feature android:name="android.hardware.camera" android:required="false"/>
+ <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+
+ <!-- App requires OpenGL ES 2.0 -->
+ <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/build.gradle
@@ -0,0 +1,46 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview_example"
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+ defaultConfig {
+ applicationId "org.mozilla.geckoview_example"
+ minSdkVersion 15
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ testCompile 'junit:junit:4.12'
+
+ compile 'com.android.support:support-annotations:23.0.1'
+
+ // Later versions (2.2.2, 0.5) requires newer support libraries, leading to
+ // "Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.0.1) and test app (23.1.1) differ."
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+ androidTestCompile 'com.android.support.test:runner:0.4.1'
+
+ compile project(':geckoview')
+}
+
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
+android.applicationVariants.all { variant ->
+ // It would be most natural for :geckoview to always include the Gecko
+ // binaries, but that's difficult; see the notes in
+ // mobile/android/gradle/with_gecko_binaries.gradle. Instead we handle our
+ // own Gecko binary inclusion.
+ configureVariantWithGeckoBinaries(variant)
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/nalexander/.mozbuild/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/ApplicationTest.java
@@ -0,0 +1,13 @@
+package org.mozilla.geckoview_example;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/GeckoViewActivityTest.java
@@ -0,0 +1,34 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+@RunWith(AndroidJUnit4.class)
+public class GeckoViewActivityTest {
+
+ @Rule
+ public ActivityTestRule<GeckoViewActivity> mActivityRule = new ActivityTestRule(GeckoViewActivity.class);
+
+ @Test
+ public void testA() throws InterruptedException {
+ onView(withId(R.id.gecko_view))
+ .check(matches(isDisplayed()));
+
+ Assert.assertEquals(1, 2);
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.mozilla.geckoview_example">
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true">
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="org.mozilla.geckoview_example.GeckoViewActivity"
+ android:label="GeckoViewActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -0,0 +1,142 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.GeckoView;
+import org.mozilla.gecko.PrefsHelper;
+
+public class GeckoViewActivity extends Activity {
+ private static final String LOGTAG = "GeckoViewActivity";
+
+ GeckoView mGeckoView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.geckoview_activity);
+
+ mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
+ mGeckoView.setChromeDelegate(new MyGeckoViewChrome());
+ mGeckoView.setContentDelegate(new MyGeckoViewContent());
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
+
+ GeckoThread.init(profile, "chrome://browser/content/geckoview.xul", /* action */ null, /* debugging */ false);
+ GeckoThread.launch();
+ }
+
+ private class MyGeckoViewChrome implements GeckoView.ChromeDelegate {
+ @Override
+ public void onReady(GeckoView view) {
+ Log.i(LOGTAG, "Gecko is ready");
+ // Inject a script that adds some code to the content window
+ mGeckoView.importScript("resource://android/assets/script.js");
+
+ // Set up remote debugging to a port number
+ PrefsHelper.setPref("layers.dump", true);
+ PrefsHelper.setPref("devtools.debugger.remote-port", 6000);
+ PrefsHelper.setPref("devtools.debugger.unix-domain-socket", "");
+ PrefsHelper.setPref("devtools.debugger.remote-enabled", true);
+
+ // The Gecko libraries have finished loading and we can use the rendering engine.
+ // Let's add a browser (required) and load a page into it.
+ // mGeckoView.addBrowser(getResources().getString(R.string.default_url));
+ }
+
+ @Override
+ public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
+ Log.i(LOGTAG, "Alert!");
+ result.confirm();
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, final GeckoView.PromptResult result) {
+ Log.i(LOGTAG, "Confirm!");
+ new AlertDialog.Builder(GeckoViewActivity.this)
+ .setTitle("javaScript dialog")
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ result.confirm();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ result.cancel();
+ }
+ })
+ .create()
+ .show();
+ }
+
+ @Override
+ public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result) {
+ result.cancel();
+ }
+
+ @Override
+ public void onDebugRequest(GeckoView view, GeckoView.PromptResult result) {
+ Log.i(LOGTAG, "Remote Debug!");
+ result.confirm();
+ }
+
+ @Override
+ public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result) {
+ Log.i(LOGTAG, "Got Script Message: " + data.toString());
+ String type = data.getString("type");
+ if ("fetch".equals(type)) {
+ Bundle ret = new Bundle();
+ ret.putString("name", "Mozilla");
+ ret.putString("url", "https://mozilla.org");
+ result.success(ret);
+ }
+ }
+ }
+
+ private class MyGeckoViewContent implements GeckoView.ContentDelegate {
+ @Override
+ public void onPageStart(GeckoView view, GeckoView.Browser browser, String url) {
+
+ }
+
+ @Override
+ public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success) {
+
+ }
+
+ @Override
+ public void onPageShow(GeckoView view, GeckoView.Browser browser) {
+
+ }
+
+ @Override
+ public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title) {
+ Log.i(LOGTAG, "Received a title: " + title);
+ }
+
+ @Override
+ public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size) {
+ Log.i(LOGTAG, "Received a favicon URL: " + url);
+ }
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml
@@ -0,0 +1,13 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <org.mozilla.gecko.GeckoView
+ android:id="@+id/gecko_view"
+ android:layout_width="fill_parent"
+ android:layout_height="match_parent"
+ android:scrollbars="none"
+ />
+
+</LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">geckoview_example</string>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/test/java/org/mozilla/geckoview_example/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package org.mozilla.geckoview_example;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -0,0 +1,105 @@
+// We run fairly hard into a fundamental limitation of the Android Gradle
+// plugin. There are many bugs filed about this, but
+// https://code.google.com/p/android/issues/detail?id=216978#c6 is a reason one.
+// The issue is that we need fine-grained control over when to include Gecko's
+// binary libraries into the GeckoView AAR and the Fennec APK, and that's hard
+// to achieve. In particular:
+//
+// * :app:automation wants :geckoview to not include Gecko binaries (automation
+// * build, before package)
+//
+// * :geckoview:withLibraries wants :geckoview to include Gecko binaries
+// * (automation build, after package)
+//
+// * non-:app:automation wants :geckoview to include Gecko binaries (local
+// * build, always after package)
+//
+// publishNonDefault (see
+// http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication)
+// is intended to address this, but doesn't handle our case. That option always
+// builds *all* configurations, which fails when the required Gecko binaries
+// don't exist (automation build, before package). So instead, we make both
+// :app and :geckoview both know how to include the Gecko binaries, and use a
+// non-default, non-published :geckoview:withGeckoBinaries configuration to
+// handle automation's needs. Simple, right?
+
+// 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()}")
+ }
+ }
+}
+
+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'
+ }
+}
+
+ext.configureVariantWithGeckoBinaries = { variant ->
+ // Like 'local' or 'localOld'; may be null.
+ def productFlavor = variant.productFlavors ? variant.productFlavors[0].name : ""
+ // Like 'debug' or 'release'.
+ def buildType = variant.buildType.name
+
+ syncOmnijarFromDistDir.dependsOn buildOmnijar
+ def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
+ generateAssetsTask.dependsOn syncOmnijarFromDistDir
+ generateAssetsTask.dependsOn syncLibsFromDistDir
+ generateAssetsTask.dependsOn syncAssetsFromDistDir
+
+ def sourceSet = productFlavor ? "${productFlavor}${buildType.capitalize()}" : buildType
+ android.sourceSets."${sourceSet}".assets.srcDir syncOmnijarFromDistDir.destinationDir
+ android.sourceSets."${sourceSet}".assets.srcDir syncAssetsFromDistDir.destinationDir
+ android.sourceSets."${sourceSet}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
+}
--- a/mobile/android/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -20,17 +20,16 @@ android {
abortOnError false
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java {
srcDir '.'
- srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
exclude 'com/adjust/**'
}
// Exclude LeakCanary: It will be added again via a gradle dependency. This version
// here is only the no-op library for mach-based builds.
exclude 'com/squareup/leakcanary/**'
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,20 +24,24 @@ if (json.substs.MOZ_BUILD_APP != 'mobile
// Set the Android SDK location. This is the *least specific* mechanism, which
// is unfortunate: we'd prefer to use the *most specific* mechanism. That is,
// local.properties (first 'sdk.dir', then 'android.dir') and then the
// environment variable ANDROID_HOME will override this. That's unfortunate,
// but it's hard to automatically arrange better.
System.setProperty('android.home', json.substs.ANDROID_SDK_ROOT)
include ':app'
+include ':geckoview'
+include ':geckoview_example'
include ':omnijar'
include ':thirdparty'
project(':app').projectDir = new File("${json.topsrcdir}/mobile/android/app")
+project(':geckoview').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview")
+project(':geckoview_example').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview_example")
project(':omnijar').projectDir = new File("${json.topsrcdir}/mobile/android/app/omnijar")
project(':thirdparty').projectDir = new File("${json.topsrcdir}/mobile/android/thirdparty")
if (json.substs.MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER) {
include ':bouncer'
project(':bouncer').projectDir = new File("${json.topsrcdir}/mobile/android/bouncer")
}
--- a/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle_dependencies.py
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle_dependencies.py
@@ -6,11 +6,15 @@ config = {
'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-gradle-dependencies/releng.manifest',
'multi_locale_config_platform': 'android',
'postflight_build_mach_commands': [
['gradle',
'assembleAutomationRelease',
'assembleAutomationDebug',
'assembleAutomationDebugAndroidTest',
'checkstyle',
+ 'geckoview:assembleRelease',
+ 'geckoview_example:assembleDebug',
+ 'geckoview_example:assembleDebugAndroidTest',
+ 'geckoview_example:assembleRelease',
],
],
}