API del puente de JavaScript
La ventana
Pantalla completa, imagen en imagen, los colores de las barras del sistema, bloqueo de capturas de pantalla, si la compilación tiene la protección contra copia activada (AppMint.copyProtection), la pantalla de inicio y el cierre de una ventana.
AppMint.copyProtection ayudante#
AppMint.copyProtection
Ejemplo
true cuando la app se compiló con Protección contra copia activada; ausente (undefined) en caso contrario. Solo lectura: una página no puede activar ni desactivar la protección, solo preguntar si está activa.
Devuelve: true, o undefined cuando la protección está desactivada (y en un navegador). Requiere: Protección contra copia activada en el Paso 3 (Integrar), en Opciones de pantalla, al compilar. Desactivada por defecto.
Con la protección activa, el texto de la app no se puede seleccionar, copiar ni cortar; copiar / cortar, el evento contextmenu y Ctrl/Cmd + C, X y A se cancelan fuera de los campos editables; y no aparece ningún menú de pulsación larga. Los inputs, textareas, selects y las regiones contenteditable siguen siendo totalmente editables, así que los formularios siguen funcionando.
Oculta tus propios botones de "Copiar" cuando la protección esté activa: no copiarían nada y parecerían rotos:
const protectedApp = !!(window.AppMint && window.AppMint.copyProtection === true);
document.querySelectorAll('[data-copy-button]').forEach((btn) => {
btn.hidden = protectedApp;
});O elige el texto de una sugerencia una sola vez, al iniciar:
const hint = document.getElementById('share-hint');
hint.textContent = (window.AppMint && window.AppMint.copyProtection === true)
? 'Tap Share to send this lesson to a friend.'
: 'Select any text to copy it, or tap Share.';Combínalo con el bloqueo de capturas de pantalla para una página que no debe salir de la app:
if (window.AppMint && AppMint.copyProtection === true && window.WebToApk && WebToApk.setSecureScreen) {
WebToApk.setSecureScreen(true); // also blocks screenshots and recents thumbnails
document.body.classList.add('protected');
}Notas: el menú de pulsación larga en enlaces e imágenes (abrir, copiar, compartir o descargar) es un interruptor de compilación aparte, Menú de pulsación larga en enlaces e imágenes, en el mismo lugar. Está desactivado por defecto, así que una pulsación larga en un enlace no hace nada a menos que lo actives; la protección contra copia siempre lo mantiene desactivado. Una página aún puede rechazar el menú en un elemento cancelando contextmenu (el.addEventListener('contextmenu', (e) => e.preventDefault())). La protección impide copiar dentro de la app; no impide una captura de pantalla: para eso, consulta WebToApk.setSecureScreen(true).
screen.orientation estándar web#
screen.orientation.lock(type) / unlock()
Ejemplo
El bloqueo estándar de Screen Orientation, que un WebView simple rechaza: gira la app a horizontal para un vídeo o un juego y la devuelve después.
Devuelve: lock(type) → una Promise que se resuelve una vez solicitada la orientación; se rechaza con TypeError para un tipo desconocido y con NotSupportedError en Android TV. unlock() vuelve a la orientación con la que se compiló la app. Requiere: nada.
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));Gira un juego a horizontal cuando empieza; en Android TV la pantalla ya es horizontal, así que continúa:
async function startGame() {
try {
await screen.orientation.lock('landscape');
} catch (e) {
if (e.name !== 'NotSupportedError') console.log('Could not rotate:', e.name);
}
runGame();
}
function quitGame() {
screen.orientation.unlock(); // back to the orientation the app was built with
showMenu();
}Sigue la forma del vídeo que se reproduce y vuelve atrás cuando termina:
const video = document.getElementById('clip');
video.addEventListener('play', () => {
const wide = video.videoWidth >= video.videoHeight;
screen.orientation.lock(wide ? 'landscape' : 'portrait').catch((e) => console.log(e.name));
});
video.addEventListener('ended', () => screen.orientation.unlock());Notas: tipos: any, natural, portrait, portrait-primary, portrait-secondary, landscape, landscape-primary, landscape-secondary. A diferencia de Chrome, la página no tiene que estar antes en pantalla completa. Las apps creadas con IA usan lockOrientation() de @/lib/appmintNative.
window.close estándar web#
window.close()
Ejemplo
Cierra la pantalla principal de la app. El shell reemplaza window.close() para que un botón de "Cerrar" o "Salir" en tu página funcione dentro de la app.
Devuelve: nada. Requiere: nada.
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
};Oculta el botón en un navegador, donde window.close() normalmente no hace nada en una pestaña que abrió el usuario:
if (!window.WebToApk) document.getElementById('exit-btn').hidden = true;Pregunta primero, porque la app se cierra en el momento en que se ejecuta window.close():
document.getElementById('quit').addEventListener('click', function () {
if (hasUnsavedChanges() && !confirm('You have unsaved changes. Close the app anyway?')) {
return;
}
window.close();
});Notas: el reemplazo se instala después de que cada página termina de cargarse, así que una llamada durante la primera ejecución de scripts de la página usa el window.close() propio del WebView (que no hace nada). En esta ruta no hay confirmación de salida. Una ventana emergente de inicio de sesión que llama a window.close() se gestiona por separado: cierra la ventana emergente, no la app.
__lockOrientation puente#
window.WebToApk.__lockOrientation(type: String): String
screen.orientation.lock(type): "ok", "type" para una cadena que no es un OrientationLockType, o "unsupported" (Android TV siempre es horizontal).
Ejemplo
Transporte interno detrás de screen.orientation.lock(type). Las páginas usan la API estándar; nunca llames a esto directamente: los métodos __ pueden cambiar sin previo aviso.
Devuelve: 'ok', 'type' para una cadena que no es un tipo de bloqueo de orientación, o 'unsupported' (Android TV). Requiere: nada que activar.
Usa la API pública:
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());Distingue un tipo de bloqueo incorrecto de un dispositivo que no puede girar:
async function lockTo(type) {
if (!screen.orientation || typeof screen.orientation.lock !== 'function') return 'unsupported';
try {
await screen.orientation.lock(type);
return 'ok';
} catch (e) {
if (e instanceof TypeError) return 'bad-type'; // not an OrientationLockType string
return 'unsupported'; // NotSupportedError (Android TV)
}
}
lockTo('portrait-primary').then((r) => console.log('orientation lock:', r));Congela la pantalla tal como el usuario la sostiene mientras firma, y luego devuelve la rotación:
async function startSigning() {
const current = screen.orientation.type; // e.g. 'landscape-primary'
try { await screen.orientation.lock(current); } catch (e) { /* keep going unlocked */ }
showSignaturePad();
}
function finishSigning() {
hideSignaturePad();
screen.orientation.unlock();
}Notas: Implementado en el shell de la app (WebApiPolyfills.kt). El mismo código de página funciona sin cambios en Chrome.
__unlockOrientation puente#
window.WebToApk.__unlockOrientation()
screen.orientation.unlock(): vuelve a la orientación con la que el creador compiló la app.
Ejemplo
Transporte interno detrás de screen.orientation.unlock(). Las páginas usan la API estándar; nunca llames a esto directamente: los métodos __ pueden cambiar sin previo aviso.
Devuelve: nada; la app vuelve a la orientación elegida al compilar. Requiere: nada que activar.
Usa la API pública:
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());Solo la ruta del juego es horizontal; todas las demás rutas vuelven a la orientación propia de la app:
function onRouteChange(path) {
if (path.startsWith('/play')) {
screen.orientation.lock('landscape').catch(() => {});
} else if (screen.orientation && typeof screen.orientation.unlock === 'function') {
screen.orientation.unlock(); // menus, settings and the store
}
}
window.addEventListener('popstate', () => onRouteChange(location.pathname));
onRouteChange(location.pathname);Mantén las diapositivas de bienvenida en vertical y luego desbloquea cuando terminen:
async function showOnboarding() {
try { await screen.orientation.lock('portrait'); } catch (e) { /* TV: no lock */ }
await runOnboardingSlides(); // your own flow
screen.orientation.unlock(); // back to the orientation the app was built with
}Notas: Implementado en el shell de la app (WebApiPolyfills.kt). El mismo código de página funciona sin cambios en Chrome.
__wakeLock puente#
window.WebToApk.__wakeLock(docId: String, held: Boolean): Boolean
Screen Wake Lock: [held] true mantiene la pantalla encendida mientras el documento [docId] tiene un sentinel. Devuelve false cuando no se pudo tomar el bloqueo.
Ejemplo
Transporte interno detrás de navigator.wakeLock.request('screen') y WakeLockSentinel.release(). Las páginas usan la API estándar; nunca llames a esto directamente: los métodos __ pueden cambiar sin previo aviso.
Devuelve: true cuando el indicador de pantalla encendida se aplicó a ese documento. Requiere: nada que activar.
Usa la API pública:
let lock = null;
async function keepScreenOn() {
if (!('wakeLock' in navigator)) return;
lock = await navigator.wakeLock.request('screen');
lock.addEventListener('release', () => { lock = null; });
}Vuelve a tomar el bloqueo cuando el usuario regrese a la app, porque todos los bloqueos se sueltan en segundo plano:
let lock = null;
let wantLock = false;
async function acquire() {
if (!('wakeLock' in navigator)) return;
try {
lock = await navigator.wakeLock.request('screen');
lock.addEventListener('release', () => { lock = null; });
} catch (e) {
lock = null; // NotAllowedError: the page was not visible
}
}
function startRecipeMode() { wantLock = true; acquire(); }
function stopRecipeMode() { wantLock = false; if (lock) lock.release(); }
document.addEventListener('visibilitychange', () => {
if (wantLock && !lock && document.visibilityState === 'visible') acquire();
});Mantén la pantalla encendida solo mientras un temporizador de entrenamiento está en marcha, y deja que vuelva a apagarse cuando termine:
async function runTimer(seconds) {
const lock = ('wakeLock' in navigator)
? await navigator.wakeLock.request('screen').catch(() => null)
: null;
try {
for (let left = seconds; left > 0; left--) {
showRemaining(left);
await new Promise((r) => setTimeout(r, 1000));
}
} finally {
if (lock && !lock.released) await lock.release();
}
}Notas: la app suelta todos los bloqueos cuando pasa a segundo plano y lanza appmint:wakelock-lost, que libera los sentinels (se dispara su evento release).
closeWindow puente#
window.WebToApk.closeWindow()
Usa el envoltorio de promesa: const r = await AppMint.requestReview(); // {result, openedStore} const r = await AppMint.requestReview({ openStore: true }); // ficha de la tienda si no hay hoja r.result: 'shown' | 'not_from_play' | 'quota' | 'error'. Consulta RatePrompt.
Descrito por su grupo, La hoja de reseñas dentro de la app de Google Play, y no por sí mismo.
Ejemplo
Cierra la pantalla principal de la app (igual que pulsar Atrás en la última página). Las páginas normalmente llaman al estándar window.close(), que el shell redirige aquí.
Devuelve: nada. Requiere: nada.
document.getElementById('quit').onclick = function () {
if (confirm('Close the app?')) window.close(); // in the app: the screen closes
};La llamada directa hace lo mismo:
if (window.WebToApk && typeof window.WebToApk.closeWindow === 'function') {
window.WebToApk.closeWindow();
}Un botón de "Salir" que primero guarda, y que se oculta en un navegador donde no hay pantalla de app que cerrar:
var exitBtn = document.getElementById('exit-btn');
if (!(window.WebToApk && typeof window.WebToApk.closeWindow === 'function')) {
exitBtn.hidden = true;
} else {
exitBtn.onclick = async function () {
exitBtn.disabled = true;
await saveDraft(); // your own save; closing does not wait for anything
window.WebToApk.closeWindow();
};
}Notas: cierra la pantalla de la app al instante, sin confirmación de salida, aunque la compilación tenga una. Guarda antes lo que no esté guardado. En un navegador, window.close() solo cierra una pestaña que el propio script abrió.
enterPip puente#
window.WebToApk.enterPip(): Boolean
Reduce toda la app a una ventana flotante de imagen en imagen ahora mismo: un vídeo o una llamada que el usuario quiere seguir viendo mientras sale. Android 8+ y solo cuando la compilación activó PiP (el indicador del manifiesto que necesita); devuelve si la ventana se abrió. Salir de la app con PiP activado lo hace por sí solo; este es el botón explícito de "minimizar".
Ejemplo
Reduce toda la app a una pequeña ventana flotante de imagen en imagen ahora mismo: un botón de "minimizar" para un vídeo o una llamada.
Devuelve: true si la ventana flotante se abrió, false en caso contrario (de forma síncrona). Requiere: Imagen en imagen (PiP) activada en el Paso 3 (Integrar), en Opciones de pantalla, al compilar. 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
};Haz que la página quepa en la ventana pequeña: la ventana se vuelve muy estrecha, así que un listener de resize puede ocultar todo salvo el vídeo.
window.addEventListener('resize', function () {
var small = window.innerWidth < 300;
document.querySelector('nav').style.display = small ? 'none' : '';
});Sigue la apertura y el cierre de la ventana flotante con el evento appmint:pip (detail.inPip), por ejemplo para ocultar los controles de una llamada:
window.addEventListener('appmint:pip', function (e) {
var inPip = !!(e.detail && e.detail.inPip);
document.body.classList.toggle('pip', inPip); // CSS: .pip .call-controls { display: none; }
if (!inPip) {
document.getElementById('chat-panel').hidden = false; // back to the full-size screen
}
});
document.getElementById('minimise').onclick = function () {
if (window.WebToApk && typeof window.WebToApk.enterPip === 'function') {
document.getElementById('chat-panel').hidden = true;
window.WebToApk.enterPip();
}
};Notas: con PiP activado, pulsar Inicio ya entra por sí solo en la ventana flotante; este método es solo para un botón explícito. Devuelve false en una compilación sin el interruptor de PiP, porque falta el indicador del manifiesto que necesita. El tamaño de la ventana de PiP lo elige Android. Las apps creadas con IA pueden usar pip de @/lib/appmintNative.
isFullscreen puente#
window.WebToApk.isFullscreen(): Boolean
Permite que la página oculte por sí misma la barra de estado (reloj/batería) y la barra de navegación, p. ej. un juego que entra en su pantalla de juego: WebToApk.setFullscreen(true) // oculta las barras del sistema WebToApk.setFullscreen(false) // las devuelve WebToApk.toggleFullscreen() WebToApk.isFullscreen() La elección se recuerda entre inicios. No hace nada en apps que el creador ya bloqueó en pantalla completa/quiosco: allí no hay nada que restaurar. El elemento "Pantalla completa" del menú lateral es el equivalente para los usuarios finales y depende de la opción enableFullscreenToggle del creador; este puente siempre está disponible para la propia página de la app.
Descrito por su grupo, Puente de pantalla completa, y no por sí mismo.
Ejemplo
Te dice si las barras de estado y de navegación de Android están ocultas ahora mismo.
Devuelve: true / false, de forma síncrona. Requiere: nada.
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';
}Maqueta la página para la elección recordada al iniciar, ya que sobrevive a los reinicios:
document.addEventListener('DOMContentLoaded', function () {
var hidden = !!(window.WebToApk && typeof window.WebToApk.isFullscreen === 'function' &&
window.WebToApk.isFullscreen());
document.documentElement.classList.toggle('bars-hidden', hidden);
// CSS: .bars-hidden header { padding-top: env(safe-area-inset-top); }
});Añade un botón de "mostrar barras" cuando la app se creó en pantalla completa y las barras no pueden volver:
function showBars() {
var W = window.WebToApk;
if (!(W && typeof W.isFullscreen === 'function')) return;
W.setFullscreen(false);
setTimeout(function () { // the change is applied on the UI thread
if (W.isFullscreen()) {
document.getElementById('bars-btn').hidden = true;
}
}, 100);
}Notas: devuelve true cuando el creador compiló la app con Pantalla completa o Modo quiosco inmersivo, O cuando la página llamó a setFullscreen(true) / toggleFullscreen(). En el primer caso, setFullscreen(false) no puede devolver las barras.
isSecureScreen puente#
window.WebToApk.isSecureScreen(): Boolean
WebToApk.setSecureScreen(true) // bloquea las capturas de pantalla en ESTA página WebToApk.setSecureScreen(false) // la libera antes de tiempo WebToApk.isSecureScreen() // -> boolean La protección pertenece a la página que la pidió. Se libera en el momento en que la app navega a cualquier otro sitio (un documento nuevo, una ruta pushState, una ruta #hash), de modo que una pantalla protegida no puede dejar el resto de la app bloqueado, y una página que quiera la protección de nuevo tras recargar simplemente la vuelve a pedir. Qué hace: las capturas de pantalla, la grabación de pantalla, la transmisión y la miniatura de la pantalla de recientes se detienen mientras está activa. Qué no puede hacer: impedir que una cámara apunte a la pantalla. Nada en Android puede, y ningún puente de aquí fingirá lo contrario.
Descrito por su grupo, Protección contra la captura de pantalla, y no por sí mismo.
Ejemplo
Te dice si la protección contra capturas de pantalla (setSecureScreen) está activa ahora mismo.
Devuelve: un booleano, al instante. Requiere: nada.
function updateLockIcon() {
var on = !!window.WebToApk &&
typeof window.WebToApk.isSecureScreen === 'function' &&
window.WebToApk.isSecureScreen();
document.getElementById('shield').hidden = !on;
}Comprueba antes de mostrar un número de tarjeta y activa primero la protección si esta página aún no la ha pedido:
function revealCardNumber(full) {
var W = window.WebToApk;
if (W && typeof W.isSecureScreen === 'function' && !W.isSecureScreen()) {
W.setSecureScreen(true); // the new state is committed when this returns
}
document.getElementById('card-number').textContent = full;
}Un interruptor "Ocultar de las capturas de pantalla" que parte del estado real y lo invierte:
var toggle = document.getElementById('privacy-toggle');
var W = window.WebToApk;
if (!W || typeof W.isSecureScreen !== 'function') {
toggle.hidden = true;
} else {
toggle.checked = W.isSecureScreen();
toggle.addEventListener('change', function () {
toggle.checked = W.setSecureScreen(!W.isSecureScreen());
});
}Notas: vuelve a false por sí sola cuando la app navega a otra página o ruta, porque la protección pertenece a la página que la pidió.
setFullscreen puente#
window.WebToApk.setFullscreen(enabled: Boolean)
Permite que la página oculte por sí misma la barra de estado (reloj/batería) y la barra de navegación, p. ej. un juego que entra en su pantalla de juego: WebToApk.setFullscreen(true) // oculta las barras del sistema WebToApk.setFullscreen(false) // las devuelve WebToApk.toggleFullscreen() WebToApk.isFullscreen() La elección se recuerda entre inicios. No hace nada en apps que el creador ya bloqueó en pantalla completa/quiosco: allí no hay nada que restaurar. El elemento "Pantalla completa" del menú lateral es el equivalente para los usuarios finales y depende de la opción enableFullscreenToggle del creador; este puente siempre está disponible para la propia página de la app.
Descrito por su grupo, Puente de pantalla completa, y no por sí mismo.
Ejemplo
Oculta (true) o muestra (false) la barra de estado y la barra de navegación de Android, por ejemplo cuando un juego entra en su pantalla de juego.
Devuelve: nada. Requiere: nada: siempre está disponible para tu página. La elección se recuerda entre inicios.
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
}
}Mantén tu contenido fuera de la zona de la cámara frontal cuando las barras estén ocultas:
document.body.style.paddingTop = 'env(safe-area-inset-top)';
document.body.style.paddingBottom = 'env(safe-area-inset-bottom)';Oculta las barras mientras se reproduce un vídeo y devuélvelas cuando se pausa o termina:
var video = document.getElementById('movie');
function hideBars(hidden) {
if (window.WebToApk && typeof window.WebToApk.setFullscreen === 'function') {
window.WebToApk.setFullscreen(hidden);
}
}
video.addEventListener('play', function () { hideBars(true); });
video.addEventListener('pause', function () { hideBars(false); });
video.addEventListener('ended', function () { hideBars(false); });Notas: no hace nada en una app compilada con Pantalla completa (o Modo quiosco inmersivo) activada en el Paso 3 (Integrar): el creador bloqueó esa elección. Solo oculta las barras de Android; la barra superior propia de la app (la del botón de menú lateral) es el interruptor Mostrar barra superior. Cuando se activa, el shell añade viewport-fit=cover a tu meta viewport para que la página se maquete en toda la pantalla. Consulta toggleFullscreen y isFullscreen. Las apps creadas con IA pueden usar fullscreen de @/lib/appmintNative.
setSecureScreen puente#
window.WebToApk.setSecureScreen(enabled: Boolean): Boolean
Establece la protección para la página ACTUAL y devuelve el estado vigente. El indicador de ventana se aplica en el hilo de la interfaz, pero el estado que devuelve ya está confirmado, así que una página puede llamar a esto y revelar de inmediato su contenido sensible sin esperar a un callback.
Ejemplo
Bloquea las capturas de pantalla, la grabación de pantalla, la transmisión y la vista previa de la pantalla de recientes mientras la página ACTUAL muestra algo privado.
Devuelve: un booleano al instante: el estado vigente ahora (el valor que pasaste). Puedes revelar el contenido privado justo después de la llamada. Requiere: nada.
function showStatement() {
if (window.WebToApk && typeof window.WebToApk.setSecureScreen === 'function') {
window.WebToApk.setSecureScreen(true);
}
render(statementView);
}Libérala tú solo cuando ocultes el contenido privado SIN navegar (se cierra un modal, se cambia de pestaña dentro de una página):
function closeCardDetails() {
hide(cardDetailsModal);
if (window.WebToApk && typeof window.WebToApk.setSecureScreen === 'function') {
window.WebToApk.setSecureScreen(false);
}
}Muestra un código de recuperación de un solo uso solo cuando la captura esté bloqueada, ya que la llamada responde con el estado vigente:
function revealRecoveryCode(code) {
var W = window.WebToApk;
if (!(W && typeof W.setSecureScreen === 'function')) {
alert('Open the app to see your recovery code.');
return;
}
if (W.setSecureScreen(true)) {
document.getElementById('recovery-code').textContent = code;
}
}Notas: la protección pertenece a la página que la pidió. Se libera en cuanto la app navega a cualquier otro sitio (una página nueva, una ruta pushState o una ruta #hash), así que no hay nada que limpiar al salir, y tras recargar la página debe volver a pedirla. No la vuelvas a aplicar con un temporizador: el indicador ya debe estar activo cuando el usuario pulse los botones de captura. Nada puede impedir que una cámara apunte a la pantalla.
setStatusBarColor puente#
window.WebToApk.setStatusBarColor(color: String, style: String): Boolean
Pinta las barras de estado y de navegación con [color] y elige el tono de los iconos: [style] 'light' significa iconos claros sobre una barra oscura, 'dark' lo contrario, '' deja que decida la luminancia. [color] es `#RGB`, `#RGBA`, `#RRGGBB`, `#AARRGGBB` (alfa PRIMERO: el orden `#RRGGBBAA` de CSS también se lee como alfa primero) o un nombre de color básico ('black', 'navy'…); `rgb()`/`hsl()` no se leen. Devuelve false cuando el color no se puede interpretar.
Ejemplo
Pinta la barra de estado de Android de un color y elige iconos claros u oscuros sobre ella.
Devuelve: true, o false cuando el color no se puede interpretar (de forma síncrona). Requiere: nada.
style es 'light' (iconos claros, para una barra oscura), 'dark' (iconos oscuros, para una barra clara) o '' (el shell elige según el brillo del color):
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 automaticallyHaz que la barra coincida con cada pantalla:
function showScreen(name) {
var colors = { home: '#0B0F1A', settings: '#FFFFFF' };
paintBar(colors[name] || '#0B0F1A', name === 'settings' ? 'dark' : 'light');
}Sigue el tema claro u oscuro del teléfono, incluido un interruptor mientras la app está abierta:
var darkTheme = window.matchMedia('(prefers-color-scheme: dark)');
function matchTheme() {
if (!(window.WebToApk && typeof window.WebToApk.setStatusBarColor === 'function')) return;
if (darkTheme.matches) {
window.WebToApk.setStatusBarColor('#121212', 'light');
} else {
window.WebToApk.setStatusBarColor('#F5F5F5', 'dark');
}
}
matchTheme();
darkTheme.addEventListener('change', matchTheme);Notas: los colores son cadenas de color de Android: #RRGGBB o #AARRGGBB (y nombres como red). Las formas CSS como rgb(...) o #RGB devuelven false. También funciona en Android 15+, donde el plugin StatusBar de Capacitor no puede pintar la barra. Mientras las barras están ocultas (pantalla completa) el color se conserva para cuando vuelvan. Las apps creadas con IA pueden usar setStatusBar de @/lib/appmintNative.
splashHold puente#
window.WebToApk.splashHold()
Mantiene la pantalla de inicio de marca hasta que se llame a [splashReady]. Por defecto, la pantalla de inicio se oculta en cuanto el cuerpo tiene elementos hijos ya maquetados, lo que, para una app que dibuja un esqueleto y luego carga su primera pantalla, significa "pantalla de inicio y luego un esqueleto". Una app que prefiera revelar una primera pantalla terminada llama a esto al importar y a [splashReady] cuando esa pantalla ya está lista. Limitado al mismo respaldo de 6 s que la comprobación de dibujo, así que una página que nunca avisa de que está lista igualmente se abre.
Ejemplo
Mantiene la pantalla de inicio de la app en pantalla hasta que llames a splashReady(), para que el usuario vea tu primera pantalla terminada, no "pantalla de inicio y luego un esqueleto de carga".
Devuelve: nada. Requiere: nada.
Llámalo lo antes posible (al principio de tu primer script) y luego llama a splashReady() cuando la primera pantalla esté dibujada:
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();
});Mantén la pantalla de inicio hasta que la imagen principal de la primera pantalla se haya cargado, para que la app se abra con una imagen terminada:
if (window.WebToApk && typeof window.WebToApk.splashHold === 'function') {
window.WebToApk.splashHold();
}
function releaseSplash() {
if (window.WebToApk && window.WebToApk.splashReady) window.WebToApk.splashReady();
}
var hero = document.getElementById('hero');
if (hero.complete) {
releaseSplash();
} else {
hero.addEventListener('load', releaseSplash);
hero.addEventListener('error', releaseSplash); // a broken image must not keep the splash up
}Mantén la pantalla de inicio solo cuando la primera pantalla necesite datos, y deja que una pantalla que se dibuja al instante se abra con normalidad:
var signedIn = !!localStorage.getItem('session');
if (signedIn && window.WebToApk && typeof window.WebToApk.splashHold === 'function') {
window.WebToApk.splashHold(); // the dashboard needs its data before it looks right
}
if (signedIn) {
loadDashboard().finally(function () {
if (window.WebToApk && window.WebToApk.splashReady) window.WebToApk.splashReady();
});
} else {
showLoginScreen(); // paints at once; the splash hides by itself
}Notas: sin splashHold() la pantalla de inicio se oculta en cuanto el cuerpo de la página tiene elementos hijos visibles. Una pantalla de inicio retenida igualmente se oculta sola tras unos segundos (el mismo respaldo que la comprobación normal de dibujo), así que una página que nunca llama a splashReady() se abre igualmente. Llama siempre a splashReady() también en la ruta de error. El tiempo mínimo de pantalla de inicio del creador sigue aplicándose. Las apps creadas con IA pueden usar splash de @/lib/appmintNative.
splashReady puente#
window.WebToApk.splashReady()
La primera pantalla ya está lista: oculta ahora la pantalla de inicio (sujeto a la duración mínima de pantalla de inicio del creador). Idempotente; inofensivo cuando no se mantenía nada.
Ejemplo
Le dice al shell que tu primera pantalla ya está lista, para que la pantalla de inicio se oculte ahora.
Devuelve: nada. Requiere: nada. Se puede llamar más de una vez sin problema, y es inofensivo cuando no se mantenía nada.
Se combina con 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();Revela la pantalla solo después de que el navegador haya dibujado lo que acabas de renderizar (dos fotogramas de animación):
renderApp(document.getElementById('root'));
requestAnimationFrame(function () {
requestAnimationFrame(function () {
if (window.WebToApk && typeof window.WebToApk.splashReady === 'function') {
window.WebToApk.splashReady();
}
});
});Espera varias tareas de arranque y oculta la pantalla de inicio aunque una de ellas falle:
if (window.WebToApk && window.WebToApk.splashHold) window.WebToApk.splashHold();
Promise.allSettled([
loadProfile(), // your own start-up requests
loadFeed(),
document.fonts.ready // no flash of the wrong font
]).then(function (results) {
results.forEach(function (r) { if (r.status === 'rejected') console.warn(r.reason); });
if (window.WebToApk && typeof window.WebToApk.splashReady === 'function') {
window.WebToApk.splashReady();
}
});Notas: la duración mínima de la pantalla de inicio del creador se sigue respetando. Si nunca llamas a splashHold(), la pantalla de inicio se oculta sola cuando la página se dibuja, y splashReady() no hace nada extra.
toggleFullscreen puente#
window.WebToApk.toggleFullscreen()
Permite que la página oculte por sí misma la barra de estado (reloj/batería) y la barra de navegación, p. ej. un juego que entra en su pantalla de juego: WebToApk.setFullscreen(true) // oculta las barras del sistema WebToApk.setFullscreen(false) // las devuelve WebToApk.toggleFullscreen() WebToApk.isFullscreen() La elección se recuerda entre inicios. No hace nada en apps que el creador ya bloqueó en pantalla completa/quiosco: allí no hay nada que restaurar. El elemento "Pantalla completa" del menú lateral es el equivalente para los usuarios finales y depende de la opción enableFullscreenToggle del creador; este puente siempre está disponible para la propia página de la app.
Descrito por su grupo, Puente de pantalla completa, y no por sí mismo.
Ejemplo
Alterna las barras de estado y de navegación de Android entre ocultas y visibles.
Devuelve: nada. Requiere: nada. El nuevo estado se recuerda entre inicios.
Un botón de "pantalla completa" que también actualiza su propia etiqueta:
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
}Toca dos veces una foto o una vista de lectura para ocultar o mostrar las barras:
var viewer = document.getElementById('photo-viewer');
viewer.addEventListener('dblclick', function () {
if (window.WebToApk && typeof window.WebToApk.toggleFullscreen === 'function') {
window.WebToApk.toggleFullscreen();
}
});Alterna con la tecla F en un teclado físico, pero no mientras el usuario escribe en un campo:
document.addEventListener('keydown', function (e) {
if (e.key !== 'f' || e.target.closest('input, textarea, [contenteditable]')) return;
if (window.WebToApk && typeof window.WebToApk.toggleFullscreen === 'function') {
e.preventDefault();
window.WebToApk.toggleFullscreen();
}
});Notas: no hace nada en una app compilada con Pantalla completa o Modo quiosco inmersivo activados (las barras permanecen ocultas). El elemento "Pantalla completa" del menú lateral para los usuarios finales es un interruptor de compilación aparte (Botón de pantalla completa en el Paso 3, en Opciones de pantalla); este método del puente funciona sin él.