API پل جاوااسکریپت
تلفن، پیامک و سابقهی تماس
برقراری تماس، ارسال یا پیشنویس پیام و خواندن تاریخچه. همهی اینها مجوزدارند و Play دربارهشان میپرسد.
composeSms پل#
window.WebToApk.composeSms(phoneNumber: String, message: String): Boolean
اپلیکیشن پیامک کاربر را با پیام از پیش پر شده باز میکند. به مجوز نیاز ندارد و Play نمیتواند ردش کند - پیشفرض درست برای هر چیز غیرخودکار. وقتی گوشی اپلیکیشن پیامک ندارد (یا باز نشد) false برمیگرداند.
نمونه
اپلیکیشن پیامک کاربر را با شماره و متنِ از پیش پر شده باز میکند. کاربر ارسال را میزند.
بازگشتی: true وقتی اپلیکیشن پیامک باز شد، false وقتی گوشی اپلیکیشن پیامک ندارد یا باز نشد (همزمان). نیازمند: هیچ چیز - بدون کلید ساخت، بدون مجوز.
یک دکمهی «برایمان پیامک بفرستید».
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');
}
});یک دکمهی «یادآوری» روی هر ردیف قرار ملاقات، که یادآوری را برای شمارهی آن مشتری از پیش پر میکند:
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.');
}
});یک فرم «ارسال با پیامک»: وقتی گوشی اپلیکیشن پیامک ندارد (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.');
});
}
});نکتهها: تا وقتی کاربر در اپلیکیشن پیامک خودش ارسال را نزند چیزی فرستاده نمیشود، پس خود این متد به مجوز پیامک نیاز ندارد. از این برای هر پیامی که کاربر مینویسد یا تأیید میکند استفاده کنید؛ sendSms را فقط وقتی استفاده کنید که اپلیکیشن واقعاً باید خودش بفرستد. برای اپلیکیشنهای ساختهشده با هوش مصنوعی و واردشده از Google AI Studio، ساخت کد شما را اسکن میکند؛ فراخوانی composeSms( گزینهی پیامک (خواندن و ارسال) را علامت نمیزند - فقط sendSms و listSms میزنند - پس دکمهی «برایمان پیامک بفرستید» مجوز محدودشدهی 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 میلیثانیه از سال ۱۹۷۰ است؛ duration ثانیه است. نیازمند: هنگام ساخت در مرحلهی ۴ (دسترسی) سابقهی تماس (خواندن) را روشن کنید؛ بار اول از کاربر مجوز پرسیده میشود.
۵۰ تماس آخر را نشان دهید.
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)
}فقط تماسهای ۲۴ ساعت اخیر، با قلاب صفحه. 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));
}یک صفحهی سابقهی تماس با دکمهی «بارگذاری بیشتر» که هر بار ۲۵ تماس میگیرد، با 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 به ۱ تا ۲۰۰۰ محدود میشود. خطاها: not_enabled، permission_denied، foreign_origin. سابقهی تماس یک مجوز محدودشدهی Google Play است: باید در Play Console یک اعلامیهی مجوزهای تأییدشده ثبت کنید، وگرنه اپلیکیشن رد میشود.
listSms پل#
window.WebToApk.listSms(requestId: String, box: String, limit: Int, offset: Int)
WebToApk.composeSms(number, message) <- بدون مجوز، اپلیکیشن پیامک را باز میکند 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.
بهجای خودش، با گروهش پیامک توضیح داده میشود.
نمونه
پیامهای پیامکی را از صندوق ورودی، ارسالی یا پیشنویسهای گوشی میخواند، تازهترین اول. این فایل همچنین نشان میدهد چطور پیامهای جدید را همزمان با رسیدن دریافت کنید.
بازگشتی: فعلاً هیچ. پاسخ رویداد appmint:sms است (و window.onAppMintSms) با detail = { requestId, messages: [{ id, address, body, date, read, box }] }. در شکست: { requestId, status: 'failed', ok: false, reason }. نیازمند: هنگام ساخت در مرحلهی ۴ (دسترسی) پیامک (خواندن و ارسال) را روشن کنید؛ بار اول از کاربر مجوز پرسیده میشود.
۳۰ پیام آخر صندوق ورودی را بخوانید.
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). پیامهایی که وقتی اپلیکیشن بسته است میرسند نگه داشته میشوند (۵۰ تای آخر) و وقتی اپلیکیشن دوباره باز شد به صفحهی شما داده میشوند.
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);
};یک ردیاب هزینه که ۲۰۰ پیام آخر صندوق ورودی را برای برداشتهای بانکی اسکن میکند و مبلغها را جمع میزند:
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 به ۱ تا ۵۰۰ محدود میشود. هر نام صندوق ناشناخته صندوق ورودی را میخواند. مقادیر reason: not_enabled، permission_denied، foreign_origin. پیامهای دریافتی همچنان به اپلیکیشن پیامک عادی کاربر هم میرسند. پیامک یک مجوز محدودشدهی Google Play است: باید در Play Console یک اعلامیهی مجوزهای تأییدشده ثبت کنید (معمولاً فقط اپلیکیشنهای پیامک پیشفرض واجد شرایطند)، وگرنه اپلیکیشن رد میشود.
makePhoneCall پل#
window.WebToApk.makePhoneCall(phoneNumber: String)
با [phoneNumber] تماس میگیرد. - مجوز تلفن در ساخت روشن باشد: تماس را مستقیم برقرار میکند. بار اول اندروید از کاربر مجوز تلفن را میپرسد؛ اگر رد کند، شمارهگیر با شمارهی پر شده باز میشود تا خودش بتواند تماس بگیرد. - مجوز تلفن خاموش باشد: شمارهگیر را با شمارهی پر شده باز میکند (شمارهگیر به مجوز نیاز ندارد) و کاربر «تماس» را میزند. - صفحهای که صفحهی خود اپلیکیشن نیست (سایت خارجی باز شده در اپلیکیشن) همیشه شمارهگیر را میگیرد، هرگز تماس مستقیم. یک پیوند سادهی `tel:` در HTML شما هم شمارهگیر را باز میکند و اغلب کافی است.
نمونه
یک تماس تلفنی با یک شماره شروع میکند، یا شمارهگیر را با شمارهی پر شده باز میکند.
بازگشتی: هیچ. نیازمند: برای باز کردن شمارهگیر هیچ چیز. اگر تماس باید مستقیم شروع شود، هنگام ساخت در مرحلهی ۴ (دسترسی) تلفن را روشن کنید.
یک دکمهی «با فروشگاه تماس بگیرید».
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
}
});نکتهها: وقتی تلفن خاموش است، شمارهگیر با شماره باز میشود و کاربر تماس را میزند. وقتی تلفن روشن است، تماس مستقیم شروع میشود؛ بار اول اندروید از کاربر مجوز تماس تلفنی را میپرسد و اگر رد کند، بهجایش شمارهگیر باز میشود تا بتواند تماس بگیرد. صفحهای که صفحهی خود اپلیکیشن نیست (سایت خارجی باز شده داخل اپلیکیشن) همیشه شمارهگیر را میگیرد. شماره را بهصورت رشتهی ساده بدهید. چیزی گزارش نمیشود - نمیتوانید بفهمید تماس برقرار شد یا نه. کلید تلفن مجوز CALL_PHONE را به فهرست Play شما اضافه میکند؛ آن را در فرم ایمنی دادهها اعلام کنید و فقط در صورت نیاز روشنش کنید.
sendSms پل#
window.WebToApk.sendSms(
WebToApk.composeSms(number, message) <- بدون مجوز، اپلیکیشن پیامک را باز میکند 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.
بهجای خودش، با گروهش پیامک توضیح داده میشود.
نمونه
یک پیامک را مستقیم از اپلیکیشن، بدون باز کردن اپلیکیشن پیامک، میفرستد و آنچه واقعاً اتفاق افتاد را گزارش میدهد.
بازگشتی: فعلاً هیچ. پاسخ رویداد appmint:sms است (و window.onAppMintSms) با detail = { requestId, status: 'sent' | 'delivered' | 'failed', ok, reason? }. با گزارش تحویل در موفقیت دو پاسخ میگیرید: sent، سپس delivered. نیازمند: هنگام ساخت در مرحلهی ۴ (دسترسی) پیامک (خواندن و ارسال) را روشن کنید؛ بار اول از کاربر مجوز پرسیده میشود.
یک کد بفرستید و وضعیتش را دنبال کنید. وقتی گزارش تحویل خواستهاید، شنونده را بعد از اولین پاسخ برندارید.
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);
}همین اطلاعیه را به فهرست کوتاهی از شمارهها یکییکی بفرستید و نتیجهی هر کدام را جمع کنید (و وقتی پیامک خاموش یا رد شده متوقف شوید، چون هر شمارهی دیگر هم به همین ترتیب شکست میخورد):
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');
});نکتهها: متن بلند به چند بخش تقسیم و بهصورت یک پیام فرستاده میشود. اگر ظرف ۶۰ ثانیه گزارش تحویل نرسد status: 'sent' با reason: 'no_delivery_report' میگیرید - پیام همچنان فرستاده شده بود. مقادیر reason: not_enabled، permission_denied، foreign_origin، invalid_arguments (شماره یا متن خالی)، no_service، radio_off، generic_failure، null_pdu، not_delivered و موارد دیگر. از یک requestId رشتهای استفاده کنید. هر پیامک ممکن است برای کاربر هزینه داشته باشد. پیامک یک مجوز محدودشدهی Google Play است: باید در Play Console یک اعلامیهی مجوزهای تأییدشده ثبت کنید (معمولاً فقط اپلیکیشنهای پیامک پیشفرض واجد شرایطند)، پس وقتی کاربر میتواند خودش ارسال را بزند composeSms را ترجیح دهید.