JavaScript bridge API
Вибрация и звук
Тактильный отклик, собственный звук щелчка приложения по вашим смысловым событиям, а не по каждому случайному касанию, и фоновая музыка, выбранная создателем при сборке: getMusicVolume возвращает -1, если в приложении её нет, поэтому экран настроек может скрыть ползунок, а не показывать нерабочий.
cancelVibrate мост#
window.WebToApk.cancelVibrate()
Простая тактильная отдача для страницы (navigator.vibrate и форма с шаблоном). Обе игнорируются, если в сборке не включено разрешение Vibrate.
Описывается в своей группе, Вибрация, а не отдельно.
Пример
Останавливает ещё идущую вибрацию. Большинство страниц используют стандартный navigator.vibrate(0), который вызывает это за вас.
Возвращает: ничего. Требуется: включите Вибрацию на шаге 4 (Доступ) при сборке.
Стандартный способ:
if ('vibrate' in navigator) navigator.vibrate(0); // or navigator.vibrate([])Прямой вызов, например чтобы остановить длинный шаблон сигнала, когда пользователь нажимает «Закрыть»:
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();
}
});Остановите идущую вибрацию, когда пользователь покидает экран, чтобы телефон не продолжал жужжать у него в кармане:
document.addEventListener('visibilitychange', function () {
if (!document.hidden) return;
if (window.WebToApk && typeof window.WebToApk.cancelVibrate === 'function') {
window.WebToApk.cancelVibrate();
}
});Примечания: Безопасно вызывать, когда ничего не вибрирует.
getMusicVolume мост#
window.WebToApk.getMusicVolume(): Int
Текущий уровень 0..100 или -1, если в этом приложении нет фоновой музыки.
Пример
Читает уровень зацикленной фоновой музыки приложения, от 0 до 100.
Возвращает: число, синхронно: 0–100 или -1, если приложение собрано без музыкального файла. Требуется: файл, выбранный в Background Music на шаге 3 (Интеграция), в разделе Display Options, при сборке.
Показывайте ползунок музыки только тогда, когда есть музыка:
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));
});
}Кнопка отключения звука, которая при включении возвращает собственный уровень пользователя (приложение тоже запоминает 0, поэтому храните прежний уровень сами):
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();Плавно измените громкость музыки с текущего уровня на новый, например во время диалоговой сцены:
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Примечания: Возвращает уровень, заданный ползунком в сборке или вашим последним вызовом setMusicVolume. Когда звук занимает другое приложение или звонок, музыка сама становится тише или ставится на паузу; число, которое вы читаете, не меняется.
getVolume мост#
window.WebToApk.getVolume(stream: String): String
Уровень одного потока: `{stream, current, max, percent, muted}` в виде строки JSON. stream: 'media' | 'alarm' | 'ring' | 'notification' | 'call'.
Пример
Считывает один из собственных уровней громкости телефона.
Возвращает: строку JSON, синхронно: {"stream":"alarm","current":5,"max":7,"percent":71,"muted":false} — current/max это собственные шаги телефона (на разных телефонах они различаются), percent — от 0 до 100. stream — это 'media', 'alarm', 'ring', 'notification' или 'call'; любое другое имя даёт {"error":"unknown_stream"}. Требуется: ничего.
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();Показать все уровни громкости на экране настроек:
['media', 'alarm', 'ring', 'notification'].forEach(function (s) {
const v = JSON.parse(WebToApk.getVolume(s));
document.getElementById('vol-' + s).value = v.percent;
});Задайте громкость и прочитайте результат. setVolume(stream, percent, showUi) отвечает строкой 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 мост#
window.WebToApk.playClick()
Воспроизводит звук нажатия приложения по запросу, чтобы страница могла запускать его на СВОИ смысловые события (нажатие кнопки), а не на каждое случайное касание — именно так делает нативное приложение, и поэтому clickSoundMode может быть «off», а это всё равно работает. Не делает ничего, только если приложение собрано БЕЗ звука нажатия (enableClickSound выключен). В режиме clickSoundMode «off» — без автоматического звука на каждое касание — явный вызов всё равно играет: этот режим нужен приложениям, которые озвучивают собственные события. В остальных случаях играет настроенную автором обратную связь.
Пример
Один раз воспроизводит звук нажатия приложения, чтобы ваши собственные кнопки щёлкали как в нативном приложении.
Возвращает: ничего. Требуется: переключатель Tap Sound на шаге 3 (Интеграция), в разделе Display Options, при сборке (включён по умолчанию). Если он выключен, вызов ничего не делает.
function clickSound() {
if (window.WebToApk && typeof window.WebToApk.playClick === 'function') {
window.WebToApk.playClick();
}
}
document.querySelectorAll('button, .tab').forEach(function (el) {
el.addEventListener('click', clickSound);
});Один обработчик на всё игровое поле, щёлкающий только при нажатии, которое что-то делает:
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);
});Дайте пользователям собственный переключатель «Звуки» поверх настройки сборки:
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();
}
}Примечания: То, что вы слышите, зависит от выбора в поле Звук нажатия:
- Как в настройках телефона (рекомендуется): системный щелчок. Он беззвучен, если пользователь отключил «Звуки при касании» в настройках телефона. Так задумано.
- Always play on every tap: собственный щелчок приложения, всегда слышимый. Каждое нажатие на страницу тоже щёлкает, поэтому в этом режиме
playClick()обычно не нужен.
Вызывайте её по настоящему нажатию кнопки, а не по таймеру.
requestDndAccess мост#
window.WebToApk.requestDndAccess(): String
Доступ к режиму «Не беспокоить», нужный для изменения громкости звонка и уведомлений, пока он включён. «granted» | «opened» (открыт экран настроек) | «not_declared».
Пример
Запрашивает доступ к режиму «Не беспокоить», который Android требует, прежде чем приложение сможет менять громкость звонка или уведомлений при включённом режиме «Не беспокоить» (тогда setVolume отвечает dnd_access_required).
Возвращает: строку: 'granted' (уже разрешено), 'opened' (открыт системный экран, где пользователь это разрешает — проверьте снова, когда он вернётся) или 'not_declared' (в этой сборке нет разрешения, поэтому Android не покажет приложение в этом списке). Требуется: вызов requestDndAccess в коде вашего приложения: тогда сборка сама объявит ACCESS_NOTIFICATION_POLICY.
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.');
}Повторите изменение громкости один раз, когда пользователь разрешит доступ:
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;
}Отключите звонок переключателем «режим встречи» и объясните причину, если это невозможно:
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 мост#
window.WebToApk.setMusicVolume(percent: Int)
Задаёт громкость фоновой музыки со страницы, 0..100. Ползунок в мастере определяет, как приложение ЗАПУСКАЕТСЯ; а это способ дать собственным пользователям приложения управление музыкой, которое ожидается в любой игре с экраном настроек. Значение ограничивается в [BackgroundMusic.setVolume], так что страница, передающая 500 или -1, не сможет передать MediaPlayer значение вне допустимого диапазона. Уровень ЗАПОМИНАЕТСЯ: следующий запуск начнётся с него, а не с уровня из мастера. Ничего не делает, если приложение собрано без трека — задавать нечего, и честный ответ — [getMusicVolume], возвращающий -1, чтобы страница могла скрыть ползунок, а не показывать бесполезный.
Пример
Меняет громкость циклической фоновой музыки приложения от 0 до 100, например на экране настроек вашей игры.
Возвращает: ничего. Требуется: файл, выбранный в разделе Фоновая музыка на шаге 3 (Интеграция), в блоке Параметры отображения, при сборке. Без музыки ничего не происходит (проверьте через getMusicVolume() для -1).
Кнопка отключения звука — приложение запоминает уровень до следующего запуска:
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);
});Кнопки «Выкл. / Слабо / Сильно» на экране настроек:
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);
});
});Приглушите музыку, пока играет ваше собственное видео, а потом верните (уровень запоминается, поэтому всегда восстанавливайте его):
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);Примечания: Заданный вами уровень сохраняется: при следующем запуске приложение начнёт с него, а не с уровня ползунка сборки (пока пользователь не очистит данные приложения). 0 делает музыку беззвучной, но она продолжает играть.
setVolume мост#
window.WebToApk.setVolume(stream: String, percent: Double, showUi: Boolean): String
Устанавливает поток на [percent] (0–100); [showUi] показывает ползунок громкости телефона. `{ok:true, stream, current, max, percent}` или `{ok:false, error}` — error: unknown_stream | bad_percent | fixed_volume | dnd_access_required | muted | not_applied | failed | foreign_origin (страница вне собственного сайта приложения).
Пример
Задаёт один из собственных уровней громкости телефона от 0 до 100 — например, делает громче поток будильника для будильника-побудки или ползунок медиа в музыкальном приложении.
Возвращает: строку JSON: {"ok":true,"stream":"alarm","current":6,"max":7,"percent":86} или {"ok":false,"error":"…"}, где error — это unknown_stream, bad_percent, fixed_volume (на этом устройстве нет регулируемой громкости), dnd_access_required (включён режим «Не беспокоить» и блокирует громкость звонка / уведомлений — см. requestDndAccess()), foreign_origin (страница вне сайта вашего приложения) или failed. showUi: true показывает собственный ползунок громкости телефона во время изменения. Требуется: ничего для медиа, будильника и звонка; для звонка и уведомлений может понадобиться доступ к режиму «Не беспокоить», пока он включён.
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.');
}
};Прочитайте уровень, затем задайте ползунок медиа:
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
};
}Показать текущую громкость звонка и реагировать на её изменение:
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;
});
}Примечания: значение округляется до ближайшего шага телефона, а в ответе указан фактически установленный уровень. Поток вызовов никогда не опускается ниже минимума (обычно 1).
vibrate мост#
window.WebToApk.vibrate(durationMs: Long)
Простая тактильная отдача для страницы (navigator.vibrate и форма с шаблоном). Обе игнорируются, если в сборке не включено разрешение Vibrate.
Описывается в своей группе, Вибрация, а не отдельно.
Пример
Один раз включает вибрацию телефона на заданное число миллисекунд. Большинство страниц используют стандартный navigator.vibrate(ms), который вызывает её за вас.
Возвращает: ничего. Требуется: включите Вибрацию на шаге 4 (Доступ) при сборке. Без этого вызов ничего не делает.
Стандартный способ (работает так же в браузере):
if ('vibrate' in navigator) navigator.vibrate(50);Прямой вызов:
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);Лёгкий «тик» каждый раз, когда ползунок переходит на новый шаг, как в системном выборе:
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
}
});Более долгая вибрация, когда форму не удалось отправить, чтобы пользователь почувствовал ошибку:
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();
});Примечания: Передайте целое число. Сила — стандартная для телефона; для более сильных или слабых касаний используйте Capacitor.Plugins.Haptics.impact или Capacitor.Plugins.AppwrightHaptics.play. Для шаблона вибрация-пауза-вибрация используйте vibratePattern.
vibratePattern мост#
window.WebToApk.vibratePattern(patternJson: String)
Вибрирует по шаблону «вкл/выкл», например «[100,50,100]» = вибрация 100 мс, пауза 50, вибрация 100. Лежит в основе navigator.vibrate(array); предпочитайте стандартный API. Ничего не делает, если создатель не включил разрешение Vibrate.
Пример
Вибрирует по шаблону включения/выключения, заданному строкой JSON. Большинство страниц используют стандартный navigator.vibrate([...]), который вызывает её за вас.
Возвращает: ничего. Требуется: включите Вибрацию на шаге 4 (Доступ) при сборке. Без этого вызов ничего не делает.
Стандартный способ:
if ('vibrate' in navigator) navigator.vibrate([100, 50, 100]); // buzz, pause, buzzПрямой вызов принимает тот же шаблон в веб-стиле как строку 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Шаблон проигрывается один раз, поэтому будильник сам повторяет его, пока пользователь не остановит:
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
}Примечания: Первое число — это вибрация, затем пауза, вибрация, пауза ... (порядок как в вебе). Приложение само добавляет начальное «ожидание» Android, поэтому не ставьте 0 первым: [0, 100] означает «вибрация 0 мс, пауза 100 мс», и вы ничего не почувствуете. Отрицательные числа считаются как 0. Шаблон проигрывается один раз.
watchVolume мост#
window.WebToApk.watchVolume(on: Boolean): Boolean
Включает (true) или выключает (false) события `appmint:volume`.
Пример
Включает или выключает живые события изменения громкости: пока включено, каждое изменение уровня громкости (аппаратные кнопки, системный ползунок, другое приложение) доходит до страницы как appmint:volume.
Возвращает: true, когда изменение вступило в силу. Требуется: ничего.
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); });
}Тот же объект доступен и через хук страницы, если вам так удобнее:
window.onAppMintVolume = function (level) {
if (level.stream === 'alarm' && level.percent === 0) showMessage('Alarm volume is now off.');
};
WebToApk.watchVolume(true);Покажите уровень один раз через getVolume, затем обновляйте его вживую, только пока экран виден:
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); }
});
}Примечания: одно событие на каждый изменившийся поток (media, alarm, ring, notification, call). Отключите его, когда экран с уровнями громкости закрывается.