mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1168980 - Search app store when there is no app to handle intent:// URI. r=margaret
This commit is contained in:
parent
197bbfbbbf
commit
882413144c
@ -16,8 +16,12 @@ import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@ -27,8 +31,14 @@ public final class IntentHelper implements GeckoEventListener {
|
||||
"Intent:GetHandlers",
|
||||
"Intent:Open",
|
||||
"Intent:OpenForResult",
|
||||
"Intent:OpenNoHandler",
|
||||
"WebActivity:Open"
|
||||
};
|
||||
|
||||
// via http://developer.android.com/distribute/tools/promote/linking.html
|
||||
private static String MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id=";
|
||||
private static String EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url";
|
||||
|
||||
private static IntentHelper instance;
|
||||
|
||||
private final Activity activity;
|
||||
@ -64,6 +74,8 @@ public final class IntentHelper implements GeckoEventListener {
|
||||
open(message);
|
||||
} else if (event.equals("Intent:OpenForResult")) {
|
||||
openForResult(message);
|
||||
} else if (event.equals("Intent:OpenNoHandler")) {
|
||||
openNoHandler(message);
|
||||
} else if (event.equals("WebActivity:Open")) {
|
||||
openWebActivity(message);
|
||||
}
|
||||
@ -105,6 +117,66 @@ public final class IntentHelper implements GeckoEventListener {
|
||||
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ResultHandler(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a URI without any valid handlers on device. In the best case, a package is specified
|
||||
* and we can bring the user directly to the application page in an app market. If a package is
|
||||
* not specified and there is a fallback url in the intent extras, we open that url. If neither
|
||||
* is present, we alert the user that we were unable to open the link.
|
||||
*/
|
||||
private void openNoHandler(final JSONObject msg) {
|
||||
final String uri = msg.optString("uri");
|
||||
|
||||
if (TextUtils.isEmpty(uri)) {
|
||||
displayToastCannotOpenLink();
|
||||
Log.w(LOGTAG, "Received empty URL. Ignoring...");
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent intent;
|
||||
try {
|
||||
// TODO (bug 1173626): This will not handle android-app uris on non 5.1 devices.
|
||||
intent = Intent.parseUri(uri, 0);
|
||||
} catch (final URISyntaxException e) {
|
||||
displayToastCannotOpenLink();
|
||||
// Don't log the exception to prevent leaking URIs.
|
||||
Log.w(LOGTAG, "Unable to parse Intent URI");
|
||||
return;
|
||||
}
|
||||
|
||||
// For this flow, we follow Chrome's lead:
|
||||
// https://developer.chrome.com/multidevice/android/intents
|
||||
//
|
||||
// Note on alternative flows: we could get the intent package from a component, however, for
|
||||
// security reasons, components are ignored when opening URIs (bug 1168998) so we should
|
||||
// ignore it here too.
|
||||
//
|
||||
// Our old flow used to prompt the user to search for their app in the market by scheme and
|
||||
// while this could help the user find a new app, there is not always a correlation in
|
||||
// scheme to application name and we could end up steering the user wrong (potentially to
|
||||
// malicious software). Better to leave that one alone.
|
||||
if (intent.getPackage() != null) {
|
||||
final String marketUri = MARKET_INTENT_URI_PACKAGE_PREFIX + intent.getPackage();
|
||||
final Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(marketUri));
|
||||
marketIntent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
activity.startActivity(marketIntent);
|
||||
|
||||
} else if (intent.hasExtra(EXTRA_BROWSER_FALLBACK_URL)) {
|
||||
final String fallbackUrl = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL);
|
||||
Tabs.getInstance().loadUrl(fallbackUrl);
|
||||
|
||||
} else {
|
||||
displayToastCannotOpenLink();
|
||||
// Don't log the URI to prevent leaking it.
|
||||
Log.w(LOGTAG, "Unable to handle URI");
|
||||
}
|
||||
}
|
||||
|
||||
private void displayToastCannotOpenLink() {
|
||||
final String errText = activity.getResources().getString(R.string.intent_uri_cannot_open);
|
||||
Toast.makeText(activity, errText, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void openWebActivity(JSONObject message) throws JSONException {
|
||||
final Intent intent = WebActivityMapper.getIntentForWebActivity(message.getJSONObject("activity"));
|
||||
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ResultHandler(message));
|
||||
|
@ -659,3 +659,5 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!-- LOCALIZATION NOTE (find_matchcase): This is meant to appear as an icon that changes color
|
||||
if match-case is activated. i.e. No more than two letters, one uppercase, one lowercase. -->
|
||||
<!ENTITY find_matchcase "Aa">
|
||||
|
||||
<!ENTITY intent_uri_cannot_open "Cannot open link">
|
||||
|
@ -538,4 +538,6 @@
|
||||
<string name="percent">&percent;</string>
|
||||
|
||||
<string name="remote_tabs_last_synced">&remote_tabs_last_synced;</string>
|
||||
|
||||
<string name="intent_uri_cannot_open">&intent_uri_cannot_open;</string>
|
||||
</resources>
|
||||
|
@ -25,14 +25,6 @@ ContentDispatchChooser.prototype =
|
||||
return this._protoSvc;
|
||||
},
|
||||
|
||||
_getChromeWin: function getChromeWin() {
|
||||
try {
|
||||
return Services.wm.getMostRecentWindow("navigator:browser");
|
||||
} catch (e) {
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
},
|
||||
|
||||
ask: function ask(aHandler, aWindowContext, aURI, aReason) {
|
||||
let window = null;
|
||||
try {
|
||||
@ -49,26 +41,12 @@ ContentDispatchChooser.prototype =
|
||||
if (aHandler.possibleApplicationHandlers.length > 1) {
|
||||
aHandler.launchWithURI(aURI, aWindowContext);
|
||||
} else {
|
||||
let win = this._getChromeWin();
|
||||
if (win && win.NativeWindow) {
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/handling.properties");
|
||||
let failedText = bundle.GetStringFromName("protocol.failed");
|
||||
let searchText = bundle.GetStringFromName("protocol.toast.search");
|
||||
let msg = {
|
||||
type: "Intent:OpenNoHandler",
|
||||
uri: aURI.spec,
|
||||
};
|
||||
|
||||
win.NativeWindow.toast.show(failedText, "long", {
|
||||
button: {
|
||||
label: searchText,
|
||||
callback: function() {
|
||||
let message = {
|
||||
type: "Intent:Open",
|
||||
url: "market://search?q=" + aURI.scheme,
|
||||
};
|
||||
|
||||
Messaging.sendRequest(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Messaging.sendRequest(msg);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -3,6 +3,3 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
download.blocked=Unable to download file
|
||||
protocol.failed=Couldn't find an app to open this link
|
||||
# A very short string shown in the button toast when no application can open the url
|
||||
protocol.toast.search=Search
|
||||
|
Loading…
Reference in New Issue
Block a user