Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
260 lines
7 KiB
JavaScript
260 lines
7 KiB
JavaScript
/* global ActionMenu, BaseModule, LazyLoader, BroadcastChannel */
|
|
'use strict';
|
|
|
|
(function() {
|
|
var MultiScreenController = function() {};
|
|
MultiScreenController.SERVICES = [
|
|
'chooseDisplay'
|
|
];
|
|
MultiScreenController.EVENTS = [
|
|
'mozChromeEvent'
|
|
];
|
|
MultiScreenController.SETTINGS = [
|
|
'multiscreen.enabled'
|
|
];
|
|
MultiScreenController.STATES = [
|
|
'enabled'
|
|
];
|
|
BaseModule.create(MultiScreenController, {
|
|
name: 'MultiScreenController',
|
|
|
|
EVENT_PREFIX: 'remote-',
|
|
DEBUG: false,
|
|
|
|
enabled: function() {
|
|
return this._enabled;
|
|
},
|
|
chooseDisplay: function(config) {
|
|
this.debug('chooseDisplay is invoked');
|
|
|
|
if (!this._enabled) {
|
|
this.debug('multi-screen is disabled');
|
|
return Promise.reject();
|
|
}
|
|
|
|
if (config.isSystemMessage || config.stayBackground) {
|
|
this.debug('unsupported config: ' + JSON.stringify(config));
|
|
return Promise.reject();
|
|
}
|
|
|
|
return this.queryExternalDisplays()
|
|
.then(this.showMenu.bind(this))
|
|
.then((displayId) => {
|
|
this.debug('chosen display id: ' + displayId);
|
|
|
|
if (!displayId) {
|
|
return Promise.reject();
|
|
}
|
|
|
|
this.postMessage(displayId, 'launch-app', config);
|
|
return Promise.resolve(displayId);
|
|
});
|
|
},
|
|
choosePresentationDevice: function() {
|
|
this.debug('chooseDisplay for presentation device selection');
|
|
|
|
var deviceList = [];
|
|
return this.queryPresentationDevices()
|
|
.then((list) => {
|
|
deviceList = list.map((device, idx) => {
|
|
return {
|
|
name: device.name,
|
|
deviceId: device.id,
|
|
id: idx,
|
|
};
|
|
});
|
|
return Promise.resolve(deviceList);
|
|
})
|
|
.then(this.showMenu.bind(this))
|
|
.then((displayId) => {
|
|
this.debug('chosen display id: ' + displayId);
|
|
|
|
if (isNaN(displayId)) {
|
|
return Promise.reject();
|
|
}
|
|
return Promise.resolve(deviceList.find((device) => {
|
|
return device.id == displayId;
|
|
}).deviceId);
|
|
});
|
|
},
|
|
showMenu: function(displays) {
|
|
this.debug('showMenu is invoked');
|
|
|
|
if (this.actionMenu) {
|
|
this.debug('actionMenu is busy');
|
|
return Promise.reject();
|
|
}
|
|
|
|
if (!displays.length) {
|
|
this.debug('no external display so cancel the menu directly');
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
LazyLoader.load('js/action_menu.js', () => {
|
|
this.actionMenu = new ActionMenu({
|
|
successCb: (choice) => {
|
|
this.actionMenu = null;
|
|
resolve(choice);
|
|
},
|
|
cancelCb: () => {
|
|
this.actionMenu = null;
|
|
resolve();
|
|
}
|
|
});
|
|
|
|
this.actionMenu.show(displays.map(function(display) {
|
|
return {
|
|
label: display.name,
|
|
value: display.id
|
|
};
|
|
}), 'multiscreen-pick');
|
|
});
|
|
});
|
|
},
|
|
queryExternalDisplays: function() {
|
|
this.debug('queryExternalDisplays is invoked');
|
|
|
|
if (this.queryPromiseCallback) {
|
|
this.debug('there\'s a pending query');
|
|
return Promise.reject();
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
this.debug('sending mozContentEvent');
|
|
|
|
this.queryPromiseCallback = {
|
|
resolve: resolve,
|
|
reject: reject
|
|
};
|
|
|
|
window.dispatchEvent(new CustomEvent('mozContentEvent', {
|
|
detail: {
|
|
type: 'get-display-list'
|
|
}
|
|
}));
|
|
});
|
|
},
|
|
queryPresentationDevices: function() {
|
|
this.debug('queryPresentationDevices is invoked');
|
|
|
|
return navigator.mozPresentationDeviceInfo.getAll();
|
|
},
|
|
postMessage: function(target, type, detail) {
|
|
this.debug('broadcast message to #' + target + ': ' +
|
|
type + ', ' + JSON.stringify(detail));
|
|
|
|
this.broadcastChannel.postMessage({
|
|
target: target,
|
|
type: type,
|
|
detail: detail
|
|
});
|
|
},
|
|
_start: function() {
|
|
this._enabled = false;
|
|
this.actionMenu = null;
|
|
this.queryPromiseCallback = null;
|
|
|
|
this.broadcastChannel = new BroadcastChannel('multiscreen');
|
|
this.broadcastChannel.addEventListener('message', this);
|
|
},
|
|
_stop: function() {
|
|
this._enabled = false;
|
|
|
|
this.broadcastChannel.close();
|
|
this.broadcastChannel = null;
|
|
|
|
if (this.queryPromiseCallback) {
|
|
this.queryPromiseCallback.reject('module has been stoped');
|
|
this.queryPromiseCallback = null;
|
|
}
|
|
if (this.actionMenu) {
|
|
this.actionMenu.hide();
|
|
if (this.actionMenu.oncancel) {
|
|
this.actionMenu.oncancel();
|
|
}
|
|
this.actionMenu = null;
|
|
}
|
|
},
|
|
_handle_mozChromeEvent: function(evt) {
|
|
var detail = evt.detail;
|
|
|
|
switch (detail.type) {
|
|
case 'presentation-select-device':
|
|
this._presentationDeviceSelectionHandler(detail);
|
|
break;
|
|
|
|
case 'get-display-list-success':
|
|
case 'get-display-list-error':
|
|
this._displayListResultHandler(detail);
|
|
break;
|
|
}
|
|
},
|
|
_presentationDeviceSelectionHandler: function(detail) {
|
|
this.debug('handle presentation-select-device event');
|
|
|
|
this.choosePresentationDevice(null)
|
|
.then((deviceId) => {
|
|
window.dispatchEvent(new CustomEvent('mozContentEvent', {
|
|
detail: {
|
|
'type': 'presentation-select-result',
|
|
'deviceId': deviceId,
|
|
'id': detail.id,
|
|
}
|
|
}));
|
|
}).catch(() => {
|
|
window.dispatchEvent(new CustomEvent('mozContentEvent', {
|
|
detail: {
|
|
'type': 'presentation-select-deny',
|
|
'id': detail.id
|
|
}
|
|
}));
|
|
});
|
|
},
|
|
_displayListResultHandler: function(detail) {
|
|
if (!this.queryPromiseCallback) {
|
|
return;
|
|
}
|
|
|
|
switch (detail.type) {
|
|
case 'get-display-list-success':
|
|
this.queryPromiseCallback.resolve(detail.display);
|
|
break;
|
|
case 'get-display-list-error':
|
|
this.queryPromiseCallback.reject(detail.error);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
this.queryPromiseCallback = null;
|
|
this.debug('got mozChromeEvent: ' + JSON.stringify(detail));
|
|
},
|
|
_handle_message: function(evt) {
|
|
var data = evt.data;
|
|
if (data.target !== undefined) {
|
|
return;
|
|
}
|
|
this.debug('got message from #' + data.source + ': ' +
|
|
data.type + ', ' + JSON.stringify(data.detail));
|
|
|
|
switch(data.type) {
|
|
case 'launch-app-success':
|
|
case 'launch-app-error':
|
|
this.publish(data.type, {
|
|
displayId: data.source,
|
|
config: data.detail.config,
|
|
reason: data.detail.reason
|
|
});
|
|
break;
|
|
}
|
|
},
|
|
'_observe_multiscreen.enabled': function(value) {
|
|
if (value == this._enabled) {
|
|
return;
|
|
}
|
|
|
|
this._enabled = value;
|
|
}
|
|
});
|
|
}());
|