ساخت APK رایگان

API پل جاوااسکریپت

اعلان‌ها روی دستگاه

ارسال و زمان‌بندی بدون سرور، مجوز POST_NOTIFICATIONS، آلارم‌های دقیق و صداهای سفارشی.

AppMint.enableNotifications کمک‌کننده#

AppMint.enableNotifications()

نمونه

«اعلان‌ها را روشن کنید» در یک فراخوانی، برای کاربرانی که پیام اعلان اندروید را رد کرده یا نادیده گرفته‌اند: دیالوگ مجوز تا وقتی اندروید هنوز می‌تواند نشانش دهد، وگرنه تنظیمات اعلان این برنامه - و وضعیت جدید وقتی کاربر برمی‌گردد.

مقدار بازگشتی: AppMint.enableNotifications() - یک Promise که همیشه با {state, changed, openedSettings} حل می‌شود؛ state برابر 'granted' | 'prompt' | 'blocked' | 'unavailable' است. AppMint.getNotificationState() - یک Promise از آن وضعیت. AppMint.openNotificationSettings() - یک Promise از true وقتی صفحه‌ی تنظیمات باز شد. پیش‌نیاز: enableNotifications() را از دکمه‌ای که کاربر زده فراخوانی کنید؛ اعلان‌ها به اعلان‌ها (یا Push / Reminders) در ساخت نیاز دارند.

// 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();

یک لینک ساده‌ی «تنظیمات اعلان» در صفحه‌ی تنظیمات خودتان:

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.');
};

نکته‌ها: دکمه‌ی پوش یا پیام درون‌برنامه‌ای که لینکش appmint://notifications/enable است، همین جریان را بدون هیچ کد صفحه‌ای اجرا می‌کند (صفحه appmint:notifications را می‌شنود). مخاطبان پوش برنامه («اعلان‌ها روشن / خاموش») به‌محض روشن کردن اعلان‌ها توسط کاربر به‌روز می‌شود.

AppMint.getNotificationState کمک‌کننده#

window.AppMint.getNotificationState

نمونه

همراه با AppMint.enableNotifications مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

«اعلان‌ها را روشن کنید» در یک فراخوانی، برای کاربرانی که پیام اعلان اندروید را رد کرده یا نادیده گرفته‌اند: دیالوگ مجوز تا وقتی اندروید هنوز می‌تواند نشانش دهد، وگرنه تنظیمات اعلان این برنامه - و وضعیت جدید وقتی کاربر برمی‌گردد.

مقدار بازگشتی: AppMint.enableNotifications() - یک Promise که همیشه با {state, changed, openedSettings} حل می‌شود؛ state برابر 'granted' | 'prompt' | 'blocked' | 'unavailable' است. AppMint.getNotificationState() - یک Promise از آن وضعیت. AppMint.openNotificationSettings() - یک Promise از true وقتی صفحه‌ی تنظیمات باز شد. پیش‌نیاز: enableNotifications() را از دکمه‌ای که کاربر زده فراخوانی کنید؛ اعلان‌ها به اعلان‌ها (یا Push / Reminders) در ساخت نیاز دارند.

// 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();

یک لینک ساده‌ی «تنظیمات اعلان» در صفحه‌ی تنظیمات خودتان:

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.');
};

نکته‌ها: دکمه‌ی پوش یا پیام درون‌برنامه‌ای که لینکش appmint://notifications/enable است، همین جریان را بدون هیچ کد صفحه‌ای اجرا می‌کند (صفحه appmint:notifications را می‌شنود). مخاطبان پوش برنامه («اعلان‌ها روشن / خاموش») به‌محض روشن کردن اعلان‌ها توسط کاربر به‌روز می‌شود.

AppMint.openNotificationSettings کمک‌کننده#

window.AppMint.openNotificationSettings

نمونه

همراه با AppMint.enableNotifications مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

«اعلان‌ها را روشن کنید» در یک فراخوانی، برای کاربرانی که پیام اعلان اندروید را رد کرده یا نادیده گرفته‌اند: دیالوگ مجوز تا وقتی اندروید هنوز می‌تواند نشانش دهد، وگرنه تنظیمات اعلان این برنامه - و وضعیت جدید وقتی کاربر برمی‌گردد.

مقدار بازگشتی: AppMint.enableNotifications() - یک Promise که همیشه با {state, changed, openedSettings} حل می‌شود؛ state برابر 'granted' | 'prompt' | 'blocked' | 'unavailable' است. AppMint.getNotificationState() - یک Promise از آن وضعیت. AppMint.openNotificationSettings() - یک Promise از true وقتی صفحه‌ی تنظیمات باز شد. پیش‌نیاز: enableNotifications() را از دکمه‌ای که کاربر زده فراخوانی کنید؛ اعلان‌ها به اعلان‌ها (یا Push / Reminders) در ساخت نیاز دارند.

// 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();

یک لینک ساده‌ی «تنظیمات اعلان» در صفحه‌ی تنظیمات خودتان:

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.');
};

نکته‌ها: دکمه‌ی پوش یا پیام درون‌برنامه‌ای که لینکش appmint://notifications/enable است، همین جریان را بدون هیچ کد صفحه‌ای اجرا می‌کند (صفحه appmint:notifications را می‌شنود). مخاطبان پوش برنامه («اعلان‌ها روشن / خاموش») به‌محض روشن کردن اعلان‌ها توسط کاربر به‌روز می‌شود.

navigator.setAppBadge استاندارد وب#

navigator.setAppBadge(count) / clearAppBadge()

نمونه

Badging API استاندارد: عددی روی آیکون لانچر برنامه، مثل تعداد نخوانده‌ها.

مقدار بازگشتی: یک Promise که وقتی نشان تنظیم شد حل می‌شود؛ TypeError برای عدد منفی یا غیرعددی. پیش‌نیاز: چیزی برای روشن کردن لازم نیست. فقط روی گوشی‌هایی وجود دارد که لانچرشان عدد نشان می‌دهد (Samsung، Huawei، Xiaomi، OPPO، Sony، …)؛ در لانچرهای سبک Pixel 'setAppBadge' in navigator برابر است با 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);

هر وقت کاربر به برنامه نگاه می‌کند نشان را پاک کنید و به‌روزرسانی ردشده را مدیریت کنید:

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 () {});
  }
});

وقتی همگام‌سازی تغییراتی پیدا می‌کند اما تعداد دقیق ندارد، یک پرچم ساده‌ی «چیز جدیدی هست» نشان دهید:

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);
  }
}

نکته‌ها: setAppBadge() بدون عدد همان نشان «پرچم» مشخصات است که روی لانچر شمارنده‌دار به‌صورت 1 نشان داده می‌شود. شمارش بعد از راه‌اندازی مجدد برنامه حفظ می‌شود. همین ذخیره‌گاه پشت badge.set() در @/lib/appmintNative است، پس هر دو همیشه یکسان هستند.

Notification استاندارد وب#

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

نمونه

Web Notifications API استاندارد درون برنامه کار می‌کند: new Notification() یک اعلان واقعی اندروید نشان می‌دهد، close() آن را حذف می‌کند و Notification.requestPermission() دیالوگ مجوز اندروید را نشان می‌دهد و منتظر پاسخ کاربر می‌ماند.

مقدار بازگشتی: Notification.permission می‌تواند 'granted'، 'denied' یا 'default' باشد و زنده از گوشی خوانده می‌شود (تغییر در تنظیمات بدون بارگذاری مجدد دیده می‌شود). requestPermission() یک Promise از 'granted' یا 'denied' برمی‌گرداند (شکل callback هم کار می‌کند). اعلان جدید هنگام ارسال show را فعال می‌کند، یا وقتی مجوز نیست error را. پیش‌نیاز: هنگام ساخت اعلان‌ها را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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);

شکل callback (کدهای قدیمی‌تر):

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

سبک service-worker (کد PWA) هم از طریق برنامه اعلان می‌فرستد، اما فقط جایی که خود WebView navigator.serviceWorker ندارد (مثلاً صفحه‌های file:// یا http://):

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

یک ضربه رویداد click را روی اعلان فعال می‌کند - برنامه جلو می‌آید و صفحه آن را می‌گیرد. اگر برنامه بسته بود (ضربه آن را شروع کرد)، آن شیء از بین رفته است، پس پنجره به‌جایش رویداد notificationclick را می‌گیرد، با شکل 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);
});

نکته‌ها: گزینه‌های استفاده‌شده: body، icon، image، tag، silent، data (JSON، کمتر از 16 KB). ضربه از میان صفحه‌ی splash و PIN به صفحه می‌رسد (رویداد پشت آن: appmint:notification-click). در اندروید 12 و قدیمی‌تر مجوز زمان اجرا وجود ندارد، پس 'granted' است مگر اینکه کاربر اعلان‌ها را خاموش کرده باشد. Web Push (pushManager.subscribe) پشتیبانی نمی‌شود؛ از AppMint Push استفاده کنید. برای دکمه‌ها، پیشرفت یا اعلان چسبنده از WebToApk.notify(...) استفاده کنید. همین کد در مرورگر عادی هم کار می‌کند.

__badgeSupported پل#

window.WebToApk.__badgeSupported(): Boolean

اینکه این لانچر می‌تواند عدد نشان دهد یا نه (navigator.setAppBadge فقط در آن صورت وجود دارد).

نمونه

زیرساخت داخلی پشت وجود navigator.setAppBadge. صفحه‌ها از API استاندارد استفاده می‌کنند؛ هرگز مستقیم آن را فراخوانی نکنید - روش‌های __ ممکن است بدون اطلاع تغییر کنند.

مقدار بازگشتی: true وقتی لانچر این گوشی عدد نشان می‌دهد. پیش‌نیاز: چیزی برای روشن کردن لازم نیست.

از API عمومی استفاده کنید:

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

تنظیم «نشان روی آیکون برنامه» را فقط روی گوشی‌هایی نشان دهید که لانچرشان می‌تواند عدد نشان دهد:

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

وقتی کاربر صندوق ورودی را باز می‌کند، عدد را پاک کنید، در هر لانچری که دارد:

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

نکته‌ها: Badging API فقط جایی نصب می‌شود که لانچر بتواند عدد نشان دهد (Samsung، Huawei، Xiaomi، OPPO، Sony، …)؛ در لانچرهای سبک Pixel مثل مرورگری که badging ندارد، وجود ندارد.

__closeNotification پل#

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

Notification.close(): اعلانی را که صفحه ارسال کرده با تگش حذف می‌کند.

نمونه

زیرساخت داخلی پشت Notification.prototype.close() - صفحه‌ها از new Notification(...) استفاده می‌کنند و روی آن close() را فراخوانی می‌کنند. اعلان را با تگش از پرده‌ی اعلان‌ها حذف می‌کند.

مقدار بازگشتی: true وقتی درخواست حذف انجام شد (حتی اگر هیچ‌چیز آن تگ را نداشت)؛ false در خطای داخلی. پیش‌نیاز: هیچ.

از API عمومی استفاده کنید:

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
}

وقتی روی اعلان ضربه زده شد، گفتگوی درست را باز کنید و سپس اعلان را از پرده‌ی اعلان‌ها حذف کنید:

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();
  };
}

یک تأیید کوتاه نشان دهید که بعد از پنج ثانیه خودش حذف می‌شود:

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);
}

نکته‌ها: مستقیماً WebToApk.__closeNotification را فراخوانی نکنید؛ روش‌های __ ممکن است بدون اطلاع تغییر کنند. close() رویداد close اعلان را فعال می‌کند.

__setAppBadge پل#

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

navigator.setAppBadge(n): n >= 0 یک عدد است (0 پاک می‌کند)، -1 نشان پرچم مشخصات.

نمونه

زیرساخت داخلی پشت navigator.setAppBadge(count) / clearAppBadge(). صفحه‌ها از API استاندارد استفاده می‌کنند؛ هرگز مستقیم آن را فراخوانی نکنید - روش‌های __ ممکن است بدون اطلاع تغییر کنند.

مقدار بازگشتی: true وقتی عدد اعمال شد. پیش‌نیاز: چیزی برای روشن کردن لازم نیست.

از API عمومی استفاده کنید:

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

عدد روی آیکون را با تعداد نخوانده هماهنگ نگه دارید و در صفر پاکش کنید:

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);

تا وقتی کار همگام‌نشده هست، یک نشان پرچم (بدون عدد) روی آیکون بگذارید:

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 () {});
}

نکته‌ها: -1 همان نشان پرچم مشخصات است (بدون عدد) که لانچر شمارنده‌دار آن را 1 نشان می‌دهد.

cancelAllNotifications پل#

window.WebToApk.cancelAllNotifications(): Boolean

خواندن و درخواست مجوز POST_NOTIFICATIONS و مدیریت اعلان‌هایی که صفحه قبلاً زمان‌بندی کرده است. ارسال و زمان‌بندی در روش‌های خودشان توضیح داده شده‌اند.

با گروهش، مجوز اعلان، لغو و فهرست کردن، توضیح داده شده است، نه جداگانه.

نمونه

همه‌ی یادآورهایی را که این برنامه زمان‌بندی کرده لغو می‌کند.

مقدار بازگشتی: true وقتی انجام شد؛ false فقط در خطای داخلی. پیش‌نیاز: چیز اضافه‌ای لازم نیست.

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');
});

هنگام خروج از حساب، یادآورهای کاربر قبلی را متوقف کنید و عدد آیکون را پاک کنید:

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

کل برنامه‌ی روزانه را از زمان‌های ذخیره‌شده دوباره بسازید (همه را لغو کنید، سپس هرکدام را اضافه کنید):

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');
  });
}

نکته‌ها: فهرست زمان‌بندی‌شده را پاک می‌کند، پس چیزی بعداً اجرا نمی‌شود و بعد از راه‌اندازی مجدد هم چیزی دوباره تنظیم نمی‌شود. اعلان‌هایی که همین الان در نوار اعلان‌ها هستند می‌مانند.

cancelNotification پل#

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

خواندن و درخواست مجوز POST_NOTIFICATIONS و مدیریت اعلان‌هایی که صفحه قبلاً زمان‌بندی کرده است. ارسال و زمان‌بندی در روش‌های خودشان توضیح داده شده‌اند.

با گروهش، مجوز اعلان، لغو و فهرست کردن، توضیح داده شده است، نه جداگانه.

نمونه

یک یادآور زمان‌بندی‌شده را با شناسه‌اش لغو می‌کند تا دیگر اجرا نشود.

مقدار بازگشتی: true (حتی وقتی هیچ یادآوری آن شناسه را نداشت)؛ false فقط در خطای داخلی. پیش‌نیاز: چیز اضافه‌ای لازم نیست (یادآورها در زمان ساخت به ⏰ Reminders نیاز دارند).

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
}

یک کلید یادآور در تنظیمات که با همان شناسه زمان‌بندی یا لغو می‌کند:

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');
  }
});

وقتی کاری حذف می‌شود، همه‌ی یادآورهای مربوط به آن را لغو کنید:

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); });
}

نکته‌ها: یادآوری را که هنوز اجرا نشده لغو می‌کند (یک‌باره یا تکرارشونده). اعلانی را که همین الان در نوار اعلان‌ها نشان داده می‌شود حذف نمی‌کند و روی اعلان‌های ارسال‌شده با تأثیر نمی‌گذارد notify() / showNotification().

canOpenAppAtTime پل#

window.WebToApk.canOpenAppAtTime(): String

اینکه یک اعلان زمان‌بندی‌شده اجازه دارد خودِ برنامه را روی صفحه بیاورد (`openApp`) یا نه. "granted" - هشدار تمام‌صفحه روی صفحه‌ی قفل نشان داده می‌شود. "denied" - اندروید 14 به بالا آن را نداده؛ requestOpenAppPermission() را فراخوانی کنید. پاسخ سومی که در آن برنامه بی‌صدا در پس‌زمینه خودش را اجرا کند وجود ندارد: اندروید از اندروید 10 به بعد شروع activity در پس‌زمینه را مسدود کرده و intent تمام‌صفحه تمام آن چیزی است که می‌ماند.

نمونه

به شما می‌گوید یادآوری با openApp: true می‌تواند خودِ برنامه را روی صفحه بیاورد (هشدار تمام‌صفحه، حتی روی صفحه‌ی قفل) یا نه.

مقدار بازگشتی: یک رشته، همزمان: 'granted' یا 'denied'. زیر اندروید 14 همیشه 'granted' است. پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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);
});

یک صفحه‌ی تنظیمات که هر دو کلید آلارم را با وضعیت فعلی‌شان نشان می‌دهد:

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
});

وقتی آلارم‌های ذخیره‌شده برای باز کردن برنامه تنظیم شده‌اند اما اندروید الان اجازه نمی‌دهد، هشدار دهید:

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.');
  }
}

نکته‌ها: وقتی 'denied' باشد، یادآور openApp همچنان اعلان heads-up ارسال می‌کند، اما برنامه خودش باز نمی‌شود. اندروید راه دیگری برای اینکه برنامه در پس‌زمینه خودش را شروع کند اجازه نمی‌دهد.

clearNotificationSounds پل#

window.WebToApk.clearNotificationSounds(): Int

نسخه‌های کپی‌شده‌ی صدا را حذف می‌کند و تعداد حذف‌شده‌ها را برمی‌گرداند. فایل وقتی دوباره کپی می‌شود که اعلان (یا یادآور زمان‌بندی‌شده، هنگام اجرا) دفعه‌ی بعد از آن صدا استفاده کند - حتی روی کانالی که از قبل وجود دارد و به همان نام فایل اشاره می‌کند (NotificationBridge.restoreBundleSound).

نمونه

نسخه‌هایی از صداهای اعلان سفارشی شما را که برنامه از بسته‌ی شما ساخته حذف می‌کند.

مقدار بازگشتی: یک عدد، همزمان: تعداد فایل‌های حذف‌شده. پیش‌نیاز: هیچ.

پاک کنید، سپس صداهایی را که هنوز استفاده می‌کنید برگردانید:

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');
});

دکمه‌ای در صفحه‌ی حافظه که فایل‌های صوتی کپی‌شده را آزاد می‌کند و می‌گوید چند تا حذف شد:

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).');
});

هنگام خروج از حساب، همه‌ی یادآورها را لغو کنید و نسخه‌های صوتی متعلق به این کاربر را حذف کنید:

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

نکته‌ها: به‌ندرت به این نیاز دارید؛ نسخه‌ها کوچک‌اند. صدا دفعه‌ی بعد که چیزی از آن استفاده کند دوباره کپی می‌شود - notify()، یادآوری هنگام اجرا، scheduleNotificationEx یا prepareNotificationSound(path) - حتی روی کانال اعلانی که از قبل وجود دارد، پس صدای سفارشی به پخش شدن ادامه می‌دهد.

enableNotifications پل#

window.WebToApk.enableNotifications(callbackId: String)

کاربر را به اعلان‌ها برمی‌گرداند: دیالوگ سیستم تا وقتی هنوز قابل نمایش است، وگرنه تنظیمات اعلان برنامه. `window.__appmintReplies[callbackId]` را با {state, changed, openedSettings} حل می‌کند (AppMint.enableNotifications() پوشش Promise است).

نمونه

کاربری را که اعلان‌ها را رد کرده یا نادیده گرفته برمی‌گرداند: دیالوگ مجوز اندروید را تا وقتی هنوز قابل نمایش است نشان می‌دهد، وگرنه تنظیمات اعلان این برنامه را باز می‌کند و وقتی کاربر برگشت پاسخ می‌دهد.

مقدار بازگشتی: مستقیماً چیزی نیست. پاسخ {state, changed, openedSettings} به window.__appmintReplies[callbackId] تحویل داده می‌شود؛ state برابر 'granted' | 'prompt' | 'blocked' | 'unavailable' است، changed وقتی با پیش از فراخوانی فرق دارد true است، openedSettings وقتی صفحه‌ی تنظیمات استفاده شد true است. اگر از قبل 'granted' (یا 'unavailable') باشد فوراً پاسخ می‌دهد. پیش‌نیاز: از دکمه‌ای که کاربر زده فراخوانی کنید؛ اعلان‌ها به اعلان‌ها (یا Push / Reminders) در ساخت نیاز دارند.

پوشش Promise یعنی AppMint.enableNotifications() را ترجیح دهید. فراخوانی خام، برای کدی که پاسخ‌هایش را خودش مدیریت می‌کند:

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;
};

هر جریان تمام‌شده هم اعلام می‌شود، از جمله جریانی که با دکمه‌ی پوش شروع شده (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;
});

نکته‌ها: دکمه‌ی پوش یا پیام درون‌برنامه‌ای که لینکش appmint://notifications/enable است همین جریان را اجرا می‌کند؛ appmint://notifications/settings صفحه‌ی تنظیمات را باز می‌کند. مخاطبان پوش برنامه وضعیت جدید را بلافاصله می‌بینند.

getAlarmPrecision پل#

window.WebToApk.getAlarmPrecision(): String

"exact" - یادآورها سر دقیقه اجرا می‌شوند. "inexact" - کاربر (یا سازنده‌ی گوشی) آلارم‌های دقیق را برای این برنامه خاموش کرده، پس Doze ممکن است آن‌ها را چند دقیقه‌ای دسته‌ای کند. از [requestExactAlarms] بخواهید درستش کند. "unavailable" - اندروید 12 به بالا و سرویس آلارم سیستم در دسترس نبود، پس دقت قابل خواندن نیست (یادآوری که الان زمان‌بندی شود ممکن است اجرا هم نشود). صفحه‌ای که باید سر وقت باشد باید این را بررسی کند، نه اینکه فرض کند. @return `"exact"`، `"inexact"` یا `"unavailable"`.

نمونه

به شما می‌گوید یادآورها دقیقاً سر دقیقه اجرا می‌شوند یا ممکن است دیر شوند، چون کاربر (یا سازنده‌ی گوشی) آلارم‌های دقیق را برای این برنامه خاموش کرده است.

مقدار بازگشتی: یک رشته، همزمان: 'exact'، 'inexact' (ممکن است با صرفه‌جویی باتری دسته‌ای شود، اغلب چند دقیقه) یا 'unavailable' (گوشی سرویس آلارم ندارد). پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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();
});

پیش از تنظیم آلارم بیدارباش بررسی کنید و اگر ممکن است دیر شود به کاربر بگویید:

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.');
  }
}

زمان‌بندی یادآور را به‌صورت یک خط در صفحه‌ی تنظیمات نشان دهید:

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();

نکته‌ها: اندروید 11 و قدیمی‌تر همیشه 'exact' پاسخ می‌دهند. یادآور 'inexact' همچنان اجرا می‌شود، فقط نه سر دقیقه.

getAlarmSupport پل#

window.WebToApk.getAlarmSupport(): String

اینکه `scheduleNotificationEx(id, {alarm: true, ...})` الان قابل زمان‌بندی است یا نه: "ready" | "not_enabled" (آلارم در این ساخت خاموش است) | "needs_exact_alarm" (requestExactAlarms را فراخوانی کنید) | "needs_notification_permission".

نمونه

به شما می‌گوید یک آلارم (scheduleNotificationEx(id, { alarm: true, ... })) الان روی این گوشی قابل تنظیم است یا نه و اگر نه، کاربر ابتدا باید چه چیزی را اجازه دهد.

مقدار بازگشتی: یک رشته، همزمان: 'ready'، 'not_enabled' (برنامه بدون 🔔 Alarm clock ساخته شده)، 'needs_exact_alarm' (اندروید 12 به بالا: آلارم‌های دقیق برای این برنامه خاموش‌اند - requestExactAlarms() را فراخوانی کنید)، یا 'needs_notification_permission' (اندروید 13 به بالا: مجوز اعلان نیست، پس دکمه‌ی Dismiss نخواهد بود - requestNotificationPermission() را فراخوانی کنید). پیش‌نیاز: هنگام ساخت 🔔 Alarm clock را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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;
  }
}

وقتی کاربر از تنظیمات برگشت دوباره بررسی کنید:

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

آلارم را فقط وقتی گوشی آماده است بسازید:

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'
  }));
}

نکته‌ها: scheduleNotificationEx با alarm: true هر وقت این 'ready' نباشد false برمی‌گرداند - آلارمی که هرگز نتواند زنگ بزند ذخیره نمی‌شود.

getNotificationPermission پل#

window.WebToApk.getNotificationPermission(): String

مجوز اعلان فعلی که زنده از سیستم‌عامل خوانده می‌شود. polyfill قبلاً در هر بارگذاری صفحه `Notification.permission = 'default'` را ثابت قرار می‌داد، پس الگوی بسیار رایج صفحه if (Notification.permission === 'granted') new Notification(...) هرگز اجرا نمی‌شد - حتی برای کاربری که قبلاً مجوز داده بود، و نه در اندروید 12 و پایین‌تر که اعلان‌ها اصلاً به مجوز زمان اجرا نیاز ندارند.

نمونه

همین الان می‌خواند که این برنامه مجاز به ارسال اعلان هست یا نه.

مقدار بازگشتی: یک رشته، همزمان: 'granted'؛ 'denied' (کاربر برای همیشه رد کرد یا اعلان‌های این برنامه را در تنظیمات خاموش کرد - ارسال اعلان نشان داده نمی‌شود)؛ یا 'default' (هنوز تصمیم نگرفته، برنامه هنوز می‌تواند بپرسد). پیش‌نیاز: برای فراخوانی چیزی لازم نیست؛ ارسال اعلان نیاز دارد که هنگام ساخت اعلان‌ها در مرحله‌ی دسترسی (مرحله‌ی 4) روشن باشد.

API استاندارد وب همان مقدار زنده را می‌خواند:

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

فراخوانی خام، مثلاً بعد از برگشتن کاربر از تنظیمات:

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();

پیش از زمان‌بندی تصمیم بگیرید که بپرسید، توضیح دهید یا ادامه دهید:

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';
}

نکته‌ها: همان پاسخ navigator.permissions.query({ name: 'notifications' }) را می‌دهد. برای پرسیدن از کاربر از Notification.requestPermission().

getNotificationState پل#

window.WebToApk.getNotificationState(): String

'granted' | 'prompt' (اندروید هنوز می‌تواند بپرسد) | 'blocked' (برای همیشه رد شده یا در تنظیمات خاموش شده - فقط تنظیمات درستش می‌کند) | 'unavailable' (اندروید 13 به بالا و این ساخت مجوز اعلان را اعلام نکرده است). NotificationOptIn را ببینید.

نمونه

همین الان می‌خواند که اعلان‌های این برنامه روشن است یا نه - و اگر نه، برنامه هنوز می‌تواند بپرسد یا فقط تنظیمات می‌تواند دوباره روشنشان کند.

مقدار بازگشتی: یک رشته، همزمان: 'granted' (اعلان‌ها به گوشی می‌رسند)؛ 'prompt' (اندروید هنوز می‌تواند دیالوگ مجوزش را نشان دهد)؛ 'blocked' (کاربر برای همیشه رد کرد یا اعلان‌های این برنامه را در تنظیمات سیستم خاموش کرد - فقط تنظیمات درستش می‌کند)؛ یا 'unavailable' (اندروید 13 به بالا و برنامه بدون اعلان‌ها / Reminders / Push ساخته شده، پس چیزی نمی‌تواند آن‌ها را روشن کند). پیش‌نیاز: برای فراخوانی چیزی لازم نیست.

فراخوانی خام، برای تصمیم‌گیری درباره‌ی نشان دادن بنر «اعلان‌ها را روشن کنید»:

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();

نکته‌ها: getNotificationPermission() برای صفحه‌های قدیمی‌تر کلمات خودش را نگه می‌دارد ('granted' / 'default' / 'denied'). برای برگرداندن کاربر، AppMint.enableNotifications() را فراخوانی کنید (دیالوگ یا تنظیمات را خودش انتخاب می‌کند).

getScheduledNotifications پل#

window.WebToApk.getScheduledNotifications(): String

خواندن و درخواست مجوز POST_NOTIFICATIONS و مدیریت اعلان‌هایی که صفحه قبلاً زمان‌بندی کرده است. ارسال و زمان‌بندی در روش‌های خودشان توضیح داده شده‌اند.

با گروهش، مجوز اعلان، لغو و فهرست کردن، توضیح داده شده است، نه جداگانه.

نمونه

یادآورهایی را که هنوز زمان‌بندی شده‌اند فهرست می‌کند.

مقدار بازگشتی: یک رشتهی JSON، همزمان. آن را parse کنید. هر مورد: { id, title, body, trigger, repeat, options? } - trigger زمان اجرای بعدی به epoch ms است، repeat برابر none|minutely|hourly|daily|weekly است و options (فقط برای scheduleNotificationEx) گزینه‌ها به‌صورت رشته‌ی JSON است. فیلدهای زنده می‌گویند واقعاً چطور تحویل می‌شود، زنده خوانده می‌شوند: precision ("exact"؛ "inexact" = «Alarms & reminders» برای برنامه خاموش است، پس اندروید آن را در بازه‌ی 10 دقیقه‌ای وقتی گوشی بیدار است تحویل می‌دهد و ممکن است وقتی خواب است بیشتر نگهش دارد؛ "alarm_clock" برای alarm: true)، windowMs + latestAt (بازه‌ای که به اندروید داده شد: 0 / trigger وقتی دقیق، 600000 / trigger + 600000 وقتی غیردقیق) و notificationsEnabled (false = اجرا می‌شود اما اندروید چیزی نشان نمی‌دهد چون اعلان‌های برنامه خاموش است). '[]' وقتی هیچ نباشد. پیش‌نیاز: چیز اضافه‌ای لازم نیست.

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);
  });
}

نشان دهید یک یادآور روشن است یا نه و دفعه‌ی بعد کی اجرا می‌شود:

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');

گزینه‌های اضافه‌ی یادآورهای ساخته‌شده با را بخوانید 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; });
}

نکته‌ها: یک یادآور یک‌باره هنگام اجرا از فهرست خارج می‌شود. یادآور تکرارشونده می‌ماند و trigger آن به زمان بعدی می‌رود. یادآوری که زمانش وقتی گوشی خاموش بود یا برنامه به‌زور متوقف شده بود گذشته، دفعه‌ی بعد که برنامه یا گوشی شروع شود یک بار، با تأخیر، نشان داده می‌شود؛ یک alarm: true دیررسیده به‌جای زنگ زدن به‌صورت missed گزارش می‌شود. یادآور روزانه، هفتگی، ماهانه یا سالانه وقتی گوشی منطقه‌ی زمانی را عوض می‌کند، ساعت محلی‌اش را حفظ می‌کند.

وقتی یادآورها سر وقت نشان داده نمی‌شوند هشدار دهید:

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 پل#

window.WebToApk.isAlarmRinging(): Boolean

true تا وقتی آلارمی زنگ می‌زند.

نمونه

به شما می‌گوید یکی از آلارم‌های برنامه همین الان زنگ می‌زند یا نه، تا صفحه بتواند صفحه‌ی توقف / تعویق خودش را نشان دهد.

مقدار بازگشتی: true وقتی آلارمی زنگ می‌زند، وگرنه false (همزمان). پیش‌نیاز: 🔔 Alarm clock در مرحله‌ی دسترسی (مرحله‌ی 4) هنگام ساخت.

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

آلارم در حال زنگ را از صفحه‌ی خودتان متوقف کنید با فراخوانی‌های متناظر.

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');
});

به باز شدن برنامه توسط آلارم در حال زنگ واکنش نشان دهید.

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

نکته‌ها: وقتی آلارم در حال زنگ برنامه را باز می‌کند، appmint:reminder هم همراه با این می‌رسد { action: 'open', ringing: true }.

notify پل#

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

اعلان غنی. فهرست کامل گزینه‌ها را در NotificationBridge.show ببینید: channel (urgent|default|quiet|ongoing)، image، largeIcon، bigText، ongoing، actions، progress، sound، silent، group، color. دریافت و رمزگشایی تصویر خارج از رشته‌ی اصلی انجام می‌شود، پس تصویر راه‌دور هرگز نمی‌تواند صفحه را مسدود کند.

نمونه

همین الان یک اعلان با هر ترکیبی از گزینه‌ها ارسال می‌کند: تصویر، متن بزرگ، دکمه‌ها، نوار پیشرفت، چسبنده (ongoing)، کانال، صدا، رنگ و تگ.

مقدار بازگشتی: true وقتی گزینه‌ها JSON معتبر بودند و اعلان به سیستم سپرده شد (در یک رشته‌ی پس‌زمینه ساخته می‌شود، پس URL تصویر خراب صفحه را متوقف نمی‌کند)؛ false برای JSON نامعتبر. پیش‌نیاز: هنگام ساخت اعلان‌ها را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید و مجوز کاربر در اندروید 13 به بالا (درخواست با Notification.requestPermission()).

اعلان ساده. tag یک شناسه است: ارسال دوباره با همان تگ، قبلی را جایگزین می‌کند.

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'
  }));
}

تصویر، آیکون بزرگ و متن بلند. وقتی هر دو داده شوند، image بر bigText غلبه می‌کند.

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'
}));

دکمه‌ها (حداکثر 3). ضربه به‌صورت رویداد 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);
});

پیشرفت چسبنده (ongoing). برای به‌روزرسانی با همان تگ دوباره ارسال کنید. وقتی کار تمام شد، وضعیت نهایی را با ongoing: false ارسال کنید تا کاربر بتواند آن را کنار بزند.

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' }));

گزینه‌های دیگر: sound (فایلی در بسته‌ی شما مثل '/audio/ding.mp3'، یک URL از نوع data:audio/... یا 'silent')، smallIcon (نام drawable)، when (epoch ms که به‌عنوان زمان نشان داده می‌شود)، openApp: true (هشدار تمام‌صفحه؛ ببینید scheduleNotificationEx).

نکته‌ها: دکمه‌ها کادر پاسخ درون‌خطی ندارند و ضربه روی دکمه فقط وقتی برنامه در حال اجراست به صفحه می‌رسد (برنامه را باز نمی‌کند)؛ اگر window.onAppMintNotificationAction(detail) را تعریف کرده باشید، رویداد آن را هم فراخوانی می‌کند. image و largeIcon یک URL از نوع https:// یا data:، یا تصویری بسته‌بندی‌شده با برنامه‌تان را می‌گیرند، با همان نامی که صفحه‌تان می‌دهد ('img/logo.png'، '/img/logo.png' یا آدرس کامل https://appassets.androidplatform.net/assets/…). تصویر خراب نادیده گرفته می‌شود و اعلان همچنان نشان داده می‌شود. ongoing: true همیشه از کانال ساکت «Ongoing» استفاده می‌کند. notify() جفت عمومی «حذف» ندارد: cancelNotification فقط یادآورهای زمان‌بندی‌شده را لغو می‌کند. اعلانی که می‌خواهید بعداً از صفحه حذف کنید باید با new Notification(...) ارسال و با close() آن حذف شود؛ کارت پیشرفت چسبنده با Capacitor.AppwrightLiveProgress (end). اندروید صدای کانال را هنگام ساخت کانال ثابت می‌کند، پس مقدار جدید sound کانال جدید می‌سازد.

openNotificationSettings پل#

window.WebToApk.openNotificationSettings(): Boolean

تنظیمات اعلان این برنامه را باز می‌کند؛ true وقتی صفحه باز شد.

نمونه

صفحه‌ی تنظیمات اعلان خود این برنامه را باز می‌کند که کاربر می‌تواند اعلان‌ها (و هر کانال) را در آن روشن یا خاموش کند.

مقدار بازگشتی: true وقتی صفحه باز شد، همزمان؛ false وقتی نمی‌شود (اندروید 13 به بالا و ساخت اعلان ندارد). در اندروید 8 به بالا صفحه‌ی اعلان برنامه است؛ در اندروید قدیمی‌تر، صفحه‌ی App info. پیش‌نیاز: چیزی برای روشن کردن لازم نیست.

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

نکته‌ها: برای برگرداندن کاربر با یک ضربه، به‌جای آن از AppMint.enableNotifications() استفاده کنید: وقتی اندروید هنوز اجازه می‌دهد دیالوگ مجوز را نشان می‌دهد و فقط وقتی اجازه نمی‌دهد این صفحه را باز می‌کند.

prepareNotificationSound پل#

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

یک فایل صوتی همراه برنامه را از پیش در جای خود کپی می‌کند و گزارش می‌دهد که به‌عنوان صدای اعلان قابل استفاده است یا نه. اختیاری - `notify()` و `scheduleNotificationEx()` خودشان این کار را می‌کنند - اما به صفحه‌ی تنظیمات اجازه می‌دهد صدایی را همان لحظه که کاربر انتخاب می‌کند بررسی کند.

نمونه

بررسی می‌کند که یک فایل صوتی در برنامه‌ی شما می‌تواند به‌عنوان صدای اعلان استفاده شود و همین الان آن را در جای خود کپی می‌کند. اختیاری: notify() و scheduleNotificationEx() همین کار را خودشان می‌کنند؛ این به صفحه‌ی تنظیمات اجازه می‌دهد صدایی را همان لحظه که کاربر انتخاب می‌کند بررسی کند.

مقدار بازگشتی: true اگر صدا قابل استفاده است؛ false اگر فایل وجود ندارد یا نوع صوتی پشتیبانی‌شده نیست. پیش‌نیاز: هیچ.

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' }));
}

یک انتخابگر صدا را فقط با صداهایی که واقعاً قابل استفاده‌اند پر کنید:

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);
  });
}

به کاربر اجازه دهید فایل صوتی خودش را انتخاب کند و برای یادآور روزانه استفاده کند، به‌صورت 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);
});

مقادیر پذیرفته‌شده: مسیری در بسته‌ی وب شما ('/audio/chime.mp3'، 'audio/chime.mp3') یا یک URL از نوع data:audio/...;base64,... که صفحه‌تان ساخته است. انواع: mp3، wav، ogg، oga، m4a، aac، flac، opus، mid، midi.

نکته‌ها: نام ساده بدون پوشه یا پسوند (یک صدای res/raw) اینجا بررسی نمی‌شود و false برمی‌گرداند، هرچند notify({sound: 'name'}) می‌تواند از آن استفاده کند. اندروید برای هر کانال یک صدا نگه می‌دارد، پس تغییر صدا کانال جدید می‌سازد و تنظیمات کاربر برای آن کانال را بازنشانی می‌کند.

requestExactAlarms پل#

window.WebToApk.requestExactAlarms(): Boolean

صفحه‌ی سیستمی را که آلارم‌های دقیق در آن اعطا می‌شوند باز می‌کند. اندروید 12 به بالا.

نمونه

صفحه‌ی سیستمی اندروید 12 به بالا را باز می‌کند که کاربر در آن اجازه می‌دهد این برنامه از آلارم‌های دقیق استفاده کند تا یادآورها سر دقیقه اجرا شوند.

مقدار بازگشتی: true وقتی صفحه‌ی تنظیمات باز شد (و در اندروید 11 و قدیمی‌تر که چیزی لازم نیست)؛ false اگر صفحه باز نشد. انتخاب کاربر را نمی‌گوید. پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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;
});

پیش از ذخیره‌ی یادآور حساس به زمان، صفحه‌ی تنظیمات را فقط وقتی پیشنهاد دهید که زمان‌بندی واقعاً مشکل دارد:

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.');
  }
}

وقتی صفحه‌ی تنظیمات باز نمی‌شود (false)، به کاربر بگویید کلید را کجا پیدا کند:

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().
});

نکته‌ها: آن را از ضربه روی دکمه فراخوانی کنید و ابتدا دلیلش را توضیح دهید - از برنامه‌ی شما خارج می‌شود. یادآورهایی که از قبل زمان‌بندی شده‌اند در هر دو حالت کار می‌کنند؛ فقط زمان‌بندی‌شان تغییر می‌کند.

requestNotificationPermission پل#

window.WebToApk.requestNotificationPermission(): String

خواندن و درخواست مجوز POST_NOTIFICATIONS و مدیریت اعلان‌هایی که صفحه قبلاً زمان‌بندی کرده است. ارسال و زمان‌بندی در روش‌های خودشان توضیح داده شده‌اند.

با گروهش، مجوز اعلان، لغو و فهرست کردن، توضیح داده شده است، نه جداگانه.

نمونه

دیالوگ «اعلان‌ها اجازه داده شود؟» اندروید 13 به بالا را نشان می‌دهد. صفحه‌ها معمولاً از Notification.requestPermission() استفاده می‌کنند که این را فراخوانی می‌کند و منتظر پاسخ کاربر می‌ماند.

مقدار بازگشتی: یک رشته، همزمان: 'granted' اگر از قبل مجاز است (یا در اندروید 12 و قدیمی‌تر)، وگرنه 'default' تا وقتی دیالوگ باز می‌شود. پاسخ کاربر را نمی‌تواند برگرداند. پیش‌نیاز: هنگام ساخت اعلان‌ها را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

از Promise API استفاده کنید - با پاسخ واقعی حل می‌شود ('granted' یا '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.');
  }
});

فراخوانی خام، فقط برای باز کردن دیالوگ بدون انتظار:

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')
}

کد قدیمی‌تری که به Notification.requestPermission یک callback می‌دهد همان پاسخ نهایی را می‌گیرد:

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
  });
});

نکته‌ها: از ضربه روی دکمه با دلیلی روشن بپرسید. بعد از اینکه کاربر دو بار رد کند، اندروید دیگر دیالوگ را نشان نمی‌دهد و پاسخ فوراً 'denied' است؛ آن‌وقت کاربر باید در تنظیمات برنامه اجازه دهد.

requestOpenAppPermission پل#

window.WebToApk.requestOpenAppPermission(): Boolean

صفحه‌ی سیستمی را که هشدار تمام‌صفحه در آن اعطا می‌شود باز می‌کند. اندروید 14 به بالا.

نمونه

صفحه‌ی سیستمی اندروید 14 به بالا را باز می‌کند که کاربر در آن هشدارهای تمام‌صفحه را برای این برنامه اجازه می‌دهد، تا یادآور openApp: true بتواند برنامه را روی صفحه بیاورد.

مقدار بازگشتی: true وقتی صفحه‌ی تنظیمات باز شد (و زیر اندروید 14 که چیزی لازم نیست)؛ false اگر باز نشد. انتخاب کاربر را نمی‌گوید. پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید.

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;
});

آلارمی را که کاربر در حال تنظیمش بود به خاطر بسپارید، او را به تنظیمات بفرستید و وقتی برگشت تمامش کنید:

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;
});

وقتی صفحه‌ی تنظیمات باز نمی‌شود (false)، بگویید کلید کجاست:

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.');
  }
});

نکته‌ها: آن را از ضربه روی یک دکمه فراخوانی کنید، بعد از توضیح دلیلش - از برنامه‌ی شما خارج می‌شود.

scheduleNotification پل#

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

خواندن و درخواست مجوز POST_NOTIFICATIONS و مدیریت اعلان‌هایی که صفحه قبلاً زمان‌بندی کرده است. ارسال و زمان‌بندی در روش‌های خودشان توضیح داده شده‌اند.

با گروهش، مجوز اعلان، لغو و فهرست کردن، توضیح داده شده است، نه جداگانه.

نمونه

یک یادآور ساده (عنوان و متن) را برای زمانی در آینده زمان‌بندی می‌کند. حتی وقتی برنامه بسته است اجرا می‌شود و بعد از راه‌اندازی مجدد گوشی دوباره تنظیم می‌شود.

مقدار بازگشتی: true وقتی زمان‌بندی شد؛ false وقتی id خالی است یا زمان عدد نیست. پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید (اعلان‌ها را هم روشن می‌کند) و مجوز اعلان کاربر در اندروید 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');
}

بدانید چه وقت یادآوری برنامه را باز کرد (کاربر روی آن ضربه زد):

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

یک یادآور یک‌باره ساعت 8:00 روزی که مهلت یک کار است:

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'
  );
}

نکته‌ها: یادآور از سبک «فوری» (heads-up) استفاده می‌کند و تگش همان شناسه است. یادآور تکرارشونده‌ای که وقتی گوشی خاموش بود از دست رفته، در گام بعدی اجرا می‌شود. برای تصویر، دکمه، صدای خودتان یا باز کردن برنامه روی صفحه‌ی قفل از scheduleNotificationEx استفاده کنید. اگر آلارم‌های دقیق برای برنامه خاموش باشند، زمان ممکن است چند دقیقه جابه‌جا شود؛ بررسی کنید getAlarmPrecision().

scheduleNotificationEx پل#

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

شکل کامل [scheduleNotification]: هر چیزی که `notify()` می‌پذیرد، در زمانی در آینده با برنامه‌ی بسته اجرا می‌شود. 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 })); صدای همراه برنامه در همین فراخوانی در جای خود کپی می‌شود، پس نام فایل اشتباه همین الان گزارش می‌شود (false) و چند روز بعد به صدای پیش‌فرض تبدیل نمی‌شود.

نمونه

یادآوری با همه‌ی گزینه‌هایی که notify() دارد (تصویر، دکمه‌ها، صدا، کانال، باز کردن برنامه) برای زمانی در آینده زمان‌بندی می‌کند. وقتی برنامه بسته است هم اجرا می‌شود و پس از راه‌اندازی مجدد باقی می‌ماند.

مقدار بازگشتی: true وقتی زمان‌بندی شد. false وقتی زمانی داده نشده (at، triggerAt، triggerAtMillis یا inSeconds)، شناسه خالی است، JSON خراب است یا sound نام فایلی را می‌دهد که در برنامه‌ی شما نیست. پیش‌نیاز: هنگام ساخت ⏰ Reminders را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید و مجوز اعلان کاربر در اندروید 13 به بالا.

یک یادآور روزانه با صدای خودتان که برنامه را باز می‌کند (روی صفحه‌ی قفل):

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);
});

فقط روزهای باشگاه - دوشنبه، چهارشنبه و جمعه ساعت 18:00 (days به‌صورت ISO هستند: دوشنبه = 1 … یکشنبه = 7؛ بدون days، 'weekdays' یعنی دوشنبه تا جمعه):

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' }));

یک آلارم واقعی (alarm: true): با صدای ALARM گوشی زنگ می‌زند - حتی در حالت بی‌صدا و در Do Not Disturb وقتی کاربر آلارم‌ها را اجازه داده - و تا Dismiss / Snooze یا ringSeconds ادامه می‌دهد. آیکون آلارم را در نوار وضعیت نشان می‌دهد و روی صفحه‌ی قفل ظاهر می‌شود:

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);
});

یک یادآور با دکمه‌ها، 30 دقیقه‌ی دیگر:

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' }));
  }
});

یک یادآور یک‌باره با تصویر هنگام شروع حراج، در کانال عادی:

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
  }));
}

نکته‌ها: همان شناسه یادآور قدیمی‌تر را جایگزین می‌کند. پیش‌فرض‌ها: تگ = شناسه، کانال = urgent، bigText = متن. ضربه روی دکمه فقط وقتی برنامه در حال اجراست به صفحه می‌رسد. فایل صدا هنگام زمان‌بندی کپی می‌شود، پس مسیر اشتباه همین الان شکست می‌خورد و نه چند روز بعد؛ mp3، wav، ogg، m4a، aac، flac و opus کار می‌کنند. openApp تنها راهی است که برنامه در زمان تعیین‌شده خودش روی صفحه بیاید؛ در اندروید 14 به بالا کاربر باید اجازه دهد (canOpenAppAtTime). repeat: 'weekdays' با days خارج از 1-7، false برمی‌گرداند. alarm: true به 🔔 Alarm clock در مرحله‌ی دسترسی (مرحله‌ی 4) نیاز دارد و false برمی‌گرداند مگر اینکه getAlarmSupport() برابر 'ready' باشد؛ هر بار فقط یک آلارم زنگ می‌زند (آلارم جدید قبلی را با missed تمام می‌کند). اگر فایل صدای خود آلارم تا زمان زنگ خوردن ناپدید شده باشد (Remote Update آن را حذف کرده)، با آهنگ آلارم گوشی زنگ می‌زند و رویداد حامل است soundError: true.

showNotification پل#

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

شکل اصلی با چهار آرگومان، که عیناً به‌عنوان آداپتور نگه داشته شده تا هر برنامه‌ای که قبلاً منتشر شده و هر قطعه‌کد راهنما همچنان کار کند. حالا به سازنده‌ی غنی ارجاع می‌دهد - یعنی آرگومان `icon` که پیاده‌سازی قدیمی بی‌صدا دور می‌انداخت، بالاخره کاری می‌کند.

نمونه

فراخوانی اصلی اعلان با چهار آرگومان. اعلانی با عنوان، متن، آیکون بزرگ اختیاری و تگ نشان می‌دهد. کد جدید باید از notify(optionsJson) یا new Notification(...) استفاده کند که امکانات بیشتری دارند.

مقدار بازگشتی: true وقتی اعلان به سیستم سپرده شد. پیش‌نیاز: هنگام ساخت اعلان‌ها را در مرحله‌ی دسترسی (مرحله‌ی 4) روشن کنید و مجوز کاربر در اندروید 13 به بالا.

API استاندارد وب (این روش را برایتان فراخوانی می‌کند):

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' });
}

فراخوانی خام. آرگومان‌ها: عنوان، متن، آیکون، تگ. برای نبود آیکون یا تگ '' بدهید.

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
  );
}

با استفاده‌ی دوباره از تگ، یک اعلان وضعیت را در همان جا به‌روز کنید:

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
  );
}

نکته‌ها: به notify({title, body, largeIcon: icon, tag}) در کانال پیش‌فرض ارجاع می‌دهد. بدون تگ، هر فراخوانی اعلان جدیدی اضافه می‌کند. برای تصویر، دکمه، پیشرفت یا اعلان چسبنده از notify.

snoozeAlarm پل#

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

آلارم در حال زنگ [id] را متوقف می‌کند ("" = هر کدام که زنگ می‌زند) و بعد از [minutes] (1-1440؛ 0 = snoozeMinutes خود آلارم) دوباره زنگ می‌زند. اگر زنگ نمی‌زند false.

نمونه

آلارم در حال زنگ را متوقف می‌کند و بعد از چند دقیقه دوباره زنگ می‌زند، مثل دکمه‌ی Snooze آن. فقط همین زنگ جابه‌جا می‌شود: آلارم روزانه همچنان فردا در زمان همیشگی‌اش زنگ می‌زند.

مقدار بازگشتی: true وقتی آلارم زنگ می‌زد و به تعویق افتاد؛ false وقتی زنگ نمی‌زد. id '' = هر کدام که زنگ می‌زند؛ minutes 1-1440، یا 0 برای snoozeMinutes خود آلارم (پیش‌فرض 10). پیش‌نیاز: 🔔 Alarm clock در مرحله‌ی دسترسی (مرحله‌ی 4) هنگام ساخت.

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);
});

یک آلارم زمان‌بندی کنید، سپس دکمه‌ی 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('');
};

به‌اندازه‌ی طول خود آلارم تعویق دهید و حالت «چیزی زنگ نمی‌زند» را مدیریت کنید:

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.');
};

نکته‌ها: زنگ به‌تعویق‌افتاده توسط getScheduledNotifications() به‌صورت <id>#snooze فهرست می‌شود؛ cancelNotification(id) آن را هم حذف می‌کند.

stopAlarm پل#

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

آلارم در حال زنگ [id] را ("" = هر کدام که زنگ می‌زند) مثل دکمه‌ی Dismiss متوقف می‌کند. آلارم تکرارشونده برای زمان بعدی‌اش زمان‌بندی‌شده می‌ماند. اگر زنگ نمی‌زند false.

نمونه

آلارم در حال زنگ را درست مثل دکمه‌ی Dismiss آن متوقف می‌کند. آلارم تکرارشونده برای زمان بعدی‌اش تنظیم می‌ماند؛ برای حذفش از cancelNotification(id) استفاده کنید.

مقدار بازگشتی: true وقتی آن آلارم زنگ می‌زد و حالا متوقف شد؛ false وقتی زنگ نمی‌زد. برای توقف هر آلارمی که زنگ می‌زند '' بدهید. پیش‌نیاز: هنگام ساخت 🔔 Alarm clock در مرحله‌ی دسترسی (مرحله‌ی 4).

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();
};

دکمه‌ی توقف را فقط جایی که پل وجود دارد ارائه دهید و پاسخ را بخوانید:

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
}

آن را با isAlarmRinging() جفت کنید تا صفحه بتواند صفحه‌ی زنگ را برای زنگی که در غیاب صفحه شروع شده نشان دهد:

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

نکته‌ها: صفحه آن‌گاه appmint:reminder را هم دریافت می‌کند، همراه با { id, action: 'dismiss', alarm: true, source: 'page' }.