VulpesOS/gaia/tv_apps/smart-system/js/activities.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

199 lines
6.1 KiB
JavaScript
Executable file

'use strict';
/* global ActionMenu, applications, ManifestHelper */
(function(exports) {
/**
* 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
*/
function Activities() {
window.addEventListener('mozChromeEvent', this);
window.addEventListener('appopened', this);
this.actionMenu = null;
}
Activities.prototype = {
HIGH_PRIORITY_ACTIVITY: Object.freeze({
//
// 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.
'view': /^(http|https|app)\:\/\/fl\.gaiamobile\.org\//,
//
// The smart-settings app listens "configure" to show the settings menu
// which is a smei-transparent menu. We need to let it have the highest
// priority to handle the activity and prevent 3rd-party apps to
// intercept this activity.
'configure': /^(http|https|app)\:\/\/smart-settings\.gaiamobile\.org\//
}),
/** @lends Activities */
/**
* 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 'appopened':
if (this.actionMenu) {
this.actionMenu.hide();
}
break;
}
},
/**
* 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) {
this._id = detail.id;
var choices = detail.choices;
this.publish('activityrequesting');
if (choices.length === 1) {
this.choose('0');
} else {
// apply activity filter.
if (this.HIGH_PRIORITY_ACTIVITY[detail.name]) {
var appIndex = choices.findIndex((function(choice) {
var matchingRegex = this.HIGH_PRIORITY_ACTIVITY[detail.name];
return matchingRegex.test(choice.manifest);
}).bind(this));
if (appIndex !== -1) {
this.choose(appIndex.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 activityNameL10nId = 'activity-' + detail.name;
if (!this.actionMenu) {
this.actionMenu = new ActionMenu(this._listItems(choices),
activityNameL10nId, this.choose.bind(this),
this.cancel.bind(this));
this.actionMenu.start();
}
}).bind(this));
}
},
/**
* The user chooses an activity from the activity menu.
* @memberof Activities.prototype
* @param {Number} choice The activity choice.
*/
choose: function(choice) {
this.actionMenu = null;
var returnedChoice = {
id: this._id,
type: 'activity-choice',
value: choice
};
this._sendEvent(returnedChoice);
delete this._id;
},
/**
* Cancels from the activity menu.
* @memberof Activities.prototype
*/
cancel: function() {
this.actionMenu = null;
var returnedChoice = {
id: this._id,
type: 'activity-choice',
value: -1
};
this._sendEvent(returnedChoice);
delete this._id;
},
publish: function(eventName) {
var event = new CustomEvent(eventName);
window.dispatchEvent(event);
},
/**
* 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,
value: index
});
});
return items;
}
};
exports.Activities = Activities;
}(window));