Buat APK Gratis

API jembatan JavaScript

Kontak

Membaca, mencari, memilih, dan menulis - pemilih sama sekali tidak memerlukan izin.

navigator.contacts standar web#

navigator.contacts.select(properties, options) — Contact Picker

Contoh

Contact Picker API standar: pemilih kontak bawaan ponsel terbuka, dan halaman hanya menerima orang yang dipilih pengguna - tanpa izin kontak.

Mengembalikan: Promise berisi array: [{ name: [string], tel: [string] }], atau [] bila pengguna menutup pemilih. Membutuhkan: tidak ada yang perlu diaktifkan; panggil dari sebuah ketukan (gestur pengguna diperlukan, jika tidak SecurityError).

document.getElementById('pick').addEventListener('click', async () => {
  if (!('contacts' in navigator)) { showManualEntry(); return; }
  const props = await navigator.contacts.getProperties();   // ['name', 'tel']
  const [person] = await navigator.contacts.select(props, { multiple: false });
  if (!person) return;                                       // closed without choosing
  document.getElementById('name').value = person.name[0] || '';
  document.getElementById('phone').value = person.tel[0] || '';
});

Isi kotak "Kirim ke" hanya dengan nomornya, dan tampilkan pesan untuk setiap cara pemilih bisa menolak:

document.getElementById('choose-number').addEventListener('click', async () => {
  if (!('contacts' in navigator)) return;
  try {
    const [person] = await navigator.contacts.select(['tel']);
    if (person && person.tel.length) document.getElementById('to').value = person.tel[0];
  } catch (err) {
    if (err.name === 'InvalidStateError') showMessage('The contact picker is already open or not available.');
    else if (err.name === 'SecurityError') showMessage('Tap the button to choose a contact.');
    else showMessage('Could not pick a contact: ' + err.message);
  }
});

Susun daftar tamu satu ketukan demi satu ketukan (satu orang per pilihan), lewati nomor yang sudah ada di daftar:

const guests = [];

document.getElementById('add-guest').addEventListener('click', async () => {
  if (!('contacts' in navigator)) { showManualEntry(); return; }
  const [person] = await navigator.contacts.select(['name', 'tel']);
  if (!person || !person.tel.length) return;   // closed, or no number

  const number = person.tel[0];
  if (guests.some((g) => g.number === number)) { showMessage('Already invited'); return; }
  guests.push({ name: person.name[0] || number, number });
  renderGuests(guests);
});

Catatan: aplikasi mendukung name dan tel - nomor yang diketuk pengguna. email, address dan icon memerlukan izin kontak penuh, jadi tidak ditawarkan dan memintanya akan ditolak dengan TypeError. Satu orang per pilihan (multiple diperbolehkan tetapi pemilih Android mengembalikan satu). Hanya berfungsi di halaman teratas. Untuk menampilkan atau mencari semua kontak gunakan WebToApk.listContacts (memerlukan sakelar Kontak).

addContact jembatan#

window.WebToApk.addContact(requestId: String, contactJson: String)

Pemilih sistem (tanpa izin) dan menulis kontak baru (WRITE_CONTACTS). Sisi baca - menampilkan dan mencari - ada di grup Kontak di atas.

Dijelaskan oleh grupnya, Kontak: pilih dan tambah, bukan secara tersendiri.

Contoh

Menyimpan kontak baru langsung ke buku alamat ponsel, tanpa membuka aplikasi Kontak.

Mengembalikan: tidak ada untuk saat ini. Jawabannya berupa event appmint:contacts (dan window.onAppMintContacts) dengan detail = { requestId, ok: true, id: '123' } jika berhasil, { requestId, ok: false, id: null } jika penyimpanan gagal, atau { requestId, error }. Membutuhkan: aktifkan Edit Kontak di Langkah 4 (Akses) saat Anda membuat build; pengguna akan dimintai izin pada panggilan pertama.

Simpan pelanggan. Kontak dikirim sebagai string JSON.

function saveCustomer(name, phone, email) {
  if (!(window.WebToApk && window.WebToApk.addContact)) return;
  var id = 'add-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);

    if (e.detail.error === 'not_enabled') { showMessage('Turn on Edit Contacts when you build'); return; }
    if (e.detail.error) { showMessage('Not saved: ' + e.detail.error); return; }
    showMessage(e.detail.ok ? 'Saved to contacts' : 'Could not save the contact');
  });

  WebToApk.addContact(id, JSON.stringify({
    displayName: name,
    phones: [{ number: phone, type: 'mobile' }],   // type: mobile | home | work
    emails: [email]
  }));
}

Bentuk singkat. Nilai tunggal juga diterima: { name: 'Ali', phone: '+911234567890', email: '[email protected]' }.

Tombol "Simpan ke kontak" di halaman kartu nama bisnis, menggunakan bentuk singkat dan Promise:

function addOne(contact) {
  return new Promise(function (resolve, reject) {
    var id = 'card-' + Date.now() + '-' + Math.random().toString(36).slice(2);
    window.addEventListener('appmint:contacts', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:contacts', handler);
      if (e.detail.error) reject(new Error(e.detail.error));
      else if (!e.detail.ok) reject(new Error('save_failed'));
      else resolve(e.detail.id);
    });
    WebToApk.addContact(id, JSON.stringify(contact));
  });
}

document.getElementById('save-card').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.addContact)) return;
  addOne({ name: 'Priya Sharma', phone: '+919876543210', email: '[email protected]' })
    .then(function () { showMessage('Priya is in your contacts now'); })
    .catch(function (err) {
      showMessage(err.message === 'permission_denied' ? 'Allow Contacts to save this card.' : 'Not saved: ' + err.message);
    });
});

Impor daftar tim satu orang demi satu orang, dengan nomor kantor dan rumah, lalu laporkan berapa yang tersimpan:

async function importTeam(team) {   // [{ name, work, home, email }]
  if (!(window.WebToApk && window.WebToApk.addContact)) return;
  var saved = 0;
  for (var i = 0; i < team.length; i++) {
    var m = team[i];
    try {
      await addOne({   // addOne() from the example above
        displayName: m.name,
        phones: [{ number: m.work, type: 'work' }, { number: m.home, type: 'home' }],
        emails: m.email ? [m.email] : []
      });
      saved++;
    } catch (err) {
      if (err.message === 'not_enabled' || err.message === 'permission_denied') break;   // the rest would fail too
    }
  }
  showMessage(saved + ' of ' + team.length + ' saved');
}

Catatan: Kesalahan: not_enabled, permission_denied. id yang dikembalikan adalah id raw-contact baru. type telepon selain home atau work disimpan sebagai mobile. Kontak disimpan di perangkat (tanpa akun). Menulis kontak adalah izin sensitif: listing Play Store Anda harus mendeklarasikannya di formulir Keamanan data dan kebijakan privasi.

getContact jembatan#

window.WebToApk.getContact(requestId: String, contactId: String)

Semua asinkron: pembacaan kursor bisa mencapai ribuan baris, dan return @JavascriptInterface yang memblokir akan menjalankannya di thread JS - menyebabkan ANR. Hasil tiba sebagai `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: kirim sebagai STRING ('pick-' + Date.now()). ANGKA mentah juga berfungsi - bridge mengubahnya melalui double ("1.787491234567E12"), dan setiap balasan mengembalikannya ke bentuk kanonis (BridgeRequestId), sehingga `res.requestId === Date.now()` dengan perbandingan ketat bernilai true dalam kedua kasus. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- TIDAK perlu izin (pemilih sistem) WebToApk.addContact(id, json) <- perlu izin tulis Kontak

Dijelaskan oleh grupnya, Kontak, bukan secara tersendiri.

Contoh

Membaca satu kontak lagi berdasarkan id-nya (id dari listContacts atau searchContacts).

Mengembalikan: tidak ada untuk saat ini. Jawabannya berupa event appmint:contacts (dan window.onAppMintContacts) dengan detail = { requestId, contact: { id, displayName, photoUri, phones: [{ number, type }], emails: [...] } }. contact bernilai null bila kontak itu sudah tidak ada. Jika gagal: { requestId, error }. Membutuhkan: aktifkan Kontak di Langkah 4 (Akses) saat Anda membuat build.

Segarkan kontak yang tersimpan menggunakan hook halaman, bukan event listener.

var pendingId = '';

window.onAppMintContacts = function (detail) {
  if (detail.requestId !== pendingId) return;
  if (detail.error) { showMessage('Could not read the contact: ' + detail.error); return; }
  if (!detail.contact) { showMessage('This contact was deleted.'); return; }

  var c = detail.contact;
  document.getElementById('name').textContent = c.displayName;
  document.getElementById('phones').textContent =
    c.phones.map(function (p) { return p.type + ': ' + p.number; }).join(', ');
  document.getElementById('emails').textContent = c.emails.join(', ');
};

function openContact(contactId) {
  if (!(window.WebToApk && window.WebToApk.getContact)) return;
  pendingId = 'one-' + Date.now();
  WebToApk.getContact(pendingId, String(contactId));
}

Segarkan daftar id kontak favorit yang tersimpan dan buang yang sudah dihapus dari ponsel sejak itu:

function getOne(contactId) {
  return new Promise(function (resolve, reject) {
    var id = 'fav-' + contactId + '-' + Date.now();
    window.addEventListener('appmint:contacts', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:contacts', handler);
      if (e.detail.error) reject(new Error(e.detail.error)); else resolve(e.detail.contact);
    });
    WebToApk.getContact(id, String(contactId));
  });
}

async function loadFavourites() {
  if (!(window.WebToApk && window.WebToApk.getContact)) return;
  var ids = JSON.parse(localStorage.getItem('favIds') || '[]');
  var people = [];
  for (var i = 0; i < ids.length; i++) {
    var c = await getOne(ids[i]);
    if (c) people.push(c);   // null = deleted from the phone
  }
  localStorage.setItem('favIds', JSON.stringify(people.map(function (c) { return c.id; })));
  renderFavourites(people);
}

Periksa apakah nomor yang disimpan aplikasi Anda untuk seorang pelanggan masih ada di kartu kontaknya:

function checkStoredNumber(customer) {   // { contactId, phone }
  if (!(window.WebToApk && window.WebToApk.getContact)) return;
  var id = 'check-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);
    if (e.detail.error) { showMessage('Could not check: ' + e.detail.error); return; }
    if (!e.detail.contact) { showMessage('This customer is no longer in your contacts.'); return; }

    var digits = function (s) { return String(s).replace(/\D/g, ''); };
    var still = e.detail.contact.phones.some(function (p) {
      return digits(p.number).slice(-10) === digits(customer.phone).slice(-10);
    });
    if (!still) showMessage('The number for ' + e.detail.contact.displayName + ' has changed.');
  });

  WebToApk.getContact(id, String(customer.contactId));
}

Catatan: window.onAppMintContacts menerima setiap jawaban kontak (daftar, cari, pilih, tambah), jadi selalu periksa requestId. Anda bisa memakainya atau addEventListener('appmint:contacts', ...) - keduanya aktif. Kesalahan: not_enabled, permission_denied, foreign_origin. id dari pickContact adalah id baris telepon, bukan id kontak, jadi tidak berfungsi di sini. Kontak adalah data pribadi: listing Play Store Anda harus mendeklarasikannya di formulir Keamanan data dan kebijakan privasi.

listContacts jembatan#

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

Semua asinkron: pembacaan kursor bisa mencapai ribuan baris, dan return @JavascriptInterface yang memblokir akan menjalankannya di thread JS - menyebabkan ANR. Hasil tiba sebagai `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: kirim sebagai STRING ('pick-' + Date.now()). ANGKA mentah juga berfungsi - bridge mengubahnya melalui double ("1.787491234567E12"), dan setiap balasan mengembalikannya ke bentuk kanonis (BridgeRequestId), sehingga `res.requestId === Date.now()` dengan perbandingan ketat bernilai true dalam kedua kasus. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- TIDAK perlu izin (pemilih sistem) WebToApk.addContact(id, json) <- perlu izin tulis Kontak

Dijelaskan oleh grupnya, Kontak, bukan secara tersendiri.

Contoh

Membaca buku alamat ponsel, halaman demi halaman, diurutkan A sampai Z menurut nama.

Mengembalikan: tidak ada untuk saat ini. Jawabannya datang kemudian sebagai event appmint:contacts (dan window.onAppMintContacts) dengan detail = { requestId, contacts: [{ id, displayName, photoUri, phones: [{ number, type }], emails: ['[email protected]'] }] }, atau { requestId, error }. Membutuhkan: aktifkan Kontak di Langkah 4 (Akses) saat Anda membuat build; pengguna akan dimintai izin pada panggilan pertama.

Muat 100 kontak pertama. Cocokkan jawabannya pada milik Anda sendiri requestId.

function loadContacts() {
  if (!(window.WebToApk && window.WebToApk.listContacts)) {
    showMessage('Contacts work only inside the app.');
    return;
  }
  var id = 'contacts-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;   // another call's answer
    window.removeEventListener('appmint:contacts', handler);

    if (e.detail.error) { showMessage('Could not read contacts: ' + e.detail.error); return; }

    var list = document.getElementById('list');
    list.innerHTML = '';
    e.detail.contacts.forEach(function (c) {
      var li = document.createElement('li');
      var num = c.phones.length ? c.phones[0].number + ' (' + c.phones[0].type + ')' : '';
      li.textContent = c.displayName + ' ' + num;
      list.appendChild(li);
    });
  });

  WebToApk.listContacts(id, 100, 0);   // limit 100, start at 0
}

Halaman berikutnya. Kirim nomor yang sudah Anda miliki sebagai offset: WebToApk.listContacts(id, 100, 100).

Muat seluruh buku alamat dalam halaman berisi 500 sampai halaman yang lebih pendek menandakan Anda sudah sampai di akhir:

function listPage(limit, offset) {
  return new Promise(function (resolve, reject) {
    var id = 'page-' + offset + '-' + Date.now();
    window.addEventListener('appmint:contacts', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:contacts', handler);
      if (e.detail.error) reject(new Error(e.detail.error));
      else resolve(e.detail.contacts);
    });
    WebToApk.listContacts(id, limit, offset);
  });
}

async function loadEveryContact() {
  if (!(window.WebToApk && window.WebToApk.listContacts)) return [];
  var all = [];
  while (true) {
    var page = await listPage(500, all.length);
    all = all.concat(page);
    if (page.length < 500) return all;   // last page
  }
}

Susun daftar "Undang teman" hanya dari nomor ponsel, dan beri tahu pengguna apa yang harus dilakukan saat pembacaan ditolak:

function showInviteList() {
  if (!(window.WebToApk && window.WebToApk.listContacts)) return;
  var id = 'invite-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);

    if (e.detail.error === 'permission_denied') { showMessage('Allow Contacts to invite friends.'); return; }
    if (e.detail.error) { showMessage('Contacts are not available: ' + e.detail.error); return; }

    var mobiles = [];
    e.detail.contacts.forEach(function (c) {
      c.phones.forEach(function (p) {
        if (p.type === 'mobile') mobiles.push({ name: c.displayName, number: p.number });
      });
    });
    renderInviteList(mobiles);   // your own list UI
  });

  WebToApk.listContacts(id, 500, 0);
}

Catatan: limit dibatasi pada 1-2000. Kesalahan: not_enabled (Kontak nonaktif saat build), permission_denied (pengguna memilih Tidak), foreign_origin (dipanggil dari halaman yang bukan milik aplikasi Anda sendiri). type telepon adalah home, mobile, work, main, work_fax, home_fax atau other. photoUri adalah alamat content://, bukan URL gambar yang dapat dimuat halaman. Untuk membiarkan pengguna memilih satu orang saja, gunakan pickContact - tidak memerlukan izin. Kontak adalah data pribadi: listing Play Store Anda harus mendeklarasikannya di formulir Keamanan data dan kebijakan privasi.

pickContact jembatan#

window.WebToApk.pickContact(requestId: String)

Pemilih kontak sistem - TIDAK memerlukan izin, dan merupakan pilihan tepat setiap kali halaman hanya ingin pengguna memilih satu orang. Memilih dari tabel PHONE, bukan tabel kontak: pemilih memberi akses baca hanya ke baris yang dikembalikannya, jadi memilih baris telepon-lah yang membuat nomor bisa dibaca tanpa READ_CONTACTS. Memilih kontak saja akan mengembalikan baris dengan nama tanpa nomor, dan membaca nomor dari situ memerlukan izin yang justru ingin dihindari metode ini.

Contoh

Membuka pemilih kontak bawaan Android agar pengguna memilih satu nomor telepon. Tidak perlu izin.

Mengembalikan: tidak ada untuk saat ini. Jawabannya berupa event appmint:contacts (dan window.onAppMintContacts) dengan detail = { requestId, contact: { id, displayName, photoUri, phones: [{ number, type: 'picked' }], emails: [] } }. Jika pengguna menutup pemilih: { requestId, error: 'cancelled' }. Membutuhkan: tidak ada - tanpa sakelar build, tanpa permintaan izin.

Isi formulir dengan orang yang dipilih.

function chooseContact() {
  if (!(window.WebToApk && window.WebToApk.pickContact)) {
    showMessage('Open this page in the app to pick a contact.');
    return;
  }
  var id = 'pick-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);

    if (e.detail.error === 'cancelled') return;              // user closed the picker
    if (e.detail.error) { showMessage('Could not open contacts'); return; }
    if (!e.detail.contact) { showMessage('Could not read that contact'); return; }

    var c = e.detail.contact;
    document.getElementById('name').value = c.displayName;
    document.getElementById('phone').value = c.phones.length ? c.phones[0].number : '';
  });

  WebToApk.pickContact(id);
}

Simpan daftar singkat kontak darurat. Simpan nama dan nomornya, bukan id, agar daftar tidak bergantung pada buku alamat:

function pickOne() {
  return new Promise(function (resolve, reject) {
    var id = 'sos-' + Date.now();
    window.addEventListener('appmint:contacts', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:contacts', handler);
      if (e.detail.error === 'cancelled') resolve(null);
      else if (e.detail.error) reject(new Error(e.detail.error));
      else resolve(e.detail.contact);
    });
    WebToApk.pickContact(id);
  });
}

document.getElementById('add-sos').addEventListener('click', async function () {
  if (!(window.WebToApk && window.WebToApk.pickContact)) return;
  var c = await pickOne();
  if (!c || !c.phones.length) return;   // closed, or nothing readable
  var list = JSON.parse(localStorage.getItem('sos') || '[]');
  list.push({ name: c.displayName, number: c.phones[0].number });
  localStorage.setItem('sos', JSON.stringify(list));
  renderSosList(list);
});

Nonaktifkan tombol saat pemilih terbuka, dan tampilkan kolom ketik saat ponsel tidak punya aplikasi kontak:

var pickBtn = document.getElementById('pick');

pickBtn.addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.pickContact)) { showManualEntry(); return; }
  var id = 'recipient-' + Date.now();
  pickBtn.disabled = true;   // one picker at a time

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);
    pickBtn.disabled = false;

    if (e.detail.error === 'no_picker') { showManualEntry(); return; }
    if (e.detail.error) return;   // 'cancelled'
    if (e.detail.contact && e.detail.contact.phones.length) {
      document.getElementById('to').value = e.detail.contact.phones[0].number;
    }
  });

  WebToApk.pickContact(id);
});

Catatan: Anda mendapatkan nama dan SATU nomor yang diketuk pengguna - tidak pernah email, tidak pernah sisa buku alamat. emails selalu kosong. Kesalahan: cancelled, no_picker (tidak ada aplikasi kontak di ponsel). Jika pembacaan gagal setelah memilih, contact bernilai null. Karena tidak ada yang dibaca tanpa pilihan pengguna, ini adalah cara yang ramah Play untuk mendapatkan nomor telepon; pilih ini daripada listContacts bila Anda hanya butuh satu orang. Untuk aplikasi buatan AI dan impor Google AI Studio, build memindai kode Anda; panggilan pickContact( tidak mencentang Kontak - hanya listContacts, searchContacts dan getContact yang mencentangnya - sehingga pemilih tidak menambahkan izin baca kontak.

searchContacts jembatan#

window.WebToApk.searchContacts(requestId: String, query: String, limit: Int)

Semua asinkron: pembacaan kursor bisa mencapai ribuan baris, dan return @JavascriptInterface yang memblokir akan menjalankannya di thread JS - menyebabkan ANR. Hasil tiba sebagai `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: kirim sebagai STRING ('pick-' + Date.now()). ANGKA mentah juga berfungsi - bridge mengubahnya melalui double ("1.787491234567E12"), dan setiap balasan mengembalikannya ke bentuk kanonis (BridgeRequestId), sehingga `res.requestId === Date.now()` dengan perbandingan ketat bernilai true dalam kedua kasus. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- TIDAK perlu izin (pemilih sistem) WebToApk.addContact(id, json) <- perlu izin tulis Kontak

Dijelaskan oleh grupnya, Kontak, bukan secara tersendiri.

Contoh

Menemukan kontak yang namanya cocok dengan kata pencarian, tanpa memuat seluruh buku alamat.

Mengembalikan: tidak ada untuk saat ini. Jawabannya berupa event appmint:contacts (dan window.onAppMintContacts) dengan detail = { requestId, contacts: [...] } - bentuk kontak yang sama seperti listContacts - atau { requestId, error }. Membutuhkan: aktifkan Kontak di Langkah 4 (Akses) saat Anda membuat build; pengguna akan dimintai izin pada panggilan pertama.

Cari saat pengguna mengetik. Hanya jawaban untuk pencarian terakhir yang ditampilkan.

var lastSearchId = '';

function searchPeople(word) {
  if (!(window.WebToApk && window.WebToApk.searchContacts)) return;
  lastSearchId = 'search-' + Date.now();
  WebToApk.searchContacts(lastSearchId, word, 20);   // at most 20 results
}

window.addEventListener('appmint:contacts', function (e) {
  if (!e.detail || e.detail.requestId !== lastSearchId) return;   // old or other request
  if (e.detail.error) { showMessage('Search failed: ' + e.detail.error); return; }

  var names = e.detail.contacts.map(function (c) {
    return c.displayName + (c.phones.length ? ' - ' + c.phones[0].number : '');
  });
  document.getElementById('results').textContent = names.join('\n') || 'No match';
});

document.getElementById('q').addEventListener('input', function (e) {
  searchPeople(e.target.value);
});

Bungkus pencarian dalam Promise dan tunggu sampai pengguna berhenti mengetik, agar mengetik cepat tidak memulai pencarian per tombol:

function searchOnce(word, limit) {
  return new Promise(function (resolve, reject) {
    var id = 'find-' + Date.now() + '-' + Math.random().toString(36).slice(2);
    window.addEventListener('appmint:contacts', function handler(e) {
      if (!e.detail || e.detail.requestId !== id) return;
      window.removeEventListener('appmint:contacts', handler);
      if (e.detail.error) reject(new Error(e.detail.error)); else resolve(e.detail.contacts);
    });
    WebToApk.searchContacts(id, word, limit);
  });
}

var timer = null;
document.getElementById('q').addEventListener('input', function (e) {
  clearTimeout(timer);
  var word = e.target.value.trim();
  timer = setTimeout(function () {
    if (!(window.WebToApk && window.WebToApk.searchContacts) || !word) return;
    searchOnce(word, 10)
      .then(function (people) { renderSuggestions(people); })
      .catch(function (err) { showMessage('Search failed: ' + err.message); });
  }, 300);
});

Isi kotak email dari buku alamat ketika nama yang diketik cocok dengan satu orang yang punya email:

function suggestEmail(name) {
  if (!(window.WebToApk && window.WebToApk.searchContacts) || name.length < 2) return;
  var id = 'email-' + Date.now();

  window.addEventListener('appmint:contacts', function handler(e) {
    if (!e.detail || e.detail.requestId !== id) return;
    window.removeEventListener('appmint:contacts', handler);
    if (e.detail.error) return;   // leave the box for the user to type

    var withEmail = e.detail.contacts.filter(function (c) { return c.emails.length > 0; });
    if (withEmail.length === 1) {
      document.getElementById('email').value = withEmail[0].emails[0];
    }
  });

  WebToApk.searchContacts(id, name, 5);
}

document.getElementById('name').addEventListener('change', function (e) {
  suggestEmail(e.target.value.trim());
});

Catatan: Kata pencarian kosong mengembalikan kontak dari awal daftar (seperti listContacts). limit dibatasi pada 1-2000 dan tidak ada offset. Kesalahan: not_enabled, permission_denied, foreign_origin. Kontak adalah data pribadi: listing Play Store Anda harus mendeklarasikannya di formulir Keamanan data dan kebijakan privasi.

Dibuat dari runtime aplikasi dan file contohnya pada setiap build dokumentasi. Baca sebagai Markdown · Semua keluarga.

Diperiksa terhadap jembatan yang dirilis pada 2026-10-22.