Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
320 lines
9.6 KiB
JavaScript
320 lines
9.6 KiB
JavaScript
'use strict';
|
|
/* global ActionMenu, BaseModule, applications,
|
|
ManifestHelper, DefaultActivityHelper, LazyLoader */
|
|
|
|
(function() {
|
|
|
|
|
|
var Activities = function Activities() {
|
|
this.actionMenu = null;
|
|
};
|
|
|
|
Activities.EVENTS = [
|
|
'mozChromeEvent',
|
|
'appopened',
|
|
'lockscreen-appopened',
|
|
'home',
|
|
'applicationinstall'
|
|
];
|
|
|
|
/**
|
|
* Handles relaying of information for web activities.
|
|
* Contains code to display the list of valid activities,
|
|
* and fires an event off when the user selects one.
|
|
* @class Activities
|
|
*/
|
|
|
|
BaseModule.create(Activities, {
|
|
/** @lends Activities */
|
|
|
|
name: 'Activities',
|
|
|
|
_start: function() {
|
|
return LazyLoader.load([
|
|
'shared/js/default_activity_helper.js',
|
|
'shared/js/settings_helper.js'
|
|
]);
|
|
},
|
|
|
|
/**
|
|
* Remove all event listeners. This is mainly used in unit tests.
|
|
*/
|
|
destroy: function() {
|
|
window.removeEventListener('mozChromeEvent', this);
|
|
window.removeEventListener('appopened', this);
|
|
window.removeEventListener('lockscreen-appopened', this);
|
|
window.removeEventListener('applicationinstall', this);
|
|
},
|
|
|
|
/**
|
|
* General event handler interface.
|
|
* Updates the overlay with as we receive load events.
|
|
* @memberof Activities.prototype
|
|
* @param {DOMEvent} evt The event.
|
|
*/
|
|
handleEvent: function(evt) {
|
|
switch (evt.type) {
|
|
case 'mozChromeEvent':
|
|
var detail = evt.detail;
|
|
switch (detail.type) {
|
|
case 'activity-choice':
|
|
this.chooseActivity(detail);
|
|
break;
|
|
}
|
|
break;
|
|
case 'home':
|
|
case 'lockscreen-appopened':
|
|
if (this._detail) {
|
|
this.cancel();
|
|
}
|
|
break;
|
|
case 'appopened':
|
|
if (this.actionMenu) {
|
|
this.actionMenu.hide();
|
|
}
|
|
break;
|
|
case 'applicationinstall':
|
|
this._onNewAppInstalled(evt.detail.application);
|
|
break;
|
|
}
|
|
},
|
|
|
|
_onNewAppInstalled: function(app) {
|
|
var activities = app && app.manifest && app.manifest.activities;
|
|
if (!activities) {
|
|
return;
|
|
}
|
|
|
|
Object.keys(activities).forEach(function(activity) {
|
|
var filters = activities[activity].filters;
|
|
|
|
// Type can be single value, array, or a definition object (with value)
|
|
var type = filters && filters.type && filters.type.value ||
|
|
filters && filters.type;
|
|
|
|
if (!type) {
|
|
return;
|
|
}
|
|
|
|
if (typeof type === 'string') {
|
|
// single value, change to Array
|
|
type = type.split(',');
|
|
}
|
|
|
|
// Now that it's an array, we check all the elements
|
|
type.forEach((diff) => {
|
|
DefaultActivityHelper.getDefaultAction(activity, diff)
|
|
.then((defApp) => {
|
|
// If default launch app is set for the type
|
|
if (defApp) {
|
|
// Delete the current default app
|
|
DefaultActivityHelper.setDefaultAction(activity, diff, null);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* This gets the index from the defaultChoice in the choices
|
|
* list.
|
|
* @param {Object} defaultChoice The default choice
|
|
* @return {Integer} The index where the default
|
|
* choice is located. -1 if it
|
|
* is not found
|
|
*/
|
|
_choiceFromDefaultAction: function(defaultChoiceManifest, detail) {
|
|
var index = -1;
|
|
if (defaultChoiceManifest) {
|
|
index = detail.choices.findIndex(function(choice) {
|
|
return choice.manifest.indexOf(defaultChoiceManifest) !== -1;
|
|
});
|
|
}
|
|
|
|
return index;
|
|
},
|
|
|
|
/**
|
|
* Displays the activity menu if needed.
|
|
* If there is only one option, the activity is automatically launched.
|
|
* @memberof Activities.prototype
|
|
* @param {Object} detail The activity choose event detail.
|
|
*/
|
|
chooseActivity: function(detail) {
|
|
var name = detail && detail.name;
|
|
var type = detail && detail.activityType;
|
|
this._detail = detail;
|
|
this.publish('activityrequesting');
|
|
|
|
DefaultActivityHelper.getDefaultAction(name, type).then(
|
|
this._gotDefaultAction.bind(this));
|
|
},
|
|
|
|
_gotDefaultAction: function(defaultChoice) {
|
|
var choices = this._detail.choices;
|
|
var index = this._choiceFromDefaultAction(defaultChoice, this._detail);
|
|
if (index > -1) {
|
|
this.choose(index);
|
|
} else if (choices.length === 1) {
|
|
this.choose('0');
|
|
} else {
|
|
//
|
|
// Our OMA Forward Lock DRM implementation relies on a "view"
|
|
// activity to invoke the "fl" app when the user clicks on a
|
|
// link to content with a mime type of
|
|
// "application/vnd.oma.dd+xml" or "application/vnd.oma.drm.message".
|
|
//
|
|
// In order for this to be secure, we need to ensure that the
|
|
// FL app is the only one that can respond to view activity
|
|
// requests for those particular mime types. Here in the System app
|
|
// we don't know what the type associated with an activity request is
|
|
// but we do know the name of the activity. So if this is an activity
|
|
// choice for a "view" activity, and the FL app is one of the choices
|
|
// then we must select the FL app without allowing the user to choose
|
|
// any of the others.
|
|
//
|
|
// If we wanted to be more general here we could perhaps
|
|
// modify this code to allow any certified app to handle the
|
|
// activity, but it is much simpler to restrict to the FL app
|
|
// only.
|
|
//
|
|
if (this._detail.name === 'view') {
|
|
var flAppIndex = choices.findIndex(function(choice) {
|
|
var matchingRegex =
|
|
/^(http|https|app)\:\/\/fl\.gaiamobile\.org\//;
|
|
return matchingRegex.test(choice.manifest);
|
|
});
|
|
if (flAppIndex !== -1) {
|
|
this.choose(flAppIndex.toString(10)); // choose() requires a string
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Since the mozChromeEvent could be triggered by a 'click', and gecko
|
|
// event are synchronous make sure to exit the event loop before
|
|
// showing the list.
|
|
setTimeout((function nextTick() {
|
|
// Bug 852785: force the keyboard to close before the activity menu
|
|
// shows
|
|
window.dispatchEvent(new CustomEvent('activitymenuwillopen'));
|
|
|
|
var name = this._detail.name;
|
|
var type = this._detail.activityType;
|
|
var config = DefaultActivityHelper.getDefaultConfig(name, type);
|
|
|
|
var titleId;
|
|
if (config) {
|
|
titleId = config.l10nId;
|
|
} else {
|
|
titleId = 'activity-' + this._detail.name;
|
|
}
|
|
|
|
var askForDefault = config !== undefined;
|
|
var items = this._listItems(choices);
|
|
|
|
if (!this.actionMenu) {
|
|
var controller = {
|
|
successCb: this.choose.bind(this),
|
|
cancelCb: this.cancel.bind(this)
|
|
};
|
|
|
|
LazyLoader.load('js/action_menu.js', function() {
|
|
this.actionMenu = new ActionMenu(controller);
|
|
this.actionMenu.show(items, titleId, askForDefault);
|
|
}.bind(this));
|
|
} else if (!this.actionMenu.active) {
|
|
this.actionMenu.show(items, titleId, askForDefault);
|
|
}
|
|
}).bind(this));
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The user chooses an activity from the activity menu.
|
|
* @memberof Activities.prototype
|
|
* @param {Number} choice The activity choice.
|
|
* @param {Boolean} setAsDefault Should this be set as the default activity.
|
|
*/
|
|
choose: function(choice, setAsDefault) {
|
|
if (this.actionMenu) {
|
|
this.actionMenu.hide();
|
|
}
|
|
var returnedChoice = {
|
|
id: this._detail.id,
|
|
type: 'activity-choice',
|
|
value: choice,
|
|
setAsDefault: setAsDefault
|
|
};
|
|
var name = this._detail.name;
|
|
var type = this._detail.activityType;
|
|
|
|
if (setAsDefault) {
|
|
DefaultActivityHelper.setDefaultAction(name, type,
|
|
this._detail.choices[choice].manifest);
|
|
}
|
|
|
|
this.publish('activitychoosen');
|
|
this._sendEvent(returnedChoice);
|
|
delete this._detail;
|
|
},
|
|
|
|
/**
|
|
* Cancels from the activity menu.
|
|
* @memberof Activities.prototype
|
|
*/
|
|
cancel: function() {
|
|
if (this.actionMenu) {
|
|
this.actionMenu.hide();
|
|
}
|
|
|
|
var returnedChoice = {
|
|
id: this._detail.id,
|
|
type: 'activity-choice',
|
|
value: -1
|
|
};
|
|
|
|
this._sendEvent(returnedChoice);
|
|
this.publish('activitycanceled');
|
|
delete this._detail;
|
|
},
|
|
|
|
/**
|
|
* Sends an event to the platform when a user makes a choice
|
|
* or cancels the activity menu.
|
|
* @memberof Activities.prototype
|
|
* @param {Number} value The index of the selected activity.
|
|
*/
|
|
_sendEvent: function(value) {
|
|
var event = document.createEvent('CustomEvent');
|
|
event.initCustomEvent('mozContentEvent', true, true, value);
|
|
window.dispatchEvent(event);
|
|
},
|
|
|
|
/**
|
|
* Formats and returns a list of activity choices.
|
|
* @memberof Activities.prototype
|
|
* @param {Array} choices The list of activity choices.
|
|
* @return {Array}
|
|
*/
|
|
_listItems: function(choices) {
|
|
var items = [];
|
|
|
|
choices.forEach(function(choice, index) {
|
|
var app = applications.getByManifestURL(choice.manifest);
|
|
if (!app) {
|
|
return;
|
|
}
|
|
|
|
items.push({
|
|
label: new ManifestHelper(app.manifest).name,
|
|
icon: choice.icon,
|
|
manifest: choice.manifest,
|
|
value: index
|
|
});
|
|
});
|
|
|
|
return items;
|
|
}
|
|
});
|
|
|
|
}());
|