Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
461 lines
14 KiB
JavaScript
Executable file
461 lines
14 KiB
JavaScript
Executable file
/* global CpScreenHelper, DUMP, MessageDB, Notification, NotificationHelper,
|
|
ParsedMessage, Promise, SiSlScreenHelper, WhiteList, Utils */
|
|
|
|
/* exported WapPushManager */
|
|
|
|
(function(exports) {
|
|
'use strict';
|
|
|
|
/**
|
|
* Handles incoming WAP Push messages and contains the functionality required
|
|
* to post notifications and display their contents
|
|
*/
|
|
var WapPushManager = {
|
|
init: wpm_init,
|
|
close: wpm_close,
|
|
displayWapPushMessage: wpm_displayWapPushMessage,
|
|
onVisibilityChange: wpm_onVisibilityChange,
|
|
onWapPushReceived: wpm_onWapPushReceived
|
|
};
|
|
|
|
/** Settings key for enabling/disabling WAP Push messages */
|
|
var wapPushEnableKey = 'wap.push.enabled';
|
|
|
|
/** Enable/disable WAP Push notifications */
|
|
var wapPushEnabled;
|
|
|
|
/** A reference to the app's object */
|
|
var app;
|
|
|
|
/** Timer used to schedule a close operation */
|
|
var closeTimeout;
|
|
|
|
/**
|
|
* Number of messages that have been received but haven't been fully
|
|
* processed yet
|
|
*/
|
|
var pendingMessages;
|
|
|
|
/** Currently displayed SI message (null for SL and CP messages) */
|
|
var displayedSiMessage;
|
|
|
|
/**
|
|
* Returns a promise used to retrieve the app's own object.
|
|
*
|
|
* @return {Object} A promise for the app object.
|
|
*/
|
|
function wpm_getApp() {
|
|
return new Promise(function(resolve, reject) {
|
|
var req = navigator.mozApps.getSelf();
|
|
|
|
req.onsuccess = function wpm_gotApp() {
|
|
resolve(this.result);
|
|
};
|
|
req.onerror = function wpm_getAppError() {
|
|
reject(this.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns a promise used to retrieve the configuration of the app.
|
|
*
|
|
* @return {Object} A promise for the app configuration.
|
|
*/
|
|
function wpm_getConfig() {
|
|
return new Promise(function(resolve, reject) {
|
|
var req = navigator.mozSettings.createLock().get(wapPushEnableKey);
|
|
|
|
req.onsuccess = function wpm_settingsLockSuccess() {
|
|
resolve(this.result[wapPushEnableKey]);
|
|
};
|
|
req.onerror = function wpm_settingsLockError() {
|
|
reject(this.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the WAP Push manager, this only subscribes to the
|
|
* wappush-received message handler at the moment.
|
|
*
|
|
* @return {Object} A promise that will be fullfilled once the component has
|
|
* been fully initialized.
|
|
*/
|
|
function wpm_init() {
|
|
// Reset the internal state to default values.
|
|
wapPushEnabled = true;
|
|
app = null;
|
|
closeTimeout = null;
|
|
pendingMessages = 0;
|
|
displayedSiMessage = null;
|
|
|
|
// Listen to settings changes right away
|
|
navigator.mozSettings.addObserver(wapPushEnableKey, wpm_onSettingsChange);
|
|
|
|
// Get the app object and configuration
|
|
var promise = Promise.all([
|
|
wpm_getApp(), wpm_getConfig(), WhiteList.init()
|
|
]);
|
|
|
|
promise = promise.then(function(values) {
|
|
app = values[0];
|
|
wapPushEnabled = values[1];
|
|
|
|
// Init screen helpers
|
|
SiSlScreenHelper.init();
|
|
CpScreenHelper.init();
|
|
|
|
// Register event and message handlers only after initialization is done
|
|
MessageDB.on('new', wpm_onNew);
|
|
MessageDB.on('update', wpm_onUpdate);
|
|
MessageDB.on('discard', wpm_onDiscard);
|
|
MessageDB.on('delete', wpm_onDelete);
|
|
|
|
document.addEventListener('visibilitychange', wpm_onVisibilityChange);
|
|
window.navigator.mozSetMessageHandler('notification', wpm_onNotification);
|
|
window.navigator.mozSetMessageHandler('wappush-received',
|
|
wpm_onWapPushReceived);
|
|
}).catch(function(error) {
|
|
// If we encountered an error don't process messages
|
|
wapPushEnabled = false;
|
|
var message = error.message || 'Unknown error';
|
|
console.error('Could not initialize the WAP push manager: ', message);
|
|
throw error;
|
|
});
|
|
|
|
return promise;
|
|
}
|
|
|
|
/**
|
|
* Handler invoked when the 'wap.push.enabled' option changes.
|
|
*
|
|
* @param {Object} A settings object holding the new value.
|
|
*/
|
|
function wpm_onSettingsChange(v) {
|
|
wapPushEnabled = v.settingValue;
|
|
}
|
|
|
|
/**
|
|
* Closes the application whenever it is hidden
|
|
*/
|
|
function wpm_onVisibilityChange() {
|
|
if (document.hidden) {
|
|
wpm_close();
|
|
} else {
|
|
window.clearTimeout(closeTimeout);
|
|
closeTimeout = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Establish if we must show this message or not; the message is shown only
|
|
* if WAP Push functionality is enabled, the sender's MSISDN is whitelisted
|
|
* or whitelisting is disabled
|
|
*
|
|
* @param {Object} message A parsed WAP Push message.
|
|
*
|
|
* @return {Boolean} true if the message should be displayed, false otherwise.
|
|
*/
|
|
function wpm_shouldDisplayMessage(message) {
|
|
if (!wapPushEnabled || (message === null)) {
|
|
/* WAP push functionality is either completely disabled, ignore it. */
|
|
return false;
|
|
}
|
|
|
|
if ((message.type !== 'text/vnd.wap.connectivity-xml') &&
|
|
(!WhiteList.has(message.sender) || message.isExpired())) {
|
|
/* The message isn't a provisioning message and comes from a non
|
|
* white-listed MSISDN or it has already been expired, ignore it. */
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Send a notification for the provided message
|
|
*
|
|
* @param {Object} message The message the user needs to be notified of
|
|
*
|
|
* @return {Object} A promise that is resolved once the notification has been
|
|
* sent and its events hooked up properly.
|
|
*/
|
|
function wpm_sendNotification(message) {
|
|
var iconURL = NotificationHelper.getIconURI(app);
|
|
|
|
/* Build the notification's text, for text/vnd.wap.connectivity-xml
|
|
* messages this needs to be localized. */
|
|
var body;
|
|
|
|
if (message.type === 'text/vnd.wap.connectivity-xml') {
|
|
body = message.text; // The text will be localized
|
|
} else {
|
|
body = {
|
|
id: (message.type === 'text/vnd.wap.si') ? 'si-message-body'
|
|
: 'sl-message-body',
|
|
args: {
|
|
text: message.text || '', // The text won't be localized
|
|
url: message.href
|
|
}
|
|
};
|
|
}
|
|
|
|
var title = Utils.prepareMessageTitle(message);
|
|
|
|
var options = {
|
|
icon: iconURL,
|
|
bodyL10n: body,
|
|
tag: message.getUniqueId()
|
|
};
|
|
|
|
return NotificationHelper.send(title, options)
|
|
.then(function(notification) {
|
|
notification.addEventListener('click',
|
|
function wpm_onNotificationClick(event) {
|
|
wpm_displayWapPushMessage(event.target.tag);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handler for the wappush-received system messages, stores the message into
|
|
* the internal database and posts a notification which can be used to
|
|
* display the message. This method returns a promise which is currently used
|
|
* only when testing. It is also exposed for testing reasons but shouldn't be
|
|
* used outside of this file.
|
|
*
|
|
* @param {Object} wapMessage The WAP Push message as provided by the system.
|
|
*
|
|
* @return {Object} A promise that is resolved when the method executed
|
|
* successfully or rejected with the error code that caused the
|
|
* method to fail.
|
|
*/
|
|
function wpm_onWapPushReceived(wapMessage) {
|
|
DUMP('Received a message: ', wapMessage);
|
|
pendingMessages++;
|
|
|
|
var message = ParsedMessage.from(wapMessage, Date.now());
|
|
|
|
if (!wpm_shouldDisplayMessage(message)) {
|
|
DUMP('The message will not be displayed');
|
|
wpm_finish();
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return message.save().catch(error => {
|
|
var message = error.message || 'Unknown error';
|
|
console.log('Could not add a message to the database: ' + message + '\n');
|
|
wpm_finish();
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Displays the contents of a WAP Push message
|
|
*
|
|
* @param {Object} message The notification event.
|
|
*/
|
|
function wpm_onNotification(message) {
|
|
if (!message.clicked) {
|
|
return;
|
|
}
|
|
|
|
wpm_displayWapPushMessage(message.tag);
|
|
}
|
|
|
|
/**
|
|
* Reacts to a new message being added to the database by showing a
|
|
* notification or directly showing the message if required by the action
|
|
* field.
|
|
*
|
|
* @param {Object} obj The new message in untyped form (i.e. JSON)
|
|
*/
|
|
function wpm_onNew(obj) {
|
|
DUMP('A new message added to the DB');
|
|
|
|
var message = new ParsedMessage(obj);
|
|
|
|
if (message.action === 'signal-high' ||
|
|
message.action === 'execute-high')
|
|
{
|
|
/* We just decrease the number of pending messages instead of invoking
|
|
* wpm_finish() since we don't want to start the close procedure. */
|
|
pendingMessages--;
|
|
wpm_displayWapPushMessage(message.getUniqueId());
|
|
} else {
|
|
wpm_sendNotification(message).then(() => wpm_finish());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reacts to a message being updated in the database by updating the
|
|
* notification and possibly the message being shown to the user.
|
|
*
|
|
* @param {Object} obj The new message in untyped form (i.e. JSON)
|
|
*/
|
|
function wpm_onUpdate(obj) {
|
|
DUMP('A message was updated in the DB');
|
|
|
|
var message = new ParsedMessage(obj);
|
|
|
|
// Update the message if we're currently displaying it
|
|
if (displayedSiMessage &&
|
|
displayedSiMessage.getUniqueId() === message.getUniqueId()) {
|
|
displayedSiMessage = message;
|
|
SiSlScreenHelper.populateScreen(message);
|
|
}
|
|
|
|
// This updates the existing notification
|
|
wpm_sendNotification(message).then(() => wpm_finish());
|
|
}
|
|
|
|
/**
|
|
* Removes the notifications for messages that have been deleted and closes
|
|
* the app if the message being viewed has been deleted.
|
|
*
|
|
* @param {Object} obj The deleted message in untyped form (i.e. JSON)
|
|
*/
|
|
function wpm_onDelete(obj) {
|
|
DUMP('A message was deleted');
|
|
|
|
var message = new ParsedMessage(obj);
|
|
|
|
// Close the application if we're displaying the deleted message
|
|
if (displayedSiMessage &&
|
|
displayedSiMessage.getUniqueId() === message.getUniqueId()) {
|
|
wpm_close();
|
|
}
|
|
|
|
// Remove the notification associated to the deleted message
|
|
wpm_clearNotifications(+message.getUniqueId());
|
|
}
|
|
|
|
/**
|
|
* Prints a debug statement when a message has been discarded, no further
|
|
* action required.
|
|
*/
|
|
function wpm_onDiscard() {
|
|
DUMP('The message was discarded');
|
|
wpm_finish();
|
|
}
|
|
|
|
/**
|
|
* Retrieves a WAP Push message from the database and displays it
|
|
*
|
|
* @param {String} id The message id as a string.
|
|
* @return {Promise} A promise that resolves once the message has been
|
|
* displayed and the associated notification cleared.
|
|
*/
|
|
function wpm_displayWapPushMessage(id) {
|
|
DUMP('Displaying message ' + id);
|
|
|
|
// Clear the close timer as the application will soon become visible
|
|
app.launch();
|
|
window.clearTimeout(closeTimeout);
|
|
closeTimeout = null;
|
|
|
|
return ParsedMessage.load(id).then(
|
|
function wpm_loadResolved(message) {
|
|
switch (message.type) {
|
|
case 'text/vnd.wap.si':
|
|
displayedSiMessage = message;
|
|
CpScreenHelper.hide();
|
|
|
|
if (message.isExpired()) {
|
|
// Notify the user that the message has expired
|
|
SiSlScreenHelper.populateScreen();
|
|
} else {
|
|
SiSlScreenHelper.populateScreen(message);
|
|
}
|
|
|
|
return wpm_clearNotifications(id);
|
|
|
|
case 'text/vnd.wap.sl':
|
|
displayedSiMessage = null;
|
|
CpScreenHelper.hide();
|
|
SiSlScreenHelper.populateScreen(message);
|
|
return wpm_clearNotifications(id);
|
|
|
|
case 'text/vnd.wap.connectivity-xml':
|
|
displayedSiMessage = null;
|
|
SiSlScreenHelper.hide();
|
|
CpScreenHelper.showConfirmInstallationDialog(message);
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
).catch(function(error) {
|
|
var message = error.message || 'Unknown error';
|
|
console.error('Could not retrieve the message: ', message);
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove notifications for a given tag.
|
|
*
|
|
* @return {Promise} A promise that gets resolved once the notifications have
|
|
* been cleared and rejects in case of error.
|
|
*/
|
|
function wpm_clearNotifications(tag) {
|
|
return Notification.get({tag: tag}).then(
|
|
function onSuccess(notifications) {
|
|
for (var i = 0; i < notifications.length; i++) {
|
|
notifications[i].close();
|
|
}
|
|
},
|
|
function onError(error) {
|
|
var message = error.message || 'Unknown error';
|
|
console.error('Could not get notification: ', message);
|
|
throw error;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Marks a message as processed and close the application if no more messages
|
|
* are present and the application is not visible.
|
|
*/
|
|
function wpm_finish() {
|
|
pendingMessages--;
|
|
|
|
if (document.hidden) {
|
|
wpm_close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the application. Whenever this function is called it
|
|
* starts a timer that will eventually close the application when no more
|
|
* messages are being processed. Calling close multiple times is safe as only
|
|
* one timer can be active at a time so subsequent calls will effectively be
|
|
* no-ops.
|
|
*/
|
|
function wpm_close() {
|
|
if (closeTimeout !== null) {
|
|
// We're already trying to close the app
|
|
return;
|
|
}
|
|
|
|
/* We do not close the app immediately, instead we spin the event loop once
|
|
* in the hope of catching pending messages that have been sent to the app
|
|
* and have not yet been processed. If some messages are pending we'll
|
|
* reschedule the close until they've all been processed. */
|
|
closeTimeout = window.setTimeout(function wpm_delayedClose() {
|
|
if (pendingMessages > 0) {
|
|
// Pending messages were received since we set this timer
|
|
closeTimeout = window.setTimeout(wpm_delayedClose, 100);
|
|
return;
|
|
}
|
|
|
|
/* There's no more pending messages and the application is in the
|
|
* background, close for real */
|
|
DUMP('Automatically closing the application');
|
|
closeTimeout = null;
|
|
window.close();
|
|
}, 100);
|
|
}
|
|
|
|
exports.WapPushManager = WapPushManager;
|
|
})(this);
|