Criar APK grátis

API da ponte JavaScript

Contatos

Ler, buscar, escolher e gravar — o seletor não precisa de nenhuma permissão.

navigator.contacts padrão web#

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

Exemplo

A API padrão Contact Picker: o seletor de contatos do próprio telefone abre, e a página recebe apenas a pessoa que o usuário escolheu — sem permissão de contatos.

Retorna: uma Promise de um array: [{ name: [string], tel: [string] }], ou [] quando o usuário fecha o seletor. Requer: nada para ativar; chame a partir de um toque (é preciso um gesto do usuário, senão 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] || '';
});

Preencha um campo "Enviar para" só com o número e mostre uma mensagem para cada forma como o seletor pode recusar:

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);
  }
});

Monte uma lista de convidados um toque por vez (uma pessoa por seleção), ignorando um número que já está na lista:

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);
});

Observações: o app oferece suporte a name e tel — o número em que o usuário tocou. email, address e icon exigiriam a permissão completa de contatos, então não são oferecidos e pedi-los rejeita com TypeError. Uma pessoa por seleção (multiple é permitido, mas o seletor do Android retorna uma). Funciona apenas na página principal. Para listar ou buscar todos os contatos, use WebToApk.listContacts (exige a chave Contatos).

addContact ponte#

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

O seletor do sistema (sem permissão) e a gravação de um novo contato (WRITE_CONTACTS). O lado de leitura — listar e buscar — está no grupo Contatos acima.

Descrito pelo seu grupo, Contatos: escolher e adicionar, e não separadamente.

Exemplo

Salva um novo contato direto na agenda do telefone, sem abrir o app Contatos.

Retorna: nada agora. A resposta é o evento appmint:contacts (e window.onAppMintContacts) com detail = { requestId, ok: true, id: '123' } em caso de sucesso, { requestId, ok: false, id: null } quando o salvamento falhou, ou { requestId, error }. Requer: ative Editar contatos na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Salve um cliente. O contato é passado como uma 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]
  }));
}

Forma curta. Valores únicos também são aceitos: { name: 'Ali', phone: '+911234567890', email: '[email protected]' }.

Um botão "Salvar nos contatos" em uma página de cartão de visita, usando a forma curta e uma 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);
    });
});

Importe a lista de uma equipe, uma pessoa por vez, com números de trabalho e de casa, e informe quantos foram salvos:

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');
}

Observações: Erros: not_enabled, permission_denied. O id retornado é o novo id de contato bruto. Um type de telefone diferente de home ou work é salvo como mobile. O contato é salvo no dispositivo (sem conta). Gravar contatos é uma permissão sensível: sua página na Play Store deve declará-la no formulário de Segurança dos dados e na política de privacidade.

getContact ponte#

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

Tudo assíncrono: a leitura do cursor pode ter milhares de linhas, e um retorno bloqueante de @JavascriptInterface executaria isso na thread do JS — um ANR. Os resultados chegam como `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: passe uma STRING ('pick-' + Date.now()). Um NÚMERO puro também funciona — a bridge o converte por um double ("1.787491234567E12"), e toda resposta o canonicaliza de volta (BridgeRequestId), então `res.requestId === Date.now()` com comparação estrita dá true nos dois casos. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NÃO precisa de permissão (seletor do sistema) WebToApk.addContact(id, json) <- precisa da permissão de escrita de Contatos

Descrito pelo seu grupo, Contatos, e não separadamente.

Exemplo

Lê um contato novamente pelo seu id (o id de listContacts ou searchContacts).

Retorna: nada agora. A resposta é o evento appmint:contacts (e window.onAppMintContacts) com detail = { requestId, contact: { id, displayName, photoUri, phones: [{ number, type }], emails: [...] } }. contact é null quando esse contato não existe mais. Em caso de falha: { requestId, error }. Requer: ative Contatos na Etapa 4 (Acesso) ao compilar.

Atualize um contato salvo usando o hook da página em vez de um listener de evento.

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));
}

Atualize uma lista salva de ids de contatos favoritos e remova os que foram apagados do telefone desde então:

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);
}

Verifique se o número que seu app guardou para um cliente ainda está no cartão de contato dele:

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));
}

Observações: window.onAppMintContacts recebe todas as respostas de contatos (listar, buscar, escolher, adicionar), então sempre verifique requestId. Você pode usá-lo ou addEventListener('appmint:contacts', ...) — ambos disparam. Erros: not_enabled, permission_denied, foreign_origin. O id de pickContact é um id de linha de telefone, não um id de contato, então não funciona aqui. Contatos são dados pessoais: sua página na Play Store deve declarar isso no formulário de Segurança dos dados e na política de privacidade.

listContacts ponte#

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

Tudo assíncrono: a leitura do cursor pode ter milhares de linhas, e um retorno bloqueante de @JavascriptInterface executaria isso na thread do JS — um ANR. Os resultados chegam como `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: passe uma STRING ('pick-' + Date.now()). Um NÚMERO puro também funciona — a bridge o converte por um double ("1.787491234567E12"), e toda resposta o canonicaliza de volta (BridgeRequestId), então `res.requestId === Date.now()` com comparação estrita dá true nos dois casos. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NÃO precisa de permissão (seletor do sistema) WebToApk.addContact(id, json) <- precisa da permissão de escrita de Contatos

Descrito pelo seu grupo, Contatos, e não separadamente.

Exemplo

Lê a agenda do telefone, página por página, em ordem alfabética (A a Z) por nome.

Retorna: nada agora. A resposta vem depois como o evento appmint:contacts (e window.onAppMintContacts) com detail = { requestId, contacts: [{ id, displayName, photoUri, phones: [{ number, type }], emails: ['[email protected]'] }] }, ou { requestId, error }. Requer: ative Contatos na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Carregue os primeiros 100 contatos. Relacione a resposta por conta própria 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
}

Próxima página. Passe o número que você já tem como offset: WebToApk.listContacts(id, 100, 100).

Carregue a agenda inteira em páginas de 500 até que uma página menor indique que você chegou ao fim:

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
  }
}

Monte uma lista "Convidar amigos" só com números de celular e diga ao usuário o que fazer quando a leitura for recusada:

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);
}

Observações: limit é limitado a 1-2000. Erros: not_enabled (Contatos estava desativado na compilação), permission_denied (o usuário disse Não), foreign_origin (chamado de uma página que não é do próprio app). O type do telefone é home, mobile, work, main, work_fax, home_fax ou other. photoUri é um endereço content://, não uma URL de imagem que uma página consiga carregar. Para deixar o usuário escolher apenas uma pessoa, use pickContact — não precisa de permissão. Contatos são dados pessoais: sua página na Play Store deve declarar isso no formulário de Segurança dos dados e na política de privacidade.

pickContact ponte#

window.WebToApk.pickContact(requestId: String)

Seletor de contatos do sistema - NĂO precisa de permissĂŁo e Ă© a escolha certa sempre que a pĂĄgina sĂł quer que o usuĂĄrio escolha uma pessoa. Escolhe da tabela PHONE, nĂŁo da tabela de contatos: o seletor concede acesso de leitura apenas Ă linha que ele retorna, e escolher uma linha de telefone Ă© o que torna o nĂșmero legĂ­vel sem READ_CONTACTS. Escolher um contato simples retornaria uma linha com nome e sem nĂșmero, e ler o nĂșmero a partir dela exigiria a permissĂŁo que este mĂ©todo existe para evitar.

Exemplo

Abre o seletor de contatos do próprio Android para o usuário escolher um número de telefone. Não precisa de permissão.

Retorna: nada agora. A resposta é o evento appmint:contacts (e window.onAppMintContacts) com detail = { requestId, contact: { id, displayName, photoUri, phones: [{ number, type: 'picked' }], emails: [] } }. Se o usuário fechar o seletor: { requestId, error: 'cancelled' }. Requer: nada — sem chave de compilação, sem pedido de permissão.

Preencha um formulário com a pessoa escolhida.

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);
}

Mantenha uma lista curta de contatos de emergência. Salve o nome e o número, não o id, para que a lista não dependa da agenda:

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);
});

Desative o botão enquanto o seletor estiver aberto e mostre os campos de digitação quando o telefone não tiver app de contatos:

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);
});

Observações: Você recebe o nome e o ÚNICO número em que o usuário tocou — nunca e-mails, nunca o resto da agenda. emails é sempre vazio. Erros: cancelled, no_picker (sem app de contatos no telefone). Se a leitura falhar após a seleção, contact é null. Como nada é lido sem a escolha do usuário, esta é a forma amigável ao Play de obter um número de telefone; prefira-a a listContacts quando você só precisa de uma pessoa. Para apps criados com IA e importações do Google AI Studio, a compilação analisa seu código; uma chamada pickContact( não marca Contatos — só listContacts, searchContacts e getContact marcam — então o seletor não adiciona permissão de leitura de contatos.

searchContacts ponte#

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

Tudo assíncrono: a leitura do cursor pode ter milhares de linhas, e um retorno bloqueante de @JavascriptInterface executaria isso na thread do JS — um ANR. Os resultados chegam como `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: passe uma STRING ('pick-' + Date.now()). Um NÚMERO puro também funciona — a bridge o converte por um double ("1.787491234567E12"), e toda resposta o canonicaliza de volta (BridgeRequestId), então `res.requestId === Date.now()` com comparação estrita dá true nos dois casos. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NÃO precisa de permissão (seletor do sistema) WebToApk.addContact(id, json) <- precisa da permissão de escrita de Contatos

Descrito pelo seu grupo, Contatos, e não separadamente.

Exemplo

Encontra contatos cujo nome corresponde a uma palavra de busca, sem carregar a agenda inteira.

Retorna: nada agora. A resposta é o evento appmint:contacts (e window.onAppMintContacts) com detail = { requestId, contacts: [...] } — o mesmo formato de contato de listContacts — ou { requestId, error }. Requer: ative Contatos na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Busque enquanto o usuário digita. Só a resposta da busca mais recente é exibida.

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);
});

Envolva a busca em uma Promise e espere até o usuário parar de digitar, para que digitar rápido não inicie uma busca por tecla:

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);
});

Preencha o campo de e-mail a partir da agenda quando o nome digitado corresponder a uma pessoa que tem e-mail:

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());
});

Observações: Uma palavra de busca vazia retorna contatos do início da lista (como listContacts). limit é limitado a 1-2000 e não há offset. Erros: not_enabled, permission_denied, foreign_origin. Contatos são dados pessoais: sua página na Play Store deve declarar isso no formulário de Segurança dos dados e na política de privacidade.

Gerado a partir do runtime do app e de seus arquivos de exemplo a cada build da documentação. Leia em Markdown · Todas as famílias.

Conferido com a ponte atual em 22/10/2026.