From 12da467cfa6c3f38e2847d81f08cfe53c576843f Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 17 Jan 2020 11:50:46 +0300 Subject: [PATCH 1/6] Fix event subscription. Fix #623 --- src/scripts/inputManager.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/scripts/inputManager.js b/src/scripts/inputManager.js index 634b5e1b30..6839b0b6f6 100644 --- a/src/scripts/inputManager.js +++ b/src/scripts/inputManager.js @@ -22,9 +22,7 @@ define(['playbackManager', 'focusManager', 'appRouter', 'dom'], function (playba var eventListenerCount = 0; function on(scope, fn) { - if (eventListenerCount) { - eventListenerCount++; - } + eventListenerCount++; dom.addEventListener(scope, 'command', fn, {}); } From 80c8ea6a489d7d23718541c4fbada1df837f1139 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 17 Jan 2020 12:05:56 +0300 Subject: [PATCH 2/6] Add playback control from TV remote. #303 --- src/components/keyboardnavigation.js | 99 ++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 13 deletions(-) diff --git a/src/components/keyboardnavigation.js b/src/components/keyboardnavigation.js index 8c0bb1a3ae..c55a066f96 100644 --- a/src/components/keyboardnavigation.js +++ b/src/components/keyboardnavigation.js @@ -1,35 +1,108 @@ -define(['inputManager', 'focusManager'], function(inputManager, focusManager) { - 'use strict'; +define(["inputManager", "layoutManager"], function (inputManager, layoutManager) { + "use strict"; console.log("keyboardnavigation"); + /** + * Key name mapping. + */ + // Add more to support old browsers + var KeyNames = { + 13: "Enter", + 19: "Pause", + 27: "Escape", + 32: "Space", + 37: "ArrowLeft", + 38: "ArrowUp", + 39: "ArrowRight", + 40: "ArrowDown", + 412: "MediaRewind", // MediaRewind (Tizen/WebOS) + 413: "MediaStop", // MediaStop (Tizen/WebOS) + 415: "MediaPlay", // MediaPlay (Tizen/WebOS) + 417: "MediaFastForward", // MediaFastForward (Tizen/WebOS) + 461: "Back", // Back (WebOS) + 10009: "Back", // Back (Tizen) + 10232: "MediaTrackPrevious", // MediaTrackPrevious (Tizen) + 10233: "MediaTrackNext", // MediaTrackNext (Tizen) + 10252: "MediaPlayPause" // MediaPlayPause (Tizen) + }; + + /** + * Returns key name from event. + * + * @param {KeyboardEvent} keyboard event + * @return {string} key name + */ + function getKeyName(event) { + return KeyNames[event.keyCode] || event.key; + } + function enable() { - document.addEventListener('keydown', function(e) { + document.addEventListener("keydown", function (e) { var capture = true; - switch (e.keyCode) { - case 37: // ArrowLeft - inputManager.handle('left'); + switch (getKeyName(e)) { + case "ArrowLeft": + inputManager.handle("left"); break; - case 38: // ArrowUp - inputManager.handle('up'); + case "ArrowUp": + inputManager.handle("up"); break; - case 39: // ArrowRight - inputManager.handle('right'); + case "ArrowRight": + inputManager.handle("right"); break; - case 40: // ArrowDown - inputManager.handle('down'); + case "ArrowDown": + inputManager.handle("down"); break; + + case "Back": + inputManager.handle("back"); + break; + + case "Escape": + if (layoutManager.tv) { + inputManager.handle("back"); + } else { + capture = false; + } + break; + + case "MediaPlay": + inputManager.handle("play"); + break; + case "Pause": + inputManager.handle("pause"); + break; + case "MediaPlayPause": + inputManager.handle("playpause"); + break; + case "MediaRewind": + inputManager.handle("rewind"); + break; + case "MediaFastForward": + inputManager.handle("fastforward"); + break; + case "MediaStop": + inputManager.handle("stop"); + break; + case "MediaTrackPrevious": + inputManager.handle("previoustrack"); + break; + case "MediaTrackNext": + inputManager.handle("nexttrack"); + break; + default: capture = false; } + if (capture) { console.log("Disabling default event handling"); e.preventDefault(); } }); - } + return { enable: enable }; From 1889dad8cc4c280ac38aaa124afbbb95a87b1744 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 17 Jan 2020 12:09:27 +0300 Subject: [PATCH 3/6] Add shortcuts to show/hide OSD. #621 --- src/controllers/playback/videoosd.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index bd12ba5075..195ea57704 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -1100,6 +1100,20 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } switch (e.key) { + case "Enter": + showOsd(); + break; + + case "Escape": + case "RCUBack": // WebOS back + case "XF86Back": // Tizen back + // Ignore key when some dialog is opened + if (currentVisibleMenu === "osd" && !document.querySelector(".dialogContainer")) { + hideOsd(); + e.stopPropagation(); + } + break; + case "k": playbackManager.playPause(currentPlayer); showOsd(); @@ -1280,7 +1294,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med showOsd(); inputManager.on(window, onInputCommand); dom.addEventListener(window, "keydown", onWindowKeyDown, { - passive: true + capture: true }); } catch (e) { require(['appRouter'], function(appRouter) { @@ -1294,7 +1308,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } dom.removeEventListener(window, "keydown", onWindowKeyDown, { - passive: true + capture: true }); stopOsdHideTimer(); headerElement.classList.remove("osdHeader"); From ec6ce5aa14384f51ff4f97a749bb4e0985aed773 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sun, 19 Jan 2020 01:09:42 +0300 Subject: [PATCH 4/6] Add keyboard compatibility for older browsers (webOS 2/3) --- src/components/keyboardnavigation.js | 17 ++++++++++++++++- src/controllers/playback/videoosd.js | 11 ++++++----- src/elements/emby-slider/emby-slider.js | 4 ++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/components/keyboardnavigation.js b/src/components/keyboardnavigation.js index c55a066f96..0971018bbf 100644 --- a/src/components/keyboardnavigation.js +++ b/src/components/keyboardnavigation.js @@ -27,6 +27,20 @@ define(["inputManager", "layoutManager"], function (inputManager, layoutManager) 10252: "MediaPlayPause" // MediaPlayPause (Tizen) }; + var hasFieldKey = false; + try { + hasFieldKey = "key" in new KeyboardEvent("keydown"); + } catch (e) { + console.log("error checking 'key' field"); + } + + if (!hasFieldKey) { + // Add [a..z] + for (var i = 65; i <= 90; i++) { + KeyNames[i] = String.fromCharCode(i).toLowerCase(); + } + } + /** * Returns key name from event. * @@ -104,6 +118,7 @@ define(["inputManager", "layoutManager"], function (inputManager, layoutManager) } return { - enable: enable + enable: enable, + getKeyName: getKeyName }; }); diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index 195ea57704..5dc12aeeed 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -1,4 +1,4 @@ -define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "mediaInfo", "focusManager", "imageLoader", "scrollHelper", "events", "connectionManager", "browser", "globalize", "apphost", "layoutManager", "userSettings", "scrollStyles", "emby-slider", "paper-icon-button-light", "css!assets/css/videoosd"], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings) { +define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "mediaInfo", "focusManager", "imageLoader", "scrollHelper", "events", "connectionManager", "browser", "globalize", "apphost", "layoutManager", "userSettings", "keyboardnavigation", "scrollStyles", "emby-slider", "paper-icon-button-light", "css!assets/css/videoosd"], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings, keyboardnavigation) { "use strict"; function seriesImageUrl(item, options) { @@ -1088,25 +1088,26 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]; function onWindowKeyDown(e) { + var key = keyboardnavigation.getKeyName(e); + if (!currentVisibleMenu && 32 === e.keyCode) { playbackManager.playPause(currentPlayer); showOsd(); return; } - if (layoutManager.tv && NavigationKeys.indexOf(e.key) != -1) { + if (layoutManager.tv && NavigationKeys.indexOf(key) != -1) { showOsd(); return; } - switch (e.key) { + switch (key) { case "Enter": showOsd(); break; case "Escape": - case "RCUBack": // WebOS back - case "XF86Back": // Tizen back + case "Back": // Ignore key when some dialog is opened if (currentVisibleMenu === "osd" && !document.querySelector(".dialogContainer")) { hideOsd(); diff --git a/src/elements/emby-slider/emby-slider.js b/src/elements/emby-slider/emby-slider.js index c340e79359..24592f4515 100644 --- a/src/elements/emby-slider/emby-slider.js +++ b/src/elements/emby-slider/emby-slider.js @@ -1,4 +1,4 @@ -define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager) { +define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager, keyboardnavigation) { 'use strict'; var EmbySliderPrototype = Object.create(HTMLInputElement.prototype); @@ -250,7 +250,7 @@ define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement * Handle KeyDown event */ function onKeyDown(e) { - switch (e.key) { + switch (keyboardnavigation.getKeyName(e)) { case 'ArrowLeft': case 'Left': stepKeyboard(this, -this.keyboardStepDown || -1); From e3dd8715009e9a44d5a7d85be060f249ef0bb381 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 24 Jan 2020 16:44:45 +0300 Subject: [PATCH 5/6] Apply suggestions from code review --- src/components/keyboardnavigation.js | 27 ++++++++++++++++++--------- src/controllers/playback/videoosd.js | 12 ++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/components/keyboardnavigation.js b/src/components/keyboardnavigation.js index 0971018bbf..d1ed03138c 100644 --- a/src/components/keyboardnavigation.js +++ b/src/components/keyboardnavigation.js @@ -16,15 +16,24 @@ define(["inputManager", "layoutManager"], function (inputManager, layoutManager) 38: "ArrowUp", 39: "ArrowRight", 40: "ArrowDown", - 412: "MediaRewind", // MediaRewind (Tizen/WebOS) - 413: "MediaStop", // MediaStop (Tizen/WebOS) - 415: "MediaPlay", // MediaPlay (Tizen/WebOS) - 417: "MediaFastForward", // MediaFastForward (Tizen/WebOS) - 461: "Back", // Back (WebOS) - 10009: "Back", // Back (Tizen) - 10232: "MediaTrackPrevious", // MediaTrackPrevious (Tizen) - 10233: "MediaTrackNext", // MediaTrackNext (Tizen) - 10252: "MediaPlayPause" // MediaPlayPause (Tizen) + // MediaRewind (Tizen/WebOS) + 412: "MediaRewind", + // MediaStop (Tizen/WebOS) + 413: "MediaStop", + // MediaPlay (Tizen/WebOS) + 415: "MediaPlay", + // MediaFastForward (Tizen/WebOS) + 417: "MediaFastForward", + // Back (WebOS) + 461: "Back", + // Back (Tizen) + 10009: "Back", + // MediaTrackPrevious (Tizen) + 10232: "MediaTrackPrevious", + // MediaTrackNext (Tizen) + 10233: "MediaTrackNext", + // MediaPlayPause (Tizen) + 10252: "MediaPlayPause" }; var hasFieldKey = false; diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index 5dc12aeeed..6392c3a1a8 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -1105,7 +1105,6 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med case "Enter": showOsd(); break; - case "Escape": case "Back": // Ignore key when some dialog is opened @@ -1114,52 +1113,45 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med e.stopPropagation(); } break; - case "k": playbackManager.playPause(currentPlayer); showOsd(); break; - case "l": case "ArrowRight": case "Right": playbackManager.fastForward(currentPlayer); showOsd(); break; - case "j": case "ArrowLeft": case "Left": playbackManager.rewind(currentPlayer); showOsd(); break; - case "f": if (!e.ctrlKey && !e.metaKey) { playbackManager.toggleFullscreen(currentPlayer); showOsd(); } break; - case "m": playbackManager.toggleMute(currentPlayer); showOsd(); break; - case "NavigationLeft": case "GamepadDPadLeft": case "GamepadLeftThumbstickLeft": - // Ignores gamepad events that are always triggered, even when not focused. + // Ignores gamepad events that are always triggered, even when not focused. if (document.hasFocus()) { playbackManager.rewind(currentPlayer); showOsd(); } break; - case "NavigationRight": case "GamepadDPadRight": case "GamepadLeftThumbstickRight": - // Ignores gamepad events that are always triggered, even when not focused. + // Ignores gamepad events that are always triggered, even when not focused. if (document.hasFocus()) { playbackManager.fastForward(currentPlayer); showOsd(); From 4b5eb8e063c89ddfd21ef59d8b85fcdad205210e Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 25 Jan 2020 17:35:16 +0300 Subject: [PATCH 6/6] Fix click on pause button right after showOsd --- src/controllers/playback/videoosd.js | 38 +++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index 6392c3a1a8..122b2c8080 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -437,6 +437,11 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med }); currentVisibleMenu = null; toggleSubtitleSync("hide"); + + // Firefox does not blur by itself + if (document.activeElement) { + document.activeElement.blur(); + } } } @@ -1087,7 +1092,15 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med */ var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]; + /** + * Clicked element. + * To skip 'click' handling on Firefox/Edge. + */ + var clickedElement; + function onWindowKeyDown(e) { + clickedElement = e.srcElement; + var key = keyboardnavigation.getKeyName(e); if (!currentVisibleMenu && 32 === e.keyCode) { @@ -1159,6 +1172,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } } + function onWindowMouseDown(e) { + clickedElement = e.srcElement; + } + + function onWindowTouchStart(e) { + clickedElement = e.srcElement; + } + function getImgUrl(item, chapter, index, maxWidth, apiClient) { if (chapter.ImageTag) { return apiClient.getScaledImageUrl(item.Id, { @@ -1289,6 +1310,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med dom.addEventListener(window, "keydown", onWindowKeyDown, { capture: true }); + dom.addEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, { + passive: true + }); + dom.addEventListener(window, "touchstart", onWindowTouchStart, { + passive: true + }); } catch (e) { require(['appRouter'], function(appRouter) { appRouter.showDirect('/'); @@ -1303,6 +1330,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med dom.removeEventListener(window, "keydown", onWindowKeyDown, { capture: true }); + dom.removeEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, { + passive: true + }); + dom.removeEventListener(window, "touchstart", onWindowTouchStart, { + passive: true + }); stopOsdHideTimer(); headerElement.classList.remove("osdHeader"); headerElement.classList.remove("osdHeader-hidden"); @@ -1472,7 +1505,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med playbackManager.previousTrack(currentPlayer); }); view.querySelector(".btnPause").addEventListener("click", function () { - playbackManager.playPause(currentPlayer); + // Ignore 'click' if another element was originally clicked (Firefox/Edge issue) + if (this.contains(clickedElement)) { + playbackManager.playPause(currentPlayer); + } }); view.querySelector(".btnNextTrack").addEventListener("click", function () { playbackManager.nextTrack(currentPlayer);