diff options
Diffstat (limited to 'mobile/android/bouncer')
-rw-r--r-- | mobile/android/bouncer/AndroidManifest.xml.in | 96 | ||||
-rw-r--r-- | mobile/android/bouncer/Makefile.in | 20 | ||||
-rw-r--r-- | mobile/android/bouncer/assets/example_asset.txt | 1 | ||||
-rw-r--r-- | mobile/android/bouncer/build.gradle | 76 | ||||
-rw-r--r-- | mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java | 129 | ||||
-rw-r--r-- | mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java | 46 | ||||
-rw-r--r-- | mobile/android/bouncer/moz.build | 45 | ||||
-rw-r--r-- | mobile/android/bouncer/res/drawable-v21/logo.xml | 15 | ||||
-rw-r--r-- | mobile/android/bouncer/res/drawable/logo.xml | 9 |
9 files changed, 437 insertions, 0 deletions
diff --git a/mobile/android/bouncer/AndroidManifest.xml.in b/mobile/android/bouncer/AndroidManifest.xml.in new file mode 100644 index 000000000..b265e7394 --- /dev/null +++ b/mobile/android/bouncer/AndroidManifest.xml.in @@ -0,0 +1,96 @@ +#filter substitution +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="@ANDROID_PACKAGE_NAME@" + android:installLocation="auto" + android:versionCode="@ANDROID_VERSION_CODE@" + android:versionName="@MOZ_APP_VERSION@" +#ifdef MOZ_ANDROID_SHARED_ID + android:sharedUserId="@MOZ_ANDROID_SHARED_ID@" +#endif + > + <uses-sdk android:minSdkVersion="@MOZ_ANDROID_MIN_SDK_VERSION@" +#ifdef MOZ_ANDROID_MAX_SDK_VERSION + android:maxSdkVersion="@MOZ_ANDROID_MAX_SDK_VERSION@" +#endif + android:targetSdkVersion="23"/> + +<!-- The bouncer APK and the main APK should define the same set of + <permission>, <uses-permission>, and <uses-feature> elements. This reduces + the likelihood of permission-related surprises when installing the main APK + on top of a pre-installed bouncer APK. Add such shared elements in the + fileincluded here, so that they can be referenced by both APKs. --> +#include ../base/FennecManifest_permissions.xml.in + + <application android:label="@MOZ_APP_DISPLAYNAME@" + android:icon="@drawable/icon" + android:logo="@drawable/logo" + android:hardwareAccelerated="true" + android:allowBackup="false" +# The preprocessor does not yet support arbitrary parentheses, so this cannot +# be parenthesized thus to clarify that the logical AND operator has precedence: +# !defined(MOZILLA_OFFICIAL) || (defined(NIGHTLY_BUILD) && defined(MOZ_DEBUG)) +#if !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) && defined(MOZ_DEBUG) + android:debuggable="true"> +#else + android:debuggable="false"> +#endif + + <activity + android:name="@MOZ_ANDROID_BROWSER_INTENT_CLASS@" + android:label="@MOZ_APP_DISPLAYNAME@" + android:theme="@android:style/Theme.Translucent"> + + <!-- Aping org.mozilla.gecko.BrowserApp. --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/> + <category android:name="android.intent.category.APP_BROWSER" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + + <meta-data android:name="com.sec.minimode.icon.portrait.normal" + android:resource="@drawable/icon"/> + + <meta-data android:name="com.sec.minimode.icon.landscape.normal" + android:resource="@drawable/icon" /> + + <intent-filter> + <action android:name="android.intent.action.WEB_SEARCH" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + <data android:scheme="" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + </intent-filter> + + <!-- Aping org.mozilla.gecko.tabqueue.TabQueueDispatcher. --> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:scheme="about" /> + <data android:scheme="javascript" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="file" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:mimeType="text/html"/> + <data android:mimeType="text/plain"/> + <data android:mimeType="application/xhtml+xml"/> + </intent-filter> + </activity> + + <service + android:name="org.mozilla.bouncer.BouncerService" + android:exported="false" /> + + </application> +</manifest> diff --git a/mobile/android/bouncer/Makefile.in b/mobile/android/bouncer/Makefile.in new file mode 100644 index 000000000..0a971d5d6 --- /dev/null +++ b/mobile/android/bouncer/Makefile.in @@ -0,0 +1,20 @@ +# 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/. + +include $(topsrcdir)/config/config.mk + +JAVAFILES := \ + java/org/mozilla/bouncer/BouncerService.java \ + java/org/mozilla/gecko/BrowserApp.java \ + $(NULL) + +ANDROID_EXTRA_JARS := \ + $(NULL) + +# Targets built very early during a Gradle build. +gradle-targets: $(abspath AndroidManifest.xml) + +.PHONY: gradle-targets + +libs:: $(ANDROID_APK_NAME).apk diff --git a/mobile/android/bouncer/assets/example_asset.txt b/mobile/android/bouncer/assets/example_asset.txt new file mode 100644 index 000000000..34338f983 --- /dev/null +++ b/mobile/android/bouncer/assets/example_asset.txt @@ -0,0 +1 @@ +This is an example asset. diff --git a/mobile/android/bouncer/build.gradle b/mobile/android/bouncer/build.gradle new file mode 100644 index 000000000..210aa7ece --- /dev/null +++ b/mobile/android/bouncer/build.gradle @@ -0,0 +1,76 @@ +buildDir "${topobjdir}/gradle/build/mobile/android/bouncer" + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION + + defaultConfig { + targetSdkVersion 23 + minSdkVersion 15 + applicationId mozconfig.substs.ANDROID_PACKAGE_NAME + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + dexOptions { + javaMaxHeapSize "2g" + } + + lintOptions { + abortOnError false + } + + buildTypes { + release { + minifyEnabled false + } + } + + sourceSets { + main { + manifest.srcFile "${topobjdir}/mobile/android/bouncer/AndroidManifest.xml" + assets { + if (mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY) { + srcDir "${mozconfig.substs.MOZ_ANDROID_DISTRIBUTION_DIRECTORY}/assets" + } + } + java { + srcDir 'java' + } + res { + srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res" // For the icon. + srcDir 'res' + } + } + } +} + +task generateCodeAndResources(type:Exec) { + workingDir "${topobjdir}" + + commandLine mozconfig.substs.GMAKE + args '-C' + args "${topobjdir}/mobile/android/bouncer" + args 'gradle-targets' + + // 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()}") + } + } +} + +afterEvaluate { + android.applicationVariants.all { + preBuild.dependsOn generateCodeAndResources + } +} diff --git a/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java b/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java new file mode 100644 index 000000000..b33d1a9ca --- /dev/null +++ b/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java @@ -0,0 +1,129 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.bouncer; + +import android.app.IntentService; +import android.content.Intent; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class BouncerService extends IntentService { + + private static final String LOGTAG = "GeckoBouncerService"; + + public BouncerService() { + super("BouncerService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + final byte[] buffer = new byte[8192]; + + Log.d(LOGTAG, "Preparing to copy distribution files"); + + final List<String> files; + try { + files = getFiles("distribution"); + } catch (IOException e) { + Log.e(LOGTAG, "Error getting distribution files from assets/distribution/**", e); + return; + } + + InputStream in = null; + for (String path : files) { + try { + Log.d(LOGTAG, "Copying distribution file: " + path); + + in = getAssets().open(path); + + final File outFile = getDataFile(path); + writeStream(in, outFile, buffer); + } catch (IOException e) { + Log.e(LOGTAG, "Error opening distribution input stream from assets", e); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + Log.e(LOGTAG, "Error closing distribution input stream", e); + } + } + } + } + } + + /** + * Recursively traverse a directory to list paths to all files. + * + * @param path Directory to traverse. + * @return List of all files in given directory. + * @throws IOException + */ + private List<String> getFiles(String path) throws IOException { + List<String> paths = new ArrayList<>(); + getFiles(path, paths); + return paths; + } + + /** + * Recursively traverse a directory to list paths to all files. + * + * @param path Directory to traverse. + * @param acc Accumulator of paths seen. + * @throws IOException + */ + private void getFiles(String path, List<String> acc) throws IOException { + final String[] list = getAssets().list(path); + if (list.length > 0) { + // We're a directory -- recurse. + for (final String file : list) { + getFiles(path + "/" + file, acc); + } + } else { + // We're a file -- accumulate. + acc.add(path); + } + } + + private String getDataDir() { + return getApplicationInfo().dataDir; + } + + private File getDataFile(final String path) { + File outFile = new File(getDataDir(), path); + File dir = outFile.getParentFile(); + + if (dir != null && !dir.exists()) { + Log.d(LOGTAG, "Creating " + dir.getAbsolutePath()); + if (!dir.mkdirs()) { + Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath()); + return null; + } + } + + return outFile; + } + + private void writeStream(InputStream fileStream, File outFile, byte[] buffer) + throws IOException { + final OutputStream outStream = new FileOutputStream(outFile); + try { + int count; + while ((count = fileStream.read(buffer)) > 0) { + outStream.write(buffer, 0, count); + } + } finally { + outStream.close(); + } + } +} diff --git a/mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java new file mode 100644 index 000000000..8a462822f --- /dev/null +++ b/mobile/android/bouncer/java/org/mozilla/gecko/BrowserApp.java @@ -0,0 +1,46 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.gecko; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import org.mozilla.bouncer.BouncerService; + +/** + * Bouncer activity version of BrowserApp. + * + * This class has the same name as org.mozilla.gecko.BrowserApp to preserve + * shortcuts created by the bouncer app. + */ +public class BrowserApp extends Activity { + private static final String LOGTAG = "GeckoBouncerActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // This races distribution installation against the Play Store killing our process to + // install the update. We'll live with it. To do better, consider using an Intent to + // notify when the service has completed. + startService(new Intent(this, BouncerService.class)); + + final String appPackageName = Uri.encode(getPackageName()); + final Uri uri = Uri.parse("market://details?id=" + appPackageName); + Log.i(LOGTAG, "Lanching activity with URL: " + uri.toString()); + + // It might be more correct to catch failure in case the Play Store isn't installed. The + // fallback action is to open the Play Store website... but doing so may offer Firefox as + // browser (since even the bouncer offers to view URLs), which will be very confusing. + // Therefore, we don't try to be fancy here, and we just fail (silently). + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + + finish(); + } +} diff --git a/mobile/android/bouncer/moz.build b/mobile/android/bouncer/moz.build new file mode 100644 index 000000000..2749fa927 --- /dev/null +++ b/mobile/android/bouncer/moz.build @@ -0,0 +1,45 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DEFINES['ANDROID_VERSION_CODE'] = '1' + +for var in ('ANDROID_PACKAGE_NAME', + 'MOZ_ANDROID_BROWSER_INTENT_CLASS', + 'MOZ_APP_DISPLAYNAME', + 'MOZ_APP_VERSION'): + DEFINES[var] = CONFIG[var] + +for var in ('MOZ_ANDROID_GCM', + 'MOZ_ANDROID_DOWNLOADS_INTEGRATION', + 'MOZ_ANDROID_BEAM', + 'MOZ_ANDROID_SEARCH_ACTIVITY', + 'MOZ_ANDROID_MLS_STUMBLER'): + if CONFIG[var]: + DEFINES[var] = CONFIG[var] + +ANDROID_APK_NAME = 'bouncer' +ANDROID_APK_PACKAGE = CONFIG['ANDROID_PACKAGE_NAME'] + +# Putting branding earlier allows branders to override default resources. +ANDROID_RES_DIRS += [ + '/' + CONFIG['MOZ_BRANDING_DIRECTORY'] + '/res', # For the icon. + 'res', +] + +ANDROID_ASSETS_DIRS += [ + 'assets', +] + +if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY']: + # If you change this, also change its equivalent in mobile/android/base. + ANDROID_ASSETS_DIRS += [ + '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets', + ] + +DEFINES['MOZ_ANDROID_SHARED_ID'] = CONFIG['MOZ_ANDROID_SHARED_ID'] +OBJDIR_PP_FILES.mobile.android.bouncer += [ + 'AndroidManifest.xml.in', +] diff --git a/mobile/android/bouncer/res/drawable-v21/logo.xml b/mobile/android/bouncer/res/drawable-v21/logo.xml new file mode 100644 index 000000000..568cbec00 --- /dev/null +++ b/mobile/android/bouncer/res/drawable-v21/logo.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> + +<!-- The action bar scales the application icon to be too large (bug 1132751) + so add some padding to prevent it from scaling so much. --> +<inset + xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/icon" + android:insetTop="6dp" + android:insetBottom="6dp" + android:insetLeft="6dp" + android:insetRight="6dp" + /> diff --git a/mobile/android/bouncer/res/drawable/logo.xml b/mobile/android/bouncer/res/drawable/logo.xml new file mode 100644 index 000000000..e188f80dc --- /dev/null +++ b/mobile/android/bouncer/res/drawable/logo.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> + +<!-- Overidden. --> +<bitmap + xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/icon"/> |