From 101e8fbf1b277da74a3510435e28fd4bcaa6edae Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Tue, 27 Oct 2026 11:47:35 +0100 Subject: fix(android): keep the screen on while a video plays The WebView has no Screen Wake Lock API, so the player's request did nothing. The bridge now supplies navigator.wakeLock over a native screen:keep-on channel (the WebView's keepScreenOn). Co-Authored-By: Claude Opus 5.5 --- .../app/src/main/assets/bridge/meshbay-bridge.js | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'packages/meshbay-android/app/src/main/assets') diff --git a/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js b/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js index e8b40b2d..e78a2337 100644 --- a/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js +++ b/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js @@ -46,6 +46,45 @@ port.postMessage(JSON.stringify({ id, ch: channel, args })); }); + // The Screen Wake Lock API, which Chrome has and the WebView does not: the + // video player (and music, when asked) holds it so the screen does not time + // out mid-film. Held natively as the WebView's keepScreenOn, and released + // when the page hides, as the API does. Cleared first, for a reload that + // left one held. + if (!('wakeLock' in navigator)) { + const held = new Set(); + const sync = () => call('screen:keep-on', held.size > 0).catch(() => {}); + class WakeLockSentinel extends EventTarget { + constructor() { super(); this.type = 'screen'; this.released = false; this.onrelease = null; } + release() { + if (!this.released) { + this.released = true; + held.delete(this); + sync(); + const event = new Event('release'); + if (typeof this.onrelease === 'function') this.onrelease(event); + this.dispatchEvent(event); + } + return Promise.resolve(); + } + } + const request = (type) => { + if (type !== 'screen') return Promise.reject(new TypeError('Unsupported wake lock type')); + if (document.visibilityState !== 'visible') { + return Promise.reject(new DOMException('The page is not visible', 'NotAllowedError')); + } + const sentinel = new WakeLockSentinel(); + held.add(sentinel); + sync(); + return Promise.resolve(sentinel); + }; + document.addEventListener('visibilitychange', () => { + if (document.visibilityState !== 'visible') [...held].forEach((s) => s.release()); + }); + Object.defineProperty(navigator, 'wakeLock', { value: Object.freeze({ request }) }); + sync(); + } + // A write is a binary message: "MBB1" | id | channel | 0 | handle | bytes, // big-endian — one copy, no JSON, no base64 (spike S-3: 136 MB/s awaited // per 48 KB chunk). Without ArrayBuffer messages, base64 over JSON. -- cgit v1.2.3