Crear APK gratis

API del puente de JavaScript

Vibración y sonido

Respuesta háptica, el sonido de clic propio de la app disparado por tus eventos semánticos en lugar de cada toque suelto, y la pista de música de fondo que el creador eligió al compilar: getMusicVolume devuelve -1 cuando la app no incluye ninguna, así que una pantalla de ajustes puede ocultar el control deslizante en lugar de mostrar uno inútil.

navigator.vibrate estándar web#

navigator.vibrate(pattern)

Ejemplo

Hace vibrar el teléfono, con la API web de Vibración normal. El navigator.vibrate propio de Android WebView nunca mueve el motor; la app lo reemplaza al iniciar el documento para que realmente vibre.

Devuelve: true cuando se aceptó la solicitud, false si la entrada no es válida (una duración negativa o que no es un número). Necesita: activar Vibración en el Paso 4 (Acceso) al compilar. Las apps creadas con IA la reciben activada cuando el código vibra.

function buzz(pattern) {
  if ('vibrate' in navigator) navigator.vibrate(pattern);
}

document.getElementById('save').addEventListener('click', function () {
  buzz(30);                    // one short buzz, 30 ms
});

document.getElementById('wrong').addEventListener('click', function () {
  buzz([80, 40, 80]);          // buzz 80, pause 40, buzz 80
});

document.getElementById('alarm-off').addEventListener('click', function () {
  buzz(0);                     // 0 or [] stops a running vibration
});

Usa el valor devuelto para detectar una duración incorrecta, por ejemplo una escrita por el usuario:

document.getElementById('test-buzz').addEventListener('click', function () {
  var ms = parseInt(document.getElementById('buzz-ms').value, 10);   // '' gives NaN
  if (!('vibrate' in navigator)) return;
  if (!navigator.vibrate(ms)) {
    showError('Enter a duration of 0 ms or more.');
  }
});

Un solo tic corto cuando un gesto de arrastrar para actualizar pasa el punto en que, al soltar, se actualizará:

var armed = false;
var list = document.getElementById('feed');

list.addEventListener('touchmove', function () {
  var ready = list.scrollTop <= 0 && pullDistance() > 80;
  if (ready && !armed && 'vibrate' in navigator) navigator.vibrate(15);
  armed = ready;
}, { passive: true });

list.addEventListener('touchend', function () {
  if (armed) refreshFeed();
  armed = false;
});

Notas:

  • El patrón es el de la web: el primer número es una vibración, luego pausa, vibración, pausa... (la app lo convierte para Android).
  • Si Vibración no se activó al compilar, la llamada igualmente devuelve true pero el teléfono no vibra.
  • Los decimales se redondean hacia abajo. En un arreglo, los números negativos cuentan como 0.
  • En un navegador de escritorio navigator.vibrate puede no existir, así que conserva la comprobación 'vibrate' in navigator.

cancelVibrate puente#

window.WebToApk.cancelVibrate()

Hápticos sencillos para la página (navigator.vibrate y una forma con patrón). Ambos se ignoran a menos que la compilación haya activado el permiso Vibrar.

Se describe por su grupo, Vibración, y no por separado.

Ejemplo

Detiene una vibración que sigue en marcha. La mayoría de las páginas usan el estándar navigator.vibrate(0), que llama a esto por ti.

Devuelve: nada. Requiere: activa Vibrar en el Paso 4 (Acceso) al compilar.

La forma estándar:

if ('vibrate' in navigator) navigator.vibrate(0);   // or navigator.vibrate([])

La llamada directa, por ejemplo para detener un patrón largo de alarma cuando el usuario toca "Descartar":

if (window.WebToApk && window.WebToApk.vibratePattern) {
  window.WebToApk.vibratePattern(JSON.stringify([1000, 500, 1000, 500, 1000]));
}

document.getElementById('dismiss').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.cancelVibrate === 'function') {
    window.WebToApk.cancelVibrate();
  }
});

Detén una vibración en curso cuando el usuario salga de la pantalla, para que el teléfono no siga vibrando en su bolsillo:

document.addEventListener('visibilitychange', function () {
  if (!document.hidden) return;
  if (window.WebToApk && typeof window.WebToApk.cancelVibrate === 'function') {
    window.WebToApk.cancelVibrate();
  }
});

Notas: Es seguro llamarlo cuando nada está vibrando.

getMusicVolume puente#

window.WebToApk.getMusicVolume(): Int

Nivel actual de 0 a 100, o -1 cuando esta app no incluye música de fondo.

Ejemplo

Lee el nivel de la música de fondo en bucle de la app, de 0 a 100.

Devuelve: un número, de forma síncrona: 0-100, o -1 cuando esta app se compiló sin un archivo de música. Necesita: un archivo elegido en Música de fondo en el Paso 3 (Integrar), dentro de Opciones de pantalla, al compilar.

Muestra un control deslizante de música solo cuando hay música:

var slider = document.getElementById('music-slider');
var level = -1;
if (window.WebToApk && typeof window.WebToApk.getMusicVolume === 'function') {
  level = window.WebToApk.getMusicVolume();
}

if (level < 0) {
  slider.parentNode.hidden = true;          // no music in this app: hide the setting
} else {
  slider.value = level;
  slider.addEventListener('input', function () {
    window.WebToApk.setMusicVolume(parseInt(slider.value, 10));
  });
}

Un botón de silencio que vuelve al nivel propio del usuario al reactivar el sonido (la app también recuerda 0, así que conserva tú el nivel anterior):

function musicLevel() {
  return window.WebToApk && typeof window.WebToApk.getMusicVolume === 'function'
    ? window.WebToApk.getMusicVolume() : -1;
}
var muteBtn = document.getElementById('mute');

function paint() {
  var v = musicLevel();
  muteBtn.hidden = v < 0;
  muteBtn.textContent = v === 0 ? 'Unmute' : 'Mute';
}

muteBtn.addEventListener('click', function () {
  var v = musicLevel();
  if (v < 0) return;
  if (v > 0) {
    try { localStorage.setItem('musicBefore', String(v)); } catch (e) {}
    window.WebToApk.setMusicVolume(0);
  } else {
    var back = 40;
    try { back = parseInt(localStorage.getItem('musicBefore') || '40', 10); } catch (e) {}
    window.WebToApk.setMusicVolume(back);
  }
  paint();
});
paint();

Atenúa la música desde su nivel actual hasta uno nuevo, por ejemplo durante una escena de diálogo:

function fadeMusicTo(target, ms) {
  if (!window.WebToApk || typeof window.WebToApk.getMusicVolume !== 'function') return;
  var start = window.WebToApk.getMusicVolume();
  if (start < 0 || start === target) return;
  var steps = 10, i = 0;
  var t = setInterval(function () {
    i++;
    window.WebToApk.setMusicVolume(Math.round(start + (target - start) * i / steps));
    if (i === steps) clearInterval(t);
  }, ms / steps);
}

fadeMusicTo(10, 800);     // dim under the dialogue
// later: fadeMusicTo(60, 800);  the last level set is the one kept for the next launch

Notas: Devuelve el nivel fijado por el control deslizante de la compilación o por tu última llamada a setMusicVolume. Cuando otra app o una llamada toma el audio, la música baja de volumen o se pausa sola; el número que lees no cambia.

getVolume puente#

window.WebToApk.getVolume(stream: String): String

El nivel de un flujo: `{stream, current, max, percent, muted}` como cadena JSON. stream: 'media' | 'alarm' | 'ring' | 'notification' | 'call'.

Ejemplo

Lee uno de los niveles de volumen del propio teléfono.

Devuelve: una cadena JSON, de forma síncrona: {"stream":"alarm","current":5,"max":7,"percent":71,"muted":false} - current/max son los pasos propios del teléfono (varían entre teléfonos), percent es 0-100. stream es 'media', 'alarm', 'ring', 'notification' o 'call'; cualquier otro nombre da {"error":"unknown_stream"}. Necesita: nada.

function warnIfAlarmSilent() {
  if (!(window.WebToApk && window.WebToApk.getVolume)) return;
  const v = JSON.parse(WebToApk.getVolume('alarm'));
  if (!v.error && v.percent === 0) {
    showMessage('Your alarm volume is off, so the alarm will not be heard.');
  }
}
warnIfAlarmSilent();

Muestra todos los niveles en una pantalla de ajustes:

['media', 'alarm', 'ring', 'notification'].forEach(function (s) {
  const v = JSON.parse(WebToApk.getVolume(s));
  document.getElementById('vol-' + s).value = v.percent;
});

Ajusta un volumen y lee el resultado. setVolume(stream, percent, showUi) responde con una cadena JSON.

function setAlarmVolume(percent) {
  if (!(window.WebToApk && WebToApk.setVolume)) return;
  const r = JSON.parse(WebToApk.setVolume('alarm', percent, true));
  if (!r.ok) showMessage('Could not change the volume: ' + r.error);   // fixed_volume, muted, ...
  else document.getElementById('vol-alarm').value = r.percent;
}

playClick puente#

window.WebToApk.playClick()

Reproduce el sonido al tocar de la app a demanda, para que una página pueda dispararlo en sus PROPIOS eventos semánticos (la pulsación de un botón) en lugar de en cada toque suelto, que es lo que hace realmente una app nativa y la razón por la que clickSoundMode puede ser "off" y esto seguir funcionando. No hace nada solo cuando la app se compiló SIN sonido al tocar (enableClickSound desactivado). Con clickSoundMode "off" (sin sonido automático en cada toque) una llamada explícita aún suena: ese modo existe para apps que suenan en sus propios eventos. En los demás casos reproduce la respuesta configurada por el creador.

Ejemplo

Reproduce una vez el sonido al tocar de la app, para que tus propios botones suenen como los de una app nativa.

Devuelve: nada. Necesita: el interruptor Sonido al tocar en el Paso 3 (Integrar), dentro de Opciones de pantalla, al compilar (activado por defecto). Con el interruptor desactivado, esto no hace nada.

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

document.querySelectorAll('button, .tab').forEach(function (el) {
  el.addEventListener('click', clickSound);
});

Un solo oyente para todo un tablero de juego, que suena solo en un toque que hace algo:

document.getElementById('board').addEventListener('click', function (e) {
  var cell = e.target.closest('.cell');
  if (!cell || cell.classList.contains('taken')) return;   // a dead tap stays silent
  if (window.WebToApk && typeof window.WebToApk.playClick === 'function') {
    window.WebToApk.playClick();
  }
  placeMark(cell);
});

Da a tus usuarios su propio interruptor de "Sonidos" además del ajuste de la compilación:

var toggle = document.getElementById('sounds-toggle');
var soundsOn = true;
try { soundsOn = localStorage.getItem('sounds') !== 'off'; } catch (e) {}
toggle.checked = soundsOn;

toggle.addEventListener('change', function () {
  soundsOn = toggle.checked;
  try { localStorage.setItem('sounds', soundsOn ? 'on' : 'off'); } catch (e) {}
});

function tapSound() {
  if (soundsOn && window.WebToApk && typeof window.WebToApk.playClick === 'function') {
    window.WebToApk.playClick();
  }
}

Notas: Lo que se oye depende de la opción Sonido al tocar:

  • Seguir el ajuste del teléfono (recomendado): el clic del sistema. Es silencioso cuando el usuario desactivó "Sonidos al tocar" en los ajustes del teléfono. Es a propósito.
  • Reproducir siempre en cada toque: el clic propio de la app, siempre audible. Cada toque en la página también hace clic, así que normalmente no necesitas playClick() en este modo.

Llámalo desde la pulsación real de un botón, no desde un temporizador.

requestDndAccess puente#

window.WebToApk.requestDndAccess(): String

Acceso a No molestar, necesario para cambiar el nivel de timbre / notificaciones mientras No molestar está activo. "granted" | "opened" (se abrió la pantalla de ajustes) | "not_declared".

Ejemplo

Pide acceso a No molestar, que Android exige antes de que una app pueda cambiar el volumen del timbre o de las notificaciones mientras No molestar está activado (setVolume responde entonces dnd_access_required).

Devuelve: una cadena: 'granted' (ya permitido), 'opened' (se abrió la pantalla del sistema donde el usuario lo permite; vuelve a comprobar cuando regrese) o 'not_declared' (esta compilación no incluye el permiso, así que Android no listaría la app allí). Necesita: llamar a requestDndAccess en el código de tu app: la compilación declara entonces ACCESS_NOTIFICATION_POLICY automáticamente.

function allowRingVolume() {
  if (!(window.WebToApk && window.WebToApk.requestDndAccess)) return;
  const r = WebToApk.requestDndAccess();
  if (r === 'opened') showMessage('Allow Do Not Disturb access for this app, then come back.');
  if (r === 'not_declared') showMessage('Turn off Do Not Disturb to change the ring volume.');
}

Reintenta el cambio de volumen una vez que el usuario haya permitido el acceso:

function setRing(percent) {
  const r = JSON.parse(WebToApk.setVolume('ring', percent, true));
  if (r.ok || r.error !== 'dnd_access_required') return r;
  const a = WebToApk.requestDndAccess();     // 'granted' | 'opened' | 'not_declared'
  if (a === 'granted') return JSON.parse(WebToApk.setVolume('ring', percent, true));
  showMessage(a === 'opened' ? 'Allow access, then press the button again.' : 'Turn off Do Not Disturb first.');
  return r;
}

Silencia el timbre con un interruptor de "modo reunión" y explica por qué cuando no puede funcionar:

function meetingMode(on) {
  if (!(window.WebToApk && WebToApk.setVolume)) return;
  const r = JSON.parse(WebToApk.setVolume('ring', on ? 0 : 70, false));
  if (!r.ok && r.error === 'dnd_access_required') {
    const a = WebToApk.requestDndAccess();
    if (a === 'not_declared') showMessage('This build cannot change the ring volume while Do Not Disturb is on.');
  }
}

setMusicVolume puente#

window.WebToApk.setMusicVolume(percent: Int)

Fija el nivel de la música de fondo desde la página, de 0 a 100. El control deslizante del asistente decide cómo INICIA la app; esto es cómo una app da a sus propios usuarios un control de música, que cualquier juego con pantalla de ajustes debe tener. Se limita en [BackgroundMusic.setVolume], así que una página que pase 500 o -1 no puede darle a MediaPlayer una ganancia fuera del rango que acepta. El nivel se RECUERDA: el siguiente inicio comienza con él, no con el nivel del asistente. No hace nada cuando la app se compiló sin pista; no hay nada que fijar, y la respuesta honesta es que [getMusicVolume] devuelva -1 para que la página pueda ocultar su control deslizante en lugar de mostrar uno que no hace nada.

Ejemplo

Cambia el nivel de la música de fondo en bucle de la app, de 0 a 100, por ejemplo desde la pantalla de ajustes de tu juego.

Devuelve: nada. Requiere: un archivo elegido en Música de fondo en el Paso 3 (Integrar), dentro de Opciones de pantalla, al compilar. Sin música esto no hace nada (comprueba getMusicVolume() para -1).

Un botón de silencio: la app recuerda el nivel para el próximo inicio:

function hasMusic() {
  return !!(window.WebToApk && window.WebToApk.getMusicVolume) && window.WebToApk.getMusicVolume() >= 0;
}

if (!hasMusic()) document.getElementById('mute').hidden = true;

document.getElementById('mute').addEventListener('click', function () {
  if (!hasMusic()) return;
  // clamped to 0..100 by the app, and kept for the next launch
  window.WebToApk.setMusicVolume(window.WebToApk.getMusicVolume() > 0 ? 0 : 40);
});

Botones Apagado / Bajo / Alto en una pantalla de ajustes:

var LEVELS = { off: 0, low: 25, high: 70 };

document.querySelectorAll('[data-music]').forEach(function (btn) {
  btn.addEventListener('click', function () {
    if (!window.WebToApk || typeof window.WebToApk.setMusicVolume !== 'function') return;
    if (window.WebToApk.getMusicVolume() < 0) return;       // built without music
    window.WebToApk.setMusicVolume(LEVELS[btn.dataset.music]);
    markSelected(btn);
  });
});

Baja la música mientras se reproduce tu propio video y luego devuélvela (el nivel se recuerda, así que siempre restáuralo):

var video = document.getElementById('intro-video');
var before = -1;

video.addEventListener('play', function () {
  if (!window.WebToApk || typeof window.WebToApk.getMusicVolume !== 'function') return;
  before = window.WebToApk.getMusicVolume();
  if (before > 0) window.WebToApk.setMusicVolume(Math.round(before / 4));
});

function restoreMusic() {
  if (before > 0) window.WebToApk.setMusicVolume(before);
  before = -1;
}
video.addEventListener('pause', restoreMusic);
video.addEventListener('ended', restoreMusic);

Notas: El nivel que fijas se guarda: el siguiente inicio comienza con él en lugar del nivel del control deslizante de la compilación (hasta que el usuario borre los datos de la app). 0 silencia la música, pero sigue reproduciéndose.

setVolume puente#

window.WebToApk.setVolume(stream: String, percent: Double, showUi: Boolean): String

Ajusta una salida a [percent] (0-100); [showUi] muestra el control deslizante de volumen del teléfono. `{ok:true, stream, current, max, percent}` o `{ok:false, error}` - error es unknown_stream | bad_percent | fixed_volume | dnd_access_required | muted | not_applied | failed | foreign_origin (una página fuera del sitio propio de la app).

Ejemplo

Ajusta uno de los niveles de volumen propios del teléfono, de 0 a 100: por ejemplo subir la alarma para un despertador, o un control deslizante de medios en una app de música.

Devuelve: una cadena JSON: {"ok":true,"stream":"alarm","current":6,"max":7,"percent":86}, o {"ok":false,"error":"…"} donde error es unknown_stream, bad_percent, fixed_volume (este dispositivo no tiene volumen ajustable), dnd_access_required (No molestar está activado y bloquea el nivel del timbre / notificaciones - consulta requestDndAccess()), foreign_origin (una página fuera del sitio propio de tu app) o failed. showUi: true muestra el control de volumen del propio teléfono mientras se mueve. Requiere: nada para multimedia, alarma y llamada; el timbre y las notificaciones pueden necesitar acceso a No molestar mientras esté activo.

function setAlarmLoud() {
  if (!(window.WebToApk && window.WebToApk.setVolume)) return;
  const r = JSON.parse(WebToApk.setVolume('alarm', 80, true));
  if (!r.ok) showMessage('Could not change the alarm volume: ' + r.error);
}

document.getElementById('ring-slider').oninput = function (e) {
  const r = JSON.parse(WebToApk.setVolume('ring', Number(e.target.value), false));
  if (r.error === 'dnd_access_required' && WebToApk.requestDndAccess() === 'not_declared') {
    showMessage('Turn off Do Not Disturb to change the ring volume.');
  }
};

Lee un nivel y luego ajusta un control deslizante de medios:

if (window.WebToApk && WebToApk.setVolume) {
  const now = JSON.parse(WebToApk.getVolume('media'));   // {stream, current, max, percent, muted}
  slider.value = now.percent;
  slider.oninput = () => {
    const r = JSON.parse(WebToApk.setVolume('media', Number(slider.value), false));
    if (r.ok) label.textContent = r.percent + '%';
    else if (r.error === 'fixed_volume') slider.disabled = true;   // this phone has no adjustable volume
  };
}

Muestra el nivel actual del timbre y reacciona cuando cambia:

if (window.WebToApk && WebToApk.getVolume) {
  const ring = JSON.parse(WebToApk.getVolume('ring'));   // {stream, current, max, percent, muted}
  document.getElementById('ring-slider').value = ring.percent;
  WebToApk.watchVolume(true);
  window.addEventListener('appmint:volume', (e) => {
    if (e.detail.stream === 'ring') document.getElementById('ring-slider').value = e.detail.percent;
  });
}

Notas: el valor se redondea al paso más cercano del teléfono, y la respuesta incluye el nivel realmente aplicado. La salida de llamadas nunca baja de su mínimo (normalmente 1).

vibrate puente#

window.WebToApk.vibrate(durationMs: Long)

Hápticos sencillos para la página (navigator.vibrate y una forma con patrón). Ambos se ignoran a menos que la compilación haya activado el permiso Vibrar.

Se describe por su grupo, Vibración, y no por separado.

Ejemplo

Hace vibrar el teléfono una vez durante los milisegundos indicados. La mayoría de las páginas usan el estándar navigator.vibrate(ms), que llama a esto por ti.

Devuelve: nada. Requiere: activa Vibrar en el Paso 4 (Acceso) al compilar. Sin eso la llamada no hace nada.

La forma estándar (funciona igual en un navegador):

if ('vibrate' in navigator) navigator.vibrate(50);

La llamada en bruto:

document.getElementById('like').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.vibrate === 'function') {
    window.WebToApk.vibrate(40);      // 40 ms
  }
});

// 0 (or any value <= 0) stops a running vibration
if (window.WebToApk && window.WebToApk.vibrate) window.WebToApk.vibrate(0);

Un pequeño toque cada vez que un control deslizante salta a un nuevo paso, como un selector nativo:

var slider = document.getElementById('size');
var last = slider.value;
slider.addEventListener('input', function () {
  if (slider.value === last) return;          // same step, no tick
  last = slider.value;
  if (window.WebToApk && typeof window.WebToApk.vibrate === 'function') {
    window.WebToApk.vibrate(10);              // 10 ms: a light tick
  }
});

Una vibración más larga cuando no se puede enviar un formulario, para que el usuario sienta el error:

document.getElementById('signup').addEventListener('submit', function (e) {
  if (this.checkValidity()) return;
  e.preventDefault();
  if (window.WebToApk && typeof window.WebToApk.vibrate === 'function') {
    window.WebToApk.vibrate(250);
  }
  this.reportValidity();
});

Notas: Pasa un número entero. La intensidad es la predeterminada del teléfono; para toques más fuertes o más suaves usa Capacitor.Plugins.Haptics.impact o Capacitor.Plugins.AppwrightHaptics.play. Para un patrón de vibración-pausa-vibración usa vibratePattern.

vibratePattern puente#

window.WebToApk.vibratePattern(patternJson: String)

Vibra con un patrón de encendido/apagado, p. ej. "[100,50,100]" = vibra 100 ms, pausa 50, vibra 100. Respalda navigator.vibrate(array); prefiere la API estándar. No hace nada a menos que el creador haya activado el permiso de Vibración.

Ejemplo

Hace vibrar un patrón de encendido/apagado, dado como cadena JSON. La mayoría de las páginas usan el estándar navigator.vibrate([...]), que llama a esto por ti.

Devuelve: nada. Requiere: activa Vibrar en el Paso 4 (Acceso) al compilar. Sin eso la llamada no hace nada.

La forma estándar:

if ('vibrate' in navigator) navigator.vibrate([100, 50, 100]);   // buzz, pause, buzz

La llamada sin procesar recibe el mismo patrón estilo web como cadena JSON:

function pattern(list) {
  if (window.WebToApk && typeof window.WebToApk.vibratePattern === 'function') {
    window.WebToApk.vibratePattern(JSON.stringify(list));
  }
}

pattern([200]);                     // one 200 ms buzz
pattern([100, 50, 100, 50, 300]);   // short, short, long
pattern([]);                        // empty list stops a running vibration

El patrón se reproduce una vez, así que una alarma lo repite por sí misma hasta que el usuario la detiene:

var alarm = null;
var ALARM = JSON.stringify([500, 300, 500, 300, 500]);   // about 2.1 s

function startAlarm() {
  if (!window.WebToApk || typeof window.WebToApk.vibratePattern !== 'function') return;
  window.WebToApk.vibratePattern(ALARM);
  alarm = setInterval(function () { window.WebToApk.vibratePattern(ALARM); }, 3000);
}

function stopAlarm() {
  if (alarm === null) return;
  clearInterval(alarm);
  alarm = null;
  window.WebToApk.vibratePattern('[]');                 // stop the buzz that is still running
}

Notas: El primer número es una vibración, luego pausa, vibración, pausa... (orden web). La app añade por ti la "espera" inicial de Android, así que no pongas un 0 al principio: [0, 100] significa "vibrar 0 ms, pausa de 100 ms" y no sentirás nada. Los números negativos cuentan como 0. El patrón se reproduce una vez.

watchVolume puente#

window.WebToApk.watchVolume(on: Boolean): Boolean

Activa (true) o desactiva (false) los eventos `appmint:volume`.

Ejemplo

Activa o desactiva los eventos de cambio de volumen en vivo: mientras está activado, cada cambio de un nivel de volumen (las teclas físicas, el control deslizante del sistema, otra app) llega a la página como appmint:volume.

Devuelve: true cuando el cambio surtió efecto. Necesita: nada.

if (window.WebToApk && window.WebToApk.watchVolume) {
  window.addEventListener('appmint:volume', function (e) {
    // e.detail = { stream: 'media', current: 9, max: 15, percent: 60, muted: false }
    document.getElementById('vol-' + e.detail.stream).value = e.detail.percent;
  });
  WebToApk.watchVolume(true);
  window.addEventListener('pagehide', function () { WebToApk.watchVolume(false); });
}

El mismo objeto también llega a un gancho de página, si prefieres uno:

window.onAppMintVolume = function (level) {
  if (level.stream === 'alarm' && level.percent === 0) showMessage('Alarm volume is now off.');
};
WebToApk.watchVolume(true);

Muestra el nivel una vez con getVolume, y luego mantenlo actualizado solo mientras la pantalla esté visible:

if (window.WebToApk && WebToApk.watchVolume && WebToApk.getVolume) {
  const meter = document.getElementById('media-meter');
  meter.value = JSON.parse(WebToApk.getVolume('media')).percent;
  const onVol = function (e) { if (e.detail.stream === 'media') meter.value = e.detail.percent; };
  document.addEventListener('visibilitychange', function () {
    if (document.hidden) { WebToApk.watchVolume(false); window.removeEventListener('appmint:volume', onVol); }
    else { window.addEventListener('appmint:volume', onVol); WebToApk.watchVolume(true); }
  });
}

Notas: un evento por cada flujo que cambió (media, alarm, ring, notification, call). Desactívalo cuando se cierre la pantalla que muestra los niveles.

Generado a partir del runtime de la app y sus archivos de ejemplo en cada compilación de la documentación. Léelo como Markdown · Todas las familias.

Comprobado con el puente publicado el 2026-10-22.