Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ fun WebUiScreen(
webView.evaluateJavascript("onDeviceIdScanned(\"${jsEscape(deviceId)}\");") {}
}

fun setTvMode(enable: Boolean) {
webView.evaluateJavascript("setTvMode($enable);") {}
fun bridgeInit(isTv: Boolean) {
webView.evaluateJavascript("bridgeInit($isTv);") {}
}

val requestQrScanner = rememberLauncherForActivityResult(
Expand Down Expand Up @@ -257,8 +257,8 @@ fun WebUiScreen(
view.evaluateJavascript(script) {}

val isTv = context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
Log.d(TAG, "Setting TV mode: $isTv")
setTvMode(isTv)
Log.d(TAG, "Initializing webview bridge with TV mode: $isTv")
bridgeInit(isTv)
}

override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) {
Expand Down
88 changes: 41 additions & 47 deletions app/src/main/res/raw/webview_bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ const settingsToDisable = new Set([
'StartBrowser',
]);

function tryMutate() {
function tryMutate(isTv) {
if (!elemFolderPath) {
elemFolderPath = document.getElementById('folderPath');
if (elemFolderPath) {
addFolderPicker(elemFolderPath);
}
}

if (!elemShareDeviceIdButtons) {
if (!isTv && !elemShareDeviceIdButtons) {
elemShareDeviceIdButtons = document.getElementById('shareDeviceIdButtons');
if (elemShareDeviceIdButtons) {
addQrScanner(elemShareDeviceIdButtons);
Expand Down Expand Up @@ -182,57 +182,51 @@ function onDeviceIdScanned(deviceId) {
elemDeviceId.dispatchEvent(new InputEvent('input'));
}

if (!tryMutate()) {
const callback = (mutationList, observer) => {
// The actual elements we need are added via innerHTML by Angular, which doesn't get
// reported as distinct mutations. It's faster to just find by element ID than to
// recursively walk mutation.addedNodes.
function bridgeInit(isTv) {
if (!tryMutate(isTv)) {
const callback = (mutationList, observer) => {
// The actual elements we need are added via innerHTML by Angular, which doesn't get
// reported as distinct mutations. It's faster to just find by element ID than to
// recursively walk mutation.addedNodes.

if (tryMutate(isTv)) {
console.log('All mutations complete; unregistering observer');
observer.disconnect();
}
};

const observer = new MutationObserver(callback);
observer.observe(document.body, {
childList: true,
subtree: true,
});
}

if (tryMutate()) {
console.log('All mutations complete; unregistering observer');
observer.disconnect();
// Prevent arrow keys from opening dropdown menus. There is no good way to leave the menu
// afterwards because the up/down arrow keys are hijacked to only move within the list and a TV
// remote has no escape button to close the menu. Spatial navigation already works very well for
// this use case.
$(document).off('keydown.bs.dropdown.data-api');

// This is atrocious. By default, the browser makes the up/down arrow keys adjust number inputs
// up and down. preventDefault() stops this, but also prevents using spatial navigation to move
// to the next element above or below the input. There is currently no way to trigger spatial
// navigation programmatically. Instead, we'll just make the field read-only for a bit so that
// the arrow keys don't change the value.
$(document).on('keydown', 'input', function (e) {
if ((e.which == 38 || e.which == 40) && e.target.type == 'number') {
const wasReadOnly = e.target.readOnly;

e.target.readOnly = true;
if (!wasReadOnly) {
setTimeout(function() { e.target.readOnly = false; }, 100);
}
}
};

const observer = new MutationObserver(callback);
observer.observe(document.body, {
childList: true,
subtree: true,
});
}

// Prevent arrow keys from opening dropdown menus. There is no good way to leave the menu afterwards
// because the up/down arrow keys are hijacked to only move within the list and a TV remote has no
// escape button to close the menu. Spatial navigation already works very well for this use case.
$(document).off('keydown.bs.dropdown.data-api');

// This is atrocious. By default, the browser makes the up/down arrow keys adjust number inputs up
// and down. preventDefault() stops this, but also prevents using spatial navigation to move to the
// next element above or below the input. There is currently no way to trigger spatial navigation
// programmatically. Instead, we'll just make the field read-only for a bit so that the arrow keys
// don't change the value.
$(document).on('keydown', 'input', function (e) {
if ((e.which == 38 || e.which == 40) && e.target.type == 'number') {
const wasReadOnly = e.target.readOnly;

e.target.readOnly = true;
if (!wasReadOnly) {
setTimeout(function() { e.target.readOnly = false; }, 100);
}
}
});

function setTvMode(enable) {
const currentStyle = document.getElementById('basicsync-tv-style');

if (enable == !!currentStyle) {
return;
} else if (enable) {
if (isTv) {
const style = document.createElement('style');
style.id = 'basicsync-tv-style';
style.innerHTML = ':focus { border: 3px dotted !important; }';
document.body.appendChild(style);
} else {
document.body.removeChild(currentStyle);
}
}