Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
183 lines
5.2 KiB
JavaScript
Executable file
183 lines
5.2 KiB
JavaScript
Executable file
/* global SettingsListener, KeyboardManager, InteractiveNotifications */
|
|
'use strict';
|
|
|
|
(function(exports) {
|
|
var _ = navigator.mozL10n.get;
|
|
|
|
var SETTINGS = 'remote-control.enabled';
|
|
|
|
var WATCHED_EVENTS = [
|
|
'mozChromeRemoteControlEvent',
|
|
'focuschanged'
|
|
];
|
|
|
|
var CURSOR_MODE_LIST = [
|
|
'app://browser.gaiamobile.org/manifest.webapp'
|
|
];
|
|
|
|
function RemoteControl() {
|
|
this._enabled = false;
|
|
this._activePIN = null;
|
|
this._isCursorMode = null;
|
|
this._settingsObserver = null;
|
|
}
|
|
|
|
RemoteControl.prototype = {
|
|
start: function() {
|
|
this._settingsObserver = this._setEnable.bind(this);
|
|
SettingsListener.observe(SETTINGS, false, this._settingsObserver);
|
|
|
|
WATCHED_EVENTS.forEach((event) => {
|
|
window.addEventListener(event, this);
|
|
});
|
|
},
|
|
|
|
stop: function() {
|
|
WATCHED_EVENTS.forEach((event) => {
|
|
window.removeEventListener(event, this);
|
|
});
|
|
|
|
SettingsListener.unobserve(SETTINGS, this._settingsObserver);
|
|
this._settingsObserver = null;
|
|
|
|
this._setEnable(false);
|
|
this._isCursorMode = null;
|
|
},
|
|
|
|
handleEvent: function(evt) {
|
|
switch(evt.type) {
|
|
case 'focuschanged':
|
|
var topMost = evt.detail.topMost;
|
|
var isCursorMode;
|
|
if (topMost.CLASS_NAME == 'AppWindow') {
|
|
isCursorMode = CURSOR_MODE_LIST.includes(topMost.manifestURL);
|
|
} else {
|
|
isCursorMode = false;
|
|
}
|
|
this._fireControlModeChanged(isCursorMode);
|
|
break;
|
|
case 'mozChromeRemoteControlEvent':
|
|
if (this._enabled) {
|
|
this._handleRemoteControlEvent(evt.detail);
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
_setEnable: function(enabled) {
|
|
this._enabled = enabled;
|
|
},
|
|
|
|
_handleRemoteControlEvent: function(detail) {
|
|
switch(detail.action) {
|
|
case 'request-control-mode':
|
|
// In case the server is, for some reason, initialized or rebooted
|
|
// after system app is loaded, it can retrieve the current control
|
|
// mode immediately by utilizing this event instead of waiting for the
|
|
// next "control-mode-changed" event triggered.
|
|
this._fireControlModeChanged(this._isCursorMode, true);
|
|
break;
|
|
case 'grant-input':
|
|
// InputFrameManager needs to deactivate the currently-active input
|
|
// frame first and then the system app can be set an active input
|
|
// frame by the server. It will be resumed as soon as the input
|
|
// procedure is finished.
|
|
KeyboardManager.inputFrameManager.pauseTemporarily(detail.value);
|
|
break;
|
|
case 'input-string':
|
|
this._inputString(detail);
|
|
break;
|
|
case 'pin-created':
|
|
this._createPIN(detail.pincode);
|
|
break;
|
|
case 'pin-destroyed':
|
|
this._destroyPIN();
|
|
break;
|
|
}
|
|
},
|
|
|
|
_inputString: function(detail) {
|
|
var mozIM = navigator.mozInputMethod;
|
|
var inputcontext = mozIM.inputcontext;
|
|
if (inputcontext) {
|
|
if (detail.clear) {
|
|
var lengthBeforeCursor = inputcontext.textBeforeCursor.length;
|
|
var lengthAfterCursor = inputcontext.textAfterCursor.length;
|
|
inputcontext.deleteSurroundingText(
|
|
-1 * lengthBeforeCursor,
|
|
lengthBeforeCursor + lengthAfterCursor
|
|
);
|
|
}
|
|
|
|
if (detail.string) {
|
|
inputcontext.setComposition(detail.string);
|
|
inputcontext.endComposition(detail.string);
|
|
}
|
|
|
|
if (detail.keycode) {
|
|
inputcontext.sendKey(detail.keycode);
|
|
}
|
|
}
|
|
},
|
|
|
|
_createPIN: function(pincode) {
|
|
if (this._activePIN) {
|
|
this._destroyPIN();
|
|
}
|
|
this._activePIN = {
|
|
title: _('pairing-code-notification-title'),
|
|
text: pincode,
|
|
icon: null,
|
|
timeout: 30000,
|
|
buttons: [{
|
|
id: 'dismiss',
|
|
label: _('dismiss-pairing-code')
|
|
}],
|
|
onClosed: (button) => {
|
|
if (button == 'dismiss') {
|
|
this._firePINDismissed('manually');
|
|
} else if (this._activePIN) {
|
|
this._firePINDismissed('timeout');
|
|
}
|
|
this._activePIN = null;
|
|
}
|
|
};
|
|
window.interactiveNotifications.showNotification(
|
|
InteractiveNotifications.TYPE.ALERT, this._activePIN);
|
|
},
|
|
|
|
_destroyPIN: function() {
|
|
var activePIN = this._activePIN;
|
|
this._activePIN = null;
|
|
window.interactiveNotifications.hideNotification(
|
|
InteractiveNotifications.TYPE.ALERT, activePIN);
|
|
},
|
|
|
|
_firePINDismissed: function(reason) {
|
|
this._sendContentEvent('remote-control-pin-dismissed', {
|
|
reason: reason
|
|
});
|
|
},
|
|
|
|
_fireControlModeChanged: function(isCursorMode, fireAnyway) {
|
|
if (!fireAnyway && isCursorMode === this._isCursorMode) {
|
|
return;
|
|
}
|
|
this._isCursorMode = isCursorMode;
|
|
this._sendContentEvent('control-mode-changed', {
|
|
cursor: isCursorMode
|
|
});
|
|
},
|
|
|
|
_sendContentEvent: function(type, detail) {
|
|
window.dispatchEvent(new CustomEvent('mozContentEvent', {
|
|
detail: {
|
|
type: type,
|
|
detail: detail
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
|
|
exports.RemoteControl = RemoteControl;
|
|
}(window));
|