Bug 942270 - Support quickshare in context menus. r=bnicholson

This commit is contained in:
Wes Johnston 2014-03-26 10:18:01 -07:00
Родитель 43735732bb
Коммит 1d56b0fe1e
6 изменённых файлов: 127 добавлений и 139 удалений

Просмотреть файл

@ -640,10 +640,6 @@ public abstract class GeckoApp
} else if (event.equals("Share:Text")) {
String text = message.getString("text");
GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, "");
} else if (event.equals("Share:Image")) {
String src = message.getString("url");
String type = message.getString("mime");
GeckoAppShell.shareImage(src, type);
} else if (event.equals("Image:SetAs")) {
String src = message.getString("url");
setImageAs(src);
@ -1585,7 +1581,6 @@ public abstract class GeckoApp
registerEventListener("Accessibility:Ready");
registerEventListener("Shortcut:Remove");
registerEventListener("Share:Text");
registerEventListener("Share:Image");
registerEventListener("Image:SetAs");
registerEventListener("Sanitize:ClearHistory");
registerEventListener("Update:Check");
@ -2119,7 +2114,6 @@ public abstract class GeckoApp
unregisterEventListener("Accessibility:Ready");
unregisterEventListener("Shortcut:Remove");
unregisterEventListener("Share:Text");
unregisterEventListener("Share:Image");
unregisterEventListener("Image:SetAs");
unregisterEventListener("Sanitize:ClearHistory");
unregisterEventListener("Update:Check");

Просмотреть файл

@ -1048,81 +1048,6 @@ public class GeckoAppShell
} catch (IOException e) {}
}
static void shareImage(String aSrc, String aType) {
Intent intent = new Intent(Intent.ACTION_SEND);
boolean isDataURI = aSrc.startsWith("data:");
OutputStream os = null;
File dir = GeckoApp.getTempDirectory();
if (dir == null) {
showImageShareFailureToast();
return;
}
GeckoApp.deleteTempFiles();
try {
// Create a temporary file for the image
File imageFile = File.createTempFile("image",
"." + aType.replace("image/",""),
dir);
os = new FileOutputStream(imageFile);
if (isDataURI) {
// We are dealing with a Data URI
int dataStart = aSrc.indexOf(',');
byte[] buf = Base64.decode(aSrc.substring(dataStart+1), Base64.DEFAULT);
os.write(buf);
} else {
// We are dealing with a URL
InputStream is = null;
try {
URL url = new URL(aSrc);
is = url.openStream();
byte[] buf = new byte[2048];
int length;
while ((length = is.read(buf)) != -1) {
os.write(buf, 0, length);
}
} finally {
safeStreamClose(is);
}
}
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
// If we were able to determine the image type, send that in the intent. Otherwise,
// use a generic type.
if (aType.startsWith("image/")) {
intent.setType(aType);
} else {
intent.setType("image/*");
}
} catch (IOException e) {
if (!isDataURI) {
// If we failed, at least send through the URL link
intent.putExtra(Intent.EXTRA_TEXT, aSrc);
intent.setType("text/plain");
} else {
showImageShareFailureToast();
return;
}
} finally {
safeStreamClose(os);
}
getContext().startActivity(Intent.createChooser(intent,
getContext().getResources().getString(R.string.share_title)));
}
// Don't fail silently, tell the user that we weren't able to share the image
private static final void showImageShareFailureToast() {
Toast toast = Toast.makeText(getContext(),
getContext().getResources().getString(R.string.share_image_failed),
Toast.LENGTH_SHORT);
toast.show();
}
static boolean isUriSafeForScheme(Uri aUri) {
// Bug 794034 - We don't want to pass MWI or USSD codes to the
// dialer, and ensure the Uri class doesn't parse a URI

Просмотреть файл

@ -37,11 +37,11 @@ public class PromptListItem {
id = aObject.optInt("id");
mSelected = aObject.optBoolean("selected");
JSONObject obj = aObject.optJSONObject("shareData");
JSONObject obj = aObject.optJSONObject("showAsActions");
if (obj != null) {
showAsActions = true;
String uri = obj.isNull("uri") ? "" : obj.optString("uri");
String type = obj.isNull("type") ? "" : obj.optString("type");
String type = obj.isNull("type") ? "text/html" : obj.optString("type", "text/html");
mIntent = GeckoAppShell.getShareIntent(GeckoAppShell.getContext(), uri, type, "");
isParent = true;
} else {

Просмотреть файл

@ -18,6 +18,7 @@ import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnClickListener;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.HashMap;
@ -47,16 +48,40 @@ public class GeckoActionProvider extends ActionProvider {
private static HashMap<String, GeckoActionProvider> mProviders = new HashMap<String, GeckoActionProvider>();
private static String getFilenameFromMimeType(String mimeType) {
String[] mime = mimeType.split("/");
if (mime.length == 1) {
return "history-" + mime[0] + ".xml";
}
// Separate out tel and mailto for their own media types
if ("text".equals(mime[0])) {
if ("tel".equals(mime[1])) {
return "history-phone.xml";
} else if ("mailto".equals(mime[1])) {
return "history-email.xml";
} else if ("html".equals(mime[1])) {
return DEFAULT_HISTORY_FILE_NAME;
}
}
return "history-" + mime[0] + ".xml";
}
// Gets the action provider for a particular mimetype
public static GeckoActionProvider getForType(String type, Context context) {
if (!mProviders.keySet().contains(type)) {
public static GeckoActionProvider getForType(String mimeType, Context context) {
if (!mProviders.keySet().contains(mimeType)) {
GeckoActionProvider provider = new GeckoActionProvider(context);
String subType = type.substring(0, type.indexOf("/"));
provider.setHistoryFileName("history-" + subType + ".xml");
mProviders.put(type, provider);
// For empty types, we just return a default provider
if (TextUtils.isEmpty(mimeType)) {
return provider;
}
provider.setHistoryFileName(getFilenameFromMimeType(mimeType));
mProviders.put(mimeType, provider);
}
return mProviders.get(type);
return mProviders.get(mimeType);
}
public GeckoActionProvider(Context context) {

Просмотреть файл

@ -508,7 +508,7 @@ var BrowserApp = {
NativeWindow.contextmenus.emailLinkContext,
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let emailAddr = NativeWindow.contextmenus._stripScheme(url);
let [,emailAddr] = NativeWindow.contextmenus._stripScheme(url);
NativeWindow.contextmenus._copyStringToDefaultClipboard(emailAddr);
});
@ -516,35 +516,59 @@ var BrowserApp = {
NativeWindow.contextmenus.phoneNumberLinkContext,
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
let [,phoneNumber] = NativeWindow.contextmenus._stripScheme(url);
NativeWindow.contextmenus._copyStringToDefaultClipboard(phoneNumber);
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.shareLink"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.linkShareableContext),
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let title = aTarget.textContent || aTarget.title;
NativeWindow.contextmenus._shareStringWithDefault(url, title);
});
NativeWindow.contextmenus.add({
label: Strings.browser.GetStringFromName("contextmenu.shareLink"),
order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER-1, // Show above HTML5 menu items
selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.linkShareableContext),
showAsActions: function(aElement) {
return {
title: aElement.textContent.trim() || aElement.title.trim(),
uri: NativeWindow.contextmenus._getLinkURL(aElement),
}
},
icon: "drawable://ic_menu_share",
callback: function(aTarget) { }
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.shareEmailAddress"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.emailLinkContext),
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let emailAddr = NativeWindow.contextmenus._stripScheme(url);
let title = aTarget.textContent || aTarget.title;
NativeWindow.contextmenus._shareStringWithDefault(emailAddr, title);
});
NativeWindow.contextmenus.add({
label: Strings.browser.GetStringFromName("contextmenu.shareEmailAddress"),
order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.emailLinkContext),
showAsActions: function(aElement) {
let url = NativeWindow.contextmenus._getLinkURL(aElement);
let [, emailAddr] = NativeWindow.contextmenus._stripScheme(url);
let title = aElement.textContent || aElement.title;
return {
title: title,
uri: emailAddr,
type: "text/mailto",
}
},
icon: "drawable://ic_menu_share",
callback: function(aTarget) { }
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.sharePhoneNumber"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.phoneNumberLinkContext),
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
let title = aTarget.textContent || aTarget.title;
NativeWindow.contextmenus._shareStringWithDefault(phoneNumber, title);
});
NativeWindow.contextmenus.add({
label: Strings.browser.GetStringFromName("contextmenu.sharePhoneNumber"),
order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.phoneNumberLinkContext),
showAsActions: function(aElement) {
let url = NativeWindow.contextmenus._getLinkURL(aElement);
let [, phoneNumber] = NativeWindow.contextmenus._stripScheme(url);
let title = aElement.textContent || aElement.title;
return {
title: title,
uri: phoneNumber,
type: "text/tel",
}
},
icon: "drawable://ic_menu_share",
callback: function(aTarget) { }
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.addToContacts"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.emailLinkContext),
@ -596,13 +620,23 @@ var BrowserApp = {
aTarget.setAttribute("controls", true);
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.shareMedia"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.SelectorContext("video")),
function(aTarget) {
let url = (aTarget.currentSrc || aTarget.src);
let title = aTarget.textContent || aTarget.title;
NativeWindow.contextmenus._shareStringWithDefault(url, title);
});
NativeWindow.contextmenus.add({
label: Strings.browser.GetStringFromName("contextmenu.shareMedia"),
order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER-1,
selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.SelectorContext("video")),
showAsActions: function(aElement) {
let url = (aElement.currentSrc || aElement.src);
let title = aElement.textContent || aElement.title;
return {
title: title,
uri: url,
type: "video/*",
}
},
icon: "drawable://ic_menu_share",
callback: function(aTarget) {
}
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
NativeWindow.contextmenus.SelectorContext("video:not(:-moz-full-screen)"),
@ -629,26 +663,26 @@ var BrowserApp = {
NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.shareImage"),
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.imageSaveableContext),
function(aTarget) {
NativeWindow.contextmenus.add({
label: Strings.browser.GetStringFromName("contextmenu.shareImage"),
selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.imageSaveableContext),
order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER-1, // Show above HTML5 menu items
showAsActions: function(aTarget) {
let doc = aTarget.ownerDocument;
let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.getImgCacheForDocument(doc);
let props = imageCache.findEntryProperties(aTarget.currentURI, doc.characterSet);
let src = aTarget.src;
let type = "";
try {
type = String(props.get("type", Ci.nsISupportsCString));
} catch(ex) {
type = "";
return {
title: src,
uri: src,
type: "image/*",
}
sendMessageToJava({
type: "Share:Image",
url: src,
mime: type,
});
});
},
icon: "drawable://ic_menu_share",
menu: true,
callback: function(aTarget) { }
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.saveImage"),
NativeWindow.contextmenus.imageSaveableContext,
@ -2205,13 +2239,13 @@ var NativeWindow = {
_getUrl: function(node) {
if ((node instanceof Ci.nsIDOMHTMLAnchorElement && node.href) ||
(node instanceof Ci.nsIDOMHTMLAreaElement && node.href)) {
return this._getLinkURL(node);
} else if (node instanceof Ci.nsIImageLoadingContent && node.currentURI) {
return node.currentURI.spec;
} else if (node instanceof Ci.nsIDOMHTMLMediaElement) {
return (node.currentSrc || node.src);
}
return "";
},
@ -2283,7 +2317,10 @@ var NativeWindow = {
let title = this._findTitle(target);
this.menuitems.sort((a,b) => {
if (a.order == b.order) return 0;
if (a.order == b.order) {
return 0;
}
return (a.order > b.order) ? 1 : -1;
});
@ -2393,7 +2430,8 @@ var NativeWindow = {
},
_stripScheme: function(aString) {
return aString.slice(aString.indexOf(":") + 1);
let index = aString.indexOf(":");
return [aString.slice(0, index), aString.slice(index + 1)];
}
}
};
@ -8313,7 +8351,7 @@ ContextMenuItem.prototype = {
return {
id: this.id,
label: this.addVal("label", elt),
shareData: this.addVal("shareData", elt),
showAsActions: this.addVal("showAsActions", elt),
icon: this.addVal("icon", elt),
isGroup: this.addVal("isGroup", elt, false),
inGroup: this.addVal("inGroup", elt, false),

Просмотреть файл

@ -195,6 +195,12 @@ Prompt.prototype = {
if (item.child)
obj.inGroup = true;
if (item.showAsActions)
obj.showAsActions = item.showAsActions;
if (item.icon)
obj.icon = item.icon;
this.msg.listitems.push(obj);
}, this);