API jembatan JavaScript
Telepon, SMS, dan log panggilan
Melakukan panggilan, mengirim atau menyusun pesan, dan membaca riwayat. Semuanya dibatasi izin dan Play menanyakannya.
composeSms jembatan#
window.WebToApk.composeSms(phoneNumber: String, message: String): Boolean
Membuka aplikasi SMS pengguna dengan pesan terisi. Tidak butuh izin dan tidak bisa ditolak Play - pilihan bawaan yang tepat untuk apa pun yang tidak otomatis. Mengembalikan false bila ponsel tidak punya aplikasi SMS (atau tidak bisa dibuka).
Contoh
Membuka aplikasi SMS pengguna dengan nomor dan teks sudah terisi. Pengguna menekan kirim.
Hasil: true bila aplikasi SMS terbuka, false bila ponsel tidak punya aplikasi SMS atau tidak bisa dibuka (sinkron). Membutuhkan: tidak ada - tanpa sakelar build, tanpa izin.
Tombol "Kirim SMS ke kami".
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');
}
});Tombol "Ingatkan" di setiap baris janji temu, yang mengisi pengingat ke nomor pelanggan tersebut:
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.');
}
});Formulir "Kirim lewat SMS": bila ponsel tidak punya aplikasi SMS (false), salin teksnya agar pengguna bisa menempelkannya di tempat lain:
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.');
});
}
});Catatan: Tidak ada yang dikirim sampai pengguna mengetuk kirim di aplikasi SMS mereka sendiri, jadi metode ini sendiri tidak butuh izin SMS. Gunakan ini untuk setiap pesan yang ditulis atau disetujui pengguna; gunakan sendSms hanya bila aplikasi benar-benar harus mengirim sendiri. Untuk aplikasi buatan AI dan impor Google AI Studio, build memindai kode Anda; panggilan composeSms( tidak mencentang SMS (baca & kirim) - hanya sendSms dan listSms - jadi tombol "Kirim SMS ke kami" tidak menambah izin terbatas Play.
listCallLog jembatan#
window.WebToApk.listCallLog(requestId: String, limit: Int, offset: Int, sinceMillis: String)
WebToApk.listCallLog(id, limit, offset, sinceMillis) Dikirim sebagai `appmint:calllog` / window.onAppMintCallLog.
Dijelaskan oleh grupnya, Log panggilan, bukan secara terpisah.
Contoh
Membaca riwayat panggilan ponsel, panggilan terbaru lebih dulu.
Hasil: tidak ada saat ini. Jawabannya adalah event appmint:calllog (dan window.onAppMintCallLog) dengan detail = { requestId, calls: [{ number, name, type, date, duration }] } atau { requestId, error }. date adalah milidetik sejak 1970; duration adalah detik. Membutuhkan: aktifkan Log panggilan (baca) di Langkah 4 (Akses) saat build; pengguna diminta izin pada panggilan pertama.
Tampilkan 50 panggilan terakhir.
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)
}Hanya panggilan 24 jam terakhir, memakai hook halaman. sinceMillis adalah STRING karena angkanya terlalu besar untuk int bridge.
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));
}Layar riwayat panggilan dengan tombol "Muat lebih banyak", mengambil 25 panggilan sekali waktu dengan 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();Catatan: limit dibatasi ke 1-2000. Kesalahan: not_enabled, permission_denied, foreign_origin. Log panggilan adalah izin terbatas Google Play: Anda harus mengajukan Deklarasi Izin yang disetujui di Play Console, atau aplikasi ditolak.
listSms jembatan#
window.WebToApk.listSms(requestId: String, box: String, limit: Int, offset: Int)
WebToApk.composeSms(number, message) <- TANPA izin, membuka aplikasi SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Hasil pengiriman: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Pesan masuk: `appmint:sms-received` / window.onAppMintSmsReceived.
Dijelaskan oleh grupnya, SMS, bukan secara terpisah.
Contoh
Membaca pesan SMS dari kotak masuk, kotak terkirim, atau draf ponsel, terbaru lebih dulu. File ini juga menunjukkan cara menerima pesan baru saat tiba.
Hasil: tidak ada saat ini. Jawabannya adalah event appmint:sms (dan window.onAppMintSms) dengan detail = { requestId, messages: [{ id, address, body, date, read, box }] }. Jika gagal: { requestId, status: 'failed', ok: false, reason }. Membutuhkan: aktifkan SMS (baca & kirim) di Langkah 4 (Akses) saat build; pengguna diminta izin pada panggilan pertama.
Baca 30 pesan kotak masuk terbaru.
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'
}Pesan baru saat tiba (appmint:sms-received, juga window.onAppMintSmsReceived). Pesan yang tiba saat aplikasi tertutup disimpan (50 terakhir) dan diberikan ke halaman Anda saat aplikasi dibuka lagi.
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);
};Pelacak pengeluaran yang memindai 200 pesan kotak masuk terakhir untuk debit bank dan menjumlahkan nominalnya:
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);
}Catatan: limit dibatasi ke 1-500. Nama kotak yang tidak dikenal membaca kotak masuk. Nilai reason: not_enabled, permission_denied, foreign_origin. Pesan yang diterima tetap sampai juga ke aplikasi SMS normal pengguna. SMS adalah izin terbatas Google Play: Anda harus mengajukan Deklarasi Izin yang disetujui di Play Console (biasanya hanya aplikasi SMS default yang memenuhi syarat), atau aplikasi ditolak.
makePhoneCall jembatan#
window.WebToApk.makePhoneCall(phoneNumber: String)
Menelepon [phoneNumber]. - Izin Telepon AKTIF di build: langsung melakukan panggilan. Pertama kali, Android meminta izin Telepon kepada pengguna; jika ditolak, dialer terbuka dengan nomor terisi agar mereka tetap bisa menelepon sendiri. - Izin Telepon NONAKTIF: membuka dialer dengan nomor terisi (dialer tidak butuh izin) dan pengguna mengetuk Telepon. - Halaman yang bukan milik aplikasi (situs eksternal yang dibuka di aplikasi) selalu mendapat dialer, tidak pernah panggilan langsung. Tautan `tel:` biasa di HTML Anda juga membuka dialer dan sering sudah cukup.
Contoh
Memulai panggilan telepon ke sebuah nomor, atau membuka dialer dengan nomor terisi.
Hasil: tidak ada. Membutuhkan: tidak ada untuk membuka dialer. Aktifkan Telepon di Langkah 4 (Akses) saat build bila panggilan harus langsung dimulai.
Tombol "Telepon toko".
document.getElementById('callShop').addEventListener('click', function () {
if (window.WebToApk && window.WebToApk.makePhoneCall) {
WebToApk.makePhoneCall('+911234567890');
}
});Tidak perlu sakelar: tautan tel: biasa membuka dialer dengan nomornya, di aplikasi maupun di browser. Ini sering sudah cukup.
<a href="tel:+911234567890">Call the shop</a>Satu handler untuk seluruh direktori staf, di mana setiap baris membawa nomornya di atribut 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
}
});Catatan: Dengan Telepon nonaktif, dialer terbuka dengan nomor dan pengguna menekan panggil. Dengan Telepon aktif, panggilan langsung dimulai; pertama kali, Android meminta izin panggilan telepon kepada pengguna, dan jika ditolak, dialer terbuka agar mereka tetap bisa menelepon. Halaman yang bukan milik aplikasi (situs eksternal yang dibuka di dalam aplikasi) selalu mendapat dialer. Berikan nomor sebagai string biasa. Tidak ada yang dilaporkan kembali - Anda tidak bisa tahu apakah panggilan dilakukan. Sakelar Telepon menambahkan izin CALL_PHONE ke listing Play Anda; nyatakan di formulir Keamanan data dan aktifkan hanya bila Anda membutuhkannya.
sendSms jembatan#
window.WebToApk.sendSms(
WebToApk.composeSms(number, message) <- TANPA izin, membuka aplikasi SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Hasil pengiriman: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Pesan masuk: `appmint:sms-received` / window.onAppMintSmsReceived.
Dijelaskan oleh grupnya, SMS, bukan secara terpisah.
Contoh
Mengirim SMS langsung dari aplikasi, tanpa membuka aplikasi SMS, dan melaporkan apa yang sungguh terjadi.
Hasil: tidak ada saat ini. Jawabannya adalah event appmint:sms (dan window.onAppMintSms) dengan detail = { requestId, status: 'sent' | 'delivered' | 'failed', ok, reason? }. Dengan laporan pengiriman Anda mendapat DUA jawaban saat berhasil: sent, lalu delivered. Membutuhkan: aktifkan SMS (baca & kirim) di Langkah 4 (Akses) saat build; pengguna diminta izin pada panggilan pertama.
Kirim kode dan ikuti statusnya. Jangan hapus listener setelah jawaban pertama bila Anda meminta laporan pengiriman.
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
}Tanpa laporan pengiriman. Berikan false sebagai nilai terakhir: Anda mendapat tepat satu jawaban, sent atau 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);
}Pemberitahuan yang sama ke daftar pendek nomor, satu per satu, mengumpulkan setiap hasil (dan berhenti saat SMS nonaktif atau ditolak, karena semua nomor lain akan gagal dengan cara yang sama):
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');
});Catatan: Teks panjang dipecah menjadi beberapa bagian dan dikirim sebagai satu pesan. Jika laporan pengiriman tidak tiba dalam 60 detik, Anda mendapat status: 'sent' dengan reason: 'no_delivery_report' - pesan tetap terkirim. Nilai reason: not_enabled, permission_denied, foreign_origin, invalid_arguments (nomor atau teks kosong), no_service, radio_off, generic_failure, null_pdu, not_delivered, dan lainnya. Gunakan requestId berupa string. Setiap SMS bisa membebani pengguna dengan biaya. SMS adalah izin terbatas Google Play: Anda harus mengajukan Deklarasi Izin yang disetujui di Play Console (biasanya hanya aplikasi SMS default yang memenuhi syarat), jadi pilih composeSms bila pengguna bisa menekan kirim.