VulpesOS/gaia/tv_apps/smart-system/js/remote_control.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

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));