मुफ़्त APK बनाएँ

JavaScript ब्रिज API

डिवाइस पर नोटिफ़िकेशन

बिना सर्वर के नोटिफ़िकेशन भेजना और शेड्यूल करना, POST_NOTIFICATIONS अनुमति, exact अलार्म और कस्टम साउंड।

AppMint.enableNotifications हेल्पर#

AppMint.enableNotifications()

उदाहरण

एक ही कॉल में "नोटिफ़िकेशन चालू करें", उन यूज़र के लिए जिन्होंने Android का नोटिफ़िकेशन प्रॉम्प्ट मना या छोड़ दिया था: जब तक Android अनुमति डायलॉग दिखा सकता है तब तक वही, वरना इस ऐप की नोटिफ़िकेशन सेटिंग्स - और यूज़र के लौटने पर नई स्थिति।

लौटाता है: AppMint.enableNotifications() - एक Promise जो हमेशा {state, changed, openedSettings} के साथ resolve होता है; state 'granted' | 'prompt' | 'blocked' | 'unavailable' है। AppMint.getNotificationState() - उस स्थिति का Promise। AppMint.openNotificationSettings() - सेटिंग्स स्क्रीन खुलने पर true का Promise। ज़रूरत: enableNotifications() को यूज़र के टैप किए बटन से कॉल करें; नोटिफ़िकेशन के लिए बिल्ड में Notifications (या 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();

आपके अपने सेटिंग्स पेज में एक सादा "Notification settings" लिंक:

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 सुनाई देता है)। जैसे ही यूज़र नोटिफ़िकेशन चालू करता है, ऐप का पुश ऑडियंस ("Notifications on / off") अपडेट हो जाता है।

AppMint.getNotificationState हेल्पर#

window.AppMint.getNotificationState

उदाहरण

AppMint.enableNotifications के साथ दस्तावेज़ित - वहाँ का उदाहरण इसे भी दिखाता है।

एक ही कॉल में "नोटिफ़िकेशन चालू करें", उन यूज़र के लिए जिन्होंने Android का नोटिफ़िकेशन प्रॉम्प्ट मना या छोड़ दिया था: जब तक Android अनुमति डायलॉग दिखा सकता है तब तक वही, वरना इस ऐप की नोटिफ़िकेशन सेटिंग्स - और यूज़र के लौटने पर नई स्थिति।

लौटाता है: AppMint.enableNotifications() - एक Promise जो हमेशा {state, changed, openedSettings} के साथ resolve होता है; state 'granted' | 'prompt' | 'blocked' | 'unavailable' है। AppMint.getNotificationState() - उस स्थिति का Promise। AppMint.openNotificationSettings() - सेटिंग्स स्क्रीन खुलने पर true का Promise। ज़रूरत: enableNotifications() को यूज़र के टैप किए बटन से कॉल करें; नोटिफ़िकेशन के लिए बिल्ड में Notifications (या 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();

आपके अपने सेटिंग्स पेज में एक सादा "Notification settings" लिंक:

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 सुनाई देता है)। जैसे ही यूज़र नोटिफ़िकेशन चालू करता है, ऐप का पुश ऑडियंस ("Notifications on / off") अपडेट हो जाता है।

AppMint.openNotificationSettings हेल्पर#

window.AppMint.openNotificationSettings

उदाहरण

AppMint.enableNotifications के साथ दस्तावेज़ित - वहाँ का उदाहरण इसे भी दिखाता है।

एक ही कॉल में "नोटिफ़िकेशन चालू करें", उन यूज़र के लिए जिन्होंने Android का नोटिफ़िकेशन प्रॉम्प्ट मना या छोड़ दिया था: जब तक Android अनुमति डायलॉग दिखा सकता है तब तक वही, वरना इस ऐप की नोटिफ़िकेशन सेटिंग्स - और यूज़र के लौटने पर नई स्थिति।

लौटाता है: AppMint.enableNotifications() - एक Promise जो हमेशा {state, changed, openedSettings} के साथ resolve होता है; state 'granted' | 'prompt' | 'blocked' | 'unavailable' है। AppMint.getNotificationState() - उस स्थिति का Promise। AppMint.openNotificationSettings() - सेटिंग्स स्क्रीन खुलने पर true का Promise। ज़रूरत: enableNotifications() को यूज़र के टैप किए बटन से कॉल करें; नोटिफ़िकेशन के लिए बिल्ड में Notifications (या 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();

आपके अपने सेटिंग्स पेज में एक सादा "Notification settings" लिंक:

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 सुनाई देता है)। जैसे ही यूज़र नोटिफ़िकेशन चालू करता है, ऐप का पुश ऑडियंस ("Notifications on / off") अपडेट हो जाता है।

navigator.setAppBadge वेब स्टैंडर्ड#

navigator.setAppBadge(count) / clearAppBadge()

उदाहरण

स्टैंडर्ड Badging API: ऐप के लॉन्चर आइकन पर एक संख्या, जैसे अनपढ़े की गिनती।

लौटाता है: एक Promise जो बैज सेट होने पर resolve होता है; ऋणात्मक या गैर-संख्या गिनती पर 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() स्पेक का "flag" बैज है, जो संख्या दिखाने वाले लॉन्चर पर 1 की तरह दिखता है। गिनती ऐप रीस्टार्ट के बाद भी बनी रहती है। वही स्टोर @/lib/appmintNative में badge.set() को भी चलाता है, इसलिए दोनों हमेशा एक जैसे रहते हैं।

Notification वेब स्टैंडर्ड#

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

उदाहरण

स्टैंडर्ड Web Notifications API ऐप के अंदर काम करती है: new Notification() असली Android नोटिफ़िकेशन दिखाता है, close() उसे हटाता है, और Notification.requestPermission() Android का अनुमति डायलॉग दिखाकर यूज़र के जवाब का इंतज़ार करता है।

लौटाता है: Notification.permission 'granted', 'denied' या 'default' होता है, जो फ़ोन से सीधे पढ़ा जाता है (Settings में बदलाव बिना रीलोड दिखता है)। requestPermission() 'granted' या 'denied' का Promise लौटाता है (कॉलबैक रूप भी चलता है)। नया नोटिफ़िकेशन भेजे जाने पर show या अनुमति न होने पर error फ़ायर करता है। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में Notifications चालू करें।

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

कॉलबैक रूप (पुराना कोड):

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 इवेंट मिलता है, जो सर्विस वर्कर जैसे आकार का होता है - 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 से कम)। टैप स्प्लैश और PIN स्क्रीन से होकर पेज तक पहुँचता है (उसके पीछे का इवेंट: appmint:notification-click)। Android 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 जैसे लॉन्चर पर यह नहीं होती, ठीक वैसे ही जैसे बैजिंग के बिना ब्राउज़र में।

__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 अनुमति को पढ़ना और माँगना, और जो नोटिफ़िकेशन पेज पहले ही शेड्यूल कर चुका है उन्हें मैनेज करना। भेजना और शेड्यूल करना अपने-अपने मेथड पर बताया गया है।

इसे अलग से नहीं, बल्कि इसके समूह नोटिफ़िकेशन की अनुमति, रद्द करना और सूची में समझाया गया है।

उदाहरण

एक शेड्यूल किया रिमाइंडर उसकी id से रद्द करता है, ताकि वह दोबारा न बजे।

लौटाता है: true (तब भी जब उस id का कोई रिमाइंडर नहीं था); false सिर्फ़ किसी अंदरूनी त्रुटि पर। ज़रूरत: अलग से कुछ नहीं (रिमाइंडर के लिए बिल्ड के समय ⏰ रिमाइंडर चाहिए)।

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
}

सेटिंग्स में रिमाइंडर स्विच जो एक ही id से शेड्यूल या रद्द करता है:

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" - Android 14+ ने इसे रोक दिया है; requestOpenAppPermission() कॉल करें। तीसरा कोई जवाब नहीं है जहाँ ऐप चुपचाप बैकग्राउंड में खुद चालू हो जाए: Android 10 से बैकग्राउंड एक्टिविटी शुरू करना रोक दिया गया है, और फ़ुल-स्क्रीन इंटेंट ही बचा हुआ पूरा तरीका है।

उदाहरण

बताता है कि openApp: true वाला रिमाइंडर ऐप को ख़ुद स्क्रीन पर ला सकता है या नहीं (फ़ुल-स्क्रीन अलर्ट, लॉक स्क्रीन के ऊपर भी)।

लौटाता है: एक स्ट्रिंग, तुरंत: 'granted' या 'denied'। Android 14 से नीचे यह हमेशा 'granted' होता है। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में ⏰ Reminders चालू करें।

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

चेतावनी दें जब सेव किए अलार्म ऐप खोलने के लिए सेट थे पर Android अभी उसकी इजाज़त नहीं देता:

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 रिमाइंडर अब भी हेड्स-अप नोटिफ़िकेशन भेजता है, पर ऐप अपने आप नहीं खुलता। Android किसी ऐप को बैकग्राउंड में खुद शुरू होने का कोई और तरीका नहीं देता।

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} के साथ resolve करता है (AppMint.enableNotifications() प्रॉमिस रैपर है)।

उदाहरण

जिस यूज़र ने नोटिफ़िकेशन मना कर दिए थे या छोड़ दिए थे, उसे वापस लाता है: जब तक Android का अनुमति डायलॉग दिखाया जा सकता है तब तक वही दिखाता है, वरना इस ऐप की नोटिफ़िकेशन सेटिंग्स खोलता है और यूज़र के लौटने पर जवाब देता है।

लौटाता है: सीधे कुछ नहीं। जवाब {state, changed, openedSettings} window.__appmintReplies[callbackId] को भेजा जाता है; state 'granted' | 'prompt' | 'blocked' | 'unavailable' है, changed कॉल से पहले से अलग होने पर true है, सेटिंग्स स्क्रीन इस्तेमाल होने पर openedSettings true है। अगर पहले से 'granted' (या 'unavailable') है तो तुरंत जवाब देता है। ज़रूरत: इसे यूज़र के टैप किए बटन से कॉल करें; नोटिफ़िकेशन के लिए बिल्ड में Notifications (या Push / Reminders) चाहिए।

प्रॉमिस रैपर 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" - यूज़र (या OEM) ने इस ऐप के लिए exact अलार्म बंद कर दिए हैं, इसलिए Doze उन्हें कुछ मिनटों के लिए इकट्ठा कर सकता है। इसे ठीक करने के लिए [requestExactAlarms] से कहें। "unavailable" - Android 12+ है और सिस्टम अलार्म सर्विस तक पहुँचा नहीं जा सका, इसलिए सटीकता पढ़ी नहीं जा सकती (अभी शेड्यूल किया रिमाइंडर शायद बजे भी नहीं)। जिस पेज को समय पर होना ज़रूरी है उसे मान लेने की जगह इसे जाँचना चाहिए। @return `"exact"`, `"inexact"` या `"unavailable"`।

उदाहरण

बताता है कि रिमाइंडर ठीक उसी मिनट पर बजेंगे, या देर से, क्योंकि यूज़र (या फ़ोन बनाने वाली कंपनी) ने इस ऐप के लिए exact अलार्म बंद कर दिए हैं।

लौटाता है: एक स्ट्रिंग, तुरंत: 'exact', 'inexact' (बैटरी सेविंग की वजह से इकट्ठा हो सकता है, अक्सर कुछ मिनट), या 'unavailable' (फ़ोन में अलार्म सर्विस नहीं है)। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में ⏰ Reminders चालू करें।

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

ध्यान दें: Android 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' (Android 12+: इस ऐप के लिए exact अलार्म बंद हैं - requestExactAlarms() कॉल करें), या 'needs_notification_permission' (Android 13+: नोटिफ़िकेशन अनुमति नहीं है, इसलिए Dismiss बटन नहीं होगा - requestNotificationPermission() कॉल करें)। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में 🔔 Alarm clock चालू करें।

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

नोट: alarm: true के साथ scheduleNotificationEx जब भी यह 'ready' न हो तो false लौटाता है - जो अलार्म कभी बज ही नहीं सकता वह कभी स्टोर नहीं होता।

getNotificationPermission ब्रिज#

window.WebToApk.getNotificationPermission(): String

नोटिफ़िकेशन की मौजूदा अनुमति, OS से सीधे पढ़ी गई। पॉलीफ़िल हर पेज लोड पर `Notification.permission = 'default'` हार्डकोड करती थी, इसलिए बेहद आम पेज पैटर्न if (Notification.permission === 'granted') new Notification(...) कभी चला ही नहीं - उस यूज़र के लिए भी नहीं जिसने पहले अनुमति दे दी थी, और Android 12 व उससे नीचे के लिए भी नहीं जहाँ नोटिफ़िकेशन को रनटाइम अनुमति चाहिए ही नहीं।

उदाहरण

अभी पढ़ता है कि यह ऐप नोटिफ़िकेशन भेज सकता है या नहीं।

लौटाता है: एक स्ट्रिंग, तुरंत: 'granted'; 'denied' (यूज़र ने हमेशा के लिए मना कर दिया, या Settings में इस ऐप के नोटिफ़िकेशन बंद कर दिए - भेजा गया नोटिफ़िकेशन दिखेगा नहीं); या 'default' (अभी तय नहीं हुआ, ऐप अब भी पूछ सकता है)। ज़रूरत: इसे कॉल करने के लिए कुछ नहीं; नोटिफ़िकेशन भेजने के लिए बिल्ड करते समय Access चरण (चरण 4) में Notifications चालू होना चाहिए।

स्टैंडर्ड वेब API वही लाइव मान पढ़ता है:

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

रॉ कॉल, जैसे यूज़र के Settings से लौटने के बाद:

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' (Android अब भी पूछ सकता है) | 'blocked' (हमेशा के लिए मना किया गया या Settings में बंद - सिर्फ़ Settings ठीक कर सकती है) | 'unavailable' (Android 13+ और यह बिल्ड नोटिफ़िकेशन अनुमति घोषित नहीं करता)। NotificationOptIn देखें।

उदाहरण

अभी पढ़ता है कि इस ऐप के नोटिफ़िकेशन चालू हैं या नहीं - और अगर नहीं, तो ऐप अब भी पूछ सकता है या उन्हें वापस चालू करना सिर्फ़ Settings से हो सकता है।

लौटाता है: एक स्ट्रिंग, तुरंत: 'granted' (नोटिफ़िकेशन फ़ोन तक पहुँचते हैं); 'prompt' (Android अब भी अपना अनुमति डायलॉग दिखा सकता है); 'blocked' (यूज़र ने हमेशा के लिए मना कर दिया, या सिस्टम सेटिंग्स में इस ऐप के नोटिफ़िकेशन बंद कर दिए - इसे सिर्फ़ सेटिंग्स ठीक कर सकती हैं); या 'unavailable' (Android 13+ और ऐप नोटिफ़िकेशन / रिमाइंडर / पुश के बिना बना था, इसलिए इन्हें कुछ भी चालू नहीं कर सकता)। ज़रूरत: इसे कॉल करने के लिए कुछ नहीं।

सीधी कॉल, यह तय करने के लिए कि "नोटिफ़िकेशन चालू करें" बैनर दिखाना है या नहीं:

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() कॉल करें (वह आपके लिए डायलॉग या Settings चुन लेता है)।

getScheduledNotifications ब्रिज#

window.WebToApk.getScheduledNotifications(): String

POST_NOTIFICATIONS अनुमति को पढ़ना और माँगना, और जो नोटिफ़िकेशन पेज पहले ही शेड्यूल कर चुका है उन्हें मैनेज करना। भेजना और शेड्यूल करना अपने-अपने मेथड पर बताया गया है।

इसे अलग से नहीं, बल्कि इसके समूह नोटिफ़िकेशन की अनुमति, रद्द करना और सूची में समझाया गया है।

उदाहरण

वे रिमाइंडर दिखाता है जो अभी भी शेड्यूल हैं।

लौटाता है: एक JSON स्ट्रिंग, तुरंत। इसे पार्स करें। हर आइटम: { id, title, body, trigger, repeat, options? } - trigger अगला समय है (epoch ms में), repeat की वैल्यू none|minutely|hourly|daily|weekly है, और options (सिर्फ़ scheduleNotificationEx के लिए) विकल्प हैं, JSON स्ट्रिंग के रूप में। लाइव फ़ील्ड बताते हैं कि यह असल में कैसे पहुँचाया जाएगा, लाइव पढ़कर: precision ("exact"; "inexact" = इस ऐप के लिए "Alarms & reminders" बंद है, इसलिए Android इसे फ़ोन के जागते रहने पर 10 मिनट की खिड़की में पहुँचाता है, और सोते समय और देर तक रोक सकता है; alarm: true के लिए "alarm_clock"), windowMs + latestAt (Android को दी गई खिड़की: सटीक होने पर 0 / trigger, अनुमानित होने पर 600000 / trigger + 600000) और notificationsEnabled (false = यह बजता है पर Android कुछ नहीं दिखाता, क्योंकि ऐप के नोटिफ़िकेशन बंद हैं)। कुछ न होने पर '[]'। ज़रूरत: अलग से कुछ नहीं।

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 (तुरंत)। ज़रूरत: बिल्ड करते समय एक्सेस चरण (चरण 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), चैनल, साउंड, रंग और टैग।

लौटाता है: विकल्प सही JSON थे और नोटिफ़िकेशन सिस्टम को दे दिया गया तो true (यह बैकग्राउंड थ्रेड पर बनता है, इसलिए खराब इमेज URL पेज को नहीं रोकता); अमान्य JSON पर false। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में Notifications चालू करें, और Android 13+ पर यूज़र की अनुमति (इससे माँगें Notification.requestPermission()).

सादा नोटिफ़िकेशन। tag एक ID है: उसी टैग से दोबारा भेजने पर पुराना नोटिफ़िकेशन बदल जाता है।

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', एक data:audio/... URL, या 'silent'), smallIcon (drawable का नाम), when (epoch ms जो समय के रूप में दिखता है), openApp: true (फ़ुल-स्क्रीन अलर्ट; देखें scheduleNotificationEx).

नोट: बटनों में इनलाइन जवाब का बॉक्स नहीं होता, और बटन का टैप पेज तक तभी पहुँचता है जब ऐप चल रहा हो (यह ऐप नहीं खोलता); अगर आपने परिभाषित किया हो तो इवेंट window.onAppMintNotificationAction(detail) को भी कॉल करता है। image और largeIcon एक https:// या data: URL लेते हैं, या आपके ऐप के साथ पैक की गई इमेज, उसी नाम से जैसे आपका पेज उसे बुलाता है ('img/logo.png', '/img/logo.png', या उसका पूरा https://appassets.androidplatform.net/assets/… पता)। खराब इमेज छोड़ दी जाती है, नोटिफ़िकेशन फिर भी दिखता है। ongoing: true हमेशा शांत "Ongoing" चैनल इस्तेमाल करता है। notify() का कोई सार्वजनिक "remove" जोड़ीदार नहीं है: cancelNotification सिर्फ़ शेड्यूल किए रिमाइंडर रद्द करता है। जिस नोटिफ़िकेशन को आप बाद में पेज से हटाना चाहें उसे new Notification(...) से भेजें और उसके close() से हटाएँ; स्टिकी प्रोग्रेस कार्ड को Capacitor.AppwrightLiveProgress (end) से। Android चैनल बनते समय उसकी आवाज़ तय कर देता है, इसलिए नया sound मान नया चैनल बनाता है।

openNotificationSettings ब्रिज#

window.WebToApk.openNotificationSettings(): Boolean

इस ऐप की नोटिफ़िकेशन सेटिंग्स खोलता है; स्क्रीन खुल सकी तो true।

उदाहरण

इस ऐप की अपनी नोटिफ़िकेशन सेटिंग्स स्क्रीन खोलता है, जहाँ यूज़र नोटिफ़िकेशन (और हर चैनल) चालू या बंद कर सकता है।

लौटाता है: स्क्रीन खुलने पर तुरंत true; न खुल पाने पर false (Android 13+ और बिल्ड में नोटिफ़िकेशन नहीं हैं)। Android 8+ पर यह ऐप का नोटिफ़िकेशन पेज है; पुराने Android पर ऐप की जानकारी वाली स्क्रीन। ज़रूरत: कुछ चालू करने की ज़रूरत नहीं।

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

नोट: यूज़र को एक टैप में वापस लाने के लिए इसके बजाय AppMint.enableNotifications() इस्तेमाल करें: Android जब तक अनुमति डायलॉग दिखाने देता है तब तक वह दिखाता है, और यह स्क्रीन तभी खोलता है जब वह नहीं दिखा सकता।

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

यूज़र को अपनी ऑडियो फ़ाइल चुनने दें और उसे रोज़ के रिमाइंडर में इस्तेमाल करें, data: URL के रूप में दी गई:

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') या आपके पेज का बनाया हुआ data:audio/...;base64,... URL। प्रकार: mp3, wav, ogg, oga, m4a, aac, flac, opus, mid, midi।

ध्यान दें: बिना फ़ोल्डर या एक्सटेंशन वाला सादा नाम (res/raw की आवाज़) यहाँ जाँचा नहीं जाता और false लौटाता है, भले ही notify({sound: 'name'}) उसे इस्तेमाल कर सकता हो। Android हर चैनल के लिए एक ही आवाज़ रखता है, इसलिए आवाज़ बदलने से नया चैनल बनता है और उस चैनल की यूज़र की सेटिंग्स रीसेट हो जाती हैं।

requestExactAlarms ब्रिज#

window.WebToApk.requestExactAlarms(): Boolean

वह सिस्टम स्क्रीन खोलता है जहाँ exact अलार्म की अनुमति दी जाती है। Android 12+।

उदाहरण

Android 12+ की वह सिस्टम स्क्रीन खोलता है जहाँ यूज़र इस ऐप को सटीक अलार्म इस्तेमाल करने की अनुमति देता है, ताकि रिमाइंडर ठीक मिनट पर बजें।

लौटाता है: सेटिंग्स स्क्रीन खुलने पर true (और Android 11 व उससे पुराने पर, जहाँ कुछ ज़रूरी नहीं है); स्क्रीन न खुल सकी तो false। यह यूज़र का चुनाव नहीं बताता। ज़रूरत: बिल्ड करते समय एक्सेस चरण (चरण 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 अनुमति को पढ़ना और माँगना, और जो नोटिफ़िकेशन पेज पहले ही शेड्यूल कर चुका है उन्हें मैनेज करना। भेजना और शेड्यूल करना अपने-अपने मेथड पर बताया गया है।

इसे अलग से नहीं, बल्कि इसके समूह नोटिफ़िकेशन की अनुमति, रद्द करना और सूची में समझाया गया है।

उदाहरण

Android 13+ का "नोटिफ़िकेशन की अनुमति दें?" डायलॉग दिखाता है। पेज आम तौर पर Notification.requestPermission() इस्तेमाल करते हैं, जो इसे कॉल करके यूज़र के जवाब का इंतज़ार करता है।

लौटाता है: एक स्ट्रिंग, तुरंत: अगर पहले से अनुमति है (या Android 12 और उससे पुराना है) तो 'granted', वरना डायलॉग खुलते समय '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 को कॉलबैक देता है, उसे भी वही अंतिम जवाब मिलता है:

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

ध्यान दें: इसे बटन टैप से और साफ़ कारण बताकर माँगें। यूज़र के दो बार मना करने के बाद Android डायलॉग दिखाना बंद कर देता है और जवाब तुरंत 'denied' आता है; तब यूज़र को ऐप की सेटिंग्स में जाकर अनुमति देनी होगी।

requestOpenAppPermission ब्रिज#

window.WebToApk.requestOpenAppPermission(): Boolean

सिस्टम की वह स्क्रीन खोलता है जहाँ फ़ुल-स्क्रीन अलर्ट की अनुमति दी जाती है। Android 14+।

उदाहरण

Android 14+ की वह सिस्टम स्क्रीन खोलता है जहाँ यूज़र इस ऐप के लिए फ़ुल-स्क्रीन अलर्ट की अनुमति देता है, ताकि openApp: true रिमाइंडर ऐप को स्क्रीन पर ला सके।

लौटाता है: सेटिंग्स स्क्रीन खुलने पर true (और Android 14 से नीचे, जहाँ कुछ ज़रूरी नहीं है); न खुल पाने पर false। यह यूज़र का चुनाव नहीं बताता। ज़रूरत: बिल्ड करते समय एक्सेस चरण (चरण 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; id खाली हो या समय संख्या न हो तो false। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में ⏰ Reminders चालू करें (यह Notifications भी चालू कर देता है), और 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');
}

जानें कि रिमाइंडर से ऐप कब खुला (यूज़र ने उस पर टैप किया):

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

नोट: रिमाइंडर "urgent" (हेड्स-अप) शैली इस्तेमाल करता है और उसका टैग id होता है। जो दोहराने वाला रिमाइंडर फ़ोन बंद रहने पर छूट गया, वह अगले चरण पर बजता है। तस्वीर, बटन, अपना साउंड या लॉक स्क्रीन के ऊपर ऐप खोलने के लिए scheduleNotificationEx इस्तेमाल करें। अगर ऐप के लिए exact अलार्म बंद हैं, तो समय कुछ मिनट खिसक सकता है; जाँचें 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। जब कोई समय न दिया हो (at, triggerAt, triggerAtMillis या inSeconds), id खाली हो, JSON खराब हो, या sound ऐसी फ़ाइल का नाम बताए जो आपके ऐप में नहीं है, तब false। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में ⏰ Reminders चालू करें, और Android 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
  }));
}

नोट: वही id पुराने रिमाइंडर की जगह ले लेता है। डिफ़ॉल्ट: टैग = id, चैनल = urgent, bigText = बॉडी। बटन का टैप पेज तक तभी पहुँचता है जब ऐप चल रहा हो। साउंड फ़ाइल शेड्यूल करते समय कॉपी होती है, इसलिए गलत पाथ अभी फेल होता है, दिनों बाद नहीं; mp3, wav, ogg, m4a, aac, flac और opus चलते हैं। openApp ही एकमात्र तरीका है जिससे ऐप तय समय पर खुद स्क्रीन पर आ सकता है; Android 14+ पर यूज़र को इसकी अनुमति देनी होगी (canOpenAppAtTime)। repeat: 'weekdays' के साथ days अगर 1-7 से बाहर हो तो false लौटता है। alarm: true के लिए Access चरण (चरण 4) में 🔔 Alarm clock चाहिए और जब तक getAlarmSupport() 'ready' न हो तब तक यह false लौटाता है; एक समय में एक ही अलार्म बजता है (नया अलार्म पिछले को missed के रूप में खत्म करता है)। अगर किसी अलार्म की अपनी साउंड फ़ाइल बजने तक गायब हो चुकी हो (Remote Update ने उसे हटा दिया), तो वह फ़ोन की अलार्म टोन से बजता है और इवेंट में यह रहता है soundError: true.

showNotification ब्रिज#

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

चार आर्गुमेंट वाला मूल रूप, एडाप्टर के रूप में ज्यों का त्यों रखा गया है ताकि बाज़ार में मौजूद हर ऐप और गाइड का हर स्निपेट चलता रहे। यह अब रिच बिल्डर को फ़ॉरवर्ड करता है - यानी `icon` आर्गुमेंट, जिसे पुराना इम्प्लीमेंटेशन चुपचाप छोड़ देता था, अब आख़िरकार कुछ करता है।

उदाहरण

चार आर्गुमेंट वाला मूल नोटिफ़िकेशन कॉल। यह शीर्षक, टेक्स्ट, वैकल्पिक बड़ा आइकन और टैग के साथ नोटिफ़िकेशन दिखाता है। नया कोड notify(optionsJson) या new Notification(...) इस्तेमाल करे, जो ज़्यादा करते हैं।

लौटाता है: नोटिफ़िकेशन सिस्टम को दे दिया गया तो true। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में Notifications चालू करें, और Android 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।

उदाहरण

बज रही अलार्म घड़ी को रोकता है और कुछ मिनट बाद उसे फिर बजाता है, ठीक उसके स्नूज़ बटन की तरह। सिर्फ़ यही रिंग आगे खिसकती है: रोज़ का अलार्म कल अपने सामान्य समय पर ही बजेगा।

लौटाता है: अलार्म बज रहा था और स्नूज़ हो गया तो true; बज नहीं रहा था तो false। id '' = जो भी बज रहा हो; minutes 1-1440, या अलार्म के अपने snoozeMinutes के लिए 0 (डिफ़ॉल्ट 10)। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में 🔔 Alarm clock।

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] को रोकता है ("" = जो भी बज रहा हो), ठीक उसके खारिज करें बटन की तरह। दोहराने वाला अलार्म अपने अगले समय के लिए शेड्यूल रहता है। अगर अलार्म बज नहीं रहा तो False।

उदाहरण

बजते अलार्म को रोकता है, बिल्कुल उसके Dismiss बटन की तरह। दोहराने वाला अलार्म अगले समय के लिए सेट रहता है; उसे हटाने के लिए cancelNotification(id) इस्तेमाल करें।

लौटाता है: वह अलार्म बज रहा था और अब रुक गया तो true; बज नहीं रहा था तो false। जो भी अलार्म बज रहा हो उसे रोकने के लिए '' दें। ज़रूरत: बिल्ड करते समय Access चरण (चरण 4) में 🔔 Alarm clock।

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

हर डॉक्स बिल्ड पर ऐप रनटाइम और उसकी उदाहरण फ़ाइलों से बनाया जाता है। Markdown में पढ़ें · सभी फ़ैमिली.

2026-10-03 को शिप किए गए ब्रिज से मिलाकर जाँचा गया।