API da ponte JavaScript
Vibração e som
Feedback tátil, o som de clique do próprio app disparado nos seus eventos semânticos em vez de em cada toque perdido, e a trilha de música de fundo que o criador escolheu na compilação - getMusicVolume retorna -1 quando o app não traz nenhuma, para que uma tela de configurações possa ocultar o controle deslizante em vez de mostrar um sem função.
cancelVibrate ponte#
window.WebToApk.cancelVibrate()
Vibração simples para a página (navigator.vibrate e uma forma com padrão). Ambas são ignoradas, a menos que o build tenha ativado a permissão de Vibrar.
Descrito pelo seu grupo, Vibração, e não isoladamente.
Exemplo
Interrompe uma vibração que ainda está em andamento. A maioria das páginas usa o navigator.vibrate(0) padrão, que chama isto para você.
Retorna: nada. Requer: ative Vibrar na Etapa 4 (Acesso) ao compilar.
A forma padrão:
if ('vibrate' in navigator) navigator.vibrate(0); // or navigator.vibrate([])A chamada direta, por exemplo para parar um padrão de alarme longo quando o usuário toca em "Dispensar":
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();
}
});Pare uma vibração em andamento quando o usuário sair da tela, para o celular não ficar vibrando no bolso dele:
document.addEventListener('visibilitychange', function () {
if (!document.hidden) return;
if (window.WebToApk && typeof window.WebToApk.cancelVibrate === 'function') {
window.WebToApk.cancelVibrate();
}
});Observações: É seguro chamar quando nada está vibrando.
getMusicVolume ponte#
window.WebToApk.getMusicVolume(): Int
Nível atual de 0 a 100, ou -1 quando este app não inclui música de fundo.
Exemplo
Lê o nível da música de fundo em loop do app, de 0 a 100.
Retorna: um número, de forma síncrona: 0-100, ou -1 quando este app foi compilado sem um arquivo de música. Requer: um arquivo escolhido em Música de fundo na Etapa 3 (Integrar), em Opções de exibição, ao compilar.
Mostre um controle deslizante de música somente quando houver 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));
});
}Um botão de silenciar que volta ao nível do próprio usuário ao reativar o som (o app lembra 0 também, então guarde você mesmo o nível 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();Faça a música passar gradualmente do nível atual para um novo, por exemplo sob uma cena 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 launchObservações: Retorna o nível definido pelo controle deslizante do build ou pela sua última chamada de setMusicVolume. Quando outro app ou uma ligação assume o áudio, a música abaixa ou pausa sozinha; o número que você lê não muda.
getVolume ponte#
window.WebToApk.getVolume(stream: String): String
O nível de um stream: `{stream, current, max, percent, muted}` como string JSON. stream: 'media' | 'alarm' | 'ring' | 'notification' | 'call'.
Exemplo
Lê um dos níveis de volume do próprio celular.
Retorna: uma string JSON, de forma síncrona: {"stream":"alarm","current":5,"max":7,"percent":71,"muted":false} - current/max são os passos do próprio celular (diferem entre celulares), percent vai de 0 a 100. stream é 'media', 'alarm', 'ring', 'notification' ou 'call'; qualquer outro nome dá {"error":"unknown_stream"}. Requer: 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();Mostre todos os níveis em uma tela de configurações:
['media', 'alarm', 'ring', 'notification'].forEach(function (s) {
const v = JSON.parse(WebToApk.getVolume(s));
document.getElementById('vol-' + s).value = v.percent;
});Defina um volume e leia o resultado. setVolume(stream, percent, showUi) responde com uma string 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 ponte#
window.WebToApk.playClick()
Toca o som de toque do app sob demanda, para que uma página possa disparar nos SEUS próprios eventos semânticos (um toque em botão) em vez de em cada toque aleatório - que é o que um app nativo realmente faz, e o motivo de clickSoundMode poder ser "off" e isto ainda funcionar. Não faz nada apenas quando o app foi compilado SEM som de toque (enableClickSound desligado). Em clickSoundMode "off" - sem som automático a cada toque - uma chamada explícita ainda toca: esse modo existe para apps que tocam som nos próprios eventos. Caso contrário, toca o feedback configurado pelo criador.
Exemplo
Toca o som de toque do app uma vez, para que seus próprios botões façam clique como um app nativo.
Retorna: nada. Requer: a chave Som de toque na Etapa 3 (Integrar), em Opções de exibição, ao compilar (ativada por padrão). Com a chave desligada, isto não faz 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);
});Um único listener para um tabuleiro de jogo inteiro, clicando só em um toque que faz alguma coisa:
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);
});Dê aos seus usuários a própria chave de "Sons" além da configuração do build:
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();
}
}Observações: O que você ouve depende da escolha em Som de toque:
- Seguir a configuração do celular (recomendado): o clique do sistema. Fica em silêncio quando o usuário desligou "Sons de toque" nas configurações do celular. Isso é intencional.
- Sempre tocar em cada toque: o clique do próprio app, sempre audível. Cada toque na página também faz um clique, então você normalmente não precisa de
playClick()neste modo.
Chame a partir de um toque real em um botão, não de um temporizador.
requestDndAccess ponte#
window.WebToApk.requestDndAccess(): String
Acesso a Não perturbe, necessário para mudar o nível de toque / notificação enquanto o Não perturbe está ativo. "granted" | "opened" (a tela de configurações foi aberta) | "not_declared".
Exemplo
Pede acesso ao Não perturbe, que o Android exige antes de um app poder mudar o volume do toque ou das notificações enquanto o Não perturbe está ligado (setVolume responde dnd_access_required nesse caso).
Retorna: uma string: 'granted' (já permitido), 'opened' (a tela do sistema onde o usuário permite foi aberta - confira de novo quando ele voltar) ou 'not_declared' (esta compilação não inclui a permissão, então o Android não listaria o app lá). Requer: chamar requestDndAccess no código do seu app: a compilação então declara ACCESS_NOTIFICATION_POLICY automaticamente.
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.');
}Tente de novo a mudança de volume depois que o usuário permitir o acesso:
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;
}Silencie o toque com uma chave de "modo reunião" e diga o motivo quando não for possível:
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 ponte#
window.WebToApk.setMusicVolume(percent: Int)
Define o nível da música de fundo a partir da página, de 0 a 100. O controle deslizante do assistente decide como o app COMEÇA; é assim que um app dá aos próprios usuários um controle de música, algo que qualquer jogo com tela de configurações deve ter. Limitado em [BackgroundMusic.setVolume], então uma página que passe 500 ou -1 não consegue dar ao MediaPlayer um ganho fora da faixa aceita. O nível é LEMBRADO: a próxima abertura começa nele, não no nível do assistente. Não faz nada quando o app foi compilado sem faixa - não há o que definir, e a resposta honesta é [getMusicVolume] retornar -1, para a página poder esconder o controle em vez de mostrar um que não faz nada.
Exemplo
Muda o nível da música de fundo em repetição do app, de 0 a 100, por exemplo na tela de configurações do seu jogo.
Retorna: nada. Requer: um arquivo escolhido em Música de fundo na Etapa 3 (Integrar), em Opções de exibição, ao compilar. Sem música isto não faz nada (verifique getMusicVolume() para -1).
Um botão de silenciar - o app lembra o nível para a próxima abertura:
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);
});Botões Desligado / Baixa / Alta em uma tela de configurações:
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);
});
});Abaixe a música enquanto seu próprio vídeo toca e depois coloque-a de volta (o nível é lembrado, então sempre restaure):
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);Observações: O nível definido é salvo: a próxima abertura começa nele, em vez do nível do controle deslizante do build (até o usuário limpar os dados do app). 0 deixa a música em silêncio, mas ela continua tocando.
setVolume ponte#
window.WebToApk.setVolume(stream: String, percent: Double, showUi: Boolean): String
Define um fluxo em [percent] (0-100); [showUi] mostra o controle deslizante de volume do celular. `{ok:true, stream, current, max, percent}` ou `{ok:false, error}` - error é unknown_stream | bad_percent | fixed_volume | dnd_access_required | muted | not_applied | failed | foreign_origin (uma página fora do site do próprio app).
Exemplo
Define um dos níveis de volume do próprio celular, de 0 a 100 - por exemplo aumentando o volume do alarme para um despertador, ou um controle deslizante de mídia em um app de música.
Retorna: uma string JSON: {"ok":true,"stream":"alarm","current":6,"max":7,"percent":86}, ou {"ok":false,"error":"…"} em que error é unknown_stream, bad_percent, fixed_volume (este dispositivo não tem volume ajustável), dnd_access_required (o Não perturbe está ligado e bloqueia o nível do toque / notificação - veja requestDndAccess()), foreign_origin (uma página fora do site do seu próprio app) ou failed. showUi: true mostra o controle de volume do próprio celular enquanto ele se move. Requer: nada para mídia, alarme e chamada; toque e notificação podem precisar de acesso ao Não perturbe enquanto ele estiver ligado.
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.');
}
};Leia um nível e depois ajuste um controle deslizante de mídia:
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
};
}Mostre o nível atual do toque e reaja quando ele mudar:
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;
});
}Observações: o valor é arredondado para o passo mais próximo do celular, e a resposta traz o nível realmente definido. O fluxo de chamadas nunca fica abaixo do mínimo (geralmente 1).
vibrate ponte#
window.WebToApk.vibrate(durationMs: Long)
Vibração simples para a página (navigator.vibrate e uma forma com padrão). Ambas são ignoradas, a menos que o build tenha ativado a permissão de Vibrar.
Descrito pelo seu grupo, Vibração, e não isoladamente.
Exemplo
Vibra o celular uma vez pelo número de milissegundos informado. A maioria das páginas usa o navigator.vibrate(ms) padrão, que chama isto para você.
Retorna: nada. Requer: ative Vibrar na Etapa 4 (Acesso) ao compilar. Sem isso a chamada não faz nada.
O jeito padrão (funciona igual em um navegador):
if ('vibrate' in navigator) navigator.vibrate(50);A chamada bruta:
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);Um pequeno toque cada vez que um controle deslizante encaixa em um novo passo, como um seletor 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
}
});Uma vibração mais longa quando um formulário não pode ser enviado, para o usuário sentir o erro:
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();
});Observações: Passe um número inteiro. A intensidade é a padrão do celular; para toques mais fortes/fracos use Capacitor.Plugins.Haptics.impact ou Capacitor.Plugins.AppwrightHaptics.play. Para um padrão vibra-pausa-vibra use vibratePattern.
vibratePattern ponte#
window.WebToApk.vibratePattern(patternJson: String)
Vibra um padrão de liga/desliga, por ex. "[100,50,100]" = vibra 100 ms, pausa 50, vibra 100. Sustenta navigator.vibrate(array); prefira a API padrão. Sem efeito a menos que o criador tenha ativado a permissão Vibrar.
Exemplo
Vibra um padrão de liga/desliga, informado como string JSON. A maioria das páginas usa o navigator.vibrate([...]) padrão, que chama isto para você.
Retorna: nada. Requer: ative Vibrar na Etapa 4 (Acesso) ao compilar. Sem isso a chamada não faz nada.
A forma padrão:
if ('vibrate' in navigator) navigator.vibrate([100, 50, 100]); // buzz, pause, buzzA chamada bruta recebe o mesmo padrão estilo web como uma string 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 vibrationO padrão toca uma vez, então um alarme o repete por conta própria até o usuário parar:
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
}Observações: O primeiro número é uma vibração, depois pausa, vibração, pausa ... (ordem da web). O app adiciona por você a "espera" inicial do Android, então não coloque um 0 no começo: [0, 100] significa "vibrar 0 ms, pausar 100 ms" e você não sente nada. Números negativos contam como 0. O padrão toca uma vez.
watchVolume ponte#
window.WebToApk.watchVolume(on: Boolean): Boolean
Liga (true) ou desliga (false) os eventos `appmint:volume`.
Exemplo
Liga ou desliga os eventos de mudança de volume ao vivo: enquanto ligado, toda mudança em um nível de volume (as teclas físicas, o controle deslizante do sistema, outro app) chega à página como appmint:volume.
Retorna: true quando a mudança surtiu efeito. Requer: 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); });
}O mesmo objeto também alcança um gancho de página, se você preferir:
window.onAppMintVolume = function (level) {
if (level.stream === 'alarm' && level.percent === 0) showMessage('Alarm volume is now off.');
};
WebToApk.watchVolume(true);Mostre o nível uma vez com getVolume e depois mantenha-o atualizado só enquanto a tela estiver visível:
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); }
});
}Observações: um evento para cada stream que mudou (media, alarm, ring, notification, call). Desligue quando a tela que mostra os níveis for fechada.