diff --git a/shared/js/smart-screen/key_navigation_adapter.js b/shared/js/smart-screen/key_navigation_adapter.js index e346b586b312..b0b50d492e05 100644 --- a/shared/js/smart-screen/key_navigation_adapter.js +++ b/shared/js/smart-screen/key_navigation_adapter.js @@ -66,6 +66,9 @@ case 'enter': this.fire(evtPrefix + 'enter' + evtPostfix); break; + case 'back': + this.fire(evtPrefix + 'back' + evtPostfix); + break; case 'esc': this.fire(evtPrefix + 'esc' + evtPostfix); break; @@ -87,7 +90,7 @@ case KeyEvent.DOM_VK_ESCAPE: return 'esc'; case KeyEvent.DOM_VK_BACK_SPACE: - return 'esc'; + return 'back'; default:// we don't consume other keys. return null; } diff --git a/tv_apps/app-deck/bower_components/smart-dialog/script.js b/tv_apps/app-deck/bower_components/smart-dialog/script.js index 3cbd4e0894e8..f2ddad86dfc5 100644 --- a/tv_apps/app-deck/bower_components/smart-dialog/script.js +++ b/tv_apps/app-deck/bower_components/smart-dialog/script.js @@ -96,10 +96,13 @@ window.SmartDialog = (function(win) { } break; case 'keyup': - // close dialog when ESC is clicked + // close dialog when Back is triggered if (this.getAttribute('esc-close') != 'false' && - (evt.keyCode === 27 || evt.key === 'Esc') && + (evt.keyCode === window.KeyEvent.DOM_VK_BACK_SPACE || + evt.key === 'Backspace') && this.classList.contains('opened')) { + evt.preventDefault(); + evt.stopPropagation(); this.close(); } break; diff --git a/tv_apps/browser/js/settings.js b/tv_apps/browser/js/settings.js index d4ec169aa09e..258c5cba6888 100644 --- a/tv_apps/browser/js/settings.js +++ b/tv_apps/browser/js/settings.js @@ -701,7 +701,6 @@ var Settings = { break; case KeyEvent.DOM_VK_BACK_SPACE : - case KeyEvent.DOM_VK_ESCAPE: if( Settings.isDialogHomepageDisplayed() ) { Settings.hideDialogHomepage(); return true; diff --git a/tv_apps/browser/js/smartList.js b/tv_apps/browser/js/smartList.js index 1eb2d2eb0ce5..f45a931e2dd9 100644 --- a/tv_apps/browser/js/smartList.js +++ b/tv_apps/browser/js/smartList.js @@ -774,8 +774,8 @@ handleListKeyUp: function(e) { switch(e.keyCode){ - case KeyEvent.DOM_VK_ESCAPE: - this.handleKeyEscape(); + case KeyEvent.DOM_VK_BACK_SPACE: + this.handleKeyBackspace(); break; default: return; @@ -892,7 +892,7 @@ } }, - handleKeyEscape: function() { + handleKeyBackspace: function() { if (!this.navState) { this.close(); } else { diff --git a/tv_apps/remote-control-client/client.html b/tv_apps/remote-control-client/client.html index 897c4427ec77..156e761442d6 100644 --- a/tv_apps/remote-control-client/client.html +++ b/tv_apps/remote-control-client/client.html @@ -25,7 +25,7 @@

-
+
diff --git a/tv_apps/remote-control/js/bootstrap.js b/tv_apps/remote-control/js/bootstrap.js index fcfc5829dc4c..58ce5a5d2856 100644 --- a/tv_apps/remote-control/js/bootstrap.js +++ b/tv_apps/remote-control/js/bootstrap.js @@ -10,7 +10,7 @@ window.addEventListener('load', function onLoad() { keyNav.init(); keyNav.on('move', app.handleMove.bind(app)); keyNav.on('enter', app.handleClick.bind(app)); - keyNav.on('esc', app.handleBack.bind(app)); + keyNav.on('back', app.handleBack.bind(app)); Settings.start(); Settings.once('ready', app.show.bind(app)); diff --git a/tv_apps/smart-home/bower_components/smart-dialog/script.js b/tv_apps/smart-home/bower_components/smart-dialog/script.js index 3cbd4e0894e8..f2ddad86dfc5 100644 --- a/tv_apps/smart-home/bower_components/smart-dialog/script.js +++ b/tv_apps/smart-home/bower_components/smart-dialog/script.js @@ -96,10 +96,13 @@ window.SmartDialog = (function(win) { } break; case 'keyup': - // close dialog when ESC is clicked + // close dialog when Back is triggered if (this.getAttribute('esc-close') != 'false' && - (evt.keyCode === 27 || evt.key === 'Esc') && + (evt.keyCode === window.KeyEvent.DOM_VK_BACK_SPACE || + evt.key === 'Backspace') && this.classList.contains('opened')) { + evt.preventDefault(); + evt.stopPropagation(); this.close(); } break; diff --git a/tv_apps/smart-settings/js/settings.js b/tv_apps/smart-settings/js/settings.js index 84fba4722e2b..fe70699a5b67 100644 --- a/tv_apps/smart-settings/js/settings.js +++ b/tv_apps/smart-settings/js/settings.js @@ -193,8 +193,6 @@ return 'left'; case KeyEvent.DOM_VK_RETURN: return 'enter'; - case KeyEvent.DOM_VK_ESCAPE: - return 'esc'; case KeyEvent.DOM_VK_BACK_SPACE: return 'esc'; default:// we don't consume other keys. diff --git a/tv_apps/smart-system/bower_components/smart-dialog/script.js b/tv_apps/smart-system/bower_components/smart-dialog/script.js index 3cbd4e0894e8..f2ddad86dfc5 100644 --- a/tv_apps/smart-system/bower_components/smart-dialog/script.js +++ b/tv_apps/smart-system/bower_components/smart-dialog/script.js @@ -96,10 +96,13 @@ window.SmartDialog = (function(win) { } break; case 'keyup': - // close dialog when ESC is clicked + // close dialog when Back is triggered if (this.getAttribute('esc-close') != 'false' && - (evt.keyCode === 27 || evt.key === 'Esc') && + (evt.keyCode === window.KeyEvent.DOM_VK_BACK_SPACE || + evt.key === 'Backspace') && this.classList.contains('opened')) { + evt.preventDefault(); + evt.stopPropagation(); this.close(); } break; diff --git a/tv_apps/smart-system/fxa/js/fxam_error_overlay.js b/tv_apps/smart-system/fxa/js/fxam_error_overlay.js index 308d7cf2a46d..5bf9bbdb9679 100644 --- a/tv_apps/smart-system/fxa/js/fxam_error_overlay.js +++ b/tv_apps/smart-system/fxa/js/fxam_error_overlay.js @@ -37,7 +37,7 @@ var FxaModuleErrorOverlay = { this.fxaErrorOk.addEventListener('keyup', e => { if (e.keyCode && (e.keyCode === KeyEvent.DOM_VK_RETURN || - e.keyCode === KeyEvent.DOM_VK_ESCAPE)) { + e.keyCode === KeyEvent.DOM_VK_BACK_SPACE)) { this.fxaErrorOk.classList.remove('active'); this.hide(); } @@ -62,7 +62,7 @@ var FxaModuleErrorOverlay = { document.activeElement.blur(); this.fxaErrorOk.focus(); - FxaModuleUI.disableEscapeButton(); + FxaModuleUI.disableBackButton(); }, hide: function fxam_overlay_hide() { @@ -70,7 +70,7 @@ var FxaModuleErrorOverlay = { this.fxaErrorOverlay.classList.remove('show'); FxaModuleKeyNavigation.enable(); - FxaModuleUI.enableEscapeButton(); + FxaModuleUI.enableBackButton(); }, prevent: function fxam_prevent(event) { diff --git a/tv_apps/smart-system/fxa/js/fxam_ui.js b/tv_apps/smart-system/fxa/js/fxam_ui.js index 877c13f32bdf..f3b5249015ad 100644 --- a/tv_apps/smart-system/fxa/js/fxam_ui.js +++ b/tv_apps/smart-system/fxa/js/fxam_ui.js @@ -80,7 +80,7 @@ var FxaModuleUI = { }, OFFLINE_TIMEOUT); }); - this.enableEscapeButton(); + this.enableBackButton(); FxaModuleNavigation.init(flow); }, @@ -197,14 +197,18 @@ var FxaModuleUI = { this.fxaModuleDone.removeAttribute('disabled'); }, onkeypress: function(e) { - if (e.keyCode === KeyEvent.DOM_VK_ESCAPE) { + var inputElementTypes = ['INPUT', 'TEXTAREA']; + var active = document.activeElement; + var isInputElementType = inputElementTypes.indexOf(active.nodeName) !== -1; + if (e.keyCode === KeyEvent.DOM_VK_BACK_SPACE && + (!isInputElementType || (isInputElementType && !active.value))) { FxaModuleNavigation.back(); } }, - disableEscapeButton: function() { + disableBackButton: function() { window.removeEventListener('keypress', this.onkeypress); }, - enableEscapeButton: function() { + enableBackButton: function() { window.addEventListener('keypress', this.onkeypress); }, focusDoneButton: function() { diff --git a/tv_apps/smart-system/js/app_window.js b/tv_apps/smart-system/js/app_window.js index 9f91159a33c6..7ea308456e44 100644 --- a/tv_apps/smart-system/js/app_window.js +++ b/tv_apps/smart-system/js/app_window.js @@ -708,7 +708,7 @@ '_closed']; AppWindow.REGISTERED_GLOBAL_EVENTS = - ['mozbrowserafterkeyup']; + ['back']; AppWindow.SUB_COMPONENTS = { 'transitionController': window.AppTransitionController, @@ -1046,13 +1046,11 @@ }; - AppWindow.prototype._handle_mozbrowserafterkeyup = - function aw__handle_mozbrowserafterkeyup(evt) { + AppWindow.prototype._handle_back = function aw__handle_back(evt) { if (document.activeElement !== this.iframe) { return; } - if ((evt.keyCode === 27 || evt.key === 'Escape') && - !evt.embeddedCancelled && !this.config.url.startsWith('app://')) { + if (!this.config.url.startsWith('app://')) { var goBackReq = this.iframe.getCanGoBack(); goBackReq.onsuccess = () => { if (goBackReq.result) { diff --git a/tv_apps/smart-system/js/browser_key_event_manager.js b/tv_apps/smart-system/js/browser_key_event_manager.js index 4a6dc3213716..6c20850ed311 100644 --- a/tv_apps/smart-system/js/browser_key_event_manager.js +++ b/tv_apps/smart-system/js/browser_key_event_manager.js @@ -1,4 +1,7 @@ 'use strict'; + +/* global KeyboardManager */ + (function(exports) { var BrowserKeyEventManager = function BrowserKeyEventManager() { }; @@ -32,6 +35,7 @@ 'power': 'sleep-button', 'exit': 'home-button', 'home': 'home-button', + 'backspace': 'back-button', 'mozhomescreen': 'home-button', 'volumeup': 'volume-up-button', 'volumedown': 'volume-down-button' @@ -53,6 +57,12 @@ var key = this._getLowerCaseKeyName(event); return (this.SYSTEM_ONLY_KEYS.indexOf(key) > -1); }, + _isBackspace: function bkem_isBackspace(event) { + var key = this._getLowerCaseKeyName(event); + return (key === 'backspace' || + event.keyCode === window.KeyEvent.DOM_VK_BACK_SPACE) && + !KeyboardManager.getHasActiveKeyboard(); + }, _isAppCancelledKey: function bkem_isAppCancelledKey(event) { var key = this._getLowerCaseKeyName(event); return (this.APP_CANCELLED_KEYS.indexOf(key) > -1); @@ -80,11 +90,11 @@ (this._isBeforeEvent(event) || this._isKeyEvent(event))) { event.preventDefault(); needTranslation = true; - } else if (this._isAppCancelledKey(event) && this._isAfterEvent(event)) { + } else if (this.isAppCancelledKeyEvent(event) && + this._isAfterEvent(event)) { // if embedded frame cancel the event, we need to translate it then needTranslation = !event.embeddedCancelled; - } else if (this._isKeyEvent(event) && - !this._targetToIframe(event)) { + } else if (this._isKeyEvent(event) && !this._targetToIframe(event)) { // When focus is on embedded iframe and user press hardware key, system // app will receive an extra keydown keyup event targeted to the iframe. // We should ignore this event otherwise we will have strange state @@ -102,6 +112,9 @@ isHardwareKeyEvent: function bkem_isHardwareKeyEvent(type) { return (this.KEY_EVENTS.indexOf(type) > -1); }, + isAppCancelledKeyEvent: function bkem_isAppCancelledKeyEvent(event) { + return this._isAppCancelledKey(event) || this._isBackspace(event); + }, getButtonEventType: function bkem_getButtonEventType(event) { var translatedType; var key; diff --git a/tv_apps/smart-system/js/hardware_buttons.js b/tv_apps/smart-system/js/hardware_buttons.js index 11a9b7573ed6..f21c4c7e66d7 100644 --- a/tv_apps/smart-system/js/hardware_buttons.js +++ b/tv_apps/smart-system/js/hardware_buttons.js @@ -9,6 +9,7 @@ var HardwareButtonsBaseState; var HardwareButtonsHomeState; var HardwareButtonsSleepState; + var HardwareButtonsBackState; var HardwareButtonsVolumeState; var HardwareButtonsWakeState; var HardwareButtonsScreenshotState; @@ -16,7 +17,7 @@ /** * After bug 989198 landing, we will be able to listen to KeyboardEvent * (e.g. keydown and keyup). Also there will be new events called - * BeforeAfterKeybaordEvent for mozbrowser-embedder iframe, say system app, + * BeforeAfterKeyboardEvent for mozbrowser-embedder iframe, say system app, * to control or override the bahavior of keydown/keyup event in * mozbrowser-embedded iframes (other apps or homescreen app). * These events are: @@ -34,7 +35,7 @@ * For detail, please see * https://wiki.mozilla.org/WebAPI/BrowserAPI/KeyboardEvent * - * This module listens for KeyboardEvent and BeforeAfterKeybaordEvent, + * This module listens for KeyboardEvent and BeforeAfterKeyboardEvent, * processes them (with the help of BrowserKeyEventManager submodule) * and generates higher-level events to handle autorepeat on * the volume keys, long presses on Home and Sleep, and the Volume Down+Sleep @@ -52,6 +53,7 @@ * | home | short press and release of home button | * | holdhome | long press and hold of home button | * | sleep | short press and release of sleep button | + * | back | short press and release of back button | * | wake | sleep or home pressed while sleeping | * | holdsleep | long press and hold of sleep button | * | volumeup | volume up pressed and released or autorepeated | @@ -229,11 +231,24 @@ case 'volume-up-button-release': case 'volume-down-button-press': case 'volume-down-button-release': + case 'back-button-press': + case 'back-button-release': this.state.process(type); break; } } else { var buttonEventType = this.browserKeyEventManager.getButtonEventType(evt); + + // In case of website only calling preventDefault() for keydown or keyup. + // If preventDefault() is called in keydown, we set state to base to exit + // previous state and force FSM do nothing for upcoming keyup / *-release. + // If preventDefault() is called in keyup, we set state to base to exit + // previous state for preventing "hold" event or infinity repeat emitted + // from previous state. + if (evt.embeddedCancelled && + this.browserKeyEventManager.isAppCancelledKeyEvent(evt)) { + this.setState('base'); + } // having a falsey buttonEventType value means the event should be // ignored by System if (!buttonEventType) { @@ -292,10 +307,14 @@ case 'volume-down-button-press': this.hardwareButtons.setState('volume', type); return; + case 'back-button-press': + this.hardwareButtons.setState('back', type); + return; case 'home-button-release': case 'sleep-button-release': case 'volume-up-button-release': case 'volume-down-button-release': + case 'back-button-release': // Ignore button releases that occur in this state. // These can happen after volumedown+sleep and home+volume. return; @@ -440,6 +459,70 @@ this.hardwareButtons.setState('base', type); }; + /** + * We enter the back state when the user presses the Back button + * We can fire back event from this state + * + * @class HardwareButtonsBackState + */ + HardwareButtonsBackState = + HardwareButtons.STATES.back = function HardwareButtonsBackState(hb) { + this.hardwareButtons = hb; + this.timer = undefined; + }; + + /** + * Entering the state. + * @memberof HardwareButtonsBackState.prototype + */ + HardwareButtonsBackState.prototype.enter = function() { + this.timer = setTimeout(function() { + /** + * When the user holds Back button more than HOLD_INTERVAL. + * @event HardwareButtonsBackState#holdback + */ + this.hardwareButtons.publish('holdback'); + this.hardwareButtons.setState('base'); + }.bind(this), this.hardwareButtons.HOLD_INTERVAL); + }; + + /** + * Leaving the state. + * @memberof HardwareButtonsBackState.prototype + */ + HardwareButtonsBackState.prototype.exit = function() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = undefined; + } + }; + + /** + * Pressing any other hardware button will cancel this state. + * @memberof HardwareButtonsBackState.prototype + * @param {String} type Name of the event to process. + */ + HardwareButtonsBackState.prototype.process = function(type) { + switch (type) { + case 'back-button-release': + /** + * When the user releases Back button before HOLD_INTERVAL. + * @event HardwareButtonsHomeState#back + */ + this.hardwareButtons.publish('back'); + this.hardwareButtons.setState('base', type); + return; + case 'home-button-press': + case 'volume-up-button-press': + case 'volume-down-button-press': + case 'sleep-button-press': + this.hardwareButtons.setState('base', type); + return; + } + console.error('Unexpected hardware key: ', type); + this.hardwareButtons.setState('base', type); + }; + /** * We enter the volume state when the user presses the volume up or * volume down buttons. @@ -673,7 +756,6 @@ this.hardwareButtons.setState('base', type); }; - /* * Start the hardware buttons events. * XXX: To be moved. diff --git a/tv_apps/smart-system/js/interactive_notifications.js b/tv_apps/smart-system/js/interactive_notifications.js index 4a61ed1c0d68..924311b8b6d5 100644 --- a/tv_apps/smart-system/js/interactive_notifications.js +++ b/tv_apps/smart-system/js/interactive_notifications.js @@ -130,7 +130,6 @@ proto._handleKeyEvent = function in_handleKeyEvent(e) { switch(e.keyCode) { - case KeyEvent.DOM_VK_ESCAPE: case KeyEvent.DOM_VK_BACK_SPACE: if (this._activeMessage) { this.hideNotification(this._activeType, this._activeMessage); diff --git a/tv_apps/smart-system/js/keyboard_manager.js b/tv_apps/smart-system/js/keyboard_manager.js index 8d6fe32c765a..09d779dd8937 100644 --- a/tv_apps/smart-system/js/keyboard_manager.js +++ b/tv_apps/smart-system/js/keyboard_manager.js @@ -403,6 +403,10 @@ var KeyboardManager = { this.transitionManager.hideImmediately(); }, + getHasActiveKeyboard: function km_getHasActiveKeyboard() { + return this._hasActiveKeyboard; + }, + setHasActiveKeyboard: function km_setHasActiveKeyboard(active) { this._hasActiveKeyboard = active; }, diff --git a/tv_apps/smart-system/js/preview_window.js b/tv_apps/smart-system/js/preview_window.js index 4eaa0b3931b0..db1020e90191 100644 --- a/tv_apps/smart-system/js/preview_window.js +++ b/tv_apps/smart-system/js/preview_window.js @@ -99,27 +99,24 @@ return this.isVisible(); }; - PreviewWindow.prototype._handle_mozbrowserafterkeyup = function(evt) { + PreviewWindow.prototype._handle_back = function(evt) { if (document.activeElement !== this.iframe) { return; } - if ((evt.keyCode === 27 || evt.key === 'Escape') && - !evt.embeddedCancelled) { - if (this.config.url.startsWith('app://')) { - this.kill(); - } else { - var goBackReq = this.iframe.getCanGoBack(); - goBackReq.onsuccess = () => { - if (goBackReq.result) { - this.iframe.goBack(); - } else { - this.kill(); - } - }; - goBackReq.onerror = () => { + if (this.config.url.startsWith('app://')) { + this.kill(); + } else { + var goBackReq = this.iframe.getCanGoBack(); + goBackReq.onsuccess = () => { + if (goBackReq.result) { + this.iframe.goBack(); + } else { this.kill(); - }; - } + } + }; + goBackReq.onerror = () => { + this.kill(); + }; } };