mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 909932 - Implemented a Notifications.jsm module to expose rich notification api to addons; r=wesj
This commit is contained in:
parent
143c06354d
commit
5ed99b8b74
@ -44,18 +44,20 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
private static final String LIGHT_ATTR = "light";
|
||||
private static final String ONGOING_ATTR = "ongoing";
|
||||
private static final String WHEN_ATTR = "when";
|
||||
private static final String PRIORITY_ATTR = "priority";
|
||||
private static final String LARGE_ICON_ATTR = "largeIcon";
|
||||
private static final String COOKIE_ATTR = "cookie";
|
||||
private static final String EVENT_TYPE_ATTR = "eventType";
|
||||
private static final String ACTIONS_ATTR = "actions";
|
||||
private static final String ACTION_ATTR = "actionKind";
|
||||
private static final String ACTION_ID_ATTR = "buttonId";
|
||||
private static final String ACTION_TITLE_ATTR = "title";
|
||||
private static final String ACTION_ICON_ATTR = "icon";
|
||||
|
||||
private static final String NOTIFICATION_SCHEME = "moz-notification";
|
||||
|
||||
private static final String BUTTON_EVENT = "notification-button-clicked";
|
||||
private static final String CLICK_EVENT = "notification-clicked";
|
||||
private static final String CLEARED_EVENT = "notification-cleared";
|
||||
private static final String CLOSED_EVENT = "notification-closed";
|
||||
|
||||
private static Context mContext;
|
||||
private static Set<String> mShowing;
|
||||
@ -127,50 +129,78 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
mShowing.remove(id);
|
||||
}
|
||||
|
||||
// If the notification was clicked, we are closing it.
|
||||
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
|
||||
hideNotification(id);
|
||||
}
|
||||
String cookie = data.getQueryParameter(COOKIE_ATTR);
|
||||
|
||||
if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put(NOTIFICATION_ID, id);
|
||||
if (cookie != null) {
|
||||
args.put(COOKIE_ATTR, cookie);
|
||||
}
|
||||
args.put(ID_ATTR, id);
|
||||
args.put(EVENT_TYPE_ATTR, notificationType);
|
||||
|
||||
if (BUTTON_EVENT.equals(notificationType)) {
|
||||
final String actionName = data.getQueryParameter(ACTION_ID_ATTR);
|
||||
args.put(ACTION_ID_ATTR, actionName);
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Event", args.toString()));
|
||||
} catch (JSONException e) {
|
||||
Log.w(LOGTAG, "Error building JSON notification arguments.", e);
|
||||
}
|
||||
}
|
||||
// If the notification was clicked, we are closing it. This must be executed after
|
||||
// sending the event to js side because when the notification is canceled no event can be
|
||||
// handled.
|
||||
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
|
||||
hideNotification(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
|
||||
Intent notificationIntent = new Intent(HELPER_BROADCAST_ACTION);
|
||||
final boolean ongoing = message.optBoolean(ONGOING_ATTR);
|
||||
notificationIntent.putExtra(ONGOING_ATTR, ongoing);
|
||||
|
||||
private Uri.Builder getNotificationBuilder(JSONObject message, String type) {
|
||||
Uri.Builder b = new Uri.Builder();
|
||||
b.scheme(NOTIFICATION_SCHEME).appendQueryParameter(EVENT_TYPE_ATTR, type);
|
||||
|
||||
try {
|
||||
final String id = message.getString(ID_ATTR);
|
||||
b.appendQueryParameter(ID_ATTR, id);
|
||||
if (message.has(COOKIE_ATTR)) {
|
||||
b.appendQueryParameter(COOKIE_ATTR,
|
||||
message.getString(COOKIE_ATTR));
|
||||
} catch (JSONException ex) {
|
||||
Log.i(LOGTAG, "buildNotificationPendingIntent, error parsing", ex);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private Intent buildNotificationIntent(JSONObject message, Uri.Builder builder) {
|
||||
Intent notificationIntent = new Intent(HELPER_BROADCAST_ACTION);
|
||||
final boolean ongoing = message.optBoolean(ONGOING_ATTR);
|
||||
notificationIntent.putExtra(ONGOING_ATTR, ongoing);
|
||||
|
||||
final Uri dataUri = builder.build();
|
||||
notificationIntent.setData(dataUri);
|
||||
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
|
||||
return notificationIntent;
|
||||
}
|
||||
|
||||
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
|
||||
Uri.Builder builder = getNotificationBuilder(message, type);
|
||||
final Intent notificationIntent = buildNotificationIntent(message, builder);
|
||||
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return pi;
|
||||
}
|
||||
|
||||
private PendingIntent buildButtonClickPendingIntent(JSONObject message, JSONObject action) {
|
||||
Uri.Builder builder = getNotificationBuilder(message, BUTTON_EVENT);
|
||||
try {
|
||||
// Action name must be in query uri, otherwise buttons pending intents
|
||||
// would be collapsed.
|
||||
if(action.has(ACTION_ID_ATTR)) {
|
||||
builder.appendQueryParameter(ACTION_ID_ATTR, action.getString(ACTION_ID_ATTR));
|
||||
} else {
|
||||
Log.i(LOGTAG, "button event with no name");
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
Log.i(LOGTAG, "buildNotificationPendingIntent, error parsing", ex);
|
||||
}
|
||||
final Uri dataUri = b.build();
|
||||
notificationIntent.setData(dataUri);
|
||||
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
|
||||
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return pi;
|
||||
final Intent notificationIntent = buildNotificationIntent(message, builder);
|
||||
PendingIntent res = PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void showNotification(JSONObject message) {
|
||||
@ -209,6 +239,11 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
builder.setWhen(when);
|
||||
}
|
||||
|
||||
if (message.has(PRIORITY_ATTR)) {
|
||||
int priority = message.optInt(PRIORITY_ATTR);
|
||||
builder.setPriority(priority);
|
||||
}
|
||||
|
||||
if (message.has(LARGE_ICON_ATTR)) {
|
||||
Bitmap b = BitmapUtils.getBitmapFromDataURI(message.optString(LARGE_ICON_ATTR));
|
||||
builder.setLargeIcon(b);
|
||||
@ -230,7 +265,7 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
try {
|
||||
for (int i = 0; i < actions.length(); i++) {
|
||||
JSONObject action = actions.getJSONObject(i);
|
||||
final PendingIntent pending = buildNotificationPendingIntent(message, action.getString(ACTION_ATTR));
|
||||
final PendingIntent pending = buildButtonClickPendingIntent(message, action);
|
||||
final String actionTitle = action.getString(ACTION_TITLE_ATTR);
|
||||
final Uri actionImage = Uri.parse(action.optString(ACTION_ICON_ATTR));
|
||||
builder.addAction(BitmapUtils.getResource(actionImage, R.drawable.ic_status_logo),
|
||||
@ -265,9 +300,24 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
hideNotification(id);
|
||||
}
|
||||
|
||||
private void sendNotificationWasClosed(String id) {
|
||||
if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
||||
return;
|
||||
}
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put(ID_ATTR, id);
|
||||
args.put(EVENT_TYPE_ATTR, CLOSED_EVENT);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Event", args.toString()));
|
||||
} catch (JSONException ex) {
|
||||
Log.w(LOGTAG, "sendNotificationWasClosed: error building JSON notification arguments.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideNotification(String id) {
|
||||
GeckoAppShell.sNotificationClient.remove(id.hashCode());
|
||||
mShowing.remove(id);
|
||||
sendNotificationWasClosed(id);
|
||||
}
|
||||
|
||||
private void clearAll() {
|
||||
|
@ -5,32 +5,21 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Notifications",
|
||||
"resource://gre/modules/Notifications.jsm");
|
||||
|
||||
const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
|
||||
const URI_PAUSE_ICON = "drawable://pause";
|
||||
const URI_CANCEL_ICON = "drawable://close";
|
||||
const URI_RESUME_ICON = "drawable://play";
|
||||
|
||||
const PAUSE_ACTION = {
|
||||
actionKind : "pause",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsPause"),
|
||||
icon : URI_PAUSE_ICON
|
||||
};
|
||||
|
||||
const CANCEL_ACTION = {
|
||||
actionKind : "cancel",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsCancel"),
|
||||
icon : URI_CANCEL_ICON
|
||||
};
|
||||
|
||||
const RESUME_ACTION = {
|
||||
actionKind : "resume",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsResume"),
|
||||
icon : URI_RESUME_ICON
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
@ -40,6 +29,7 @@ var Downloads = {
|
||||
_progressAlert: null,
|
||||
_privateDownloads: [],
|
||||
_showingPrompt: false,
|
||||
_downloadsIdMap: {},
|
||||
|
||||
_getLocalFile: function dl__getLocalFile(aFileURI) {
|
||||
// if this is a URL, get the file from that
|
||||
@ -58,7 +48,6 @@ var Downloads = {
|
||||
this._progressAlert = new AlertDownloadProgressListener();
|
||||
this._dlmgr.addPrivacyAwareListener(this._progressAlert);
|
||||
Services.obs.addObserver(this, "last-pb-context-exited", true);
|
||||
Services.obs.addObserver(this, "Notification:Event", true);
|
||||
},
|
||||
|
||||
openDownload: function dl_openDownload(aDownload) {
|
||||
@ -76,54 +65,12 @@ var Downloads = {
|
||||
|
||||
cancelDownload: function dl_cancelDownload(aDownload) {
|
||||
aDownload.cancel();
|
||||
|
||||
let fileURI = aDownload.target.spec;
|
||||
let f = this._getLocalFile(fileURI);
|
||||
|
||||
OS.File.remove(f.path);
|
||||
},
|
||||
|
||||
getNotificationIdFromDownload: function dl_getNotificationIdFromDownload(aDownload) {
|
||||
return aDownload.target.spec.replace("file:", "download:");
|
||||
},
|
||||
|
||||
showNotification: function dl_showNotification(aDownload, aMessage, aTitle, aOptions) {
|
||||
let msg = {
|
||||
type: "Notification:Show",
|
||||
id: this.getNotificationIdFromDownload(aDownload),
|
||||
title: aTitle,
|
||||
smallIcon: URI_GENERIC_ICON_DOWNLOAD,
|
||||
text: aMessage,
|
||||
ongoing: false,
|
||||
cookie: aDownload.guid,
|
||||
when: aDownload.startTime
|
||||
};
|
||||
|
||||
if (aOptions && aOptions.icon) {
|
||||
msg.smallIcon = aOptions.icon;
|
||||
}
|
||||
|
||||
if (aOptions && aOptions.percentComplete) {
|
||||
msg.progress_value = aOptions.percentComplete;
|
||||
msg.progress_max = 100;
|
||||
}
|
||||
if (aOptions && aOptions.actions) {
|
||||
msg.actions = aOptions.actions;
|
||||
}
|
||||
if (aOptions && aOptions.ongoing) {
|
||||
msg.ongoing = aOptions.ongoing;
|
||||
}
|
||||
this._bridge.handleGeckoMessage(JSON.stringify(msg));
|
||||
},
|
||||
|
||||
removeNotification: function dl_removeNotification(aDownload) {
|
||||
let msg = {
|
||||
type: "Notification:Hide",
|
||||
id: this.getNotificationIdFromDownload(aDownload)
|
||||
};
|
||||
this._bridge.handleGeckoMessage(JSON.stringify(msg));
|
||||
},
|
||||
|
||||
showCancelConfirmPrompt: function dl_showCancelConfirmPrompt(aDownload) {
|
||||
if (this._showingPrompt)
|
||||
return;
|
||||
@ -149,48 +96,66 @@ var Downloads = {
|
||||
this.showCancelConfirmPrompt(aDownload);
|
||||
},
|
||||
|
||||
handleNotificationEvent: function dl_handleNotificationEvent(aNotifData, aDownload) {
|
||||
switch (aNotifData.eventType) {
|
||||
case "notification-clicked":
|
||||
this.handleClickEvent(aDownload);
|
||||
break;
|
||||
case "cancel":
|
||||
this.cancelDownload(aDownload);
|
||||
break;
|
||||
case "pause":
|
||||
aDownload.pause();
|
||||
break;
|
||||
case "resume":
|
||||
aDownload.resume();
|
||||
break;
|
||||
case "notification-cleared":
|
||||
// notification cleared by the user
|
||||
break;
|
||||
}
|
||||
clickCallback: function dl_clickCallback(aDownloadId) {
|
||||
this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
this.handleClickEvent(download);
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
pauseClickCallback: function dl_buttonPauseCallback(aDownloadId) {
|
||||
this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
download.pause();
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
resumeClickCallback: function dl_buttonPauseCallback(aDownloadId) {
|
||||
this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
download.resume();
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
cancelClickCallback: function dl_buttonPauseCallback(aDownloadId) {
|
||||
this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
this.cancelDownload(download);
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
notificationCanceledCallback: function dl_notifCancelCallback(aId, aDownloadId) {
|
||||
let notificationId = this._downloadsIdMap[aDownloadId];
|
||||
if (notificationId && notificationId == aId)
|
||||
delete this._downloadsIdMap[aDownloadId];
|
||||
},
|
||||
|
||||
createNotification: function dl_createNotif(aDownload, aOptions) {
|
||||
let notificationId = Notifications.create(aOptions);
|
||||
this._downloadsIdMap[aDownload.guid] = notificationId;
|
||||
},
|
||||
|
||||
updateNotification: function dl_updateNotif(aDownload, aOptions) {
|
||||
let notificationId = this._downloadsIdMap[aDownload.guid];
|
||||
if (notificationId)
|
||||
Notifications.update(notificationId, aOptions);
|
||||
},
|
||||
|
||||
cancelNotification: function dl_cleanNotif(aDownload) {
|
||||
Notifications.cancel(this._downloadsIdMap[aDownload.guid]);
|
||||
delete this._downloadsIdMap[aDownload.guid];
|
||||
},
|
||||
|
||||
// observer for last-pb-context-exited
|
||||
observe: function dl_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "Notification:Event": {
|
||||
let data = JSON.parse(aData);
|
||||
let guid = data.cookie;
|
||||
this._dlmgr.getDownloadByGUID(guid, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
this.handleNotificationEvent(data, download);
|
||||
}).bind(this));
|
||||
break;
|
||||
}
|
||||
case "last-pb-context-exited": {
|
||||
let download;
|
||||
while ((download = this._privateDownloads.pop())) {
|
||||
try {
|
||||
Downloads.removeNotification(download);
|
||||
} catch (e) {
|
||||
dump("Error removing private download: " + e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
let download;
|
||||
while ((download = this._privateDownloads.pop())) {
|
||||
try {
|
||||
let notificationId = aDownload.guid;
|
||||
Notifications.clear(notificationId);
|
||||
Downloads.removeNotification(download);
|
||||
} catch (e) {
|
||||
dump("Error removing private download: " + e);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -201,15 +166,57 @@ var Downloads = {
|
||||
!aIID.equals(Ci.nsISupportsWeakReference))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
},
|
||||
|
||||
get _bridge() {
|
||||
delete this._bridge;
|
||||
return this._bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const PAUSE_BUTTON = {
|
||||
buttonId: "pause",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsPause"),
|
||||
icon : URI_PAUSE_ICON,
|
||||
onClicked: function (aId, aCookie) {
|
||||
Downloads.pauseClickCallback(aCookie);
|
||||
}
|
||||
};
|
||||
|
||||
const CANCEL_BUTTON = {
|
||||
buttonId: "cancel",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsCancel"),
|
||||
icon : URI_CANCEL_ICON,
|
||||
onClicked: function (aId, aCookie) {
|
||||
Downloads.cancelClickCallback(aCookie);
|
||||
}
|
||||
};
|
||||
|
||||
const RESUME_BUTTON = {
|
||||
buttonId: "resume",
|
||||
title : Strings.browser.GetStringFromName("alertDownloadsResume"),
|
||||
icon: URI_RESUME_ICON,
|
||||
onClicked: function (aId, aCookie) {
|
||||
Downloads.resumeClickCallback(aCookie);
|
||||
}
|
||||
};
|
||||
|
||||
function DownloadNotifOptions (aDownload, aTitle, aMessage) {
|
||||
this.icon = URI_GENERIC_ICON_DOWNLOAD;
|
||||
this.onCancel = function (aId, aCookie) {
|
||||
Downloads.notificationCanceledCallback(aId, aCookie);
|
||||
}
|
||||
this.onClick = function (aId, aCookie) {
|
||||
Downloads.clickCallback(aCookie);
|
||||
}
|
||||
this.title = aTitle;
|
||||
this.message = aMessage;
|
||||
this.buttons = null;
|
||||
this.cookie = aDownload.guid;
|
||||
}
|
||||
|
||||
function DownloadProgressNotifOptions (aDownload, aButtons) {
|
||||
DownloadNotifOptions.apply(this, [aDownload, aDownload.displayName, aDownload.percentComplete + "%"]);
|
||||
this.ongoing = true;
|
||||
this.progress = aDownload.percentComplete;
|
||||
this.buttons = aButtons;
|
||||
}
|
||||
|
||||
// AlertDownloadProgressListener is used to display progress in the alert notifications.
|
||||
function AlertDownloadProgressListener() { }
|
||||
|
||||
@ -225,8 +232,9 @@ AlertDownloadProgressListener.prototype = {
|
||||
} catch(ex) { }
|
||||
let contentLength = aDownload.size;
|
||||
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
|
||||
Downloads.showNotification(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||
strings.GetStringFromName("alertDownloadsSize"));
|
||||
Downloads.updateNotification(aDownload, new DownloadNotifOptions(aDownload,
|
||||
strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||
strings.GetStringFromName("alertDownloadsSize")));
|
||||
aDownload.cancel();
|
||||
}
|
||||
|
||||
@ -234,33 +242,30 @@ AlertDownloadProgressListener.prototype = {
|
||||
// Undetermined progress is not supported yet
|
||||
return;
|
||||
}
|
||||
Downloads.showNotification(aDownload, aDownload.percentComplete + "%",
|
||||
aDownload.displayName, { percentComplete: aDownload.percentComplete,
|
||||
ongoing: true,
|
||||
actions: [PAUSE_ACTION, CANCEL_ACTION] });
|
||||
|
||||
Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [PAUSE_BUTTON, CANCEL_BUTTON]));
|
||||
},
|
||||
|
||||
onDownloadStateChange: function(aState, aDownload) {
|
||||
let state = aDownload.state;
|
||||
switch (state) {
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED: {
|
||||
NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
|
||||
Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsStart2"),
|
||||
aDownload.displayName);
|
||||
Downloads.createNotification(aDownload, new DownloadNotifOptions(aDownload,
|
||||
Strings.browser.GetStringFromName("alertDownloadsStart2"),
|
||||
aDownload.displayName));
|
||||
break;
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
|
||||
Downloads.showNotification(aDownload, aDownload.percentComplete + "%",
|
||||
aDownload.displayName, { percentComplete: aDownload.percentComplete,
|
||||
ongoing: true,
|
||||
actions: [RESUME_ACTION, CANCEL_ACTION] });
|
||||
}
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_PAUSED: {
|
||||
Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [RESUME_BUTTON, CANCEL_BUTTON]));
|
||||
break;
|
||||
}
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
|
||||
Downloads.removeNotification(aDownload);
|
||||
Downloads.cancelNotification(aDownload);
|
||||
if (aDownload.isPrivate) {
|
||||
let index = Downloads._privateDownloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
@ -269,8 +274,9 @@ AlertDownloadProgressListener.prototype = {
|
||||
}
|
||||
|
||||
if (state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
|
||||
Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsDone2"),
|
||||
aDownload.displayName);
|
||||
Downloads.createNotification(aDownload, new DownloadNotifOptions(aDownload,
|
||||
Strings.browser.GetStringFromName("alertDownloadsDone2"),
|
||||
aDownload.displayName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
209
mobile/android/modules/Notifications.jsm
Normal file
209
mobile/android/modules/Notifications.jsm
Normal file
@ -0,0 +1,209 @@
|
||||
/* 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/. */
|
||||
"use strict"
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Notifications"];
|
||||
|
||||
function log(msg) {
|
||||
// Services.console.logStringMessage(msg);
|
||||
}
|
||||
|
||||
var _notificationsMap = {};
|
||||
|
||||
function Notification(aId, aOptions) {
|
||||
this._id = aId;
|
||||
this._when = (new Date).getTime();
|
||||
this.fillWithOptions(aOptions);
|
||||
}
|
||||
|
||||
Notification.prototype = {
|
||||
fillWithOptions: function(aOptions) {
|
||||
if ("icon" in aOptions && aOptions.icon != null)
|
||||
this._icon = aOptions.icon;
|
||||
else
|
||||
throw "Notification icon is mandatory";
|
||||
|
||||
if ("title" in aOptions && aOptions.title != null)
|
||||
this._title = aOptions.title;
|
||||
else
|
||||
throw "Notification title is mandatory";
|
||||
|
||||
if ("message" in aOptions && aOptions.message != null)
|
||||
this._message = aOptions.message;
|
||||
else
|
||||
this._message = null;
|
||||
|
||||
if ("priority" in aOptions && aOptions.priority != null)
|
||||
this._priority = aOptions.priority;
|
||||
|
||||
if ("buttons" in aOptions && aOptions.buttons != null) {
|
||||
if (aOptions.buttons.length > 3)
|
||||
throw "Too many buttons provided. The max number is 3";
|
||||
|
||||
this._buttons = {};
|
||||
for (let i = 0; i < aOptions.buttons.length; i++) {
|
||||
let button_id = aOptions.buttons[i].buttonId;
|
||||
this._buttons[button_id] = aOptions.buttons[i];
|
||||
}
|
||||
} else {
|
||||
this._buttons = null;
|
||||
}
|
||||
|
||||
if ("ongoing" in aOptions && aOptions.ongoing != null)
|
||||
this._ongoing = aOptions.ongoing;
|
||||
else
|
||||
this._ongoing = false;
|
||||
|
||||
if ("progress" in aOptions && aOptions.progress != null)
|
||||
this._progress = aOptions.progress;
|
||||
else
|
||||
this._progress = null;
|
||||
|
||||
if ("onCancel" in aOptions && aOptions.onCancel != null)
|
||||
this._onCancel = aOptions.onCancel;
|
||||
else
|
||||
this._onCancel = null;
|
||||
|
||||
if ("onClick" in aOptions && aOptions.onClick != null)
|
||||
this._onClick = aOptions.onClick;
|
||||
else
|
||||
this._onClick = null;
|
||||
|
||||
if ("cookie" in aOptions && aOptions.cookie != null)
|
||||
this._cookie = aOptions.cookie;
|
||||
else
|
||||
this._cookie = null;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
let msg = {
|
||||
type: "Notification:Show",
|
||||
id: this._id,
|
||||
title: this._title,
|
||||
smallIcon: this._icon,
|
||||
ongoing: this._ongoing,
|
||||
when: this._when
|
||||
};
|
||||
|
||||
if (this._message)
|
||||
msg.text = this._message;
|
||||
|
||||
if (this._progress) {
|
||||
msg.progress_value = this._progress;
|
||||
msg.progress_max = 100;
|
||||
}
|
||||
|
||||
if (this._priority)
|
||||
msg.priority = this._priority;
|
||||
|
||||
if (this._buttons) {
|
||||
msg.actions = [];
|
||||
let buttonName;
|
||||
for (buttonName in this._buttons) {
|
||||
let button = this._buttons[buttonName];
|
||||
let obj = {
|
||||
buttonId: button.buttonId,
|
||||
title : button.title,
|
||||
icon : button.icon
|
||||
};
|
||||
msg.actions.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
Services.androidBridge.handleGeckoMessage(JSON.stringify(msg));
|
||||
return this;
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
let msg = {
|
||||
type: "Notification:Hide",
|
||||
id: this._id
|
||||
};
|
||||
Services.androidBridge.handleGeckoMessage(JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
|
||||
var Notifications = {
|
||||
_initObserver: function() {
|
||||
if (!this._observerAdded) {
|
||||
Services.obs.addObserver(this, "Notification:Event", true);
|
||||
this._observerAdded = true;
|
||||
}
|
||||
},
|
||||
|
||||
get idService() {
|
||||
delete this.idService;
|
||||
return this.idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
},
|
||||
|
||||
create: function notif_notify(aOptions) {
|
||||
this._initObserver();
|
||||
let id = this.idService.generateUUID().toString();
|
||||
let notification = new Notification(id, aOptions);
|
||||
_notificationsMap[id] = notification;
|
||||
notification.show();
|
||||
return id;
|
||||
},
|
||||
|
||||
update: function notif_update(aId, aOptions) {
|
||||
let notification = _notificationsMap[aId];
|
||||
if (!notification)
|
||||
throw "Unknown notification id";
|
||||
notification.fillWithOptions(aOptions);
|
||||
notification.show();
|
||||
},
|
||||
|
||||
cancel: function notif_cancel(aId) {
|
||||
let notification = _notificationsMap[aId];
|
||||
if (notification)
|
||||
notification.cancel();
|
||||
},
|
||||
|
||||
observe: function notif_observe(aSubject, aTopic, aData) {
|
||||
let data = JSON.parse(aData);
|
||||
let id = data.id;
|
||||
let notification = _notificationsMap[id];
|
||||
if (!notification) {
|
||||
Services.console.logStringMessage("Notifications.jsm observe: received unknown event id " + id);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.eventType) {
|
||||
case "notification-clicked":
|
||||
if (notification._onClick)
|
||||
notification._onClick(id, notification._cookie);
|
||||
break;
|
||||
case "notification-button-clicked": {
|
||||
if (!notification._buttons) {
|
||||
Services.console.logStringMessage("Notifications.jsm: received button clicked event but no buttons are available");
|
||||
break;
|
||||
}
|
||||
|
||||
let button = notification._buttons[data.buttonId];
|
||||
if (button)
|
||||
button.onClicked(id, notification._cookie);
|
||||
}
|
||||
break;
|
||||
case "notification-cleared":
|
||||
case "notification-closed":
|
||||
if (notification._onCancel)
|
||||
notification._onCancel(id, notification._cookie);
|
||||
delete _notificationsMap[id]; // since the notification was dismissed, we no longer need to hold a reference.
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: function (aIID) {
|
||||
if (!aIID.equals(Ci.nsISupports) &&
|
||||
!aIID.equals(Ci.nsIObserver) &&
|
||||
!aIID.equals(Ci.nsISupportsWeakReference))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ EXTRA_JS_MODULES += [
|
||||
'Home.jsm',
|
||||
'JNI.jsm',
|
||||
'LightweightThemeConsumer.jsm',
|
||||
'Notifications.jsm',
|
||||
'OrderedBroadcast.jsm',
|
||||
'Prompt.jsm',
|
||||
'Sanitizer.jsm',
|
||||
|
Loading…
Reference in New Issue
Block a user