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

JavaScript ब्रिज API

फ़ोन, SMS और कॉल लॉग

कॉल करना, संदेश भेजना या ड्राफ़्ट करना, और इतिहास पढ़ना। इनमें से हर एक अनुमति से बँधा है और Play इनके बारे में पूछता है।

composeSms ब्रिज#

window.WebToApk.composeSms(phoneNumber: String, message: String): Boolean

यूज़र का SMS ऐप संदेश भरकर खोलता है। इसे अनुमति नहीं चाहिए और Play इसे रिजेक्ट नहीं कर सकता - हर गैर-ऑटोमैटिक चीज़ के लिए सही डिफ़ॉल्ट। फ़ोन में SMS ऐप न होने (या न खुल पाने) पर false लौटाता है।

उदाहरण

यूज़र का SMS ऐप नंबर और टेक्स्ट पहले से भरकर खोलता है। यूज़र भेजें दबाता है।

लौटाता है: SMS ऐप खुलने पर true, फ़ोन में SMS ऐप न होने या न खुल पाने पर false (तुरंत)। ज़रूरी: कुछ नहीं - कोई बिल्ड स्विच नहीं, कोई अनुमति नहीं।

एक “Text us” बटन।

document.getElementById('textUs').addEventListener('click', function () {
  if (window.WebToApk && window.WebToApk.composeSms) {
    var ok = WebToApk.composeSms('+911234567890', 'Hello, I need help with my order');
    if (!ok) showMessage('Could not open the SMS app.');
  } else {
    // In a normal browser the standard sms: link does the same job.
    location.href = 'sms:+911234567890?body=' + encodeURIComponent('Hello, I need help with my order');
  }
});

हर अपॉइंटमेंट पंक्ति पर एक “Remind” बटन, जो उस ग्राहक के नंबर पर रिमाइंडर भरकर देता है:

document.getElementById('appointments').addEventListener('click', function (e) {
  var row = e.target.closest('[data-phone]');
  if (!row || !(window.WebToApk && typeof window.WebToApk.composeSms === 'function')) return;
  var when = new Date(Number(row.getAttribute('data-time')));
  var text = 'Hi ' + row.getAttribute('data-name') + ', a reminder of your appointment on ' +
             when.toLocaleDateString() + ' at ' +
             when.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + '.';
  if (!WebToApk.composeSms(row.getAttribute('data-phone'), text)) {
    showMessage('This phone has no SMS app.');
  }
});

एक “Send by SMS” फ़ॉर्म: जब फ़ोन में SMS ऐप न हो (false), तो टेक्स्ट कॉपी कर दें ताकि यूज़र उसे कहीं और पेस्ट कर सके:

document.getElementById('smsForm').addEventListener('submit', function (e) {
  e.preventDefault();
  var number = document.getElementById('to').value.trim();
  var text = document.getElementById('message').value;
  if (!number || !text) return;

  var opened = window.WebToApk && typeof window.WebToApk.composeSms === 'function' &&
               WebToApk.composeSms(number, text);
  if (!opened && navigator.clipboard) {
    navigator.clipboard.writeText(text).then(function () {
      showMessage('No SMS app here. The message was copied instead.');
    });
  }
});

ध्यान दें: जब तक यूज़र अपने SMS ऐप में भेजें नहीं दबाता, कुछ नहीं भेजा जाता, इसलिए इस मेथड को खुद SMS अनुमति नहीं चाहिए। यूज़र के लिखे या मंज़ूर किए हर संदेश के लिए इसे इस्तेमाल करें; sendSms सिर्फ़ तब इस्तेमाल करें जब ऐप को सचमुच खुद भेजना हो। AI से बने ऐप और Google AI Studio इम्पोर्ट के लिए बिल्ड आपका कोड स्कैन करता है; composeSms( कॉल SMS (read & send) पर टिक नहीं लगाती - सिर्फ़ sendSms और listSms लगाते हैं - इसलिए “Text us” बटन Play की कोई प्रतिबंधित अनुमति नहीं जोड़ता।

listCallLog ब्रिज#

window.WebToApk.listCallLog(requestId: String, limit: Int, offset: Int, sinceMillis: String)

WebToApk.listCallLog(id, limit, offset, sinceMillis) `appmint:calllog` / window.onAppMintCallLog के रूप में मिलता है।

इसका वर्णन अलग से नहीं, बल्कि इसके समूह कॉल लॉग में है।

उदाहरण

फ़ोन का कॉल इतिहास पढ़ता है, सबसे नई कॉल पहले।

लौटाता है: अभी कुछ नहीं। जवाब appmint:calllog इवेंट (और window.onAppMintCallLog) में detail = { requestId, calls: [{ number, name, type, date, duration }] } या { requestId, error } के साथ आता है। date 1970 से मिलीसेकंड है; duration सेकंड है। ज़रूरी: बिल्ड करते समय चरण 4 (Access) में Call log (read) चालू करें; पहली कॉल पर यूज़र से अनुमति माँगी जाती है।

आखिरी 50 कॉल दिखाएँ।

function loadCalls() {
  if (!(window.WebToApk && window.WebToApk.listCallLog)) return;
  var id = 'calls-' + Date.now();

  window.addEventListener('appmint:calllog', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:calllog', handler);
    if (e.detail.error) { showMessage('Call log: ' + e.detail.error); return; }

    e.detail.calls.forEach(function (c) {
      // c.type: incoming | outgoing | missed | voicemail | rejected | blocked | other
      addRow((c.name || c.number) + ' - ' + c.type + ' - ' +
             new Date(c.date).toLocaleString() + ' - ' + c.duration + 's');
    });
  });

  WebToApk.listCallLog(id, 50, 0, '0');   // limit, offset, since (string)
}

सिर्फ़ पिछले 24 घंटों की कॉल, पेज हुक से। sinceMillis एक स्ट्रिंग है क्योंकि नंबर ब्रिज के int के लिए बहुत बड़ा है।

window.onAppMintCallLog = function (detail) {
  if (detail.requestId !== 'today') return;
  if (detail.error) return;
  document.getElementById('count').textContent = detail.calls.length + ' calls today';
};

if (window.WebToApk && window.WebToApk.listCallLog) {
  WebToApk.listCallLog('today', 500, 0, String(Date.now() - 24 * 60 * 60 * 1000));
}

कॉल-इतिहास स्क्रीन जिसमें “Load more” बटन है, और एक बार में 25 कॉल लाती है offset:

var PAGE = 25, loaded = 0;

function loadMoreCalls() {
  if (!(window.WebToApk && typeof window.WebToApk.listCallLog === 'function')) return;
  var id = 'page-' + loaded;

  window.addEventListener('appmint:calllog', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:calllog', handler);
    if (e.detail.error) { showMessage('Call log: ' + e.detail.error); return; }

    e.detail.calls.forEach(function (c) { addRow(c.name || c.number, c.type, c.date); });
    loaded += e.detail.calls.length;
    document.getElementById('more').hidden = e.detail.calls.length < PAGE;   // last page
  });

  WebToApk.listCallLog(id, PAGE, loaded, '0');   // skip the calls already shown
}

document.getElementById('more').addEventListener('click', loadMoreCalls);
loadMoreCalls();

ध्यान दें: limit को 1-2000 के बीच रखा जाता है। त्रुटियाँ: not_enabled, permission_denied, foreign_origin। कॉल लॉग Google Play की प्रतिबंधित अनुमति है: आपको Play Console में मंज़ूर Permissions Declaration दाखिल करनी होगी, वरना ऐप रिजेक्ट हो जाता है।

listSms ब्रिज#

window.WebToApk.listSms(requestId: String, box: String, limit: Int, offset: Int)

WebToApk.composeSms(number, message) <- कोई अनुमति नहीं, SMS ऐप खोलता है WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) भेजने के नतीजे: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} आने वाले संदेश: `appmint:sms-received` / window.onAppMintSmsReceived।

इसका वर्णन अलग से नहीं, बल्कि इसके समूह SMS में है।

उदाहरण

फ़ोन के इनबॉक्स, भेजे गए या ड्राफ़्ट से SMS संदेश पढ़ता है, सबसे नए पहले। यह फ़ाइल यह भी दिखाती है कि नए संदेश आते ही कैसे पाएँ।

लौटाता है: अभी कुछ नहीं। जवाब appmint:sms इवेंट (और window.onAppMintSms) में detail = { requestId, messages: [{ id, address, body, date, read, box }] } के साथ आता है। फ़ेल होने पर: { requestId, status: 'failed', ok: false, reason }। ज़रूरी: बिल्ड करते समय चरण 4 (Access) में SMS (read & send) चालू करें; पहली कॉल पर यूज़र से अनुमति माँगी जाती है।

इनबॉक्स के ताज़ा 30 संदेश पढ़ें।

function loadInbox() {
  if (!(window.WebToApk && window.WebToApk.listSms)) return;
  var id = 'inbox-' + Date.now();

  window.addEventListener('appmint:sms', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:sms', handler);
    if (e.detail.status === 'failed') { showMessage('Cannot read SMS: ' + e.detail.reason); return; }

    e.detail.messages.forEach(function (m) {
      addRow(m.address + ': ' + m.body + (m.read ? '' : ' (new)') +
             ' - ' + new Date(m.date).toLocaleString());
    });
  });

  WebToApk.listSms(id, 'inbox', 30, 0);   // box: 'inbox' | 'sent' | 'draft'
}

नए संदेश आते ही (appmint:sms-received, साथ ही window.onAppMintSmsReceived)। ऐप बंद रहते समय आए संदेश रखे जाते हैं (आखिरी 50) और ऐप फिर खुलने पर आपके पेज को दिए जाते हैं।

window.addEventListener('appmint:sms-received', function (e) {
  // e.detail = { address: '+91...', body: 'Your OTP is 4821', date: 1737000000000 }
  var code = (e.detail.body.match(/\b\d{4,6}\b/) || [])[0];
  if (code) document.getElementById('otp').value = code;
});

// or, as a page hook:
window.onAppMintSmsReceived = function (sms) {
  console.log('SMS from ' + sms.address);
};

एक खर्च ट्रैकर जो इनबॉक्स के आखिरी 200 संदेशों में बैंक डेबिट खोजता है और रकम जोड़ता है:

function scanSpending() {
  if (!(window.WebToApk && typeof window.WebToApk.listSms === 'function')) return;
  var id = 'spend-' + Date.now();

  window.addEventListener('appmint:sms', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:sms', handler);
    if (e.detail.status === 'failed') { showMessage('Cannot read SMS: ' + e.detail.reason); return; }

    var total = 0;
    e.detail.messages.forEach(function (m) {
      if (!/debited/i.test(m.body)) return;
      var amount = m.body.match(/(?:Rs\.?|INR)\s*([\d,]+(?:\.\d{1,2})?)/i);
      if (amount) total += parseFloat(amount[1].replace(/,/g, ''));
    });
    document.getElementById('spent').textContent = 'Spent: Rs ' + total.toFixed(2);
  });

  WebToApk.listSms(id, 'inbox', 200, 0);
}

ध्यान दें: limit को 1-500 के बीच रखा जाता है। कोई भी अनजाना बॉक्स नाम इनबॉक्स पढ़ता है। reason के मान: not_enabled, permission_denied, foreign_origin। मिले हुए संदेश यूज़र के सामान्य SMS ऐप तक भी पहुँचते हैं। SMS Google Play की प्रतिबंधित अनुमति है: आपको Play Console में मंज़ूर Permissions Declaration दाखिल करनी होगी (आम तौर पर सिर्फ़ डिफ़ॉल्ट SMS ऐप को यह मिलती है), वरना ऐप रिजेक्ट हो जाता है।

makePhoneCall ब्रिज#

window.WebToApk.makePhoneCall(phoneNumber: String)

[phoneNumber] को कॉल करता है। - बिल्ड में Phone अनुमति चालू: कॉल सीधे करता है। पहली बार Android यूज़र से Phone अनुमति माँगता है; अगर वे मना करें, तो नंबर भरा हुआ डायलर खुलता है ताकि वे खुद कॉल कर सकें। - Phone अनुमति बंद: नंबर भरा हुआ डायलर खोलता है (डायलर को अनुमति नहीं चाहिए) और यूज़र Call दबाता है। - जो पेज ऐप का अपना नहीं है (ऐप में खुली बाहरी साइट) उसे हमेशा डायलर मिलता है, सीधा कॉल कभी नहीं। आपके HTML में सादा `tel:` लिंक भी डायलर खोलता है और अक्सर काफ़ी होता है।

उदाहरण

किसी नंबर पर फ़ोन कॉल शुरू करता है, या नंबर भरा हुआ डायलर खोलता है।

लौटाता है: कुछ नहीं। ज़रूरी: डायलर खोलने के लिए कुछ नहीं। अगर कॉल सीधे शुरू होनी चाहिए तो बिल्ड करते समय चरण 4 (Access) में Phone चालू करें।

एक “Call the shop” बटन।

document.getElementById('callShop').addEventListener('click', function () {
  if (window.WebToApk && window.WebToApk.makePhoneCall) {
    WebToApk.makePhoneCall('+911234567890');
  }
});

किसी स्विच की ज़रूरत नहीं: सादा tel: लिंक नंबर के साथ डायलर खोलता है, ऐप में भी और ब्राउज़र में भी। अक्सर आपको बस इतना ही चाहिए।

<a href="tel:+911234567890">Call the shop</a>

पूरी स्टाफ़ डायरेक्टरी के लिए एक हैंडलर, जहाँ हर पंक्ति अपना नंबर data-phone एट्रिब्यूट में रखती है:

document.getElementById('staffList').addEventListener('click', function (e) {
  var btn = e.target.closest('[data-phone]');
  if (!btn) return;
  var number = btn.getAttribute('data-phone').replace(/[^\d+]/g, '');   // keep digits and +
  if (!number) return;
  if (window.WebToApk && typeof window.WebToApk.makePhoneCall === 'function') {
    WebToApk.makePhoneCall(number);
  } else {
    location.href = 'tel:' + number;   // a normal browser
  }
});

ध्यान दें: Phone बंद होने पर नंबर के साथ डायलर खुलता है और यूज़र कॉल दबाता है। Phone चालू होने पर कॉल सीधे शुरू होती है; पहली बार Android यूज़र से फ़ोन-कॉल की अनुमति माँगता है, और मना करने पर डायलर खुलता है ताकि वे फिर भी कॉल कर सकें। जो पेज ऐप का अपना नहीं है (ऐप के अंदर खुली बाहरी साइट) उसे हमेशा डायलर मिलता है। नंबर सादी स्ट्रिंग के रूप में दें। कुछ भी वापस नहीं बताया जाता - आप नहीं जान सकते कि कॉल हुई या नहीं। Phone स्विच आपकी Play लिस्टिंग में CALL_PHONE अनुमति जोड़ता है; इसे Data safety फ़ॉर्म में घोषित करें और तभी चालू करें जब ज़रूरत हो।

sendSms ब्रिज#

window.WebToApk.sendSms(

WebToApk.composeSms(number, message) <- कोई अनुमति नहीं, SMS ऐप खोलता है WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) भेजने के नतीजे: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} आने वाले संदेश: `appmint:sms-received` / window.onAppMintSmsReceived।

इसका वर्णन अलग से नहीं, बल्कि इसके समूह SMS में है।

उदाहरण

ऐप से सीधे SMS भेजता है, SMS ऐप खोले बिना, और बताता है कि असल में क्या हुआ।

लौटाता है: अभी कुछ नहीं। जवाब appmint:sms इवेंट (और window.onAppMintSms) में detail = { requestId, status: 'sent' | 'delivered' | 'failed', ok, reason? } के साथ आता है। डिलीवरी रिपोर्ट के साथ सफल होने पर आपको दो जवाब मिलते हैं: sent, फिर delivered। ज़रूरी: बिल्ड करते समय चरण 4 (Access) में SMS (read & send) चालू करें; पहली कॉल पर यूज़र से अनुमति माँगी जाती है।

कोड भेजें और उसका स्टेटस देखें। जब आपने डिलीवरी रिपोर्ट माँगी हो तो पहले जवाब के बाद लिस्नर न हटाएँ।

function sendCode(phone, code) {
  if (!(window.WebToApk && window.WebToApk.sendSms)) {
    showMessage('Sending SMS works only inside the app.');
    return;
  }
  var id = 'sms-' + Date.now();

  function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    var d = e.detail;
    if (d.status === 'sent' && d.reason === 'no_delivery_report') {
      showStatus('Sent (the network gave no delivery report)');
      window.removeEventListener('appmint:sms', handler);
    } else if (d.status === 'sent') {
      showStatus('Sent');
    } else if (d.status === 'delivered') {
      showStatus('Delivered');
      window.removeEventListener('appmint:sms', handler);
    } else {                                   // 'failed'
      showStatus('Failed: ' + d.reason);
      window.removeEventListener('appmint:sms', handler);
    }
  }
  window.addEventListener('appmint:sms', handler);

  WebToApk.sendSms(id, phone, 'Your code is ' + code, true);   // true = delivery report
}

कोई डिलीवरी रिपोर्ट नहीं। आखिरी मान के रूप में false दें: आपको ठीक एक जवाब मिलता है, sent या failed.

window.onAppMintSms = function (d) {
  if (d.requestId === 'alert-1') showStatus(d.ok ? 'Alert sent' : 'Failed: ' + d.reason);
};
if (window.WebToApk && window.WebToApk.sendSms) {
  WebToApk.sendSms('alert-1', '+911234567890', 'Gate opened at ' + new Date().toLocaleTimeString(), false);
}

यही सूचना नंबरों की छोटी सूची को एक के बाद एक भेजें, हर नतीजा जमा करें (और SMS बंद या मना होने पर रुक जाएँ, क्योंकि बाकी हर नंबर भी उसी तरह फ़ेल होगा):

function sendToAll(numbers, text, done) {
  if (!(window.WebToApk && typeof window.WebToApk.sendSms === 'function')) { done(null); return; }
  var results = [], i = 0;

  function next() {
    if (i >= numbers.length) { done(results); return; }
    var number = numbers[i++];
    var id = 'notice-' + Date.now() + '-' + i;

    window.addEventListener('appmint:sms', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:sms', handler);
      results.push({ number: number, ok: e.detail.ok, reason: e.detail.reason });
      var r = e.detail.reason;
      if (r === 'not_enabled' || r === 'permission_denied' || r === 'foreign_origin') done(results);
      else next();
    });
    WebToApk.sendSms(id, number, text, false);   // one answer per number
  }
  next();
}

sendToAll(['+911234567890', '+919876543210'], 'Practice moved to 6 pm today.', function (results) {
  if (!results) return showMessage('Sending SMS works only inside the app.');
  showStatus(results.filter(function (r) { return r.ok; }).length + ' of ' + results.length + ' sent');
});

ध्यान दें: लंबा टेक्स्ट हिस्सों में बँटकर एक संदेश के रूप में जाता है। अगर 60 सेकंड में डिलीवरी रिपोर्ट नहीं आती तो आपको reason: 'no_delivery_report' के साथ status: 'sent' मिलता है - संदेश फिर भी भेजा गया था। reason के मान: not_enabled, permission_denied, foreign_origin, invalid_arguments (खाली नंबर या टेक्स्ट), no_service, radio_off, generic_failure, null_pdu, not_delivered, और अन्य। स्ट्रिंग requestId इस्तेमाल करें। हर SMS पर यूज़र का पैसा लग सकता है। SMS Google Play की प्रतिबंधित अनुमति है: आपको Play Console में मंज़ूर Permissions Declaration दाखिल करनी होगी (आम तौर पर सिर्फ़ डिफ़ॉल्ट SMS ऐप को यह मिलती है), इसलिए जब यूज़र खुद भेज सकता हो तो composeSms को प्राथमिकता दें।

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

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