Buat APK Gratis

API jembatan JavaScript

Notifikasi di perangkat

Mengirim dan menjadwalkan tanpa server, izin POST_NOTIFICATIONS, alarm tepat waktu, dan suara kustom.

AppMint.enableNotifications helper#

AppMint.enableNotifications()

Contoh

"Turn on notifications" dalam satu panggilan, untuk pengguna yang menolak atau melewati permintaan notifikasi Android: dialog izin selama Android masih dapat menampilkannya, jika tidak, pengaturan notifikasi aplikasi ini - dan state baru saat pengguna kembali.

Mengembalikan: AppMint.enableNotifications() - sebuah Promise yang selalu terselesaikan {state, changed, openedSettings}; state adalah 'granted' | 'prompt' | 'blocked' | 'unavailable'. AppMint.getNotificationState() - sebuah Promise berisi state itu. AppMint.openNotificationSettings() - sebuah Promise berisi true saat layar pengaturan terbuka. Membutuhkan: panggil enableNotifications() dari tombol yang diketuk pengguna; notifikasi membutuhkan Notifications (atau Push / Reminders) di build.

// A banner that only shows while notifications are off, and turns them on in one tap.
async function setupNotifBanner() {
  if (!(window.AppMint && AppMint.enableNotifications)) return;   // a browser: no banner
  var banner = document.getElementById('notif-banner');
  var state = await AppMint.getNotificationState();
  banner.hidden = !(state === 'prompt' || state === 'blocked');
  document.getElementById('notif-turn-on').onclick = async function () {
    var r = await AppMint.enableNotifications();
    banner.hidden = r.state === 'granted';
    if (r.state === 'blocked') showToast('Switch on "Allow notifications" to get updates.');
  };
}
setupNotifBanner();

Tautan "Notification settings" sederhana di halaman pengaturan Anda sendiri:

document.getElementById('notif-settings').onclick = async function () {
  var opened = window.AppMint && AppMint.openNotificationSettings && await AppMint.openNotificationSettings();
  if (!opened) showToast('Open Settings > Apps > this app > Notifications.');
};

Catatan: tombol push atau pesan dalam aplikasi yang tautannya appmint://notifications/enable menjalankan alur yang sama tanpa kode halaman (halaman mendengar appmint:notifications). Audiens push aplikasi ("Notifications on / off") diperbarui segera setelah pengguna menyalakannya.

AppMint.getNotificationState helper#

window.AppMint.getNotificationState

Contoh

Didokumentasikan bersama AppMint.enableNotifications - contoh di sana menunjukkan yang ini juga.

"Turn on notifications" dalam satu panggilan, untuk pengguna yang menolak atau melewati permintaan notifikasi Android: dialog izin selama Android masih dapat menampilkannya, jika tidak, pengaturan notifikasi aplikasi ini - dan state baru saat pengguna kembali.

Mengembalikan: AppMint.enableNotifications() - sebuah Promise yang selalu terselesaikan {state, changed, openedSettings}; state adalah 'granted' | 'prompt' | 'blocked' | 'unavailable'. AppMint.getNotificationState() - sebuah Promise berisi state itu. AppMint.openNotificationSettings() - sebuah Promise berisi true saat layar pengaturan terbuka. Membutuhkan: panggil enableNotifications() dari tombol yang diketuk pengguna; notifikasi membutuhkan Notifications (atau Push / Reminders) di build.

// A banner that only shows while notifications are off, and turns them on in one tap.
async function setupNotifBanner() {
  if (!(window.AppMint && AppMint.enableNotifications)) return;   // a browser: no banner
  var banner = document.getElementById('notif-banner');
  var state = await AppMint.getNotificationState();
  banner.hidden = !(state === 'prompt' || state === 'blocked');
  document.getElementById('notif-turn-on').onclick = async function () {
    var r = await AppMint.enableNotifications();
    banner.hidden = r.state === 'granted';
    if (r.state === 'blocked') showToast('Switch on "Allow notifications" to get updates.');
  };
}
setupNotifBanner();

Tautan "Notification settings" sederhana di halaman pengaturan Anda sendiri:

document.getElementById('notif-settings').onclick = async function () {
  var opened = window.AppMint && AppMint.openNotificationSettings && await AppMint.openNotificationSettings();
  if (!opened) showToast('Open Settings > Apps > this app > Notifications.');
};

Catatan: tombol push atau pesan dalam aplikasi yang tautannya appmint://notifications/enable menjalankan alur yang sama tanpa kode halaman (halaman mendengar appmint:notifications). Audiens push aplikasi ("Notifications on / off") diperbarui segera setelah pengguna menyalakannya.

AppMint.openNotificationSettings helper#

window.AppMint.openNotificationSettings

Contoh

Didokumentasikan bersama AppMint.enableNotifications - contoh di sana menunjukkan yang ini juga.

"Turn on notifications" dalam satu panggilan, untuk pengguna yang menolak atau melewati permintaan notifikasi Android: dialog izin selama Android masih dapat menampilkannya, jika tidak, pengaturan notifikasi aplikasi ini - dan state baru saat pengguna kembali.

Mengembalikan: AppMint.enableNotifications() - sebuah Promise yang selalu terselesaikan {state, changed, openedSettings}; state adalah 'granted' | 'prompt' | 'blocked' | 'unavailable'. AppMint.getNotificationState() - sebuah Promise berisi state itu. AppMint.openNotificationSettings() - sebuah Promise berisi true saat layar pengaturan terbuka. Membutuhkan: panggil enableNotifications() dari tombol yang diketuk pengguna; notifikasi membutuhkan Notifications (atau Push / Reminders) di build.

// A banner that only shows while notifications are off, and turns them on in one tap.
async function setupNotifBanner() {
  if (!(window.AppMint && AppMint.enableNotifications)) return;   // a browser: no banner
  var banner = document.getElementById('notif-banner');
  var state = await AppMint.getNotificationState();
  banner.hidden = !(state === 'prompt' || state === 'blocked');
  document.getElementById('notif-turn-on').onclick = async function () {
    var r = await AppMint.enableNotifications();
    banner.hidden = r.state === 'granted';
    if (r.state === 'blocked') showToast('Switch on "Allow notifications" to get updates.');
  };
}
setupNotifBanner();

Tautan "Notification settings" sederhana di halaman pengaturan Anda sendiri:

document.getElementById('notif-settings').onclick = async function () {
  var opened = window.AppMint && AppMint.openNotificationSettings && await AppMint.openNotificationSettings();
  if (!opened) showToast('Open Settings > Apps > this app > Notifications.');
};

Catatan: tombol push atau pesan dalam aplikasi yang tautannya appmint://notifications/enable menjalankan alur yang sama tanpa kode halaman (halaman mendengar appmint:notifications). Audiens push aplikasi ("Notifications on / off") diperbarui segera setelah pengguna menyalakannya.

navigator.setAppBadge standar web#

navigator.setAppBadge(count) / clearAppBadge()

Contoh

API Badging standar: angka di ikon launcher aplikasi, seperti jumlah belum dibaca.

Mengembalikan: sebuah Promise yang terselesaikan saat badge diatur; TypeError untuk angka negatif atau bukan angka. Membutuhkan: tidak ada yang perlu diaktifkan. API ini hanya ada di ponsel yang launcher-nya menampilkan angka (Samsung, Huawei, Xiaomi, OPPO, Sony, …); di launcher ala Pixel 'setAppBadge' in navigator bernilai false.

function updateBadge(unread) {
  if (!('setAppBadge' in navigator)) return;      // this launcher cannot show a count
  if (unread > 0) navigator.setAppBadge(unread);
  else navigator.clearAppBadge();
}

updateBadge(messages.filter((m) => !m.read).length);

Hapus badge setiap kali pengguna melihat aplikasi, dan tangani pembaruan yang ditolak:

function setUnread(count) {
  if (!('setAppBadge' in navigator)) return;
  navigator.setAppBadge(count).catch(function (err) {
    console.warn('badge not updated:', err.name);   // NotAllowedError or TypeError
  });
}

document.addEventListener('visibilitychange', function () {
  if (!document.hidden && 'clearAppBadge' in navigator) {
    navigator.clearAppBadge().catch(function () {});
  }
});

Tampilkan penanda "ada yang baru" biasa saat sinkronisasi menemukan perubahan tetapi tidak punya jumlah pasti:

async function afterSync(result) {
  if (!('setAppBadge' in navigator)) return;
  try {
    if (result.newCount > 0) await navigator.setAppBadge(result.newCount);
    else if (result.hasChanges) await navigator.setAppBadge();   // flag badge, shown as 1
    else await navigator.clearAppBadge();
  } catch (e) {
    console.warn('badge update failed', e);
  }
}

Catatan: setAppBadge() tanpa angka adalah badge "flag" dari spesifikasi, ditampilkan sebagai 1 di launcher berhitung. Hitungan bertahan setelah aplikasi dimulai ulang. Penyimpanan yang sama mendasari badge.set() di @/lib/appmintNative, sehingga keduanya selalu sepakat.

Notification standar web#

new Notification(title, options) / Notification.requestPermission()

Contoh

API Web Notifications standar berfungsi di dalam aplikasi: new Notification() menampilkan notifikasi Android sungguhan, close() menghapusnya, dan Notification.requestPermission() menampilkan dialog izin Android dan menunggu jawaban pengguna.

Mengembalikan: Notification.permission adalah 'granted', 'denied' atau 'default', dibaca langsung dari ponsel (perubahan di Pengaturan tampak tanpa memuat ulang). requestPermission() mengembalikan Promise berisi 'granted' atau 'denied' (bentuk callback juga berfungsi). Notifikasi baru memicu show saat dikirim, atau error saat izin tidak ada. Membutuhkan: aktifkan Notifications di langkah Access (Langkah 4) saat Anda membuat build.

async function notifyWhenSaved() {
  if (!('Notification' in window)) return;
  let perm = Notification.permission;
  if (perm === 'default') perm = await Notification.requestPermission();
  if (perm !== 'granted') {
    showMessage('Turn on notifications for this app in Settings to get alerts.');
    return;
  }
  const n = new Notification('Saved', {
    body: 'Your note was saved',
    icon: 'https://example.com/avatar.png',   // shown as the large icon
    image: 'https://example.com/note.jpg',    // big picture
    tag: 'note'                               // same tag replaces the old one
  });
  n.onshow = function () { console.log('posted'); };
  n.onerror = function () { console.log('not posted'); };
  setTimeout(function () { n.close(); }, 60000);   // removes it from the shade
}

document.getElementById('save').addEventListener('click', notifyWhenSaved);

Bentuk callback (kode lama):

Notification.requestPermission(function (result) {
  if (result === 'granted') new Notification('Welcome!', { body: 'Alerts are on', silent: true });
});

Gaya service worker (kode PWA) juga mengirim lewat aplikasi, tetapi hanya di tempat WebView sendiri tidak memiliki navigator.serviceWorker (misalnya halaman file:// atau http://):

if ('serviceWorker' in navigator && Notification.permission === 'granted') {
  navigator.serviceWorker.ready.then(function (reg) {
    return reg.showNotification('Backup done', { body: '12 files', tag: 'backup' });
  });
}

Sebuah ketukan memicu click pada notifikasi - aplikasi maju ke depan dan halaman menerimanya. Bila aplikasi ditutup (ketukan memulainya), objeknya sudah tidak ada, sehingga jendela mendapat event notificationclick sebagai gantinya, berbentuk seperti milik service worker - event.notification.tag / title / body / data:

const n = new Notification('Order shipped', { body: '#7', tag: 'order-7', data: { orderId: 7 } });
n.onclick = () => openOrder(7);                         // the app was running
window.addEventListener('notificationclick', (e) => {   // the tap started the app
  if (e.notification.data && e.notification.data.orderId) openOrder(e.notification.data.orderId);
});

Catatan: opsi yang dipakai: body, icon, image, tag, silent, data (JSON, di bawah 16 KB). Ketukan sampai ke halaman melewati layar splash dan PIN (event di baliknya: appmint:notification-click). Di Android 12 dan lebih lama tidak ada izin saat berjalan, sehingga nilainya 'granted' kecuali pengguna mematikan notifikasi. Web Push (pushManager.subscribe) tidak didukung; gunakan AppMint Push. Untuk tombol, kemajuan, atau notifikasi menempel gunakan WebToApk.notify(...). Kode yang sama berfungsi di browser biasa.

__badgeSupported jembatan#

window.WebToApk.__badgeSupported(): Boolean

Apakah launcher ini dapat menampilkan angka badge (navigator.setAppBadge hanya ada saat itu).

Contoh

Saluran internal di balik keberadaan navigator.setAppBadge. Halaman memakai API standar; jangan pernah memanggil ini langsung - metode __ dapat berubah tanpa pemberitahuan.

Mengembalikan: true bila launcher ponsel ini menampilkan angka badge. Membutuhkan: tidak ada yang perlu diaktifkan.

Gunakan API publik:

if ('setAppBadge' in navigator) navigator.setAppBadge(unreadCount);

Tampilkan pengaturan "badge on app icon" hanya di ponsel yang launcher-nya dapat menampilkan angka:

function renderBadgeSetting() {
  const row = document.getElementById('badge-setting');
  // setAppBadge exists only when this launcher shows counts
  row.hidden = !('setAppBadge' in navigator);
}
renderBadgeSetting();

Hapus angka saat pengguna membuka kotak masuk, di launcher mana pun yang memilikinya:

function openInbox() {
  showInbox();
  if ('clearAppBadge' in navigator) {
    navigator.clearAppBadge().catch(function (e) {
      console.warn('badge not cleared', e.name);   // NotAllowedError
    });
  }
}

Catatan: API Badging hanya dipasang di tempat launcher dapat menampilkan angka (Samsung, Huawei, Xiaomi, OPPO, Sony, …); di launcher ala Pixel API ini tidak ada, seperti di browser tanpa badging.

__closeNotification jembatan#

window.WebToApk.__closeNotification(tag: String): Boolean

Notification.close(): menghapus notifikasi yang dikirim halaman, berdasarkan tag-nya.

Contoh

Saluran internal di balik Notification.prototype.close() - halaman memakai new Notification(...) dan memanggil close() padanya. Ini menghapus notifikasi dari bayangan notifikasi berdasarkan tag-nya.

Mengembalikan: true bila permintaan hapus dibuat (juga bila tidak ada yang memiliki tag itu); false pada kesalahan internal. Membutuhkan: tidak ada.

Gunakan API publik:

let uploadNote = null;

function showUploading() {
  if (!('Notification' in window) || Notification.permission !== 'granted') return;
  uploadNote = new Notification('Uploading…', { body: 'Keep the app open', tag: 'upload', silent: true });
  uploadNote.onclose = function () { uploadNote = null; };
}

function uploadFinished() {
  if (uploadNote) uploadNote.close();   // removes it from the notification shade
}

Buka obrolan yang tepat saat notifikasi diketuk, lalu hapus dari bayangan notifikasi:

function notifyNewMessage(msg) {
  if (!('Notification' in window) || Notification.permission !== 'granted') return;
  const n = new Notification(msg.from, {
    body: msg.text,
    tag: 'chat-' + msg.threadId,          // one notification per chat
    data: { threadId: msg.threadId }
  });
  n.onclick = function () {
    openThread(n.data.threadId);
    n.close();
  };
}

Tampilkan konfirmasi singkat yang menghapus dirinya sendiri setelah lima detik:

function confirmCopied() {
  if (!('Notification' in window) || Notification.permission !== 'granted') return;
  const n = new Notification('Link copied', { body: 'Paste it anywhere', tag: 'copied', silent: true });
  setTimeout(function () { n.close(); }, 5000);
}

Catatan: jangan panggil WebToApk.__closeNotification secara langsung; metode __ dapat berubah tanpa pemberitahuan. close() memicu event close milik notifikasi.

__setAppBadge jembatan#

window.WebToApk.__setAppBadge(contents: Long): Boolean

navigator.setAppBadge(n): n >= 0 adalah hitungan (0 menghapus), -1 badge flag dari spesifikasi.

Contoh

Saluran internal di balik navigator.setAppBadge(count) / clearAppBadge(). Halaman memakai API standar; jangan pernah memanggil ini langsung - metode __ dapat berubah tanpa pemberitahuan.

Mengembalikan: true bila hitungan diterapkan. Membutuhkan: tidak ada yang perlu diaktifkan.

Gunakan API publik:

if ('setAppBadge' in navigator) navigator.setAppBadge(unreadCount);

Jaga agar angka di ikon selaras dengan jumlah belum dibaca, dan hapus saat nol:

async function syncBadge(unread) {
  if (!('setAppBadge' in navigator)) return;
  try {
    if (unread > 0) await navigator.setAppBadge(unread);
    else await navigator.clearAppBadge();
  } catch (e) {
    console.warn('badge not applied', e.name);   // NotAllowedError when the launcher refused it
  }
}
syncBadge(messages.filter(function (m) { return !m.read; }).length);

Pasang badge flag (tanpa angka) di ikon selama ada pekerjaan yang belum tersinkron:

function markPendingSync(hasPending) {
  if (!('setAppBadge' in navigator)) return;
  const p = hasPending
    ? navigator.setAppBadge()      // no argument = flag badge; a count launcher shows 1
    : navigator.clearAppBadge();
  p.catch(function () {});
}

Catatan: -1 adalah badge flag dari spesifikasi (tanpa angka), yang ditampilkan launcher berhitung sebagai 1.

cancelAllNotifications jembatan#

window.WebToApk.cancelAllNotifications(): Boolean

Membaca dan meminta izin POST_NOTIFICATIONS, serta mengelola notifikasi yang sudah dijadwalkan halaman. Pengiriman dan penjadwalan didokumentasikan pada metodenya masing-masing.

Dijelaskan oleh grupnya, Izin notifikasi, pembatalan, dan daftar, bukan sendiri.

Contoh

Membatalkan setiap pengingat yang dijadwalkan aplikasi ini.

Mengembalikan: true bila selesai; false hanya pada kesalahan internal. Membutuhkan: tidak ada tambahan.

document.getElementById('reset-reminders').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.cancelAllNotifications)) return;
  if (!confirm('Remove all your reminders?')) return;
  const ok = WebToApk.cancelAllNotifications();
  showMessage(ok ? 'All reminders removed' : 'Could not remove reminders');
});

Saat keluar, hentikan pengingat pengguna sebelumnya dan hapus angka di ikon:

async function signOut() {
  if (window.WebToApk && window.WebToApk.cancelAllNotifications) {
    WebToApk.cancelAllNotifications();
  }
  if ('clearAppBadge' in navigator) navigator.clearAppBadge().catch(function () {});
  await auth.signOut();
  showLoginScreen();
}

Bangun ulang seluruh jadwal harian dari waktu yang tersimpan (batalkan semua, lalu tambahkan satu per satu):

function applyMedicineTimes(times) {        // e.g. ['08:00', '20:00']
  const b = window.WebToApk;
  if (!(b && b.cancelAllNotifications)) return;
  b.cancelAllNotifications();
  times.forEach(function (hhmm, i) {
    const parts = hhmm.split(':');
    const at = new Date();
    at.setHours(Number(parts[0]), Number(parts[1]), 0, 0);
    if (at.getTime() <= Date.now()) at.setDate(at.getDate() + 1);   // next occurrence
    b.scheduleNotification('med-' + i, 'Medicine', 'Time for your ' + hhmm + ' dose',
      String(at.getTime()), 'daily');
  });
}

Catatan: ini mengosongkan daftar terjadwal, sehingga tidak ada yang berbunyi nanti dan tidak ada yang diatur ulang setelah dimulai ulang. Notifikasi yang sudah tampil di tray tetap di sana.

cancelNotification jembatan#

window.WebToApk.cancelNotification(id: String): Boolean

Membaca dan meminta izin POST_NOTIFICATIONS, serta mengelola notifikasi yang sudah dijadwalkan halaman. Pengiriman dan penjadwalan didokumentasikan pada metodenya masing-masing.

Dijelaskan oleh grupnya, Izin notifikasi, pembatalan, dan daftar, bukan sendiri.

Contoh

Membatalkan satu pengingat terjadwal berdasarkan id-nya, sehingga tidak akan berbunyi lagi.

Mengembalikan: true (juga bila tidak ada pengingat dengan id itu); false hanya pada kesalahan internal. Membutuhkan: tidak ada tambahan (pengingat membutuhkan ⏰ Reminders saat build).

function stopWaterReminder() {
  if (!(window.WebToApk && window.WebToApk.cancelNotification)) return;
  WebToApk.cancelNotification('water');   // the id you gave scheduleNotification / scheduleNotificationEx

  // Check it is gone:
  const left = JSON.parse(WebToApk.getScheduledNotifications() || '[]');
  const stillThere = left.some(function (r) { return r.id === 'water'; });
  console.log('water reminder still scheduled?', stillThere);   // false
}

Sakelar pengingat di pengaturan yang menjadwalkan atau membatalkan dengan id yang sama:

document.getElementById('water-toggle').addEventListener('change', function (e) {
  const b = window.WebToApk;
  if (!(b && b.cancelNotification && b.scheduleNotification)) return;
  if (e.target.checked) {
    b.scheduleNotification('water', 'Drink water', 'Time for a glass of water',
      String(Date.now() + 60 * 60 * 1000), 'hourly');
  } else {
    b.cancelNotification('water');
  }
});

Saat tugas dihapus, batalkan setiap pengingat miliknya:

function cancelTaskReminders(taskId) {
  const b = window.WebToApk;
  if (!(b && b.cancelNotification)) return;
  let list = [];
  try { list = JSON.parse(b.getScheduledNotifications() || '[]'); } catch (e) { list = []; }
  list
    .filter(function (r) { return r.id.indexOf('task-' + taskId + '-') === 0; })
    .forEach(function (r) { b.cancelNotification(r.id); });
}

Catatan: ini membatalkan pengingat yang belum berbunyi (sekali jalan atau berulang). Ini tidak menghapus notifikasi yang sudah tampil di tray, dan tidak memengaruhi notifikasi yang dikirim dengan notify() / showNotification().

canOpenAppAtTime jembatan#

window.WebToApk.canOpenAppAtTime(): String

Apakah notifikasi terjadwal boleh menampilkan APLIKASI ITU SENDIRI di layar (`openApp`). "granted" - peringatan layar penuh akan tampil, di atas layar kunci. "denied" - Android 14+ menahannya; panggil requestOpenAppPermission(). Tidak ada jawaban ketiga di mana aplikasi diam-diam meluncurkan dirinya di latar belakang: Android memblokir peluncuran aktivitas latar belakang sejak Android 10, dan full-screen intent adalah satu-satunya yang tersisa.

Contoh

Memberi tahu apakah pengingat dengan openApp: true boleh menampilkan aplikasi itu sendiri di layar (peringatan layar penuh, juga di atas layar kunci).

Mengembalikan: sebuah string, secara sinkron: 'granted' atau 'denied'. Di bawah Android 14 selalu 'granted'. Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build.

function prepareAlarm() {
  const b = window.WebToApk;
  if (!(b && b.canOpenAppAtTime)) return;
  if (b.canOpenAppAtTime() === 'denied') {
    showMessage('To open the app at alarm time, allow "full-screen notifications" for this app.');
    b.requestOpenAppPermission();
    return;
  }
  const t = new Date();
  t.setDate(t.getDate() + 1);
  t.setHours(7, 0, 0, 0);                    // tomorrow 07:00
  b.scheduleNotificationEx('wake-up', JSON.stringify({
    at: t.getTime(), repeat: 'daily',
    title: 'Wake up', body: 'Good morning!',
    openApp: true
  }));
}

window.addEventListener('appmint:reminder', function (e) {
  // e.detail.overLockScreen is true when the app was shown as a full-screen alert
  if (e.detail.overLockScreen) showBigAlarmScreen(e.detail.id);
});

Layar pengaturan yang menampilkan kedua sakelar alarm beserta state-nya saat ini:

function renderAlarmStatus() {
  const b = window.WebToApk;
  if (!(b && b.canOpenAppAtTime && b.getAlarmPrecision)) return;
  const openApp = b.canOpenAppAtTime();       // 'granted' | 'denied'
  const precision = b.getAlarmPrecision();    // 'exact' | 'inexact' | 'unavailable'
  document.getElementById('open-app-state').textContent =
    openApp === 'granted' ? 'Opens over the lock screen' : 'Notification only';
  document.getElementById('open-app-fix').hidden = openApp === 'granted';
  document.getElementById('timing-state').textContent =
    precision === 'exact' ? 'On the minute' : 'May be late';
}

renderAlarmStatus();
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) renderAlarmStatus();  // back from Settings
});

Beri peringatan saat alarm tersimpan diatur untuk membuka aplikasi tetapi Android tidak mengizinkannya sekarang:

function checkSavedAlarms() {
  const b = window.WebToApk;
  if (!(b && b.canOpenAppAtTime && b.getScheduledNotifications)) return;
  if (b.canOpenAppAtTime() === 'granted') return;
  // [{ id, title, body, trigger, repeat, options }] - options is the JSON string you scheduled
  const items = JSON.parse(b.getScheduledNotifications());
  const affected = items.filter(function (r) {
    try { return JSON.parse(r.options || '{}').openApp === true; } catch (e) { return false; }
  });
  if (affected.length) {
    showMessage(affected.length + ' alarm(s) will only show a notification. Tap "Allow" to let them open the app.');
  }
}

Catatan: bila nilainya 'denied', pengingat openApp tetap menampilkan notifikasi heads-up, tetapi aplikasi tidak terbuka sendiri. Android tidak mengizinkan cara lain bagi aplikasi untuk memulai dirinya sendiri di latar belakang.

clearNotificationSounds jembatan#

window.WebToApk.clearNotificationSounds(): Int

Membuang file suara hasil salinan dan mengembalikan berapa banyak yang dihapus. File disalin lagi saat notifikasi (atau pengingat terjadwal, saat berbunyi) berikutnya memakai suara itu - termasuk pada kanal yang sudah ada, yang tetap menunjuk ke nama file yang sama (NotificationBridge.restoreBundleSound).

Contoh

Menghapus salinan suara notifikasi kustom Anda yang dibuat aplikasi dari bundel Anda.

Mengembalikan: sebuah angka, secara sinkron: berapa banyak file yang dihapus. Membutuhkan: tidak ada.

Hapus, lalu pasang kembali suara yang masih Anda pakai:

document.getElementById('reset-sounds').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.clearNotificationSounds)) return;
  const removed = b.clearNotificationSounds();

  // Sounds still in use must be copied again, or they play the default sound:
  ['/audio/alarm.mp3', '/audio/chime.mp3'].forEach(function (path) {
    if (!b.prepareNotificationSound(path)) console.warn('missing sound', path);
  });
  showMessage(removed + ' sound file(s) refreshed');
});

Tombol layar penyimpanan yang membebaskan file suara hasil salinan dan menyebutkan berapa banyak yang dihapus:

document.getElementById('free-space').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.clearNotificationSounds)) return;
  const removed = b.clearNotificationSounds();
  showMessage(removed === 0 ? 'No sound files to remove.' : 'Removed ' + removed + ' sound file(s).');
});

Saat keluar, batalkan setiap pengingat dan hapus salinan suara milik pengguna ini:

function signOutAndReset() {
  const b = window.WebToApk;
  if (b && b.cancelAllNotifications) b.cancelAllNotifications();
  if (b && b.clearNotificationSounds) b.clearNotificationSounds();
  localStorage.removeItem('alarmSound');
  goToLoginScreen();
}

Catatan: Anda jarang membutuhkan ini; salinannya kecil. Suara disalin kembali saat berikutnya ada yang memakainya - notify(), pengingat saat berbunyi, scheduleNotificationEx, atau prepareNotificationSound(path) - termasuk pada kanal notifikasi yang sudah ada, sehingga suara kustom tetap berbunyi.

enableNotifications jembatan#

window.WebToApk.enableNotifications(callbackId: String)

Mengembalikan pengguna ke notifikasi: dialog sistem selama masih dapat ditampilkan, jika tidak, pengaturan notifikasi aplikasi. Menyelesaikan `window.__appmintReplies[callbackId]` dengan {state, changed, openedSettings} (AppMint.enableNotifications() adalah pembungkus promise).

Contoh

Mengembalikan pengguna yang menolak atau melewati notifikasi: menampilkan dialog izin Android selama masih dapat ditampilkan, jika tidak, membuka pengaturan notifikasi aplikasi ini dan menjawab saat pengguna kembali.

Mengembalikan: tidak ada secara langsung. Jawaban {state, changed, openedSettings} dikirim ke window.__appmintReplies[callbackId]; state adalah 'granted' | 'prompt' | 'blocked' | 'unavailable', changed adalah true bila berbeda dari sebelum panggilan, openedSettings adalah true bila layar pengaturan dipakai. Bila sudah 'granted' (atau 'unavailable') menjawab seketika. Membutuhkan: panggil dari tombol yang diketuk pengguna; notifikasi membutuhkan Notifications (atau Push / Reminders) di build.

Utamakan pembungkus promise AppMint.enableNotifications(). Panggilan mentah, untuk kode yang mengelola balasannya sendiri:

function enableNotificationsRaw() {
  return new Promise(function (resolve) {
    if (!(window.WebToApk && WebToApk.enableNotifications)) return resolve({ state: 'unavailable', changed: false, openedSettings: false });
    var replies = window.__appmintReplies = window.__appmintReplies || {};
    var id = 'my_notif_' + Date.now();
    replies[id] = resolve;
    WebToApk.enableNotifications(id);
  });
}

document.getElementById('turn-on-btn').onclick = async function () {
  var r = await enableNotificationsRaw();
  if (r.state === 'granted') document.getElementById('notif-banner').hidden = true;
};

Setiap alur yang selesai juga diumumkan, termasuk yang dimulai oleh tombol push (appmint://notifications/enable):

window.addEventListener('appmint:notifications', function (e) {
  // e.detail = { state, changed, openedSettings }
  if (e.detail.state === 'granted') document.getElementById('notif-banner').hidden = true;
});

Catatan: tombol push atau pesan dalam aplikasi yang tautannya appmint://notifications/enable menjalankan alur yang sama; appmint://notifications/settings membuka layar pengaturan. Audiens push aplikasi melihat state baru seketika.

getAlarmPrecision jembatan#

window.WebToApk.getAlarmPrecision(): String

"exact" - pengingat berbunyi tepat pada menitnya. "inexact" - pengguna (atau OEM) mematikan alarm tepat waktu untuk aplikasi ini, sehingga Doze dapat menggabungkannya hingga beberapa menit. Minta [requestExactAlarms] untuk memperbaikinya. "unavailable" - Android 12+ dan layanan alarm sistem tidak dapat dijangkau, sehingga ketepatannya tidak dapat dibaca (pengingat yang dijadwalkan sekarang mungkin juga tidak berbunyi). Halaman yang harus tepat waktu sebaiknya memeriksa ini alih-alih berasumsi. @return `"exact"`, `"inexact"` atau `"unavailable"`.

Contoh

Memberi tahu apakah pengingat akan berbunyi tepat pada menitnya, atau bisa terlambat karena pengguna (atau pembuat ponsel) mematikan alarm tepat waktu untuk aplikasi ini.

Mengembalikan: sebuah string, secara sinkron: 'exact', 'inexact' (dapat digabung oleh penghemat baterai, sering hingga beberapa menit), atau 'unavailable' (ponsel tidak memiliki layanan alarm). Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build.

function checkReminderTiming() {
  if (!(window.WebToApk && window.WebToApk.getAlarmPrecision)) return;
  const precision = WebToApk.getAlarmPrecision();
  const warn = document.getElementById('timing-warning');
  warn.hidden = precision === 'exact';
  if (precision === 'inexact') {
    warn.textContent = 'Reminders may be a few minutes late. Tap to allow exact alarms.';
    warn.onclick = function () { WebToApk.requestExactAlarms(); };
  }
}
checkReminderTiming();
// Re-check when the user returns from Settings:
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) checkReminderTiming();
});

Periksa sebelum mengatur alarm bangun tidur, dan beri tahu pengguna bila mungkin terlambat:

function setWakeUp(atMs) {
  const b = window.WebToApk;
  if (!(b && b.getAlarmPrecision && b.scheduleNotificationEx)) return;
  const precision = b.getAlarmPrecision();
  if (precision === 'unavailable') { showMessage('Alarms cannot be set on this phone right now.'); return; }
  b.scheduleNotificationEx('wake', JSON.stringify({ at: atMs, title: 'Wake up', body: 'Good morning', openApp: true }));
  if (precision === 'inexact') {
    showMessage('Alarm set, but it may ring a few minutes late. Allow exact alarms for on-time alarms.');
  }
}

Tampilkan ketepatan waktu pengingat sebagai satu baris di layar pengaturan:

function renderTimingRow() {
  if (!(window.WebToApk && window.WebToApk.getAlarmPrecision)) return;
  const labels = {
    exact: 'Reminders arrive on time',
    inexact: 'Reminders may be a few minutes late',
    unavailable: 'Reminders are not available on this phone'
  };
  document.getElementById('timing-row').textContent = labels[WebToApk.getAlarmPrecision()];
}
renderTimingRow();

Catatan: Android 11 ke bawah selalu menjawab 'exact'. Pengingat 'inexact' tetap berbunyi, hanya tidak tepat pada menitnya.

getAlarmSupport jembatan#

window.WebToApk.getAlarmSupport(): String

Apakah `scheduleNotificationEx(id, {alarm: true, ...})` dapat dijadwalkan sekarang: "ready" | "not_enabled" (Alarm clock mati di build ini) | "needs_exact_alarm" (panggil requestExactAlarms) | "needs_notification_permission".

Contoh

Memberi tahu apakah jam alarm (scheduleNotificationEx(id, { alarm: true, ... })) dapat diatur di ponsel ini sekarang, dan bila tidak, apa yang harus diizinkan pengguna lebih dulu.

Mengembalikan: sebuah string, secara sinkron: 'ready', 'not_enabled' (aplikasi dibuat tanpa đź”” Alarm clock), 'needs_exact_alarm' (Android 12+: alarm tepat waktu mati untuk aplikasi ini - panggil requestExactAlarms()), atau 'needs_notification_permission' (Android 13+: tidak ada izin notifikasi, sehingga tidak akan ada tombol Dismiss - panggil requestNotificationPermission()). Membutuhkan: aktifkan đź”” Alarm clock di langkah Access (Langkah 4) saat Anda membuat build.

function prepareAlarms() {
  const b = window.WebToApk;
  if (!(b && b.getAlarmSupport)) { showMessage('Alarms work in the installed app only.'); return false; }
  switch (b.getAlarmSupport()) {
    case 'ready': return true;
    case 'needs_exact_alarm':
      showMessage('Allow "Alarms & reminders" for this app, then come back.');
      b.requestExactAlarms();
      return false;
    case 'needs_notification_permission':
      b.requestNotificationPermission();
      return false;
    default:
      showMessage('This app was built without alarm clocks.');
      return false;
  }
}

Periksa lagi saat pengguna kembali dari Pengaturan:

document.addEventListener('visibilitychange', function () {
  if (document.hidden || !(window.WebToApk && window.WebToApk.getAlarmSupport)) return;
  document.getElementById('alarm-setup').hidden = WebToApk.getAlarmSupport() === 'ready';
});

Buat alarm hanya saat ponsel siap:

function setAlarmIn(hours) {
  const b = window.WebToApk;
  if (!(b && b.getAlarmSupport)) return false;
  if (b.getAlarmSupport() !== 'ready') { prepareAlarms(); return false; }   // asks for what is missing
  return b.scheduleNotificationEx('wake-up', JSON.stringify({
    at: Date.now() + hours * 3600 * 1000,
    alarm: true,
    title: 'Wake up',
    body: 'Good morning'
  }));
}

Catatan: scheduleNotificationEx dengan alarm: true mengembalikan false setiap kali ini bukan 'ready' - alarm yang tidak mungkin berbunyi tidak pernah disimpan.

getNotificationPermission jembatan#

window.WebToApk.getNotificationPermission(): String

Izin notifikasi SAAT INI, dibaca langsung dari OS. Polyfill dulu menetapkan secara kaku `Notification.permission = 'default'` pada setiap pemuatan halaman, sehingga pola halaman yang sangat umum if (Notification.permission === 'granted') new Notification(...) tidak pernah berjalan - bahkan untuk pengguna yang sudah mengizinkan, dan tidak di Android 12 ke bawah tempat notifikasi sama sekali tidak butuh izin saat berjalan.

Contoh

Membaca, saat ini juga, apakah aplikasi ini boleh mengirim notifikasi.

Mengembalikan: sebuah string, secara sinkron: 'granted'; 'denied' (pengguna menolak permanen, atau mematikan notifikasi aplikasi ini di Pengaturan - kiriman tidak akan tampil); atau 'default' (belum diputuskan, aplikasi masih dapat bertanya). Membutuhkan: tidak ada untuk memanggilnya; pengiriman membutuhkan Notifications diaktifkan di langkah Access (Langkah 4) saat Anda membuat build.

API web standar membaca nilai langsung yang sama:

if ('Notification' in window && Notification.permission === 'granted') {
  document.getElementById('alerts-on').hidden = false;
}

Panggilan mentah, misalnya setelah pengguna kembali dari Pengaturan:

function refreshAlertSetting() {
  if (!(window.WebToApk && window.WebToApk.getNotificationPermission)) return;
  const perm = WebToApk.getNotificationPermission();   // 'granted' | 'denied' | 'default'
  document.getElementById('enable-alerts').hidden = perm !== 'default';   // can still ask
  document.getElementById('open-settings-hint').hidden = perm !== 'denied';
}
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) refreshAlertSetting();
});
refreshAlertSetting();

Putuskan sebelum menjadwalkan apakah akan meminta, menjelaskan, atau langsung lanjut:

async function ensureAlerts() {
  const b = window.WebToApk;
  if (!(b && b.getNotificationPermission)) return false;
  const perm = b.getNotificationPermission();
  if (perm === 'granted') return true;
  if (perm === 'denied') {
    showMessage('Notifications are off for this app. Turn them on in Settings.');
    return false;
  }
  // 'default': the app can still ask
  return (await Notification.requestPermission()) === 'granted';
}

Catatan: ini memberi jawaban yang sama dengan navigator.permissions.query({ name: 'notifications' }). Untuk bertanya kepada pengguna, gunakan Notification.requestPermission().

getNotificationState jembatan#

window.WebToApk.getNotificationState(): String

'granted' | 'prompt' (Android masih dapat bertanya) | 'blocked' (ditolak permanen atau dimatikan di Pengaturan - hanya Pengaturan yang dapat memperbaikinya) | 'unavailable' (Android 13+ dan build ini tidak mendeklarasikan izin notifikasi). Lihat NotificationOptIn.

Contoh

Membaca, saat ini juga, apakah notifikasi aplikasi ini menyala - dan bila tidak, apakah aplikasi masih dapat bertanya atau hanya Pengaturan yang dapat menyalakannya kembali.

Mengembalikan: sebuah string, secara sinkron: 'granted' (notifikasi sampai ke ponsel); 'prompt' (Android masih dapat menampilkan dialog izinnya); 'blocked' (pengguna menolak permanen, atau mematikan notifikasi aplikasi ini di Pengaturan sistem - hanya Pengaturan yang dapat memperbaikinya); atau 'unavailable' (Android 13+ dan aplikasi dibuat tanpa Notifications / Reminders / Push, sehingga tidak ada yang dapat menyalakannya). Membutuhkan: tidak ada untuk memanggilnya.

Panggilan mentah, untuk memutuskan apakah menampilkan banner "Turn on notifications":

function refreshNotifBanner() {
  if (!(window.WebToApk && WebToApk.getNotificationState)) return;
  var state = WebToApk.getNotificationState();   // 'granted' | 'prompt' | 'blocked' | 'unavailable'
  var banner = document.getElementById('notif-banner');
  banner.hidden = !(state === 'prompt' || state === 'blocked');
  banner.querySelector('.hint').textContent = state === 'blocked'
    ? 'Notifications are off. Tap to open Settings and switch them on.'
    : 'Get order updates. Tap to turn on notifications.';
}
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) refreshNotifBanner();
});
refreshNotifBanner();

Catatan: getNotificationPermission() mempertahankan kata-katanya sendiri ('granted' / 'default' / 'denied') untuk halaman lama. Untuk mengembalikan pengguna, panggil AppMint.enableNotifications() (ia memilih dialog atau Pengaturan untuk Anda).

getScheduledNotifications jembatan#

window.WebToApk.getScheduledNotifications(): String

Membaca dan meminta izin POST_NOTIFICATIONS, serta mengelola notifikasi yang sudah dijadwalkan halaman. Pengiriman dan penjadwalan didokumentasikan pada metodenya masing-masing.

Dijelaskan oleh grupnya, Izin notifikasi, pembatalan, dan daftar, bukan sendiri.

Contoh

Mencantumkan pengingat yang masih terjadwal.

Mengembalikan: sebuah string JSON, secara sinkron. Parse. Setiap item: { id, title, body, trigger, repeat, options? } - trigger adalah waktu berbunyi berikutnya dalam epoch ms, repeat adalah none|minutely|hourly|daily|weekly, dan options (hanya untuk scheduleNotificationEx) adalah opsi sebagai string JSON. Field langsung menunjukkan bagaimana pengiriman sebenarnya, dibaca langsung: precision ("exact"; "inexact" = "Alarms & reminders" mati untuk aplikasi, sehingga Android mengirimkannya dalam jendela 10 menit saat ponsel aktif, dan dapat menahannya lebih lama saat tidur; "alarm_clock" untuk alarm: true), windowMs + latestAt (jendela yang diberikan ke Android: 0 / trigger bila tepat waktu, 600000 / trigger + 600000 bila tidak tepat) dan notificationsEnabled (false = berbunyi tetapi Android tidak menampilkan apa pun, karena notifikasi aplikasi mati). '[]' bila tidak ada. Membutuhkan: tidak ada tambahan.

function renderReminders() {
  if (!(window.WebToApk && window.WebToApk.getScheduledNotifications)) return;
  let list = [];
  try { list = JSON.parse(WebToApk.getScheduledNotifications() || '[]'); } catch (e) { list = []; }

  const ul = document.getElementById('reminders');
  ul.innerHTML = '';
  list.sort(function (a, b) { return a.trigger - b.trigger; }).forEach(function (r) {
    const li = document.createElement('li');
    li.textContent = r.title + ' — ' + new Date(r.trigger).toLocaleString() +
      (r.repeat !== 'none' ? ' (' + r.repeat + ')' : '');
    ul.appendChild(li);
  });
}

Tampilkan apakah satu pengingat menyala, dan kapan berbunyi berikutnya:

function describeReminder(id) {
  if (!(window.WebToApk && window.WebToApk.getScheduledNotifications)) return 'Not available';
  let list = [];
  try { list = JSON.parse(WebToApk.getScheduledNotifications() || '[]'); } catch (e) { list = []; }
  const r = list.find(function (x) { return x.id === id; });
  return r ? 'Next: ' + new Date(r.trigger).toLocaleString() : 'Off';
}
document.getElementById('water-status').textContent = describeReminder('water');

Baca opsi tambahan dari pengingat yang dibuat dengan scheduleNotificationEx:

function listAlarmStyleReminders() {
  if (!(window.WebToApk && window.WebToApk.getScheduledNotifications)) return [];
  let list = [];
  try { list = JSON.parse(WebToApk.getScheduledNotifications() || '[]'); } catch (e) { list = []; }
  return list.filter(function (r) {
    if (!r.options) return false;                  // made with scheduleNotification
    let o = {};
    try { o = JSON.parse(r.options); } catch (e) { return false; }
    return o.openApp === true;                     // puts the app on screen when it fires
  }).map(function (r) { return r.id; });
}

Catatan: pengingat sekali jalan keluar dari daftar saat berbunyi. Yang berulang tetap ada, dengan trigger dipindahkan ke waktu berikutnya. Pengingat yang waktunya lewat saat ponsel mati atau aplikasi dihentikan paksa ditampilkan sekali, terlambat, saat aplikasi atau ponsel berikutnya dijalankan; alarm: true yang terlambat dilaporkan sebagai missed alih-alih berbunyi. Pengingat harian, mingguan, bulanan, atau tahunan mempertahankan waktu lokalnya saat ponsel berganti zona waktu.

Beri peringatan saat pengingat tidak akan muncul tepat waktu:

function reminderHealth() {
  if (!(window.WebToApk && window.WebToApk.getScheduledNotifications)) return '';
  let list = [];
  try { list = JSON.parse(WebToApk.getScheduledNotifications() || '[]'); } catch (e) { list = []; }
  if (!list.length) return '';
  if (list.some(function (r) { return r.notificationsEnabled === false; })) return 'Notifications are off for this app: reminders will not appear.';
  if (list.some(function (r) { return r.precision === 'inexact'; })) return 'Reminders may arrive late. Turn on Alarms & reminders.';
  return '';
}

isAlarmRinging jembatan#

window.WebToApk.isAlarmRinging(): Boolean

True selama jam alarm berbunyi.

Contoh

Memberi tahu apakah salah satu jam alarm aplikasi sedang berbunyi sekarang, agar halaman dapat menampilkan layar berhenti / tunda miliknya sendiri.

Mengembalikan: true selama alarm berbunyi, jika tidak false (secara sinkron). Membutuhkan: đź”” Alarm clock di langkah Access (Langkah 4) saat Anda membuat build.

function showRingScreenIfNeeded() {
  const b = window.WebToApk;
  const ringing = !!(b && b.isAlarmRinging && b.isAlarmRinging());
  document.getElementById('ring-screen').hidden = !ringing;
}
showRingScreenIfNeeded();
document.addEventListener('visibilitychange', showRingScreenIfNeeded);

Hentikan alarm yang berbunyi dari layar Anda sendiri dengan panggilan yang sesuai.

document.getElementById('stop').addEventListener('click', function () {
  const b = window.WebToApk;
  if (b && b.isAlarmRinging && b.isAlarmRinging()) b.stopAlarm('');   // '' = whichever is ringing
});
document.getElementById('snooze').addEventListener('click', function () {
  const b = window.WebToApk;
  if (b && b.snoozeAlarm && !b.snoozeAlarm('', 10)) showMessage('Nothing is ringing');
});

Tanggapi aplikasi yang dibuka oleh alarm yang berbunyi.

window.addEventListener('appmint:reminder', function (e) {
  if (e.detail.action === 'open' && e.detail.ringing) {
    document.getElementById('ring-screen').hidden = false;
  }
});

Catatan: saat alarm yang berbunyi membuka aplikasi, appmint:reminder juga tiba dengan { action: 'open', ringing: true }.

notify jembatan#

window.WebToApk.notify(optionsJson: String): Boolean

Notifikasi kaya. Lihat NotificationBridge.show untuk daftar opsi lengkap: channel (urgent|default|quiet|ongoing), image, largeIcon, bigText, ongoing, actions, progress, sound, silent, group, color. Pengambilan dan dekode gambar terjadi di luar thread utama, sehingga gambar jarak jauh tidak pernah dapat menghalangi halaman.

Contoh

Mengirim notifikasi sekarang juga, dengan kombinasi opsi apa pun: gambar, teks panjang, tombol, bilah kemajuan, menempel (ongoing), kanal, suara, warna, dan tag.

Mengembalikan: true bila opsi berupa JSON yang valid dan notifikasi diserahkan ke sistem (dibangun di thread latar belakang, sehingga URL gambar yang buruk tidak menghalangi halaman); false untuk JSON yang tidak valid. Membutuhkan: aktifkan Notifications di langkah Access (Langkah 4) saat Anda membuat build, dan izin pengguna di Android 13+ (minta dengan Notification.requestPermission()).

Notifikasi sederhana. tag adalah ID: mengirim lagi dengan tag yang sama menggantikan yang lama.

async function notifyOrderReady() {
  if (!(window.WebToApk && window.WebToApk.notify)) return;
  if (window.Notification && Notification.permission !== 'granted') {
    const p = await Notification.requestPermission();
    if (p !== 'granted') { showMessage('Notifications are off for this app.'); return; }
  }
  WebToApk.notify(JSON.stringify({
    title: 'Order ready',
    body: 'Your order is ready to collect',
    channel: 'default',            // urgent | default | quiet | ongoing
    tag: 'order-42'
  }));
}

Gambar, ikon besar, dan teks panjang. image menang atas bigText bila keduanya diberikan.

WebToApk.notify(JSON.stringify({
  title: 'New photo',
  body: 'Tap to see it',
  image: 'https://example.com/photo.jpg',     // expanded big picture
  largeIcon: 'https://example.com/avatar.png', // round icon on the right
  color: '#00C6FF',
  tag: 'photo-7'
}));

WebToApk.notify(JSON.stringify({
  title: 'Weekly summary',
  body: '3 tasks done, 2 left',
  bigText: 'Done: pay rent, call bank, water plants. Left: tax form, dentist.',
  channel: 'quiet',                            // no sound, low priority
  group: 'summaries'
}));

Tombol (maksimal 3). Ketukan kembali sebagai event appmint:notification-action.

WebToApk.notify(JSON.stringify({
  title: 'Ali sent you a message',
  body: 'Are we still on for 6pm?',
  channel: 'urgent',                           // heads-up banner
  tag: 'chat-ali',
  actions: [
    { id: 'open', label: 'Open chat' },
    { id: 'done', label: 'Mark read' }
  ]
}));

window.addEventListener('appmint:notification-action', function (e) {
  // e.detail = { actionId: 'done', tag: 'chat-ali' }
  if (e.detail.actionId === 'open') openChat(e.detail.tag);
  if (e.detail.actionId === 'done') markRead(e.detail.tag);
});

Kemajuan menempel (ongoing). Kirim ulang dengan tag yang sama untuk memperbaruinya. Saat pekerjaan selesai, kirim state akhir dengan ongoing: false agar pengguna dapat menggesernya.

function showUpload(percent) {
  WebToApk.notify(JSON.stringify({
    title: 'Uploading', body: percent + '%',
    ongoing: true,                             // cannot be swiped away
    progress: { current: percent, max: 100 },  // or { indeterminate: true }
    silent: true,
    tag: 'upload'
  }));
}
showUpload(40);
// finished:
WebToApk.notify(JSON.stringify({ title: 'Upload complete', body: '3 photos saved', tag: 'upload' }));

Opsi lain: sound (file di bundel Anda seperti '/audio/ding.mp3', URL data:audio/..., atau 'silent'), smallIcon (nama drawable), when (epoch ms yang ditampilkan sebagai waktu), openApp: true (peringatan layar penuh; lihat scheduleNotificationEx).

Catatan: tombol tidak memiliki kotak balasan inline, dan ketukan tombol sampai ke halaman hanya saat aplikasi berjalan (tidak membuka aplikasi); event itu juga memanggil window.onAppMintNotificationAction(detail) bila Anda mendefinisikannya. image dan largeIcon menerima URL https:// atau data:, atau gambar yang dikemas bersama aplikasi Anda dengan nama sesuai penyebutan halaman Anda ('img/logo.png', '/img/logo.png', atau alamat lengkap https://appassets.androidplatform.net/assets/…). Gambar yang buruk dilewati, notifikasi tetap tampil. ongoing: true selalu memakai kanal "Ongoing" yang tenang. notify() tidak memiliki pasangan "hapus" publik: cancelNotification hanya membatalkan pengingat terjadwal. Notifikasi yang ingin Anda hapus nanti dari halaman sebaiknya dikirim dengan new Notification(...) dan dihapus dengan close()-nya; kartu kemajuan menempel dengan Capacitor.AppwrightLiveProgress (end). Android menetapkan suara kanal saat kanal dibuat, sehingga nilai sound baru membuat kanal baru.

openNotificationSettings jembatan#

window.WebToApk.openNotificationSettings(): Boolean

Membuka pengaturan notifikasi aplikasi ini; true bila layar dapat dibuka.

Contoh

Membuka layar pengaturan notifikasi milik aplikasi ini, tempat pengguna dapat menyalakan atau mematikan notifikasi (dan setiap kanal).

Mengembalikan: true bila layar dibuka, secara sinkron; false bila tidak dapat (Android 13+ dan build tidak memiliki notifikasi). Di Android 8+ itu adalah halaman notifikasi aplikasi; di Android lebih lama, layar App info. Membutuhkan: tidak ada yang perlu diaktifkan.

document.getElementById('notif-settings-link').onclick = function () {
  if (!(window.WebToApk && WebToApk.openNotificationSettings && WebToApk.openNotificationSettings())) {
    showMessage('Open Settings > Apps > this app > Notifications.');
  }
};

Catatan: untuk mengembalikan pengguna dengan satu ketukan, gunakan AppMint.enableNotifications(): ia menampilkan dialog izin saat Android masih mengizinkan dan membuka layar ini hanya bila tidak.

prepareNotificationSound jembatan#

window.WebToApk.prepareNotificationSound(spec: String): Boolean

Menyalin file audio bawaan ke tempatnya lebih dulu dan melaporkan apakah dapat dipakai sebagai suara notifikasi. Opsional - `notify()` dan `scheduleNotificationEx()` melakukannya sendiri - tetapi ini memungkinkan layar pengaturan memeriksa suara begitu pengguna memilihnya.

Contoh

Memeriksa bahwa file audio di aplikasi Anda dapat dipakai sebagai suara notifikasi, dan menyalinnya ke tempatnya sekarang. Opsional: notify() dan scheduleNotificationEx() melakukan hal yang sama sendiri; ini memungkinkan layar pengaturan memeriksa suara begitu pengguna memilihnya.

Mengembalikan: true bila suara dapat dipakai; false bila file tidak ada atau bukan tipe audio yang didukung. Membutuhkan: tidak ada.

function chooseAlarmSound(path) {             // e.g. '/audio/chime.mp3'
  if (!(window.WebToApk && window.WebToApk.prepareNotificationSound)) return;
  if (!WebToApk.prepareNotificationSound(path)) {
    showMessage('This sound file is missing from the app: ' + path);
    return;
  }
  localStorage.setItem('alarmSound', path);
  // Use it:
  WebToApk.notify(JSON.stringify({ title: 'Sound test', body: 'This is your alarm sound', sound: path, tag: 'sound-test' }));
}

Isi pemilih suara hanya dengan suara yang benar-benar dapat dipakai:

const SOUNDS = [
  { label: 'Chime', path: '/audio/chime.mp3' },
  { label: 'Bell',  path: '/audio/bell.ogg' },
  { label: 'Beep',  path: '/audio/beep.wav' }
];

function renderSoundPicker(select) {
  const b = window.WebToApk;
  if (!(b && b.prepareNotificationSound)) return;
  select.innerHTML = '';
  SOUNDS.forEach(function (s) {
    if (!b.prepareNotificationSound(s.path)) return;   // missing or unsupported file
    const opt = document.createElement('option');
    opt.value = s.path; opt.textContent = s.label;
    select.appendChild(opt);
  });
}

Biarkan pengguna memilih file audionya sendiri dan memakainya untuk pengingat harian, diberikan sebagai URL data::

document.getElementById('own-sound').addEventListener('change', function (e) {
  const file = e.target.files[0];
  const b = window.WebToApk;
  if (!file || !(b && b.prepareNotificationSound)) return;
  const reader = new FileReader();
  reader.onload = function () {
    const dataUrl = reader.result;                 // 'data:audio/mpeg;base64,...'
    if (!b.prepareNotificationSound(dataUrl)) {
      showMessage('That file cannot be used as a notification sound.');
      return;
    }
    b.scheduleNotificationEx('daily-walk', JSON.stringify({
      at: nextEvening(), repeat: 'daily',
      title: 'Walk time', body: 'Time for your evening walk', sound: dataUrl
    }));
  };
  reader.readAsDataURL(file);
});

Nilai yang diterima: path di bundel web Anda ('/audio/chime.mp3', 'audio/chime.mp3') atau URL data:audio/...;base64,... yang dibuat halaman Anda. Tipe: mp3, wav, ogg, oga, m4a, aac, flac, opus, mid, midi.

Catatan: nama polos tanpa folder atau ekstensi (suara res/raw) tidak diperiksa di sini dan mengembalikan false, meskipun notify({sound: 'name'}) dapat memakainya. Android menyimpan satu suara per kanal, sehingga mengganti suara membuat kanal baru dan mengatur ulang pengaturan pengguna untuk kanal itu.

requestExactAlarms jembatan#

window.WebToApk.requestExactAlarms(): Boolean

Membuka layar sistem tempat alarm tepat waktu diberikan. Android 12+.

Contoh

Membuka layar sistem Android 12+ tempat pengguna mengizinkan aplikasi ini memakai alarm tepat waktu, sehingga pengingat berbunyi tepat pada menitnya.

Mengembalikan: true bila layar pengaturan dibuka (dan di Android 11 ke bawah, tempat tidak ada yang dibutuhkan); false bila layar tidak dapat dibuka. Ini tidak memberi tahu pilihan pengguna. Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build.

document.getElementById('allow-exact').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.requestExactAlarms)) return;
  if (WebToApk.getAlarmPrecision() === 'exact') return;   // already fine
  WebToApk.requestExactAlarms();
});

// The user comes back from Settings: read the real answer.
document.addEventListener('visibilitychange', function () {
  if (document.hidden || !window.WebToApk) return;
  const exact = WebToApk.getAlarmPrecision() === 'exact';
  document.getElementById('allow-exact').hidden = exact;
});

Sebelum menyimpan pengingat yang kritis waktu, tawarkan layar pengaturan hanya bila ketepatan waktu memang mati:

function saveMedicineReminder(at) {
  const b = window.WebToApk;
  if (!(b && b.scheduleNotificationEx && b.getAlarmPrecision)) return;
  b.scheduleNotificationEx('medicine', JSON.stringify({
    at: at, repeat: 'daily', title: 'Medicine', body: 'Time for your tablet'
  }));
  const precision = b.getAlarmPrecision();   // 'exact' | 'inexact' | 'unavailable'
  if (precision === 'inexact') {
    showBanner('Reminders may arrive a few minutes late.', 'Fix timing', function () {
      b.requestExactAlarms();                // runs from the banner button tap
    });
  } else if (precision === 'unavailable') {
    showBanner('The phone\'s alarm service is not answering; this reminder may not fire.');
  }
}

Saat layar pengaturan tidak dapat dibuka (false), beri tahu pengguna di mana menemukan sakelarnya:

document.getElementById('fix-timing').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.requestExactAlarms)) return;
  const opened = b.requestExactAlarms();
  if (!opened) {
    showMessage('Open Settings > Apps > this app > "Alarms & reminders" and turn it on.');
  }
  // When it did open, read the answer on return with getAlarmPrecision().
});

Catatan: panggil dari ketukan tombol dan jelaskan alasannya lebih dulu - ini membawa pengguna keluar dari aplikasi Anda. Pengingat yang sudah dijadwalkan tetap berfungsi bagaimanapun; hanya ketepatan waktunya yang berubah.

requestNotificationPermission jembatan#

window.WebToApk.requestNotificationPermission(): String

Membaca dan meminta izin POST_NOTIFICATIONS, serta mengelola notifikasi yang sudah dijadwalkan halaman. Pengiriman dan penjadwalan didokumentasikan pada metodenya masing-masing.

Dijelaskan oleh grupnya, Izin notifikasi, pembatalan, dan daftar, bukan sendiri.

Contoh

Menampilkan dialog "Allow notifications?" Android 13+. Halaman biasanya memakai Notification.requestPermission(), yang memanggil ini dan menunggu jawaban pengguna.

Mengembalikan: sebuah string, secara sinkron: 'granted' bila sudah diizinkan (atau di Android 12 ke bawah), jika tidak 'default' saat dialog terbuka. Tidak dapat mengembalikan jawaban pengguna. Membutuhkan: aktifkan Notifications di langkah Access (Langkah 4) saat Anda membuat build.

Gunakan Promise API - ia terselesaikan dengan jawaban sebenarnya ('granted' atau 'denied'):

document.getElementById('enable-alerts').addEventListener('click', async function () {
  if (!('Notification' in window)) return;
  const perm = await Notification.requestPermission();
  if (perm === 'granted') {
    showMessage('Alerts are on');
  } else {
    showMessage('Alerts are off. You can turn them on in Settings.');
  }
});

Panggilan mentah, hanya untuk membuka dialog tanpa menunggu:

if (window.WebToApk && window.WebToApk.requestNotificationPermission) {
  const now = WebToApk.requestNotificationPermission();  // 'granted' or 'default'
  if (now === 'granted') enableAlertFeatures();
  // otherwise re-check later with WebToApk.getNotificationPermission()
  // ('granted' | 'denied' | 'default')
}

Kode lama yang memberikan callback ke Notification.requestPermission mendapat jawaban akhir yang sama:

document.getElementById('turn-on-reminders').addEventListener('click', function () {
  if (!('Notification' in window)) return;
  if (Notification.permission !== 'default') { updateReminderUi(Notification.permission); return; }
  Notification.requestPermission(function (perm) {
    updateReminderUi(perm);   // 'granted' or 'denied', once the user answers
  });
});

Catatan: minta dari ketukan tombol dengan alasan yang jelas. Setelah pengguna menolak dua kali, Android berhenti menampilkan dialog dan jawabannya langsung 'denied'; pengguna lalu harus mengizinkannya di Pengaturan aplikasi.

requestOpenAppPermission jembatan#

window.WebToApk.requestOpenAppPermission(): Boolean

Membuka layar sistem tempat peringatan layar penuh diberikan. Android 14+.

Contoh

Membuka layar sistem Android 14+ tempat pengguna mengizinkan peringatan layar penuh untuk aplikasi ini, sehingga pengingat openApp: true dapat menampilkan aplikasi di layar.

Mengembalikan: true bila layar pengaturan dibuka (dan di bawah Android 14, tempat tidak ada yang dibutuhkan); false bila tidak dapat dibuka. Ini tidak memberi tahu pilihan pengguna. Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build.

document.getElementById('allow-open-app').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.requestOpenAppPermission)) return;
  if (b.canOpenAppAtTime() === 'granted') return;
  b.requestOpenAppPermission();
});

// Read the real answer when the user comes back:
document.addEventListener('visibilitychange', function () {
  if (document.hidden || !window.WebToApk) return;
  const ok = WebToApk.canOpenAppAtTime() === 'granted';
  document.getElementById('allow-open-app').hidden = ok;
});

Ingat alarm yang sedang diatur pengguna, kirim mereka ke Pengaturan, dan selesaikan saat mereka kembali:

let pendingAlarm = null;

function setWakeAlarm(at) {
  const b = window.WebToApk;
  if (!(b && b.requestOpenAppPermission)) return;
  if (b.canOpenAppAtTime() === 'denied') {
    pendingAlarm = at;
    b.requestOpenAppPermission();
    return;
  }
  b.scheduleNotificationEx('wake', JSON.stringify({ at: at, title: 'Alarm', body: 'Wake up', openApp: true }));
}

document.addEventListener('visibilitychange', function () {
  if (document.hidden || pendingAlarm === null || !window.WebToApk) return;
  if (WebToApk.canOpenAppAtTime() === 'granted') setWakeAlarm(pendingAlarm);
  else showMessage('Without that setting the alarm cannot open the app by itself.');
  pendingAlarm = null;
});

Saat layar pengaturan tidak dapat dibuka (false), beri tahu di mana sakelarnya:

document.getElementById('allow-fullscreen').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.requestOpenAppPermission)) return;
  if (!b.requestOpenAppPermission()) {
    showMessage('Open Settings > Apps > this app > Notifications and allow full-screen notifications.');
  }
});

Catatan: panggil dari ketukan tombol setelah menjelaskan alasannya - ini membawa pengguna keluar dari aplikasi Anda.

scheduleNotification jembatan#

window.WebToApk.scheduleNotification(id: String, title: String, body: String, triggerAtMillis: String, repeat: String): Boolean

Membaca dan meminta izin POST_NOTIFICATIONS, serta mengelola notifikasi yang sudah dijadwalkan halaman. Pengiriman dan penjadwalan didokumentasikan pada metodenya masing-masing.

Dijelaskan oleh grupnya, Izin notifikasi, pembatalan, dan daftar, bukan sendiri.

Contoh

Menjadwalkan pengingat sederhana (judul dan teks) untuk waktu mendatang. Berbunyi bahkan saat aplikasi ditutup dan diatur ulang setelah ponsel dimulai ulang.

Mengembalikan: true bila terjadwal; false bila id kosong atau waktunya bukan angka. Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build (ini juga mengaktifkan Notifications), dan izin notifikasi pengguna di Android 13+.

function remindToDrinkWater() {
  if (!(window.WebToApk && window.WebToApk.scheduleNotification)) {
    showMessage('Reminders work in the installed app only.');
    return;
  }
  const ok = WebToApk.scheduleNotification(
    'water',                                  // your own id; same id replaces the old reminder
    'Drink water',                            // title
    'Time for a glass of water',              // body
    String(Date.now() + 60 * 60 * 1000),      // epoch milliseconds, AS A STRING
    'daily'                                   // none | minutely | hourly | daily | weekly
  );
  if (!ok) showMessage('Could not schedule the reminder');
}

Ketahui kapan pengingat membuka aplikasi (pengguna mengetuknya):

window.addEventListener('appmint:reminder', function (e) {
  // e.detail = { id: 'water', overLockScreen: false }
  if (e.detail.id === 'water') openWaterLog();
});

Pengingat sekali jalan pukul 8:00 pada hari tugas jatuh tempo:

function remindTask(task) {
  if (!(window.WebToApk && window.WebToApk.scheduleNotification)) return false;
  const at = new Date(task.dueDate);
  at.setHours(8, 0, 0, 0);
  if (at.getTime() <= Date.now()) return false;     // that morning has passed
  return WebToApk.scheduleNotification(
    'task-' + task.id,
    'Due today',
    task.title,
    String(at.getTime()),                           // epoch ms as a string
    'none'
  );
}

Catatan: pengingat memakai gaya "urgent" (heads-up) dan tag-nya adalah id. Pengingat berulang yang terlewat saat ponsel mati berbunyi pada langkah berikutnya. Untuk gambar, tombol, suara Anda sendiri, atau membuka aplikasi di atas layar kunci, gunakan scheduleNotificationEx. Bila alarm tepat waktu mati untuk aplikasi, waktunya dapat bergeser beberapa menit; periksa getAlarmPrecision().

scheduleNotificationEx jembatan#

window.WebToApk.scheduleNotificationEx(id: String, optionsJson: String): Boolean

Bentuk lengkap dari [scheduleNotification]: semua yang diterima `notify()`, berbunyi pada waktu mendatang saat aplikasi ditutup. WebToApk.scheduleNotificationEx('dose-1', JSON.stringify({ at: Date.now() + 3600000, // epoch millis (alias: triggerAt) repeat: 'daily', // none|minutely|hourly|daily|weekly|weekdays|monthly|yearly // days: [1, 3, 5], // with 'weekdays': ISO Mon=1..Sun=7 (default Mon-Fri) // alarm: true, // ring like an alarm clock (getAlarmSupport()) title: 'Time for your dose', body: 'Vitamin D - 1 tablet', sound: '/audio/chime.mp3', // a file in YOUR bundle; 'silent' for none openApp: true // open the app itself, over the lock screen })); Suara bawaan disalin ke tempatnya selama panggilan ini, sehingga nama file yang salah dilaporkan sekarang (false) dan tidak berubah menjadi suara default berhari-hari kemudian.

Contoh

Menjadwalkan pengingat dengan semua opsi yang dimiliki notify() (gambar, tombol, suara, kanal, buka aplikasi), untuk waktu mendatang. Berbunyi saat aplikasi ditutup dan bertahan setelah dimulai ulang.

Mengembalikan: true bila terjadwal. false bila tidak ada waktu yang diberikan (at, triggerAt, triggerAtMillis atau inSeconds), id kosong, JSON-nya buruk, atau sound menyebut file yang tidak ada di aplikasi Anda. Membutuhkan: aktifkan ⏰ Reminders di langkah Access (Langkah 4) saat Anda membuat build, dan izin notifikasi pengguna di Android 13+.

Pengingat harian dengan suara Anda sendiri yang membuka aplikasi (di atas layar kunci):

function scheduleStandup(tomorrowAt9am) {
  const b = window.WebToApk;
  if (!(b && b.scheduleNotificationEx)) { showMessage('Reminders work in the installed app only.'); return; }

  // Both are user settings: check, and ask if needed.
  if (b.getAlarmPrecision() === 'inexact') b.requestExactAlarms();
  if (b.canOpenAppAtTime() === 'denied') b.requestOpenAppPermission();

  const ok = b.scheduleNotificationEx('standup', JSON.stringify({
    at: tomorrowAt9am,          // epoch ms (or triggerAt, or inSeconds: 3600)
    repeat: 'daily',            // none | minutely | hourly | daily | weekly | weekdays | monthly | yearly
    title: 'Stand-up',
    body: 'Daily sync starts now',
    sound: '/audio/alarm.mp3',  // a file in YOUR app; 'silent' for none
    openApp: true               // full-screen alert that puts the app on screen
  }));
  if (!ok) showMessage('Check the sound file path — nothing was scheduled.');
}

window.addEventListener('appmint:reminder', function (e) {
  // e.detail = { id: 'standup', overLockScreen: true, action: 'open', ringing: false }
  openMeetingScreen(e.detail.id);
});

Hanya hari olahraga - Senin, Rabu, dan Jumat pukul 18:00 (days berformat ISO: Senin = 1 … Minggu = 7; tanpa days, 'weekdays' berarti Senin-Jumat):

WebToApk.scheduleNotificationEx('gym', JSON.stringify({
  at: nextAt(18, 0),          // epoch ms of the first time; moved to the next listed day if needed
  repeat: 'weekdays',
  days: [1, 3, 5],
  title: 'Gym time',
  body: 'Leg day'
}));

// Rent on the 31st: 'monthly' rings on the last day of shorter months (28/29/30) and on the 31st again when there is one.
WebToApk.scheduleNotificationEx('rent', JSON.stringify({ at: rentDueAt, repeat: 'monthly', title: 'Rent is due' }));
// A birthday: same date every year (29 February rings on the 28th in other years).
WebToApk.scheduleNotificationEx('bday-ana', JSON.stringify({ at: anaBirthdayAt9am, repeat: 'yearly', title: 'Ana\'s birthday' }));

Jam alarm sungguhan (alarm: true): berbunyi pada volume ALARM ponsel - menembus mode senyap, dan menembus Do Not Disturb bila pengguna mengizinkan alarm - dan terus berbunyi hingga Dismiss / Snooze atau ringSeconds. Menampilkan ikon alarm di bilah status dan muncul di layar di atas layar kunci:

function setWakeAlarm(atMs) {
  const b = window.WebToApk;
  if (!(b && b.getAlarmSupport)) { showMessage('Alarms work in the installed app only.'); return; }
  const support = b.getAlarmSupport();   // 'ready' | 'not_enabled' | 'needs_exact_alarm' | 'needs_notification_permission'
  if (support === 'needs_exact_alarm') { b.requestExactAlarms(); return; }
  if (support !== 'ready') { showMessage('Alarms are not available: ' + support); return; }

  const ok = b.scheduleNotificationEx('wake', JSON.stringify({
    at: atMs,
    repeat: 'weekdays',          // daily/weekly/weekdays/monthly/yearly keep 07:00 across clock changes
    title: 'Wake up',
    body: 'Good morning',
    alarm: true,
    sound: '',                   // '' = the phone's alarm tone; or '/audio/rooster.mp3'; 'silent' = vibrate only
    vibrate: true,               // needs Vibration on in the build
    ringSeconds: 300,            // stop and count as missed after 5 minutes (default 600, max 3600)
    snoozeMinutes: 9,            // 0 hides the Snooze button (default 10)
    snoozeLabel: 'Snooze', dismissLabel: 'Stop'
  }));
  if (!ok) showMessage('The alarm was not set.');
}

window.addEventListener('appmint:reminder', function (e) {
  const d = e.detail;
  // Opened by the ringing alarm:  { id: 'wake', action: 'open', ringing: true, overLockScreen: true }
  // How a ring ended (also delivered later if the app was closed):
  //   { id: 'wake', action: 'dismiss' | 'snooze' | 'missed' | 'error', alarm: true,
  //     source: 'notification' | 'page' | 'timeout' | 'replaced' | 'system',
  //     snoozeMinutes?, snoozedUntil?, error?, soundError? }
  if (d.action === 'open' && d.ringing) showRingScreen(d.id);   // your Stop / Snooze buttons call stopAlarm / snoozeAlarm
  if (d.action === 'missed') logMissedAlarm(d.id);
});

Pengingat dengan tombol, dalam 30 menit:

WebToApk.scheduleNotificationEx('dose-1', JSON.stringify({
  inSeconds: 30 * 60,
  title: 'Time for your dose',
  body: 'Vitamin D — 1 tablet',
  actions: [{ id: 'taken', label: 'Taken' }, { id: 'snooze', label: 'Snooze' }]
}));

window.addEventListener('appmint:notification-action', function (e) {
  // e.detail = { actionId: 'taken', tag: 'dose-1' }
  if (e.detail.actionId === 'snooze') {
    WebToApk.scheduleNotificationEx(e.detail.tag, JSON.stringify({ inSeconds: 600, title: 'Time for your dose', body: 'Snoozed reminder' }));
  }
});

Pengingat sekali jalan dengan gambar saat promo dimulai, di kanal normal:

function remindSale(sale) {                        // sale.startsAt = epoch ms
  if (!(window.WebToApk && window.WebToApk.scheduleNotificationEx)) return false;
  return WebToApk.scheduleNotificationEx('sale-' + sale.id, JSON.stringify({
    at: sale.startsAt,
    title: 'The sale starts now',
    body: sale.name,
    image: 'https://example.com/banners/' + sale.id + '.jpg',   // expanded picture
    channel: 'default'                             // not the heads-up style reminders use by default
  }));
}

Catatan: id yang sama menggantikan pengingat yang lebih lama. Default: tag = id, kanal = urgent, bigText = isi pesan. Ketukan tombol sampai ke halaman hanya saat aplikasi berjalan. File suara disalin saat Anda menjadwalkan, sehingga path yang salah gagal sekarang dan bukan berhari-hari kemudian; mp3, wav, ogg, m4a, aac, flac, dan opus berfungsi. openApp adalah satu-satunya cara aplikasi dapat tampil di layar sendiri pada waktu yang ditentukan; di Android 14+ pengguna harus mengizinkannya (canOpenAppAtTime). repeat: 'weekdays' dengan days di luar 1-7 mengembalikan false. alarm: true membutuhkan đź”” Alarm clock di langkah Access (Langkah 4) dan mengembalikan false kecuali getAlarmSupport() bernilai 'ready'; hanya satu alarm berbunyi pada satu waktu (yang baru mengakhiri yang sebelumnya sebagai missed). Bila file suara alarm itu sendiri hilang saat berbunyi (Remote Update menghapusnya), alarm berbunyi dengan nada alarm ponsel dan event membawa soundError: true.

showNotification jembatan#

window.WebToApk.showNotification(title: String, body: String, icon: String, tag: String): Boolean

Bentuk ASLI empat argumen, dipertahankan apa adanya sebagai adaptor agar setiap aplikasi yang sudah beredar dan setiap cuplikan panduan tetap berfungsi. Kini ia meneruskan ke pembangun yang kaya - artinya argumen `icon`, yang diam-diam dibuang implementasi lama, akhirnya berfungsi.

Contoh

Panggilan notifikasi asli dengan empat argumen. Menampilkan notifikasi dengan judul, teks, ikon besar opsional, dan tag. Kode baru sebaiknya memakai notify(optionsJson) atau new Notification(...), yang berbuat lebih banyak.

Mengembalikan: true bila notifikasi diserahkan ke sistem. Membutuhkan: aktifkan Notifications di langkah Access (Langkah 4) saat Anda membuat build, dan izin pengguna di Android 13+.

API web standar (ia memanggil metode ini untuk Anda):

async function tellUser() {
  if (!('Notification' in window)) return;
  const p = Notification.permission === 'granted' ? 'granted' : await Notification.requestPermission();
  if (p === 'granted') new Notification('Saved', { body: 'Your note was saved', tag: 'note' });
}

Panggilan mentah. Argumen: judul, isi, ikon, tag. Berikan '' untuk tanpa ikon atau tanpa tag.

if (window.WebToApk && window.WebToApk.showNotification) {
  WebToApk.showNotification(
    'Saved',                                  // title
    'Your note was saved',                    // body
    'https://example.com/avatar.png',         // large icon (https://, data:, or 'img/x.png' in your app), or ''
    'note'                                    // tag: same tag replaces the old notification
  );
}

Perbarui satu notifikasi status di tempat dengan memakai ulang tag-nya:

function showSyncStatus(done, total) {
  const b = window.WebToApk;
  if (!(b && b.showNotification)) return;
  if (b.getNotificationPermission() !== 'granted') return;
  b.showNotification(
    'Syncing photos',
    done + ' of ' + total + ' uploaded',
    '',          // no large icon
    'sync'       // same tag every time: the text updates, no new notification
  );
}

Catatan: ini meneruskan ke notify({title, body, largeIcon: icon, tag}) pada kanal default. Tanpa tag, setiap panggilan menambah notifikasi baru. Untuk gambar, tombol, kemajuan, atau notifikasi menempel gunakan notify.

snoozeAlarm jembatan#

window.WebToApk.snoozeAlarm(id: String, minutes: Int): Boolean

Menghentikan alarm yang berbunyi [id] ("" = alarm mana pun yang berbunyi) dan membunyikannya lagi dalam [minutes] (1-1440; 0 = snoozeMinutes milik alarm itu). False bila tidak berbunyi.

Contoh

Menghentikan jam alarm yang berbunyi dan membunyikannya lagi setelah beberapa menit, seperti tombol Snooze-nya. Hanya dering ini yang bergeser: alarm harian tetap berbunyi pada waktu biasanya besok.

Mengembalikan: true bila alarm sedang berbunyi dan kini ditunda; false bila tidak berbunyi. id '' = alarm mana pun yang berbunyi; minutes 1-1440, atau 0 untuk snoozeMinutes milik alarm itu sendiri (default 10). Membutuhkan: đź”” Alarm clock di langkah Access (Langkah 4) saat Anda membuat build.

document.getElementById('snooze').onclick = function () {
  if (!(window.WebToApk && window.WebToApk.snoozeAlarm)) return;
  if (WebToApk.snoozeAlarm('', 5)) showMessage('Ringing again in 5 minutes');
  hideRingScreen();
};

window.addEventListener('appmint:reminder', function (e) {
  // { id: 'wake', action: 'snooze', alarm: true, source: 'page', snoozeMinutes: 5, snoozedUntil: 1767340800000 }
  if (e.detail.action === 'snooze') updateNextAlarmLabel(e.detail.snoozedUntil);
});

Jadwalkan jam alarm, lalu hubungkan tombol dismiss:

const b = window.WebToApk;
if (b && b.snoozeAlarm && b.getAlarmSupport() === 'ready') {
  b.scheduleNotificationEx('wake', JSON.stringify({
    at: Date.now() + 8 * 3600000, repeat: 'daily', alarm: true,
    title: 'Wake up', body: 'Good morning'
  }));
}
document.getElementById('dismiss').onclick = () => {
  if (WebToApk.isAlarmRinging()) WebToApk.stopAlarm('');
};

Tunda sesuai panjang alarm itu sendiri, dan tangani "tidak ada yang berbunyi":

document.getElementById('snooze').onclick = () => {
  if (!(window.WebToApk && WebToApk.snoozeAlarm)) return;
  const ok = WebToApk.snoozeAlarm('', 0);   // 0 = the alarm's own snoozeMinutes (default 10)
  if (!ok) showMessage('No alarm is ringing right now.');
};

Catatan: dering yang ditunda dicantumkan oleh getScheduledNotifications() sebagai <id>#snooze; cancelNotification(id) juga menghapusnya.

stopAlarm jembatan#

window.WebToApk.stopAlarm(id: String): Boolean

Menghentikan alarm yang berbunyi [id] ("" = alarm mana pun yang berbunyi), seperti tombol Dismiss-nya. Alarm berulang tetap terjadwal untuk waktu berikutnya. False bila tidak berbunyi.

Contoh

Menghentikan jam alarm yang berbunyi, persis seperti tombol Dismiss-nya. Alarm berulang tetap diatur untuk waktu berikutnya; gunakan cancelNotification(id) untuk menghapusnya.

Mengembalikan: true bila alarm itu sedang berbunyi dan kini dihentikan; false bila tidak berbunyi. Berikan '' untuk menghentikan alarm mana pun yang sedang berbunyi. Membutuhkan: đź”” Alarm clock di langkah Access (Langkah 4) saat Anda membuat build.

window.addEventListener('appmint:reminder', function (e) {
  const d = e.detail;                 // { id, action: 'open', ringing: true, overLockScreen }
  if (d.action === 'open' && d.ringing) showRingScreen(d.id);
});

document.getElementById('stop').onclick = function () {
  if (window.WebToApk && window.WebToApk.stopAlarm) WebToApk.stopAlarm(currentAlarmId);
  hideRingScreen();
};

Tawarkan tombol berhenti hanya di tempat jembatan ada, dan baca jawabannya:

const stopBtn = document.getElementById('stop');
if (window.WebToApk && typeof WebToApk.stopAlarm === 'function') {
  stopBtn.onclick = function () {
    const wasRinging = WebToApk.stopAlarm('');      // '' = whichever alarm is ringing
    if (!wasRinging) showMessage('Nothing was ringing.');
  };
} else {
  stopBtn.hidden = true;                             // plain browser: no alarm clock
}

Pasangkan dengan isAlarmRinging() agar halaman dapat menampilkan layar dering untuk dering yang dimulai saat halaman tidak ada:

document.addEventListener('visibilitychange', function () {
  if (document.hidden || !window.WebToApk) return;
  if (WebToApk.isAlarmRinging && WebToApk.isAlarmRinging()) showRingScreen();
  else hideRingScreen();
});

Catatan: halaman lalu juga menerima appmint:reminder dengan { id, action: 'dismiss', alarm: true, source: 'page' }.

Dibuat dari runtime aplikasi dan file contohnya pada setiap build dokumentasi. Baca sebagai Markdown · Semua keluarga.

Diperiksa terhadap jembatan yang dirilis pada 2026-10-22.