Bug 778279 - support multiple apps per origin in Android web runtime; r=mfinkle

This commit is contained in:
Myk Melez 2014-07-22 12:52:45 -07:00
parent 3695251c1e
commit 2fdb33074a
11 changed files with 41 additions and 136 deletions

View File

@ -748,7 +748,7 @@ public class BrowserApp extends GeckoApp
String title = tab.getDisplayTitle();
Bitmap favicon = tab.getFavicon();
if (url != null && title != null) {
GeckoAppShell.createShortcut(title, url, url, favicon, "");
GeckoAppShell.createShortcut(title, url, favicon);
}
}
}

View File

@ -597,13 +597,6 @@ public abstract class GeckoApp
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
} else if ("Shortcut:Remove".equals(event)) {
final String url = message.getString("url");
final String origin = message.getString("origin");
final String title = message.getString("title");
final String type = message.getString("shortcutType");
GeckoAppShell.removeShortcut(title, url, origin, type);
} else if ("SystemUI:Visibility".equals(event)) {
setSystemUiVisible(message.getBoolean("visible"));
@ -1515,7 +1508,6 @@ public abstract class GeckoApp
"Sanitize:ClearHistory",
"Session:StatePurged",
"Share:Text",
"Shortcut:Remove",
"SystemUI:Visibility",
"Toast:Show",
"ToggleChrome:Focus",
@ -2050,7 +2042,6 @@ public abstract class GeckoApp
"Sanitize:ClearHistory",
"Session:StatePurged",
"Share:Text",
"Shortcut:Remove",
"SystemUI:Visibility",
"Toast:Show",
"ToggleChrome:Focus",

View File

@ -135,9 +135,6 @@ public class GeckoAppShell
// See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
public static final String SHORTCUT_TYPE_WEBAPP = "webapp";
public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark";
static private int sDensityDpi = 0;
static private int sScreenDepth = 0;
@ -254,7 +251,7 @@ public class GeckoAppShell
@Override
public void onFaviconLoaded(String pageUrl, String faviconURL, Bitmap favicon) {
GeckoAppShell.createShortcut(title, url, url, favicon, "");
GeckoAppShell.createShortcut(title, url, favicon);
}
}
@ -781,59 +778,25 @@ public class GeckoAppShell
restartScheduled = true;
}
public static Intent getWebappIntent(String aURI, String aOrigin, String aTitle, Bitmap aIcon) {
Intent intent;
Allocator slots = Allocator.getInstance(getContext());
int index = slots.getIndexForOrigin(aOrigin);
if (index == -1) {
return null;
}
String packageName = slots.getAppForIndex(index);
intent = getContext().getPackageManager().getLaunchIntentForPackage(packageName);
if (aURI != null) {
intent.setData(Uri.parse(aURI));
}
return intent;
}
// "Installs" an application by creating a shortcut
// This is the entry point from AndroidBridge.h
// Creates a homescreen shortcut for a web page.
// This is the entry point from nsIShellService.
@WrapElementForJNI
static void createShortcut(String aTitle, String aURI, String aIconData, String aType) {
if ("webapp".equals(aType)) {
Log.w(LOGTAG, "createShortcut with no unique URI should not be used for aType = webapp!");
}
createShortcut(aTitle, aURI, aURI, aIconData, aType);
}
// For non-webapps.
public static void createShortcut(String aTitle, String aURI, Bitmap aBitmap, String aType) {
createShortcut(aTitle, aURI, aURI, aBitmap, aType);
}
// Internal, for webapps.
static void createShortcut(final String aTitle, final String aURI, final String aUniqueURI, final String aIconData, final String aType) {
static void createShortcut(final String aTitle, final String aURI, final String aIconData) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
// TODO: use the cache. Bug 961600.
Bitmap icon = FaviconDecoder.getMostSuitableBitmapFromDataURI(aIconData, getPreferredIconSize());
GeckoAppShell.doCreateShortcut(aTitle, aURI, aURI, icon, aType);
GeckoAppShell.doCreateShortcut(aTitle, aURI, icon);
}
});
}
public static void createShortcut(final String aTitle, final String aURI, final String aUniqueURI,
final Bitmap aIcon, final String aType) {
public static void createShortcut(final String aTitle, final String aURI, final Bitmap aBitmap) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
GeckoAppShell.doCreateShortcut(aTitle, aURI, aUniqueURI, aIcon, aType);
GeckoAppShell.doCreateShortcut(aTitle, aURI, aBitmap);
}
});
}
@ -841,23 +804,17 @@ public class GeckoAppShell
/**
* Call this method only on the background thread.
*/
private static void doCreateShortcut(final String aTitle, final String aURI, final String aUniqueURI,
final Bitmap aIcon, final String aType) {
private static void doCreateShortcut(final String aTitle, final String aURI, final Bitmap aIcon) {
// The intent to be launched by the shortcut.
Intent shortcutIntent;
if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) {
shortcutIntent = getWebappIntent(aURI, aUniqueURI, aTitle, aIcon);
} else {
shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT);
shortcutIntent.setData(Uri.parse(aURI));
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS_NAME);
}
Intent shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT);
shortcutIntent.setData(Uri.parse(aURI));
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS_NAME);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getLauncherIcon(aIcon, aType));
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getLauncherIcon(aIcon));
if (aTitle != null) {
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
@ -872,41 +829,6 @@ public class GeckoAppShell
getContext().sendBroadcast(intent);
}
public static void removeShortcut(final String aTitle, final String aURI, final String aType) {
removeShortcut(aTitle, aURI, null, aType);
}
public static void removeShortcut(final String aTitle, final String aURI, final String aUniqueURI, final String aType) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
// the intent to be launched by the shortcut
Intent shortcutIntent;
if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) {
shortcutIntent = getWebappIntent(aURI, aUniqueURI, "", null);
if (shortcutIntent == null)
return;
} else {
shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT);
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS_NAME);
shortcutIntent.setData(Uri.parse(aURI));
}
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
if (aTitle != null)
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
else
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI);
intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
getContext().sendBroadcast(intent);
}
});
}
@JNITarget
static public int getPreferredIconSize() {
if (android.os.Build.VERSION.SDK_INT >= 11) {
@ -925,7 +847,7 @@ public class GeckoAppShell
}
}
static private Bitmap getLauncherIcon(Bitmap aSource, String aType) {
static private Bitmap getLauncherIcon(Bitmap aSource) {
final int kOffset = 6;
final int kRadius = 5;
int size = getPreferredIconSize();
@ -941,8 +863,8 @@ public class GeckoAppShell
// If we aren't drawing a favicon, just use an orange color.
paint.setColor(Color.HSVToColor(DEFAULT_LAUNCHER_ICON_HSV));
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
} else if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP) || aSource.getWidth() >= insetSize || aSource.getHeight() >= insetSize) {
// otherwise, if this is a webapp or if the icons is lare enough, just draw it
} else if (aSource.getWidth() >= insetSize || aSource.getHeight() >= insetSize) {
// Otherwise, if the icon is large enough, just draw it.
Rect iconBounds = new Rect(0, 0, size, size);
canvas.drawBitmap(aSource, null, iconBounds, null);
return bitmap;

View File

@ -110,10 +110,6 @@ public class Allocator {
return findSlotForPrefix(PREFIX_PACKAGE_NAME, packageName);
}
public synchronized int getIndexForOrigin(String origin) {
return findSlotForPrefix(PREFIX_ORIGIN, origin);
}
protected int findSlotForPrefix(String prefix, String value) {
for (int i = 0; i < MAX_WEB_APPS; ++i) {
if (mPrefs.getString(prefix + i, "").equals(value)) {

View File

@ -45,7 +45,7 @@ public class EventListener implements NativeEventListener {
"Webapps:InstallApk",
"Webapps:UninstallApk",
"Webapps:Postinstall",
"Webapps:Open",
"Webapps:Launch",
"Webapps:Uninstall",
"Webapps:GetApkVersions");
}
@ -56,7 +56,7 @@ public class EventListener implements NativeEventListener {
"Webapps:InstallApk",
"Webapps:UninstallApk",
"Webapps:Postinstall",
"Webapps:Open",
"Webapps:Launch",
"Webapps:Uninstall",
"Webapps:GetApkVersions");
}
@ -70,14 +70,8 @@ public class EventListener implements NativeEventListener {
uninstallApk(GeckoAppShell.getGeckoInterface().getActivity(), message);
} else if (event.equals("Webapps:Postinstall")) {
postInstallWebapp(message.getString("apkPackageName"), message.getString("origin"));
} else if (event.equals("Webapps:Open")) {
Intent intent = GeckoAppShell.getWebappIntent(message.getString("manifestURL"),
message.getString("origin"),
"", null);
if (intent == null) {
return;
}
GeckoAppShell.getGeckoInterface().getActivity().startActivity(intent);
} else if (event.equals("Webapps:Launch")) {
launchWebapp(message.getString("packageName"));
} else if (event.equals("Webapps:GetApkVersions")) {
JSONObject obj = new JSONObject();
obj.put("versions", getApkVersions(GeckoAppShell.getGeckoInterface().getActivity(),
@ -95,6 +89,11 @@ public class EventListener implements NativeEventListener {
allocator.putOrigin(index, aOrigin);
}
private void launchWebapp(String aPackageName) {
Intent intent = GeckoAppShell.getContext().getPackageManager().getLaunchIntentForPackage(aPackageName);
GeckoAppShell.getGeckoInterface().getActivity().startActivity(intent);
}
public static void uninstallWebapp(final String packageName) {
// On uninstall, we need to do a couple of things:
// 1. nuke the running app process.

View File

@ -110,8 +110,7 @@ function addApplication(aApp) {
let container = document.createElement("div");
container.className = "app list-item";
container.setAttribute("contextmenu", "appmenu");
container.setAttribute("id", "app-" + aApp.origin);
container.setAttribute("mozApp", aApp.origin);
container.setAttribute("id", "app-" + aApp.manifestURL);
container.setAttribute("title", manifest.name);
let img = document.createElement("img");
@ -140,7 +139,7 @@ function addApplication(aApp) {
}
function onInstall(aEvent) {
let node = document.getElementById("app-" + aEvent.application.origin);
let node = document.getElementById("app-" + aEvent.application.manifestURL);
if (node)
return;
@ -149,7 +148,7 @@ function onInstall(aEvent) {
}
function onUninstall(aEvent) {
let node = document.getElementById("app-" + aEvent.application.origin);
let node = document.getElementById("app-" + aEvent.application.manifestURL);
if (node) {
let parent = node.parentNode;
parent.removeChild(node);

View File

@ -20,7 +20,7 @@ interface nsIShellService : nsISupports
* @param aTitle the user-friendly name of the shortcut.
* @param aURI the URI to open.
* @param aIconData a base64-encoded data: URI representation of the shortcut's icon, as accepted by the favicon decoder.
* @param aIntent how the URI should be opened. Examples: "default", "bookmark" and "webapp"
* @param aIntent obsolete and ignored, but remains for backward compatibility; pass an empty string
*/
void createShortcut(in AString aTitle, in AString aURI, in AString aIconData, in AString aIntent);
};

View File

@ -25,6 +25,6 @@ nsShellService::CreateShortcut(const nsAString& aTitle, const nsAString& aURI,
if (!aTitle.Length() || !aURI.Length() || !aIconData.Length())
return NS_ERROR_FAILURE;
mozilla::widget::android::GeckoAppShell::CreateShortcut(aTitle, aURI, aIconData, aIntent);
mozilla::widget::android::GeckoAppShell::CreateShortcut(aTitle, aURI, aIconData);
return NS_OK;
}

View File

@ -198,13 +198,12 @@ this.WebappManager = {
this.writeDefaultPrefs(file, localeManifest);
},
launch: function({ manifestURL, origin }) {
debug("launchWebapp: " + manifestURL);
launch: function({ apkPackageName }) {
debug("launch: " + apkPackageName);
sendMessageToJava({
type: "Webapps:Open",
manifestURL: manifestURL,
origin: origin
type: "Webapps:Launch",
packageName: apkPackageName,
});
},

View File

@ -108,7 +108,7 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) {
jConnectionGetMimeType = getStaticMethod("connectionGetMimeType", "(Ljava/net/URLConnection;)Ljava/lang/String;");
jCreateInputStream = getStaticMethod("createInputStream", "(Ljava/net/URLConnection;)Ljava/io/InputStream;");
jCreateMessageListWrapper = getStaticMethod("createMessageList", "(JJ[Ljava/lang/String;IIZI)V");
jCreateShortcut = getStaticMethod("createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jCreateShortcut = getStaticMethod("createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jDeleteMessageWrapper = getStaticMethod("deleteMessage", "(II)V");
jDisableBatteryNotifications = getStaticMethod("disableBatteryNotifications", "()V");
jDisableNetworkNotifications = getStaticMethod("disableNetworkNotifications", "()V");
@ -348,18 +348,17 @@ void GeckoAppShell::CreateMessageListWrapper(int64_t a0, int64_t a1, jobjectArra
env->PopLocalFrame(nullptr);
}
void GeckoAppShell::CreateShortcut(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3) {
void GeckoAppShell::CreateShortcut(const nsAString& a0, const nsAString& a1, const nsAString& a2) {
JNIEnv *env = AndroidBridge::GetJNIEnv();
if (env->PushLocalFrame(4) != 0) {
if (env->PushLocalFrame(3) != 0) {
AndroidBridge::HandleUncaughtException(env);
MOZ_CRASH("Exception should have caused crash.");
}
jvalue args[4];
jvalue args[3];
args[0].l = AndroidBridge::NewJavaString(env, a0);
args[1].l = AndroidBridge::NewJavaString(env, a1);
args[2].l = AndroidBridge::NewJavaString(env, a2);
args[3].l = AndroidBridge::NewJavaString(env, a3);
env->CallStaticVoidMethodA(mGeckoAppShellClass, jCreateShortcut, args);
AndroidBridge::HandleUncaughtException(env);

View File

@ -30,7 +30,7 @@ public:
static jstring ConnectionGetMimeType(jobject a0);
static jobject CreateInputStream(jobject a0);
static void CreateMessageListWrapper(int64_t a0, int64_t a1, jobjectArray a2, int32_t a3, int32_t a4, bool a5, int32_t a6);
static void CreateShortcut(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3);
static void CreateShortcut(const nsAString& a0, const nsAString& a1, const nsAString& a2);
static void DeleteMessageWrapper(int32_t a0, int32_t a1);
static void DisableBatteryNotifications();
static void DisableNetworkNotifications();