зеркало из https://github.com/mozilla/gecko-dev.git
Bug 741621 - Allow opening market links in the market webapp. r=mfinkle
This commit is contained in:
Родитель
bb0843a774
Коммит
40954ea543
|
@ -93,6 +93,7 @@
|
|||
<data android:mimeType="text/plain"/>
|
||||
<data android:mimeType="application/xhtml+xml"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.WEB_SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -111,6 +112,25 @@
|
|||
|
||||
#include @OBJDIR@/WebAppManifestFragment.xml.in
|
||||
|
||||
<activity android:name=".MarketplaceApp"
|
||||
android:label="Mozilla Marketplace"
|
||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize"
|
||||
android:windowSoftInputMode="stateUnspecified|adjustResize"
|
||||
android:icon="@drawable/marketplace"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/Gecko.NoActionBar"
|
||||
android:noHistory="true">
|
||||
|
||||
<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" android:host="marketplace.mozilla.org"/>
|
||||
<data android:scheme="https" android:host="marketplace.mozilla.org"/>
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<!-- Masquerade as the Resolver so that we can be opened from the Marketplace. -->
|
||||
<activity-alias
|
||||
android:name="com.android.internal.app.ResolverActivity"
|
||||
|
|
|
@ -171,7 +171,7 @@ abstract public class GeckoApp
|
|||
private HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>();
|
||||
|
||||
protected int mRestoreMode = GeckoAppShell.RESTORE_NONE;
|
||||
private boolean mInitialized = false;
|
||||
protected boolean mInitialized = false;
|
||||
|
||||
public enum LaunchState {Launching, WaitForDebugger,
|
||||
Launched, GeckoRunning, GeckoExiting};
|
||||
|
@ -1921,7 +1921,7 @@ abstract public class GeckoApp
|
|||
* Handles getting a uri from and intent in a way that is backwards
|
||||
* compatable with our previous implementations
|
||||
*/
|
||||
private String getURIFromIntent(Intent intent) {
|
||||
protected String getURIFromIntent(Intent intent) {
|
||||
String uri = intent.getDataString();
|
||||
if (uri != null)
|
||||
return uri;
|
||||
|
|
|
@ -817,10 +817,14 @@ public class GeckoAppShell
|
|||
if (index == -1)
|
||||
return null;
|
||||
|
||||
return getWebAppIntent(index, aURI);
|
||||
}
|
||||
|
||||
public static Intent getWebAppIntent(int aIndex, String aURI) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(GeckoApp.ACTION_WEBAPP_PREFIX + index);
|
||||
intent.setAction(GeckoApp.ACTION_WEBAPP_PREFIX + aIndex);
|
||||
intent.setData(Uri.parse(aURI));
|
||||
intent.setClassName(GeckoApp.mAppContext, GeckoApp.mAppContext.getPackageName() + ".WebApps$WebApp" + index);
|
||||
intent.setClassName(GeckoApp.mAppContext, GeckoApp.mAppContext.getPackageName() + ".WebApps$WebApp" + aIndex);
|
||||
return intent;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@ endif
|
|||
|
||||
FENNEC_PP_JAVA_FILES = \
|
||||
App.java \
|
||||
MarketplaceApp.java \
|
||||
WebApp.java \
|
||||
WebApps.java \
|
||||
GeckoActivity.java \
|
||||
|
@ -998,6 +999,7 @@ MOZ_ANDROID_DRAWABLES += \
|
|||
mobile/android/base/resources/drawable/tabs_tray_list_divider.xml \
|
||||
mobile/android/base/resources/drawable/tabs_shadow.xml \
|
||||
mobile/android/base/resources/drawable/shadow.png \
|
||||
mobile/android/base/resources/drawable/marketplace.png \
|
||||
$(NULL)
|
||||
|
||||
MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi)
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#filter substitution
|
||||
package @ANDROID_PACKAGE_NAME@;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.WebAppAllocator;
|
||||
|
||||
/*
|
||||
* This is a stub activity, meant to just install the marketplace WebApp
|
||||
* and then launch it
|
||||
*/
|
||||
public class MarketplaceApp extends WebApp {
|
||||
protected int mWebAppIndex;
|
||||
private static final String LOGTAG = "GeckoMarketplaceApp";
|
||||
public static final String MARKETPLACE_HOST = "marketplace.mozilla.org";
|
||||
public static final String MARKETPLACE_URI = "https://marketplace.mozilla.org";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check that the uri being requested is a marketplace uri
|
||||
String passedUri = null;
|
||||
String uri = getURIFromIntent(getIntent());
|
||||
if (uri != null && uri.length() > 0) {
|
||||
passedUri = uri;
|
||||
}
|
||||
|
||||
if (passedUri != null)
|
||||
handleMarketplaceLink(passedUri);
|
||||
}
|
||||
|
||||
// Use the default profile in order to ensure this is installed in the Fennec
|
||||
// webapp registry. The marketplace WEBapp will have its own profile
|
||||
@Override
|
||||
protected String getDefaultProfileName() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onNewIntent");
|
||||
|
||||
if (checkLaunchState(LaunchState.GeckoExiting)) {
|
||||
// We're exiting and shouldn't try to do anything else just incase
|
||||
// we're hung for some reason we'll force the process to exit
|
||||
System.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we were previously OOM killed, we can end up here when launching
|
||||
// from external shortcuts, so set this as the intent for initialization
|
||||
if (!mInitialized) {
|
||||
setIntent(intent);
|
||||
return;
|
||||
}
|
||||
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_VIEW.equals(action)) {
|
||||
String uri = intent.getDataString();
|
||||
if (Uri.parse(uri).getHost().equals(MARKETPLACE_HOST)) {
|
||||
handleMarketplaceLink(uri);
|
||||
return;
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
|
||||
Log.i(LOGTAG,"onNewIntent: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
// All marketplace links from the system will come to the receiver first. It will determine if the
|
||||
// marketplace app is installed. If the marketplace is not installed it will install it and send the link
|
||||
// to the marketplace app
|
||||
private void handleMarketplaceLink(final String url) {
|
||||
// see if the marketplace app is registered, if its not, install it
|
||||
int index = WebAppAllocator.getInstance(this).getIndexForApp(MARKETPLACE_URI);
|
||||
if (index < 0) {
|
||||
// If the app isn't installed, we send gecko a message to install it and then launch it with this url
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("url", url);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "error building JSON arguments");
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("WebApps:InstallMarketplace", args.toString()));
|
||||
} else {
|
||||
// otherwise just launch the webapp with this url
|
||||
Intent webappIntent = GeckoAppShell.getWebAppIntent(index, url);
|
||||
if (webappIntent == null) {
|
||||
Log.i(LOGTAG, "bounce launch");
|
||||
return;
|
||||
}
|
||||
Log.i(LOGTAG, "Open " + url + " in marketplace app");
|
||||
startActivity(webappIntent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -22,13 +22,18 @@ public class WebAppAllocator {
|
|||
if (sInstance == null) {
|
||||
if (!(cx instanceof GeckoApp))
|
||||
throw new RuntimeException("Context needs to be a GeckoApp");
|
||||
|
||||
|
||||
sContext = (GeckoApp) cx;
|
||||
sInstance = new WebAppAllocator(cx);
|
||||
}
|
||||
|
||||
if (cx != sContext)
|
||||
throw new RuntimeException("Tried to get WebAppAllocator instance for different context than it was created for");
|
||||
// The marketplaceApp will run in this same process, but has a different context
|
||||
// Rather than just failing, we want to create a new Allocator instead
|
||||
if (cx != sContext) {
|
||||
sInstance = null;
|
||||
sContext = (GeckoApp) cx;
|
||||
sInstance = new WebAppAllocator(cx);
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.3 KiB |
|
@ -48,7 +48,7 @@ var HelperApps = {
|
|||
return found;
|
||||
},
|
||||
|
||||
openUriInApp: function openUriInApp(uri) {
|
||||
openUriInApp: function openUriInApp(uri) {
|
||||
var possibleHandlers = this.getAppsForUri(uri);
|
||||
if (possibleHandlers.length == 1) {
|
||||
possibleHandlers[0].launchWithURI(uri);
|
||||
|
@ -56,5 +56,50 @@ var HelperApps = {
|
|||
let handlerInfoProto = this.urlHandlerService.getURLHandlerInfoFromOS(uri, {});
|
||||
handlerInfoProto.preferredApplicationHandler.launchWithURI(uri);
|
||||
}
|
||||
},
|
||||
|
||||
showDoorhanger: function showDoorhanger(aUri, aCallback) {
|
||||
let permValue = Services.perms.testPermission(aUri, "native-intent");
|
||||
if (permValue != Services.perms.UNKNOWN_ACTION) {
|
||||
if (permValue == Services.perms.ALLOW_ACTION) {
|
||||
if (aCallback)
|
||||
aCallback(aUri);
|
||||
|
||||
this.openUriInApp(aUri);
|
||||
} else if (permValue == Services.perms.DENY_ACTION) {
|
||||
// do nothing
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let apps = this.getAppsForUri(aUri);
|
||||
let strings = Strings.browser;
|
||||
|
||||
let message = "";
|
||||
if (apps.length == 1)
|
||||
message = strings.formatStringFromName("helperapps.openWithApp2", [apps[0].name], 1);
|
||||
else
|
||||
message = strings.GetStringFromName("helperapps.openWithList2");
|
||||
|
||||
let buttons = [{
|
||||
label: strings.GetStringFromName("helperapps.open"),
|
||||
callback: function(aChecked) {
|
||||
if (aChecked)
|
||||
Services.perms.add(aUri, "native-intent", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
if (aCallback)
|
||||
aCallback(aUri);
|
||||
else
|
||||
this.openUriInApp(aUri);
|
||||
}
|
||||
}, {
|
||||
label: strings.GetStringFromName("helperapps.ignore"),
|
||||
callback: function(aChecked) {
|
||||
if (aChecked)
|
||||
Services.perms.add(aUri, "native-intent", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
}
|
||||
}];
|
||||
|
||||
let options = { checkbox: Strings.browser.GetStringFromName("helperapps.dontAskAgain") };
|
||||
NativeWindow.doorhanger.show(message, "helperapps-open", buttons, BrowserApp.selectedTab.id, options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -38,6 +38,11 @@ var WebAppRT = {
|
|||
if (isUpdate == "new") {
|
||||
this.getDefaultPrefs().forEach(this.addPref);
|
||||
|
||||
// prevent offering to use helper apps for things that this app handles
|
||||
// i.e. don't show the "Open in market?" popup when we're showing the market app
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
Services.perms.add(uri, "native-intent", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
|
||||
// update the blocklist url to use a different app id
|
||||
let blocklist = Services.prefs.getCharPref("extensions.blocklist.url");
|
||||
blocklist = blocklist.replace(/%APP_ID%/g, "webapprt-mobile@mozilla.org");
|
||||
|
|
|
@ -247,7 +247,7 @@ var BrowserApp = {
|
|||
|
||||
let updated = this.isAppUpdated();
|
||||
if (pinned) {
|
||||
WebAppRT.init(updated);
|
||||
WebAppRT.init(updated, url);
|
||||
} else {
|
||||
SearchEngines.init();
|
||||
this.initContextMenu();
|
||||
|
@ -1990,6 +1990,7 @@ nsBrowserAccess.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
|
||||
|
||||
_getBrowser: function _getBrowser(aURI, aOpener, aWhere, aContext) {
|
||||
console.log("GetBrowser");
|
||||
let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
if (isExternal && aURI && aURI.schemeIs("chrome"))
|
||||
return null;
|
||||
|
@ -2728,6 +2729,7 @@ Tab.prototype = {
|
|||
}
|
||||
|
||||
case "DOMWillOpenModalDialog": {
|
||||
console.log("DOMWillOpenModalDialog");
|
||||
if (!aEvent.isTrusted)
|
||||
return;
|
||||
|
||||
|
@ -2926,7 +2928,7 @@ Tab.prototype = {
|
|||
|
||||
let documentURI = contentWin.document.documentURIObject.spec;
|
||||
let contentType = contentWin.document.contentType;
|
||||
|
||||
|
||||
// If fixedURI matches browser.lastURI, we assume this isn't a real location
|
||||
// change but rather a spurious addition like a wyciwyg URI prefix. See Bug 747883.
|
||||
// Note that we have to ensure fixedURI is not the same as aLocationURI so we
|
||||
|
@ -2941,6 +2943,18 @@ Tab.prototype = {
|
|||
this.shouldShowPluginDoorhanger = true;
|
||||
this.clickToPlayPluginsActivated = false;
|
||||
|
||||
// This is where we might check for helper apps.
|
||||
// For now it is special cased to only check for the marketplace urls
|
||||
if (WebappsUI.isMarketplace(aLocationURI)) {
|
||||
// the marketplace app may not actually be installed, so instead we use a custom
|
||||
// callback that will install and launch it for us if necessary
|
||||
HelperApps.showDoorhanger(aLocationURI, function() {
|
||||
WebappsUI.installAndLaunchMarketplace(aLocationURI.spec);
|
||||
if (aRequest)
|
||||
aRequest.cancel(Cr.NS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
let message = {
|
||||
gecko: {
|
||||
type: "Content:LocationChange",
|
||||
|
@ -5951,6 +5965,7 @@ var WebappsUI = {
|
|||
Services.obs.addObserver(this, "webapps-sync-install", false);
|
||||
Services.obs.addObserver(this, "webapps-sync-uninstall", false);
|
||||
Services.obs.addObserver(this, "webapps-install-error", false);
|
||||
Services.obs.addObserver(this, "WebApps:InstallMarketplace", false);
|
||||
},
|
||||
|
||||
uninit: function unint() {
|
||||
|
@ -5959,6 +5974,7 @@ var WebappsUI = {
|
|||
Services.obs.removeObserver(this, "webapps-sync-install");
|
||||
Services.obs.removeObserver(this, "webapps-sync-uninstall");
|
||||
Services.obs.removeObserver(this, "webapps-install-error", false);
|
||||
Services.obs.removeObserver(this, "WebApps:InstallMarketplace", false);
|
||||
},
|
||||
|
||||
DEFAULT_PREFS_FILENAME: "default-prefs.js",
|
||||
|
@ -6024,6 +6040,55 @@ var WebappsUI = {
|
|||
}
|
||||
});
|
||||
break;
|
||||
case "WebApps:InstallMarketplace":
|
||||
this.installAndLaunchMarketplace(data.url);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
MARKETPLACE: {
|
||||
MANIFEST: "https://marketplace.mozilla.org/manifest.webapp",
|
||||
get URI() {
|
||||
delete this.URI;
|
||||
return this.URI = Services.io.newURI(this.MANIFEST, null, null);
|
||||
}
|
||||
},
|
||||
|
||||
isMarketplace: function isMarketplace(aUri) {
|
||||
return aUri.host == this.MARKETPLACE.URI.host;
|
||||
},
|
||||
|
||||
// installs the marketplace, if a url is passed in, will launch it when the install
|
||||
// is complete
|
||||
installAndLaunchMarketplace: function installAndLaunchMarketplace(aLaunchUrl) {
|
||||
// TODO: Add a flag to hide other install prompt dialogs. This should be silent if possible
|
||||
let request = navigator.mozApps.getInstalled();
|
||||
request.onsuccess = function() {
|
||||
let foundMarket = false;
|
||||
for (let i = 0; i < request.result.length; i++) {
|
||||
if (request.result[i].origin == this.MARKETPLACE.URI.prePath)
|
||||
foundMarket = true;
|
||||
}
|
||||
|
||||
let launchFun = (function() {
|
||||
if (aLaunchUrl)
|
||||
WebappsUI.openURL(aLaunchUrl || WebappsUI.MARKETPLACE.URI.prePath, WebappsUI.MARKETPLACE.URI.prePath);
|
||||
}).bind(this);
|
||||
|
||||
if (foundMarket) {
|
||||
launchFun();
|
||||
} else {
|
||||
let r = navigator.mozApps.install(WebappsUI.MARKETPLACE.MANIFEST);
|
||||
r.onsuccess = function() {
|
||||
launchFun();
|
||||
};
|
||||
r.onerror = function() {
|
||||
console.log("error installing market " + this.error.name);
|
||||
};
|
||||
}
|
||||
};
|
||||
request.onerror = function() {
|
||||
console.log("error getting installed " + this.error.name);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -6063,7 +6128,13 @@ var WebappsUI = {
|
|||
doInstall: function doInstall(aData) {
|
||||
let manifest = new DOMApplicationManifest(aData.app.manifest, aData.app.origin);
|
||||
let name = manifest.name ? manifest.name : manifest.fullLaunchPath();
|
||||
if (Services.prompt.confirm(null, Strings.browser.GetStringFromName("webapps.installTitle"), name)) {
|
||||
let showPrompt = true;
|
||||
|
||||
// skip showing the prompt if this is for the marketplace app
|
||||
if (aData.app.origin == this.MARKETPLACE.URI.prePath)
|
||||
showPrompt = false;
|
||||
|
||||
if (!showPrompt || Services.prompt.confirm(null, Strings.browser.GetStringFromName("webapps.installTitle"), name)) {
|
||||
// Add a homescreen shortcut -- we can't use createShortcut, since we need to pass
|
||||
// a unique ID for Android webapp allocation
|
||||
this.makeBase64Icon(this.getBiggestIcon(manifest.icons, Services.io.newURI(aData.app.origin, null, null)),
|
||||
|
|
|
@ -278,5 +278,8 @@ remoteIncomingPromptMessage=An incoming request to permit remote debugging conne
|
|||
remoteIncomingPromptDisable=Disable
|
||||
|
||||
# Helper apps
|
||||
helperapps.open=Open
|
||||
helperapps.ignore=Ignore
|
||||
helperapps.dontAskAgain=Don't ask again for this site
|
||||
helperapps.openWithApp2=Open With %S App
|
||||
helperapps.openWithList2=Open With an App
|
||||
|
|
Загрузка…
Ссылка в новой задаче