зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1200445 - Expose android native apps trough the navigator.mozApps api r=snorp,ferjm
This commit is contained in:
Родитель
c1e44ec580
Коммит
1a84049c64
|
@ -0,0 +1,123 @@
|
|||
/* 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/. */
|
||||
|
||||
const { interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["AndroidUtils"];
|
||||
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
|
||||
"resource://gre/modules/Messaging.jsm");
|
||||
|
||||
let appsRegistry = null;
|
||||
|
||||
function debug() {
|
||||
//dump("-*- AndroidUtils " + Array.slice(arguments) + "\n");
|
||||
}
|
||||
|
||||
// Helper functions to manage Android native apps. We keep them in the
|
||||
// registry with a `kind` equals to "android-native" and we also store
|
||||
// the package name and class name in the registry.
|
||||
// Communication with the android side happens through json messages.
|
||||
|
||||
this.AndroidUtils = {
|
||||
init: function(aRegistry) {
|
||||
appsRegistry = aRegistry;
|
||||
Services.obs.addObserver(this, "Android:Apps:Installed", false);
|
||||
Services.obs.addObserver(this, "Android:Apps:Uninstalled", false);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
Services.obs.removeObserver(this, "Android:Apps:Installed");
|
||||
Services.obs.removeObserver(this, "Android:Apps:Uninstalled");
|
||||
},
|
||||
|
||||
getOriginAndManifestURL: function(aPackageName) {
|
||||
let origin = "android://" + aPackageName.toLowerCase();
|
||||
let manifestURL = origin + "/manifest.webapp";
|
||||
return [origin, manifestURL];
|
||||
},
|
||||
|
||||
getPackageAndClassFromManifestURL: function(aManifestURL) {
|
||||
debug("getPackageAndClassFromManifestURL " + aManifestURL);
|
||||
let app = appsRegistry.getAppByManifestURL(aManifestURL);
|
||||
if (!app) {
|
||||
debug("No app for " + aManifestURL);
|
||||
return [];
|
||||
}
|
||||
return [app.android_packagename, app.android_classname];
|
||||
},
|
||||
|
||||
buildAndroidAppData: function(aApp) {
|
||||
// Use the package and class name to get a unique origin.
|
||||
// We put the version with the normal case as part of the manifest url.
|
||||
let [origin, manifestURL] =
|
||||
this.getOriginAndManifestURL(aApp.packagename);
|
||||
// TODO: Bug 1204557 to improve the icons support.
|
||||
let manifest = {
|
||||
name: aApp.name,
|
||||
icons: { "96": aApp.icon }
|
||||
}
|
||||
debug("Origin is " + origin);
|
||||
let appData = {
|
||||
app: {
|
||||
installOrigin: origin,
|
||||
origin: origin,
|
||||
manifest: manifest,
|
||||
manifestURL: manifestURL,
|
||||
manifestHash: AppsUtils.computeHash(JSON.stringify(manifest)),
|
||||
appStatus: Ci.nsIPrincipal.APP_STATUS_INSTALLED,
|
||||
removable: aApp.removable,
|
||||
android_packagename: aApp.packagename,
|
||||
android_classname: aApp.classname
|
||||
},
|
||||
isBrowser: false,
|
||||
isPackage: false
|
||||
};
|
||||
|
||||
return appData;
|
||||
},
|
||||
|
||||
installAndroidApps: function() {
|
||||
return Messaging.sendRequestForResult({ type: "Apps:GetList" }).then(
|
||||
aApps => {
|
||||
debug("Got " + aApps.apps.length + " android apps.");
|
||||
let promises = [];
|
||||
aApps.apps.forEach(app => {
|
||||
debug("App is " + app.name + " removable? " + app.removable);
|
||||
let p = new Promise((aResolveInstall, aRejectInstall) => {
|
||||
let appData = this.buildAndroidAppData(app);
|
||||
appsRegistry.confirmInstall(appData, null, aResolveInstall);
|
||||
});
|
||||
promises.push(p);
|
||||
});
|
||||
|
||||
// Wait for all apps to be installed.
|
||||
return Promise.all(promises);
|
||||
}
|
||||
).then(appsRegistry._saveApps.bind(appsRegistry));
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(aData);
|
||||
} catch(e) {
|
||||
debug(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTopic == "Android:Apps:Installed") {
|
||||
let appData = this.buildAndroidAppData(data);
|
||||
appsRegistry.confirmInstall(appData);
|
||||
} else if (aTopic == "Android:Apps:Uninstalled") {
|
||||
let [origin, manifestURL] =
|
||||
this.getOriginAndManifestURL(data.packagename);
|
||||
appsRegistry.uninstall(manifestURL);
|
||||
}
|
||||
},
|
||||
}
|
|
@ -130,6 +130,10 @@ function _setAppProperties(aObj, aApp) {
|
|||
aObj.kind = aApp.kind;
|
||||
aObj.enabled = aApp.enabled !== undefined ? aApp.enabled : true;
|
||||
aObj.sideloaded = aApp.sideloaded;
|
||||
#ifdef MOZ_B2GDROID
|
||||
aObj.android_packagename = aApp.android_packagename;
|
||||
aObj.android_classname = aApp.android_classname;
|
||||
#endif
|
||||
}
|
||||
|
||||
this.AppsUtils = {
|
||||
|
|
|
@ -91,6 +91,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ImportExport",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
|
||||
"resource://gre/modules/Messaging.jsm");
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
XPCOMUtils.defineLazyGetter(this, "libcutils", function() {
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
|
@ -195,6 +198,7 @@ this.DOMApplicationRegistry = {
|
|||
get kPackaged() "packaged",
|
||||
get kHosted() "hosted",
|
||||
get kHostedAppcache() "hosted-appcache",
|
||||
get kAndroid() "android-native",
|
||||
|
||||
// Path to the webapps.json file where we store the registry data.
|
||||
appsFile: null,
|
||||
|
@ -259,6 +263,11 @@ this.DOMApplicationRegistry = {
|
|||
this.getFullAppByManifestURL.bind(this));
|
||||
|
||||
MessageBroadcaster.init(this.getAppByManifestURL);
|
||||
|
||||
if (AppConstants.MOZ_B2GDROID) {
|
||||
Cu.import("resource://gre/modules/AndroidUtils.jsm");
|
||||
AndroidUtils.init(this);
|
||||
}
|
||||
},
|
||||
|
||||
// loads the current registry, that could be empty on first run.
|
||||
|
@ -464,7 +473,9 @@ this.DOMApplicationRegistry = {
|
|||
}),
|
||||
|
||||
appKind: function(aApp, aManifest) {
|
||||
if (aApp.origin.startsWith("app://")) {
|
||||
if (aApp.origin.startsWith("android://")) {
|
||||
return this.kAndroid;
|
||||
} if (aApp.origin.startsWith("app://")) {
|
||||
return this.kPackaged;
|
||||
} else {
|
||||
// Hosted apps, can be appcached or not.
|
||||
|
@ -522,7 +533,10 @@ this.DOMApplicationRegistry = {
|
|||
|
||||
// Installs a 3rd party app.
|
||||
installPreinstalledApp: function installPreinstalledApp(aId) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!AppConstants.MOZ_B2GDROID && AppConstants.platform !== "gonk") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some cases, the app might be already installed under a different ID but
|
||||
// with the same manifestURL. In that case, the only content of the webapp will
|
||||
// be the id of the old version, which is the one we'll keep.
|
||||
|
@ -625,7 +639,6 @@ this.DOMApplicationRegistry = {
|
|||
zipReader.close();
|
||||
}
|
||||
return isPreinstalled;
|
||||
#endif
|
||||
},
|
||||
|
||||
// For hosted apps, uninstall an app served from http:// if we have
|
||||
|
@ -792,6 +805,10 @@ this.DOMApplicationRegistry = {
|
|||
yield this.installSystemApps();
|
||||
}
|
||||
|
||||
if (AppConstants.MOZ_B2GDROID) {
|
||||
yield AndroidUtils.installAndroidApps();
|
||||
}
|
||||
|
||||
// At first run, install preloaded apps and set up their permissions.
|
||||
for (let id in this.webapps) {
|
||||
let isPreinstalled = this.installPreinstalledApp(id);
|
||||
|
@ -804,7 +821,7 @@ this.DOMApplicationRegistry = {
|
|||
}
|
||||
// Need to update the persisted list of apps since
|
||||
// installPreinstalledApp() removes the ones failing to install.
|
||||
this._saveApps();
|
||||
yield this._saveApps();
|
||||
|
||||
Services.prefs.setBoolPref("dom.apps.reset-permissions", true);
|
||||
}
|
||||
|
@ -1195,6 +1212,9 @@ this.DOMApplicationRegistry = {
|
|||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
cpmm = null;
|
||||
ppmm = null;
|
||||
if (AppConstants.MOZ_B2GDROID) {
|
||||
AndroidUtils.uninit();
|
||||
}
|
||||
} else if (aTopic == "memory-pressure") {
|
||||
// Clear the manifest cache on memory pressure.
|
||||
this._manifestCache = {};
|
||||
|
@ -1304,22 +1324,22 @@ this.DOMApplicationRegistry = {
|
|||
this.registryReady.then( () => {
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Install": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstall(msg, mm);
|
||||
#endif
|
||||
if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) {
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
|
||||
} else {
|
||||
this.doInstall(msg, mm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Webapps:GetSelf":
|
||||
this.getSelf(msg, mm);
|
||||
break;
|
||||
case "Webapps:Uninstall":
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
|
||||
#else
|
||||
this.doUninstall(msg, mm);
|
||||
#endif
|
||||
if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) {
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
|
||||
} else {
|
||||
this.doUninstall(msg, mm);
|
||||
}
|
||||
break;
|
||||
case "Webapps:Launch":
|
||||
this.doLaunch(msg, mm);
|
||||
|
@ -1337,11 +1357,11 @@ this.DOMApplicationRegistry = {
|
|||
this.getNotInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:InstallPackage": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstallPackage(msg, mm);
|
||||
#endif
|
||||
if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) {
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
|
||||
} else {
|
||||
this.doInstallPackage(msg, mm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Webapps:Download":
|
||||
|
@ -1602,6 +1622,19 @@ this.DOMApplicationRegistry = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Delegate native android apps launch.
|
||||
if (this.kAndroid == app.kind) {
|
||||
debug("Launching android app " + app.origin);
|
||||
let [packageName, className] =
|
||||
AndroidUtils.getPackageAndClassFromManifestURL(aManifestURL);
|
||||
debug(" " + packageName + " " + className);
|
||||
Messaging.sendRequest({ type: "Apps:Launch",
|
||||
packagename: packageName,
|
||||
classname: className });
|
||||
aOnSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
// We have to clone the app object as nsIDOMApplication objects are
|
||||
// stringified as an empty object. (see bug 830376)
|
||||
let appClone = AppsUtils.cloneAppObject(app);
|
||||
|
@ -2046,8 +2079,9 @@ this.DOMApplicationRegistry = {
|
|||
}
|
||||
|
||||
// If the app is packaged and its manifestURL has an app:// scheme,
|
||||
// then we can't have an update.
|
||||
if (app.kind == this.kPackaged && app.manifestURL.startsWith("app://")) {
|
||||
// or if it's a native Android app then we can't have an update.
|
||||
if (app.kind == this.kAndroid ||
|
||||
(app.kind == this.kPackaged && app.manifestURL.startsWith("app://"))) {
|
||||
sendError("NOT_UPDATABLE");
|
||||
return;
|
||||
}
|
||||
|
@ -2779,8 +2813,10 @@ this.DOMApplicationRegistry = {
|
|||
_setupApp: function(aData, aId) {
|
||||
let app = aData.app;
|
||||
|
||||
// app can be uninstalled
|
||||
app.removable = true;
|
||||
// app can be uninstalled by default.
|
||||
if (app.removable === undefined) {
|
||||
app.removable = true;
|
||||
}
|
||||
|
||||
if (aData.isPackage) {
|
||||
// Override the origin with the correct id.
|
||||
|
@ -2813,7 +2849,8 @@ this.DOMApplicationRegistry = {
|
|||
appObject.downloading = true;
|
||||
appObject.downloadSize = aLocaleManifest.size;
|
||||
appObject.readyToApplyDownload = false;
|
||||
} else if (appObject.kind == this.kHosted) {
|
||||
} else if (appObject.kind == this.kHosted ||
|
||||
appObject.kind == this.kAndroid) {
|
||||
appObject.installState = "installed";
|
||||
appObject.downloadAvailable = false;
|
||||
appObject.downloading = false;
|
||||
|
@ -2869,8 +2906,6 @@ this.DOMApplicationRegistry = {
|
|||
|
||||
app.appStatus = AppsUtils.getAppManifestStatus(aManifest);
|
||||
|
||||
app.removable = true;
|
||||
|
||||
// Reuse the app ID if the scheme is "app".
|
||||
let uri = Services.io.newURI(app.origin, null, null);
|
||||
if (uri.scheme == "app") {
|
||||
|
@ -2960,6 +2995,7 @@ this.DOMApplicationRegistry = {
|
|||
let appObject = this._cloneApp(aData, app, manifest, jsonManifest, id, localId);
|
||||
|
||||
this.webapps[id] = appObject;
|
||||
this._manifestCache[id] = jsonManifest;
|
||||
|
||||
// For package apps, the permissions are not in the mini-manifest, so
|
||||
// don't update the permissions yet.
|
||||
|
@ -4064,12 +4100,24 @@ this.DOMApplicationRegistry = {
|
|||
try {
|
||||
aData.app = yield this._getAppWithManifest(aData.manifestURL);
|
||||
|
||||
let prefName = "dom.mozApps.auto_confirm_uninstall";
|
||||
if (Services.prefs.prefHasUserValue(prefName) &&
|
||||
Services.prefs.getBoolPref(prefName)) {
|
||||
yield this._uninstallApp(aData.app);
|
||||
if (this.kAndroid == aData.app.kind) {
|
||||
debug("Uninstalling android app " + aData.app.origin);
|
||||
let [packageName, className] =
|
||||
AndroidUtils.getPackageAndClassFromManifestURL(aData.manifestURL);
|
||||
Messaging.sendRequest({ type: "Apps:Uninstall",
|
||||
packagename: packageName,
|
||||
classname: className });
|
||||
// We have to wait for Android's uninstall before sending the
|
||||
// uninstall event, so fake an error here.
|
||||
response = "Webapps:Uninstall:Return:KO";
|
||||
} else {
|
||||
yield this._promptForUninstall(aData);
|
||||
let prefName = "dom.mozApps.auto_confirm_uninstall";
|
||||
if (Services.prefs.prefHasUserValue(prefName) &&
|
||||
Services.prefs.getBoolPref(prefName)) {
|
||||
yield this._uninstallApp(aData.app);
|
||||
} else {
|
||||
yield this._promptForUninstall(aData);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
aData.error = error;
|
||||
|
|
|
@ -42,6 +42,11 @@ EXTRA_JS_MODULES += [
|
|||
'UserCustomizations.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2GDROID']:
|
||||
EXTRA_JS_MODULES += [
|
||||
'AndroidUtils.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'AppsUtils.jsm',
|
||||
'ImportExport.jsm',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
ANDROID_MANIFEST_FILE := src/main/AndroidManifest.xml
|
||||
|
||||
JAVAFILES := \
|
||||
src/main/java/org/mozilla/b2gdroid/Apps.java \
|
||||
src/main/java/org/mozilla/b2gdroid/Launcher.java \
|
||||
src/main/java/org/mozilla/b2gdroid/ScreenStateObserver.java \
|
||||
$(NULL)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/* 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.b2gdroid;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
|
||||
class Apps extends BroadcastReceiver
|
||||
implements GeckoEventListener {
|
||||
private static final String LOGTAG = "B2G:Apps";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
Apps(Context context) {
|
||||
mContext = context;
|
||||
EventDispatcher.getInstance()
|
||||
.registerGeckoThreadListener(this,
|
||||
"Apps:GetList",
|
||||
"Apps:Launch",
|
||||
"Apps:Uninstall");
|
||||
|
||||
// Observe app installation and removal.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
mContext.registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
mContext.unregisterReceiver(this);
|
||||
EventDispatcher.getInstance()
|
||||
.unregisterGeckoThreadListener(this,
|
||||
"Apps:GetList",
|
||||
"Apps:Launch",
|
||||
"Apps:Uninstall");
|
||||
}
|
||||
|
||||
JSONObject activityInfoToJson(ActivityInfo info, PackageManager pm) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("name", info.loadLabel(pm).toString());
|
||||
obj.put("packagename", info.packageName);
|
||||
obj.put("classname", info.name);
|
||||
|
||||
final ApplicationInfo appInfo = info.applicationInfo;
|
||||
// Pre-installed apps can't be uninstalled.
|
||||
final boolean removable =
|
||||
(appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0;
|
||||
|
||||
obj.put("removable", removable);
|
||||
|
||||
// For now, create a data: url for the icon, since we need additional
|
||||
// android:// protocol support for icons. Once it's there we'll do
|
||||
// something like: obj.put("icon", "android:icon/" + info.packageName);
|
||||
Drawable d = pm.getApplicationIcon(info.packageName);
|
||||
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
|
||||
byte[] byteArray = byteArrayOutputStream.toByteArray();
|
||||
String encoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
|
||||
obj.put("icon", "data:image/png;base64," + encoded);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error building ActivityInfo JSON", ex);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
Log.w(LOGTAG, "Received " + event);
|
||||
|
||||
if ("Apps:GetList".equals(event)) {
|
||||
JSONObject ret = new JSONObject();
|
||||
JSONArray array = new JSONArray();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
|
||||
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
final Iterator<ResolveInfo> i = pm.queryIntentActivities(mainIntent, 0).iterator();
|
||||
try {
|
||||
while (i.hasNext()) {
|
||||
ActivityInfo info = i.next().activityInfo;
|
||||
array.put(activityInfoToJson(info, pm));
|
||||
}
|
||||
ret.put("apps", array);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "error, making list of apps", ex);
|
||||
}
|
||||
EventDispatcher.sendResponse(message, ret);
|
||||
} else if ("Apps:Launch".equals(event)) {
|
||||
try {
|
||||
String className = message.getString("classname");
|
||||
String packageName = message.getString("packagename");
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN, null);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setClassName(packageName, className);
|
||||
mContext.startActivity(intent);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error launching app", ex);
|
||||
}
|
||||
} else if ("Apps:Uninstall".equals(event)) {
|
||||
try {
|
||||
String packageName = message.getString("packagename");
|
||||
Uri packageUri = Uri.parse("package:" + packageName);
|
||||
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
|
||||
mContext.startActivity(intent);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error uninstalling app", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(LOGTAG, intent.getAction() + " " + intent.getDataString());
|
||||
|
||||
String packageName = intent.getDataString().substring(8);
|
||||
String action = intent.getAction();
|
||||
if ("android.intent.action.PACKAGE_ADDED".equals(action)) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
Intent launch = pm.getLaunchIntentForPackage(packageName);
|
||||
if (launch == null) {
|
||||
Log.d(LOGTAG, "No launchable intent for " + packageName);
|
||||
return;
|
||||
}
|
||||
ActivityInfo info = launch.resolveActivityInfo(pm, 0);
|
||||
|
||||
JSONObject obj = activityInfoToJson(info, pm);
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Apps:Installed", obj.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
} else if ("android.intent.action.PACKAGE_REMOVED".equals(action)) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("packagename", packageName);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error building PACKAGE_REMOVED JSON", ex);
|
||||
}
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Apps:Uninstalled", obj.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,6 @@
|
|||
|
||||
package org.mozilla.b2gdroid;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.KeyguardManager;
|
||||
|
@ -14,24 +11,12 @@ import android.app.KeyguardManager.KeyguardLock;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import org.mozilla.gecko.BaseGeckoInterface;
|
||||
import org.mozilla.gecko.ContactService;
|
||||
|
@ -45,6 +30,7 @@ import org.mozilla.gecko.IntentHelper;
|
|||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
import org.mozilla.b2gdroid.ScreenStateObserver;
|
||||
import org.mozilla.b2gdroid.Apps;
|
||||
|
||||
public class Launcher extends Activity
|
||||
implements GeckoEventListener, ContextGetter {
|
||||
|
@ -52,6 +38,7 @@ public class Launcher extends Activity
|
|||
|
||||
private ContactService mContactService;
|
||||
private ScreenStateObserver mScreenStateObserver;
|
||||
private Apps mApps;
|
||||
|
||||
/** ContextGetter */
|
||||
public Context getContext() {
|
||||
|
@ -68,6 +55,7 @@ public class Launcher extends Activity
|
|||
|
||||
GeckoBatteryManager.getInstance().start(this);
|
||||
mContactService = new ContactService(EventDispatcher.getInstance(), this);
|
||||
mApps = new Apps(this);
|
||||
}
|
||||
|
||||
private void hideSplashScreen() {
|
||||
|
@ -123,6 +111,7 @@ public class Launcher extends Activity
|
|||
"Launcher:Ready");
|
||||
|
||||
mContactService.destroy();
|
||||
mApps.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Загрузка…
Ссылка в новой задаче