Create Free APK

JavaScript bridge API

The window

Fullscreen, picture-in-picture, the system bar colours, screenshot blocking, the splash screen and closing a window.

navigator.wakeLock web standard#

navigator.wakeLock.request('screen')

Example

The standard Screen Wake Lock API: keeps the screen from dimming while a recipe, a map, a boarding pass or a workout timer is on screen.

Returns: a Promise of a WakeLockSentinel {type:'screen', released, release(), onrelease}. It rejects with NotAllowedError when the page is not visible. Needs: nothing to switch on, no permission.

let wakeLock = null;

async function startCooking() {
  try {
    wakeLock = await navigator.wakeLock.request('screen');
    wakeLock.addEventListener('release', () => { wakeLock = null; });
  } catch (e) {
    console.log('Screen may dim:', e.name);
  }
}

async function stopCooking() {
  if (wakeLock) await wakeLock.release();
}

// The lock is dropped when the app goes to the background — take it again on return.
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible' && cookingModeOn && !wakeLock) startCooking();
});

Notes: as the spec says, every lock is released when the page is hidden (the app goes to the background, or navigates); each sentinel's release event fires. Several sentinels may be held at once - the screen stays on until the last is released. AI-built apps use keepAwake(true/false) from @/lib/appmintNative. Event behind it: appmint:wakelock-lost.

screen.orientation web standard#

screen.orientation.lock(type) / unlock()

Example

The standard Screen Orientation lock, which a plain WebView refuses: turns the app to landscape for a video or a game and back again.

Returns: lock(type) → a Promise that resolves once the orientation is requested; rejects TypeError for an unknown type and NotSupportedError on Android TV. unlock() returns to the orientation the app was built with. Needs: nothing.

const player = document.getElementById('player');

document.getElementById('fullscreen').addEventListener('click', async () => {
  try {
    await screen.orientation.lock('landscape');       // either landscape, follows the sensor
  } catch (e) {
    console.log('Could not rotate:', e.name);
  }
  player.classList.add('wide');
});

document.getElementById('done').addEventListener('click', () => {
  screen.orientation.unlock();
  player.classList.remove('wide');
});

screen.orientation.addEventListener('change', () => console.log(screen.orientation.type));

Notes: types: any, natural, portrait, portrait-primary, portrait-secondary, landscape, landscape-primary, landscape-secondary. Unlike Chrome, the page does not have to be in fullscreen first. AI-built apps use lockOrientation() from @/lib/appmintNative.

window.close web standard#

window.close()

Example

Closes the app's main screen. The shell replaces window.close() so a "Close" or "Exit" button in your page works inside the app.

Returns: nothing. Needs: nothing.

document.getElementById('exit-btn').onclick = function () {
  saveDraft();          // anything unsaved — the screen closes at once
  window.close();       // in the app: WebToApk.closeWindow(); in a browser: normal behaviour
};

Hide the button in a browser, where window.close() usually does nothing on a tab the user opened:

if (!window.WebToApk) document.getElementById('exit-btn').hidden = true;

Notes: The replacement is installed after each page finishes loading, so a call during the very first script run of the page uses the WebView's own window.close() (which does nothing). There is no exit confirmation on this path. A sign-in popup that calls window.close() is handled separately: it closes the popup, not the app.

__lockOrientation bridge#

window.WebToApk.__lockOrientation(type: String): String

screen.orientation.lock(type): "ok", "type" for a string that is not an OrientationLockType, or "unsupported" (Android TV is always landscape).

Example

Internal transport behind screen.orientation.lock(type). Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: 'ok', 'type' for a string that is not an orientation lock type, or 'unsupported' (Android TV). Needs: nothing to switch on.

Use the public API:

document.getElementById('play').addEventListener('click', async () => {
  try { await screen.orientation.lock('landscape'); } catch (e) { /* NotSupportedError on TV */ }
});
document.getElementById('exit').addEventListener('click', () => screen.orientation.unlock());

Notes: Implemented in the app shell (WebApiPolyfills.kt). The same page code works unchanged in Chrome.

__unlockOrientation bridge#

window.WebToApk.__unlockOrientation()

screen.orientation.unlock(): back to the orientation the creator built with.

Example

Internal transport behind screen.orientation.unlock(). Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: nothing; the app goes back to the orientation chosen at build time. Needs: nothing to switch on.

Use the public API:

document.getElementById('play').addEventListener('click', async () => {
  try { await screen.orientation.lock('landscape'); } catch (e) { /* NotSupportedError on TV */ }
});
document.getElementById('exit').addEventListener('click', () => screen.orientation.unlock());

Notes: Implemented in the app shell (WebApiPolyfills.kt). The same page code works unchanged in Chrome.

__wakeLock bridge#

window.WebToApk.__wakeLock(docId: String, held: Boolean): Boolean

Screen Wake Lock: [held] true keeps the screen on while document [docId] holds a sentinel. Returns false when the lock could not be taken.

Example

Internal transport behind navigator.wakeLock.request('screen') and WakeLockSentinel.release(). Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: true when the screen-on flag was applied for that document. Needs: nothing to switch on.

Use the public API:

let lock = null;
async function keepScreenOn() {
  if (!('wakeLock' in navigator)) return;
  lock = await navigator.wakeLock.request('screen');
  lock.addEventListener('release', () => { lock = null; });
}

Notes: The app drops every lock when it goes to the background and fires appmint:wakelock-lost, which releases the sentinels (their release event fires).

closeWindow bridge#

window.WebToApk.closeWindow()

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

Example

Closes the app's main screen (the same as pressing Back on the last page). Pages normally call the standard window.close(), which the shell routes here.

Returns: nothing. Needs: nothing.

document.getElementById('quit').onclick = function () {
  if (confirm('Close the app?')) window.close();   // in the app: the screen closes
};

The raw call does the same thing:

if (window.WebToApk && typeof window.WebToApk.closeWindow === 'function') {
  window.WebToApk.closeWindow();
}

Notes: It finishes the app's screen at once - no exit confirmation, even if the build has one. Save anything unsaved first. In a browser window.close() only closes a tab the script opened itself.

enterPip bridge#

window.WebToApk.enterPip(): Boolean

Shrinks the whole app into a floating picture-in-picture window right now - a video or a call the user wants to keep watching while they leave. Android 8+ and only when the build enabled PiP (the manifest flag it needs); returns whether the window opened. Leaving the app with PiP enabled does this by itself; this is the explicit "minimise" button.

Example

Shrinks the whole app into a small floating picture-in-picture window right now - a "minimise" button for a video or a call.

Returns: true if the floating window opened, false otherwise (synchronously). Needs: Picture-in-Picture (PiP) switched on in Step 3 (Integrate), under Display Options, when you build. Android 8+.

var pipBtn = document.getElementById('pip-btn');
pipBtn.onclick = function () {
  if (!(window.WebToApk && typeof window.WebToApk.enterPip === 'function')) return;
  var ok = window.WebToApk.enterPip();
  if (!ok) pipBtn.hidden = true;   // PiP not enabled in this build, or Android 7 and older
};

Make the page fit the small window: the window gets very narrow, so a resize listener can hide everything but the video.

window.addEventListener('resize', function () {
  var small = window.innerWidth < 300;
  document.querySelector('nav').style.display = small ? 'none' : '';
});

Notes: With PiP enabled, pressing Home already enters the floating window by itself; this method is only for an explicit button. It returns false in a build without the PiP switch, because the manifest flag it needs is missing. The PiP window size is chosen by Android. AI-built apps can use pip from @/lib/appmintNative.

isFullscreen bridge#

window.WebToApk.isFullscreen(): Boolean

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

Example

Tells you whether the Android status and navigation bars are hidden right now.

Returns: true / false, synchronously. Needs: nothing.

function barsHidden() {
  if (window.WebToApk && typeof window.WebToApk.isFullscreen === 'function') {
    return window.WebToApk.isFullscreen();
  }
  return false;   // in a browser
}

if (barsHidden()) {
  document.getElementById('fs-btn').textContent = 'Exit fullscreen';
}

Notes: It returns true when the creator built the app with Fullscreen or Immersive Kiosk Mode, OR when the page called setFullscreen(true) / toggleFullscreen(). In the first case setFullscreen(false) cannot bring the bars back.

isSecureScreen bridge#

window.WebToApk.isSecureScreen(): Boolean

WebToApk.setSecureScreen(true) // block screenshots on THIS page WebToApk.setSecureScreen(false) // release it early WebToApk.isSecureScreen() // -> boolean Protection belongs to the page that asked for it. It is released the moment the app navigates anywhere else - a new document, a pushState route, a #hash route - so a protected screen cannot leave the rest of the app locked down, and a page that wants protection back after a reload simply asks again. What it does: screenshots, screen recording, casting and the recents-screen thumbnail all stop while it is on. What it cannot do: stop a camera pointed at the screen. Nothing on Android can, and no bridge here will pretend otherwise.

Described by its group, Screen-capture protection, rather than on its own.

Example

Tells you whether screenshot protection (setSecureScreen) is on right now.

Returns: a boolean, at once. Needs: nothing.

function updateLockIcon() {
  var on = !!window.WebToApk &&
    typeof window.WebToApk.isSecureScreen === 'function' &&
    window.WebToApk.isSecureScreen();
  document.getElementById('shield').hidden = !on;
}

Notes: It goes back to false by itself when the app navigates to another page or route, because protection belongs to the page that asked for it.

setFullscreen bridge#

window.WebToApk.setFullscreen(enabled: Boolean)

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

Example

Hides (true) or shows (false) the Android status bar and navigation bar, for example when a game enters its play screen.

Returns: nothing. Needs: nothing - it is always available to your page. The choice is remembered across launches.

function enterPlayScreen() {
  if (window.WebToApk && typeof window.WebToApk.setFullscreen === 'function') {
    window.WebToApk.setFullscreen(true);    // hide clock/battery and the nav bar
  }
}
function leavePlayScreen() {
  if (window.WebToApk && window.WebToApk.setFullscreen) {
    window.WebToApk.setFullscreen(false);   // bring the bars back
  }
}

Keep your content out from under the camera cut-out when the bars are hidden:

document.body.style.paddingTop = 'env(safe-area-inset-top)';
document.body.style.paddingBottom = 'env(safe-area-inset-bottom)';

Notes: It does nothing in an app built with Fullscreen (or Immersive Kiosk Mode) switched on in Step 3 (Integrate) - the creator locked that choice. It only hides the Android bars; the app's own top bar (the one with the side-menu button) is the Show Top Bar switch. When turned on, the shell adds viewport-fit=cover to your viewport meta so the page lays out into the full screen. See toggleFullscreen and isFullscreen. AI-built apps can use fullscreen from @/lib/appmintNative.

setSecureScreen bridge#

window.WebToApk.setSecureScreen(enabled: Boolean): Boolean

Sets protection for the CURRENT page and returns the state now in force. The window flag itself is applied on the UI thread, but the state this returns is already committed, so a page can call this and immediately reveal its sensitive content without waiting for a callback.

Example

Blocks screenshots, screen recording, casting and the recents-screen preview while the CURRENT page shows something private.

Returns: a boolean at once - the state now in force (the value you passed). You can reveal the private content right after the call. Needs: nothing.

function showStatement() {
  if (window.WebToApk && typeof window.WebToApk.setSecureScreen === 'function') {
    window.WebToApk.setSecureScreen(true);
  }
  render(statementView);
}

Release it yourself only when you hide the private content WITHOUT navigating (a modal closes, a tab switches inside one page):

function closeCardDetails() {
  hide(cardDetailsModal);
  if (window.WebToApk && typeof window.WebToApk.setSecureScreen === 'function') {
    window.WebToApk.setSecureScreen(false);
  }
}

Notes: Protection belongs to the page that asked. It is released as soon as the app navigates anywhere else - a new page, a pushState route or a #hash route - so there is nothing to clean up on the way out, and after a reload the page must ask again. Do not re-apply it on a timer: the flag must already be on when the user presses the screenshot buttons. Nothing can stop a camera pointed at the screen.

setStatusBarColor bridge#

window.WebToApk.setStatusBarColor(color: String, style: String): Boolean

Paints the status and navigation bars [color] and picks the icon tint: [style] 'light' means light icons on a dark bar, 'dark' the reverse, '' lets the luminance decide. [color] is `#RGB`, `#RGBA`, `#RRGGBB`, `#AARRGGBB` (alpha FIRST - CSS's `#RRGGBBAA` order is read as alpha-first too) or a basic colour name ('black', 'navy'…); `rgb()`/`hsl()` are not read. Returns false when the colour does not parse.

Example

Paints the Android status bar a colour and chooses light or dark icons on it.

Returns: true, or false when the colour cannot be parsed (synchronously). Needs: nothing.

style is 'light' (light icons, for a dark bar), 'dark' (dark icons, for a light bar), or '' (the shell picks from the colour's brightness):

function paintBar(color, style) {
  if (window.WebToApk && typeof window.WebToApk.setStatusBarColor === 'function') {
    return window.WebToApk.setStatusBarColor(color, style);
  }
  return false;   // in a browser
}

paintBar('#0B0F1A', 'light');   // dark bar, light icons
paintBar('#FFFFFF', 'dark');    // white bar, dark icons
paintBar('#0E7C86', '');        // icons chosen automatically

Match the bar to each screen:

function showScreen(name) {
  var colors = { home: '#0B0F1A', settings: '#FFFFFF' };
  paintBar(colors[name] || '#0B0F1A', name === 'settings' ? 'dark' : 'light');
}

Notes: Colours are Android colour strings: #RRGGBB or #AARRGGBB (and names like red). CSS forms such as rgb(...) or #RGB return false. It works on Android 15+ too, where Capacitor's StatusBar plugin cannot paint the bar. While the bars are hidden (fullscreen) the colour is kept for when they come back. AI-built apps can use setStatusBar from @/lib/appmintNative.

splashHold bridge#

window.WebToApk.splashHold()

Keeps the branded launch splash up until [splashReady] is called. By default the splash hides as soon as the body has laid-out children, which for an app that paints a skeleton and then fetches its first screen means "splash, then a skeleton". An app that would rather reveal a finished first screen calls this at import and [splashReady] once that screen is on. Capped at the same 6s backstop as the paint poll, so a page that never reports ready still opens.

Example

Keeps the app's launch splash on screen until you call splashReady() - so the user sees your finished first screen, not "splash, then a loading skeleton".

Returns: nothing. Needs: nothing.

Call it as early as possible (at the top of your first script), then call splashReady() once the first screen is drawn:

if (window.WebToApk && typeof window.WebToApk.splashHold === 'function') {
  window.WebToApk.splashHold();
}

loadFirstScreen()                      // your own async start-up (fetch data, render)
  .catch(function (err) { showError(err); })
  .then(function () {
    if (window.WebToApk && window.WebToApk.splashReady) window.WebToApk.splashReady();
  });

Notes: Without splashHold() the splash hides as soon as the page body has visible children. A held splash still hides by itself after a few seconds (the same backstop as the normal paint check), so a page that never calls splashReady() still opens. Always call splashReady() on the error path too. The creator's minimum splash time still applies. AI-built apps can use splash from @/lib/appmintNative.

splashReady bridge#

window.WebToApk.splashReady()

The first screen is on - hide the launch splash now (subject to the creator's minimum splash duration). Idempotent; harmless when nothing was held.

Example

Tells the shell your first screen is on, so the launch splash hides now.

Returns: nothing. Needs: nothing. Safe to call more than once, and harmless when nothing was held.

It pairs with splashHold():

if (window.WebToApk && window.WebToApk.splashHold) window.WebToApk.splashHold();

async function start() {
  try {
    var res = await fetch('/api/today');
    renderToday(await res.json());
  } catch (e) {
    renderOfflineMessage();
  } finally {
    if (window.WebToApk && typeof window.WebToApk.splashReady === 'function') {
      window.WebToApk.splashReady();   // hide the splash in every case
    }
  }
}
start();

Notes: The creator's minimum splash duration is still honoured. If you never call splashHold(), the splash hides on its own when the page paints, and splashReady() does nothing extra.

toggleFullscreen bridge#

window.WebToApk.toggleFullscreen()

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

Example

Switches the Android status and navigation bars between hidden and shown.

Returns: nothing. Needs: nothing. The new state is remembered across launches.

A "fullscreen" button that also updates its own label:

var btn = document.getElementById('fs-btn');
if (window.WebToApk && typeof window.WebToApk.toggleFullscreen === 'function') {
  btn.onclick = function () {
    window.WebToApk.toggleFullscreen();
    // The change is applied on the UI thread; read the state a moment later.
    setTimeout(function () {
      btn.textContent = window.WebToApk.isFullscreen() ? 'Exit fullscreen' : 'Fullscreen';
    }, 100);
  };
} else {
  btn.hidden = true;   // in a browser
}

Notes: It does nothing in an app built with Fullscreen or Immersive Kiosk Mode on (the bars stay hidden). The side-menu "Fullscreen" item for end users is a separate build switch (Fullscreen Button in Step 3, under Display Options); this bridge method works without it.

Generated from the app runtime and its example files on every docs build. Read it as Markdown · All families.

Checked against the shipped bridge on 2026-09-23.