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

यहाँ से शुरू करें

अपने ऐप के लिए HTML लिखना

ऐप रनटाइम आपके पेज को क्या देता है - कॉन्टैक्ट, SMS, नोटिफ़िकेशन, डिवाइस जानकारी, बायोमेट्रिक्स - और उसे सही तरह कॉल करने के नियम

1नियम 1 - हमेशा पहले जाँचें#

window.WebToApk सिर्फ़ आपके बनाए ऐप के अंदर मौजूद होता है। ब्राउज़र में यह नहीं मिलता।

इसलिए कॉल करने से पहले हमेशा जाँचें। नहीं जाँचेंगे तो आपका पेज ऐप के अंदर के सिवा हर जगह टूट जाएगा।

कुछ चीज़ों की कोई जाँच नहीं चाहिए: navigator.vibrate(), speechSynthesis और Notification पहले से काम करते हैं - ऐप उन्हें आपके लिए जोड़ देता है। बस सामान्य वेब कोड इस्तेमाल करें।

// GOOD — safe everywhere
if (window.WebToApk) {
  WebToApk.playClick();
}

// BAD — this breaks your page in a browser
WebToApk.playClick();

2नियम 2 - कुछ जवाब बाद में आते हैं#

कॉन्टैक्ट, SMS या कॉल लॉग पढ़ने में समय लगता है। इसलिए ये तुरंत जवाब नहीं देते। अगर देते, तो आपका ऐप फ़्रीज़ हो जाता।

आप कॉल को एक नाम (id) देते हैं। जवाब बाद में एक इवेंट के रूप में आता है। आप उस इवेंट को सुनते हैं।

इवेंट के नाम: appmint:contacts, appmint:calllog, appmint:sms, appmint:sms-received, appmint:notification-action.

// 1. Listen for the answer
window.addEventListener('appmint:contacts', function (e) {
  if (e.detail.error) {
    alert('Problem: ' + e.detail.error);
    return;
  }
  console.log(e.detail.contacts);
});

// 2. Ask the question ('my1' is any name you choose)
WebToApk.listContacts('my1', 50, 0);

3नियम 3 - दो स्विच ON होने चाहिए#

फ़ोन के किसी फ़ीचर के लिए दो चीज़ें चाहिए:

  1. ऐप बनाते समय आपने इसे चरण 4 (Access) में चालू किया था। अगर नहीं, तो आपको त्रुटि 'not_enabled' मिलती है और यूज़र को कुछ नहीं दिखता।
  1. यूज़र ने फ़ोन पर Yes कहा। अगर उसने No कहा, तो आपको त्रुटि 'permission_denied' मिलती है।

दोनों त्रुटियाँ हमेशा संभालें ताकि यूज़र को पता चले कि क्या हुआ।

आसान तरीका: pickContact() और composeSms() को बिल्कुल कोई अनुमति नहीं चाहिए। जब ये काम आएँ तो इन्हें इस्तेमाल करें - ये कम झंझट में वही काम करते हैं।

window.addEventListener('appmint:contacts', function (e) {
  if (e.detail.error === 'not_enabled') {
    alert('Turn on Contacts when you build the app');
  } else if (e.detail.error === 'permission_denied') {
    alert('Please allow contacts access');
  } else {
    showContacts(e.detail.contacts);
  }
});

4तस्वीरों और बटनों वाले नोटिफ़िकेशन#

notify() विकल्पों की एक सूची लेता है। आप बड़ी तस्वीर, आइकन, बटन और प्रोग्रेस बार जोड़ सकते हैं।

channel ये हो सकता है: urgent, default, quiet या ongoing।

ऐसे नोटिफ़िकेशन के लिए ongoing:true इस्तेमाल करें जिसे यूज़र स्वाइप करके हटा न सके। ध्यान दें: वे उसे Android सेटिंग्स से फिर भी हटा सकते हैं। कोई ऐप ऐसा नोटिफ़िकेशन नहीं बना सकता जिसे हटाया ही न जा सके।

उसे नाम देने के लिए tag इस्तेमाल करें। उसी tag के साथ दोबारा भेजने पर नया जुड़ने के बजाय पुराना बदल जाता है।

WebToApk.notify(JSON.stringify({
  title: 'Order shipped',
  body: 'Arriving Tuesday',
  channel: 'urgent',
  image: 'https://mysite.com/box.jpg',
  actions: [{ id: 'track', label: 'Track' }],
  tag: 'order-482'
}));

// Know which button the user pressed
window.addEventListener('appmint:notification-action', function (e) {
  if (e.detail.actionId === 'track') showTracking();
});

5फ़ोन की जानकारी और फ़िंगरप्रिंट#

getDeviceInfo() को किसी अनुमति की ज़रूरत नहीं। यह Android वर्ज़न, वर्ज़न ID (buildId), फ़ोन मॉडल, स्क्रीन साइज़, बैटरी लेवल और बहुत कुछ देता है।

डिवाइस ID के बारे में: कोई IMEI या सीरियल नंबर नहीं मिलता। Android 10 से हर ऐप के लिए इसे बंद कर दिया गया है। इसके बजाय getInstallId() इस्तेमाल करें - इस इंस्टॉल की एक स्थिर ID, जो अपडेट के बाद भी वही रहती है।

var info = JSON.parse(WebToApk.getDeviceInfo());
info.android.release;      // "14"
info.android.buildId;      // "TQ3A.230805.001"  <- version ID
info.hardware.model;       // "Pixel 7"
info.runtime.batteryLevel; // 82

// Fingerprint
window.__webToApkAuth = window.__webToApkAuth || {};
window.__webToApkAuth['unlock'] = function (json) {
  var r = JSON.parse(json);
  if (r.ok) showMyApp();
};
WebToApk.authenticateBiometricEx('unlock', JSON.stringify({
  title: 'Unlock',
  allowDeviceCredential: true
}));

6अपने पेज को पूरी स्क्रीन भरने दें#

जब आप Fullscreen चालू करते हैं, तो ऐप आपके लिए Android बार छिपा देता है। पर यह CSS जोड़ें ताकि आपका कंटेंट फ़ोन के नॉच के नीचे न छिपे।

ज़रूरी: Fullscreen ANDROID बार (घड़ी, बैटरी, बैक बटन) छिपाता है। आपके ऐप की अपनी रंगीन ऊपरी बार एक अलग स्विच है जिसका नाम 'Show top bar' है। अगर Fullscreen चालू करने के बाद भी आपको कोई बार दिखे, तो बंद करने लायक वही है।

साथ ही: अपने ऐप को घुमाकर भी टेस्ट करें। लैंडस्केप में नॉच बगल में आ जाता है।

body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  min-height: 100vh;   /* old phones */
  min-height: 100dvh;  /* correct when bars are hidden */
}

7डाउनलोड आपका चुना फ़ाइल नाम बनाए रखते हैं#

आपका सामान्य डाउनलोड कोड पहले से चलता है। जब आपका पेज कोई फ़ाइल सेव करता है - PDF रिपोर्ट, CSV एक्सपोर्ट, बैकअप - तो ऐप उसे पकड़कर Android की 'Save as…' शीट खोलता है, जिसमें आपका फ़ाइल नाम पहले से भरा होता है।

यह इसके हर आम तरीके को कवर करता है: <a download="...">, कोड से a.click(), html2pdf / jsPDF, FileSaver.js का saveAs(), और blob URL पर window.open()। File ऑब्जेक्ट अपना नाम खुद साथ लाता है।

अगर आप <a> टैग बनाने के बजाय सीधे माँगना चाहें, तो इस्तेमाल करें AppMint.downloadFile().

एक बात जानना ज़रूरी है: नाम सिर्फ़ आपके पेज के JavaScript में रहता है - Android उसे अपने आप नहीं देख पाता। इसलिए हर बार download= सेट करें (या नाम पास करें), वरना फ़ाइल 'download' नाम से सेव होगी।

// The usual way — saves as Site_Report_2026-08.pdf
const blob = await html2pdf().from(el).outputPdf('blob');
const a = document.createElement('a');
a.download = 'Site_Report_2026-08.pdf';
a.href = URL.createObjectURL(blob);
a.click();

// Or ask directly
AppMint.downloadFile(base64String, 'Ledger_Q3.csv', 'text/csv');
AppMint.saveBlob(myBlob, 'Backup.json');

8Back बटन वैसा करवाएँ जैसा आपका ऐप चाहता है#

अगर आपका ऐप एलिमेंट दिखाकर और छिपाकर स्क्रीन बदलता है (URL नहीं बदलता, history.pushState नहीं), तो Android का Back बटन उन स्क्रीन को नहीं देख पाता - उसकी हिस्ट्री खाली है, इसलिए Back कहीं से भी ऐप से बाहर निकाल देता है।

एक बैक हैंडलर रजिस्टर करें और खुद तय करें: दबाव आपने संभाल लिया (मेन्यू बंद किया, एक स्क्रीन पीछे गए) तो true लौटाएँ; सामान्य व्यवहार चलने देने के लिए false लौटाएँ - पेज हिस्ट्री में पीछे, फिर बाहर निकलने की पुष्टि, फिर निकलना।

जो ऐप हर स्क्रीन के लिए history.pushState इस्तेमाल करते हैं उन्हें इसकी ज़रूरत नहीं: Back पहले से उनकी हिस्ट्री में चलता है। और ऐप के अटक जाने की चिंता न करें - अगर आपका हैंडलर कभी अटक भी जाए, तो थोड़ी देर बाद फ़ोन का Back अपने आप नेटिव तरीके से काम करने लगता है।

AppMint.setBackHandler(() => {
  if (isMenuOpen)   { closeMenu();  return true; }   // consumed
  if (screen !== 'home') { goTo('home'); return true; }
  return false;   // nothing open — normal exit behaviour
});

// Or listen instead: e.preventDefault() consumes the press
window.addEventListener('appmint:back', e => {
  if (closeTopmost()) e.preventDefault();
});

बिल्ड विज़ार्ड में 'Receive shares from other apps' चालू करें और टिक करें कि आपका ऐप क्या स्वीकार करता है: टेक्स्ट और लिंक, डेटा फ़ाइलें (JSON, CSV, TXT, XML, Markdown, GPX, TCX, FIT), फ़ोटो, वीडियो, ऑडियो, दस्तावेज़ (PDF, Word, Excel, PowerPoint)। तब आपका ऐप हर दूसरे ऐप के Share मेन्यू में ठीक उन्हीं प्रकारों के लिए दिखता है - टेक्स्ट के लिए नोट्स ऐप, वीडियो के लिए प्लेयर।

कोई लिंक या टेक्स्ट (YouTube ऐप से कोई वीडियो शेयर करने पर उसका लिंक आता है) WebToApk.getSharedText() से पढ़ा जाता है और appmint:shared के रूप में घोषित होता है। फ़ाइल AppMint.getOpenedFile() से माँगी जाती है - जब आप तैयार हों तब माँगें, फ़ाइल आपका इंतज़ार करती है। यह React, Vue और Angular ऐप्स के लिए मायने रखता है: वे पेज लोड होने के बाद शुरू होते हैं, इसलिए जो ऐप सिर्फ़ इवेंट सुनता था वह फ़ाइल चूक जाता था और बस होम स्क्रीन दिखा देता था।

f.kind बताता है कि क्या आया: image, video, audio, document या data। फ़ोटो, वीडियो और ऑडियो f.url के रूप में आते हैं - एक स्ट्रीम और सीक करने लायक पता, जिसे आप सीधे <video src>, <img src> या <audio src> में डाल सकते हैं, ताकि 300 MB की क्लिप को मेमोरी में समाने की ज़रूरत न पड़े; जब बाइट्स सचमुच चाहिए हों तो f.getFile() File ले आता है। डेटा फ़ाइलें और दस्तावेज़ f.file के साथ आते हैं, साथ में शॉर्टकट के रूप में टेक्स्ट फ़ॉर्मैट के लिए f.text और छोटी बाइनरी के लिए f.base64। कैप्शन के साथ शेयर की गई फ़ोटो का कैप्शन भी शेयर्ड टेक्स्ट के रूप में आता है।

// A link or text someone shared
const shared = JSON.parse(WebToApk.getSharedText() || 'null');
if (shared) saveLink(shared.url || shared.text);
window.addEventListener('appmint:shared', e => saveLink(e.detail.url || e.detail.text));

// A photo, video, audio clip, document or data file — ask on startup, however late
const f = await AppMint.getOpenedFile();   // null if opened normally
if (f) {
  if (f.kind === 'video')         player.src = f.url;                 // streams, seekable
  else if (f.kind === 'image')    img.src = f.url;
  else if (f.kind === 'document') await upload(await f.getFile());   // a real File
  else importActivity(f.text ?? await f.file.text());               // data files
}

// Or listen, if you prefer
window.addEventListener('appmint:fileopen', e => handle(e.detail));

10ब्लूटूथ सेंसर - मानक Web Bluetooth#

हार्ट-रेट स्ट्रैप, कैडेंस और स्पीड पॉड, पावर मीटर, स्मार्ट ट्रेनर, वज़न की मशीनें - ब्लूटूथ LE बोलने वाली कोई भी चीज़।

navigator.bluetooth इस्तेमाल करें, ठीक वही कोड जो आप डेस्कटॉप पर Chrome के लिए लिखते। AppMint इसे ऐप के अंदर देता है; सामान्य Android WebView में यह नहीं होता, इसीलिए यह कोड दूसरे ऐप बिल्डर्स में कुछ नहीं करता।

डिवाइस चुनने वाली स्क्रीन ऐप बनाता है, ब्राउज़र की तरह - आपका पेज सिर्फ़ उसी डिवाइस तक पहुँच सकता है जिसे यूज़र ने चुना।

चरण 4 (Access) में Bluetooth अनुमति चालू करें। अगर आपके कोड में navigator.bluetooth है, तो ZIP चुनते समय AppMint इसे अपने आप चालू कर देता है।

उपलब्ध नहीं: getDevices(), watchAdvertisements() और requestLEScan() - ये NotSupportedError के साथ रिजेक्ट हो जाते हैं, इसलिए इस्तेमाल से पहले जाँच लें।

const device = await navigator.bluetooth.requestDevice({
  filters: [{ services: ['heart_rate'] }]
});
const server  = await device.gatt.connect();
const service = await server.getPrimaryService('heart_rate');
const chr     = await service.getCharacteristic('heart_rate_measurement');

chr.addEventListener('characteristicvaluechanged', e => {
  const v = e.target.value;   // a DataView
  const bpm = (v.getUint8(0) & 1) ? v.getUint16(1, true) : v.getUint8(1);
  document.getElementById('bpm').textContent = bpm;
});
await chr.startNotifications();

device.addEventListener('gattserverdisconnected', () => showReconnect());

11फ़ोटो अपलोड - Android फ़ोटो पिकर, बिना अनुमति के#

सामान्य फ़ाइल इनपुट इस्तेमाल करें। और कुछ जोड़ना नहीं है।

ऐप में <input type="file" accept="image/*"> Android फ़ोटो पिकर खोलता है: यूज़र की फ़ोटो ग्रिड में, और आपके पेज को सिर्फ़ वही मिलती हैं जो वे चुनते हैं। न स्टोरेज अनुमति, न 'Allow access to photos?' पॉपअप। यह पुराने फ़ोन पर भी चलता है - वहाँ Google Play services पिकर इंस्टॉल कर देता है।

  • accept="image/*" - सिर्फ़ फ़ोटो
  • accept="video/*" - सिर्फ़ वीडियो
  • accept="image/*,video/*" - दोनों
  • multiple जोड़ें - यूज़र कई चुन सकता है
  • capture="environment" जोड़ें - इसके बजाय कैमरा खुलता है (चरण 4 (Access) में Camera पर टिक होना चाहिए)

दूसरी फ़ाइलें भी जोड़ दें (accept="image/*,.pdf") तो सामान्य फ़ाइल पिकर खुलता है, ताकि PDF भी चुनी जा सके।

<input type="file" id="photos" accept="image/*" multiple>

<script>
document.getElementById('photos').onchange = function (e) {
  for (const file of e.target.files) {
    const img = document.createElement('img');
    img.src = URL.createObjectURL(file);
    document.body.appendChild(img);
  }
};
</script>

12QR कोड या बारकोड स्कैन करें - सिर्फ़ एक लाइन#

AppMint.scanCode() Google की स्कैनर स्क्रीन खोलता है। यूज़र कैमरा घुमाता है, और आपको कोड वापस मिल जाता है।

कैमरा अनुमति नहीं चाहिए। कोई वीडियो एलिमेंट नहीं। कुछ बनाने की ज़रूरत नहीं। छोटे कोड के लिए स्कैनर खुद ज़ूम कर लेता है। किसी फ़ोन पर पहली बार Google Play services स्कैनर डाउनलोड करता है (कुछ सेकंड)।

जवाब में value (कोड में मौजूद टेक्स्ट) और format (qr_code, ean_13, …) होते हैं। आम QR कोड के लिए आपको तैयार हिस्से भी मिलते हैं: url, wifi (ssid, password), phone, email, sms, geo, contact।

अगर ok false है, तो code वजह बताता है: cancelled (यूज़र ने बंद कर दिया), unavailable (Google Play services नहीं है, या डाउनलोड फ़ेल हुआ), busy (स्कैन पहले से खुला है)।

AppMint.scanCode सिर्फ़ ऐप के अंदर मौजूद होता है - पहले इसे जाँच लें।

async function scan() {
  if (!window.AppMint || !AppMint.scanCode) return;   // in a browser

  const r = await AppMint.scanCode({ formats: ['qr_code'] });
  if (!r.ok) return;                                  // closed, or no scanner

  console.log(r.value);          // the text inside the code
  if (r.url) location.href = r.url.url;
}

// Any code type: AppMint.scanCode()
// Let the user type it when it will not scan: AppMint.scanCode({ manualInput: true })

13पासकी - फ़िंगरप्रिंट या चेहरे से साइन इन, बिना पासवर्ड#

अगर आपकी वेबसाइट में पहले से पासकी हैं, तो वे अब आपके ऐप के अंदर भी चलती हैं। सामान्य Android WebView पासकी नहीं चला सकता; AppMint navigator.credentials को Android के पासकी मैनेजर से जोड़ता है, इसलिए SimpleWebAuthn, Hanko, Corbado या Auth0 जैसी लाइब्रेरी बिना बदलाव के चलती हैं।

आपको एक सर्वर चाहिए जो पासकी के विकल्प बनाए और जवाब की जाँच करे (जैसे आपके बैकएंड पर SimpleWebAuthn)। पासकी की जाँच पेज के अंदर नहीं हो सकती।

दो चीज़ें एक बार सेट करनी हैं:

  1. आपकी वेबसाइट को बताना होगा कि वह ऐप पर भरोसा करती है। AppMint.passkey.assetLinks() से मिली फ़ाइल को https://YOUR-DOMAIN/.well-known/assetlinks.json पर रखें। इसे Google Play से इंस्टॉल किए ऐप से लें - Play आपके ऐप को अपनी कुंजी से साइन करता है, इसलिए टेस्ट APK की फ़ाइल Play यूज़र्स से मेल नहीं खाएगी। डोमेन ठीक वही होना चाहिए जो आपके सर्वर का rpID है (www और बिना www अलग माने जाते हैं)।
  1. आपके सर्वर को ऐप का ऑरिजिन स्वीकार करना होगा। ऐप से मिले जवाब में https://… की जगह origin 'android:apk-key-hash:…' लिखा होता है - वह मान AppMint.passkey.origin() से लें और अपने सर्वर के अनुमत ऑरिजिन में जोड़ें।

त्रुटियाँ: NotAllowedError = यूज़र ने रद्द किया या इस फ़ोन पर कोई पासकी नहीं है। InvalidStateError = यह पासकी पहले से मौजूद है। SecurityError = assetlinks.json में ऐप की सूची नहीं है।

पासकी के लिए Google Play services वाला Android 9 या नया वर्ज़न चाहिए - AppMint.passkey.isAvailable() बताता है। टेक्स्ट फ़ील्ड में पासकी ऑटोफ़िल (mediation: 'conditional') ऐप्स में उपलब्ध नहीं है; साइन इन किसी बटन से शुरू करें।

// Your normal passkey code works as it is:
const cred = await navigator.credentials.get({ publicKey: options });

// Or with your server's JSON options (SimpleWebAuthn style):
const options = await (await fetch('/passkey/login-options')).json();
const answer = await AppMint.passkey.get(options);
await fetch('/passkey/login-verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(answer)
});

// Once, on the Play-installed app — copy these to your server setup:
console.log(JSON.stringify(AppMint.passkey.assetLinks(), null, 2));
console.log(AppMint.passkey.origin());

14होम-स्क्रीन विजेट - अपना डेटा होम स्क्रीन पर दिखाएँ#

आपके ऐप में एक होम-स्क्रीन विजेट है। यूज़र इसे फ़ोन की विजेट सूची से जोड़ता है (होम स्क्रीन को दबाए रखें, फिर Widgets), या आप AppMint.widget.requestPin() से Add शीट खोल सकते हैं।

जब तक आपका पेज उसे नहीं भरता, विजेट खाली रहता है ("Nothing to show yet")। यह एक छोटा Android कार्ड है, वेब पेज नहीं, इसलिए आपका HTML नहीं दिखा सकता। आप उसे एक शीर्षक, एक बड़ा मान, छह तक छोटी पंक्तियाँ और रंग देते हैं।

ऐप खुलते ही AppMint.widget.set() कॉल करें, और हर बार तब भी जब उसका दिखाया डेटा बदले। रीस्टार्ट के बाद भी विजेट आख़िरी कार्ड रखता है। route वह जगह है जहाँ विजेट पर टैप करने से आपका ऐप खुलता है।

function updateWidget() {
  if (!window.AppMint || !AppMint.widget) return;   // in a browser

  AppMint.widget.set({
    title: 'Today',
    value: '3 events',
    lines: ['09:00 Dentist', '13:00 Lunch with Sam', '18:30 Gym'],
    route: '/',
    bg: '#0E7C86', ink: '#FFFFFF', accent: '#FFE08A'
  });
}

updateWidget();   // on start-up, and after every change

// An "Add to home screen" button:
async function addWidget() {
  if ((await AppMint.widget.placedCount()) > 0) return;
  await AppMint.widget.requestPin();
}

15कॉपी सुरक्षा और लंबे दबाव वाला मेन्यू#

चरण 3 (Integrate) में Display Options के नीचे दो स्विच हैं। दोनों तब तक बंद रहते हैं जब तक आप उन्हें चालू न करें।

  • लिंक और इमेज पर लंबे दबाव वाला मेन्यू: किसी लिंक या तस्वीर को दबाए रखने पर उसे खोल, कॉपी, शेयर या सेव कर सकते हैं। यह डिफ़ॉल्ट रूप से बंद रहता है, इसलिए लिंक पर लंबा दबाव कुछ नहीं करता। पेज चाहे तो contextmenu इवेंट रद्द करके किसी एक एलिमेंट पर मेन्यू को मना कर सकता है।
  • कॉपी सुरक्षा: कोई भी आपके ऐप का टेक्स्ट चुन, कॉपी या कट नहीं कर सकता। फ़ॉर्म में टाइप करना फिर भी चलता है। यह लंबे दबाव वाला मेन्यू भी बंद रखता है।

आपका पेज बता सकता है: सुरक्षा चालू होने पर AppMint.copyProtection true होता है, और बंद होने पर (और ब्राउज़र में) मौजूद नहीं होता। यह सिर्फ़ पढ़ने के लिए है। सुरक्षा चालू होने पर अपने Copy बटन छिपा दें, क्योंकि वे कुछ कॉपी नहीं करेंगे।

const protectedApp = !!(window.AppMint && window.AppMint.copyProtection === true);

// A Copy button would copy nothing, so hide it
document.querySelectorAll('[data-copy-button]').forEach(btn => {
  btn.hidden = protectedApp;
});

16{exampleCount} तैयार उदाहरण - यहीं खोलें#

कॉपी करने लायक चलता हुआ कोड: SMS भेजना, कॉन्टैक्ट पढ़ना, तस्वीर वाला नोटिफ़िकेशन दिखाना, फ़िंगरप्रिंट लॉक, फ़ोन की जानकारी, फ़ाइलें सेव करना, ब्लूटूथ सेंसर, और बहुत कुछ। हर एक छोटा है और आसान शब्दों में समझाया गया है।

यह AppMint के अंदर खुलता है। आप इसमें खोज सकते हैं, और हर कोड ब्लॉक में Copy बटन है।

उसी स्क्रीन पर 'All methods' भी है - आपका ऐप जो भी {methodCount} चीज़ें कॉल कर सकता है, और हर एक के साथ कॉपी करने लायक चलता हुआ उदाहरण। यह सूची ऐप रनटाइम से ही बनती है, इसलिए यह हमेशा ठीक वही दिखाती है जो आपका ऐप कर सकता है।

17उदाहरण अपने फ़ोन या कंप्यूटर में सेव करें#

तीन फ़ाइलें Downloads/AppMint/Guides में सेव करता है:

  • उदाहरण - ऊपर के {exampleCount} उदाहरण
  • मेथड सूची - सभी {methodCount} मेथड
  • index.html - एक तैयार टेस्ट पेज

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

ऐप में, इस चरण का बटन इन्हें आपके फ़ोन में सेव करता है। यहाँ ये सीधे डाउनलोड होते हैं।

AppMint-examples.md MD · 49 KB AppMint-all-methods.md MD · 1.4 MB index.html HTML · 6 KB

18लॉक स्क्रीन के ऊपर बजने वाली अलार्म घड़ी#

Access चरण में Alarm clock पर टिक करें। alarm: true के साथ scheduleNotificationEx आपकी चुनी आवाज़ के साथ तब तक बजता है जब तक यूज़र उसे बंद या स्नूज़ न कर दे, ऐप बंद होने पर भी। दोहराव रोज़, साप्ताहिक, कार्यदिवस, मासिक या वार्षिक हो सकता है, या हफ़्ते के दिन चुनें।

शेड्यूल करने से पहले getAlarmSupport() से पूछें। यह 'ready', 'not_enabled', 'needs_exact_alarm' या 'needs_notification_permission' जवाब देता है। फिर stopAlarm(id), snoozeAlarm(id, minutes) और isAlarmRinging(id) बजते हुए अलार्म को नियंत्रित करते हैं।

const B = window.WebToApk;
if (B && B.getAlarmSupport && B.getAlarmSupport() === 'ready') {
  B.scheduleNotificationEx('wake', JSON.stringify({
    at: firstAtMs, alarm: true, title: 'Wake up', repeat: 'weekdays', sound: '/audio/alarm.mp3'
  }));
}
document.getElementById('snooze').onclick = () => B.snoozeAlarm('wake', 10);

19सिस्टम वॉल्यूम#

getVolume(stream) मीडिया, अलार्म, रिंग, नोटिफ़िकेशन या कॉल वॉल्यूम JSON में पढ़ता है। setVolume(stream, percent, showUi) उसे बदलता है। watchVolume(true) हर बदलाव पर appmint:volume इवेंट भेजता है। रिंग वॉल्यूम को चुप कराने के लिए Do Not Disturb एक्सेस चाहिए: requestDndAccess()।

if (window.WebToApk && WebToApk.getVolume) {
  const v = JSON.parse(WebToApk.getVolume('alarm'));
  if (v.percent === 0) alert('Alarm volume is off');
  window.addEventListener('appmint:volume', (e) => console.log(e.detail.stream, e.detail.percent));
  WebToApk.watchVolume(true);
}

20बैकग्राउंड सर्विस, बैकग्राउंड लोकेशन, जियोफ़ेंस और टास्क#

startBackgroundService यूज़र के दूर रहने पर भी आपका पेज, ऑडियो और टाइमर एक नोटिफ़िकेशन के साथ चालू रखता है। requestBackgroundLocation 'Allow all the time' माँगता है। addGeofence ऐप बंद होने पर भी enter, exit या dwell चलाता है। AppMint.background.register आपका अपना पेज कोड हर 15 मिनट या ज़्यादा पर चलाता है। ये सब फ़ोन रीस्टार्ट के बाद फिर शुरू हो जाते हैं।

इनके लिए विज़ार्ड (Access चरण) में उनका स्विच चाहिए; उसके बिना कॉल { ok: false, reason: 'not_enabled' } का जवाब देता है।

const r = JSON.parse(WebToApk.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Keeping data fresh' })));
if (!r.ok) console.warn(r.reason);
if (AppMint.background.isBackgroundRun()) {
  window.addEventListener('appmint:background', async (e) => {
    await refreshData();
    AppMint.background.done(e.detail.runId, { ok: true });
  });
} else {
  AppMint.background.register('refresh', { intervalMinutes: 60 });
}

21क्रैश रिपोर्ट#

Integrate चरण में Crash reports पर टिक करें। अनकॉट त्रुटियाँ आपके लिए रिकॉर्ड हो जाती हैं। logError(message, stack) उस त्रुटि को दर्ज करता है जिसे आपने पकड़ा, setCrashUserId(id) रिपोर्ट पर आपकी अपनी यूज़र id लगाता है (कभी ईमेल नहीं), और getLastCrash() सबसे नया क्रैश JSON में लौटाता है, या खाली स्ट्रिंग। रिपोर्ट Update the App में, फिर अपने ऐप में, फिर Crash reports में पढ़ें।

if (window.WebToApk && WebToApk.logError) {
  try { risky(); } catch (err) { WebToApk.logError(err.message, err.stack || ''); }
  WebToApk.setCrashUserId('u_42');
  const last = WebToApk.getLastCrash();   // '' when none
}

22डिवाइस इंटेग्रिटी और Play Integrity#

Integrate चरण में Device integrity checks पर टिक करें। getDeviceIntegrity() रूट, एमुलेटर और डीबगर के संकेत JSON में लौटाता है; यह एक चेतावनी है, सबूत नहीं। भरोसेमंद फ़ैसले के लिए requestIntegrityToken(nonce, '') कॉल करें और appmint:integrity इवेंट से मिला टोकन अपने सर्वर पर भेजें, जो Google Play से फ़ैसला माँगता है।

const d = JSON.parse(WebToApk.getDeviceIntegrity());
if (d.rooted || d.emulator) alert('This phone is modified');
window.addEventListener('appmint:integrity', (e) => sendToServer(e.detail.token));
WebToApk.requestIntegrityToken(serverNonceBase64, '');

23वैकल्पिक ऐप आइकन और TV#

Basic चरण में 4 तक वैकल्पिक आइकन जोड़ें। setAppIcon(name) लॉन्चर आइकन बदलता है ('default' वापस पुराने पर ले जाता है), getAppIcon() मौजूदा का नाम बताता है और getAvailableIcons() सभी की सूची देता है। isTv() बताता है कि ऐप टेलीविज़न पर है, ताकि आप रिमोट के अनुकूल लेआउट दिखा सकें।

if (window.WebToApk && WebToApk.setAppIcon) WebToApk.setAppIcon('dark');
if (window.WebToApk && WebToApk.isTv && WebToApk.isTv()) {
  document.documentElement.classList.add('tv');
}

24Facebook साइन-इन, एनालिटिक्स और RevenueCat#

Facebook साइन-इन सपोर्ट किया जाता है। Integrate चरण में अपना Facebook App ID और Client Token डालें; लॉगिन ब्राउज़र टैब में खुलता है और ऐप में लौट आता है। AppMint.facebook.login(['public_profile', 'email']) कॉल करें और टोकन अपने सर्वर को भेजें। AppMint.analytics (Firebase) और AppMint.facebook.logEvent (Meta App Events) यूज़र के सहमत होने के बाद ही रिकॉर्ड करते हैं। AppMint.revenuecat आपकी सार्वजनिक Android कुंजी के साथ सब्सक्रिप्शन संभालता है।

const r = await AppMint.facebook.login(['public_profile', 'email']);
if (r.ok) await fetch('/api/login/facebook', { method: 'POST', body: JSON.stringify({ token: r.accessToken }) });
AppMint.analytics.log('share', { method: 'whatsapp' });
const offerings = await AppMint.revenuecat.getOfferings();

25फ़ोटो, PDF और विजेट से टेक्स्ट#

AppMint.textRecognition.recognize(image, { script }) लैटिन, देवनागरी, चीनी, जापानी या कोरियाई में लिखा टेक्स्ट पढ़ता है। AppMint.pdf.fromPage() और fromHtml(html, options) बिना प्रिंट डायलॉग के असली PDF बनाते हैं। AppMint.widget.set(card) होम-स्क्रीन विजेट भरता है।

const r = await AppMint.textRecognition.recognize(file, { script: 'devanagari' });
const pdf = await AppMint.pdf.fromHtml('<h1>Invoice</h1>', { fileName: 'invoice', save: 'share' });
await AppMint.widget.set({ title: 'Today', value: '3 events', route: '/calendar' });

26हेल्थ डेटा और ब्लूटूथ बीकन#

AppMint.health Health Connect के ज़रिए कदम, हृदय गति, नींद, वज़न और व्यायाम पढ़ता-लिखता है; Access चरण में Health data और उसके प्रकारों पर टिक करें। AppMint.beacons.startScan() iBeacon और Eddystone बीकन खोजता है और appmint:beacon इवेंट भेजता है।

const a = await AppMint.health.isAvailable();
if (a.available) {
  await AppMint.health.requestPermissions({ read: ['steps'] });
  const steps = await AppMint.health.read('steps', { start: new Date(Date.now() - 864e5), end: new Date(), aggregate: true });
}
window.addEventListener('appmint:beacon', (e) => console.log(e.detail));
await AppMint.beacons.startScan();

27तैयार प्लगइन: SQLite, नोटिफ़िकेशन, पुश, स्कैनर, कॉन्टैक्ट#

जिन पेजों को ऐप खुद परोसता है, उन पर window.Capacitor.Plugins में CapacitorSQLite (एक निजी डेटाबेस), LocalNotifications, PushNotifications, CapacitorBarcodeScanner और Contacts होते हैं। पहले जाँचें कि प्लगइन मौजूद है: Website-मोड के पेज को window.Capacitor कभी नहीं मिलता।

const P = window.Capacitor && window.Capacitor.Plugins;
if (P && P.CapacitorBarcodeScanner) {
  const s = await P.CapacitorBarcodeScanner.scanBarcode({ hint: 17 });
  console.log(s.ScanResult);
}
if (P && P.LocalNotifications) await P.LocalNotifications.requestPermissions();

यह पेज ऐप के अंदर की इंटीग्रेशन गाइड से बनाया गया है, इसलिए इसमें वही लिखा है जो मौजूदा बिल्ड में है। इसे Markdown में पढ़ें.

2026-10-03 को शिप की गई गाइड से मिलान किया गया।