# The window - JavaScript bridge API

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

- **Applies to:** AppMint
- **Source:** extracted from the app runtime and its per-method example files; this page is generated from them.
- **HTML:** https://freewebtoapk.com/docs/api/screen

### `navigator.wakeLock`

```js
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.

```js
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`

```js
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.

```js
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`

```js
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.

```js
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:

```js
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`

```js
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:**

```js
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`

```js
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:**

```js
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`

```js
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:**

```js
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`

```js
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.

```js
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:

```js
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`

```js
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+.

```js
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.

```js
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`

```js
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.

```js
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`

```js
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.

```js
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`

```js
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.

```js
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:

```js
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`

```js
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.

```js
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):

```js
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`

```js
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):

```js
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:

```js
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`

```js
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:

```js
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`

```js
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()`:

```js
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`

```js
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:

```js
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.

