Criar APK grátis

API da ponte JavaScript

Telefone, SMS e histórico de chamadas

Fazer uma ligação, enviar ou preparar uma mensagem e ler o histórico. Todas essas ações exigem permissão e a Play pergunta sobre elas.

composeSms ponte#

window.WebToApk.composeSms(phoneNumber: String, message: String): Boolean

Abre o app de SMS do usuário com a mensagem preenchida. Não precisa de permissão e não pode ser rejeitado pela Play: é o padrão certo para tudo que não seja automatizado. Retorna false quando o celular não tem app de SMS (ou ele não pôde ser aberto).

Exemplo

Abre o app de SMS do usuário com o número e o texto já preenchidos. O usuário toca em enviar.

Retorna: true quando o app de SMS abriu, false quando o celular não tem app de SMS ou ele não pôde ser aberto (síncrono). Requer: nada: nenhuma chave de build, nenhuma permissão.

Um botão "Envie uma mensagem".

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

Um botão "Lembrar" em cada linha de compromisso, preenchendo um lembrete para o número daquele cliente:

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

Um formulário "Enviar por SMS": quando o celular não tem app de SMS (false), copie o texto para o usuário poder colá-lo em outro lugar:

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

Observações: Nada é enviado até o usuário tocar em enviar no próprio app de SMS, então este método em si não precisa de permissão de SMS. Use-o para toda mensagem que o usuário escreve ou aprova; use sendSms somente quando o app realmente precisar enviar sozinho. Em apps criados com IA e importações do Google AI Studio, o build analisa seu código; uma chamada composeSms( não marca SMS (ler e enviar): só sendSms e listSms marcam, então um botão "Envie uma mensagem" não adiciona nenhuma permissão restrita da Play.

listCallLog ponte#

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

WebToApk.listCallLog(id, limit, offset, sinceMillis) Entregue como `appmint:calllog` / window.onAppMintCallLog.

Descrito por seu grupo, Histórico de chamadas, e não separadamente.

Exemplo

Lê o histórico de chamadas do celular, da mais recente para a mais antiga.

Retorna: nada por enquanto. A resposta é o evento appmint:calllog (e window.onAppMintCallLog) com detail = { requestId, calls: [{ number, name, type, date, duration }] } ou { requestId, error }. date é em milissegundos desde 1970; duration é em segundos. Requer: ative Histórico de chamadas (leitura) na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Mostre as últimas 50 chamadas.

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

Somente chamadas das últimas 24 horas, usando o hook da página. sinceMillis é uma STRING porque o número é grande demais para o int da ponte.

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

Uma tela de histórico de chamadas com um botão "Carregar mais", buscando 25 chamadas por vez com 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();

Observações: limit é limitado a 1-2000. Erros: not_enabled, permission_denied, foreign_origin. O histórico de chamadas é uma permissão restrita do Google Play: você precisa enviar uma Declaração de Permissões aprovada no Play Console, ou o app é rejeitado.

listSms ponte#

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

WebToApk.composeSms(number, message) <- SEM permissão, abre o app de SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Resultados de envio: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Mensagens recebidas: `appmint:sms-received` / window.onAppMintSmsReceived.

Descrito por seu grupo, SMS, e não separadamente.

Exemplo

Lê mensagens SMS da caixa de entrada, da caixa de enviadas ou dos rascunhos do celular, da mais recente para a mais antiga. Este arquivo também mostra como receber novas mensagens à medida que chegam.

Retorna: nada por enquanto. A resposta é o evento appmint:sms (e window.onAppMintSms) com detail = { requestId, messages: [{ id, address, body, date, read, box }] }. Em caso de falha: { requestId, status: 'failed', ok: false, reason }. Requer: ative SMS (ler e enviar) na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Leia as últimas 30 mensagens da caixa de entrada.

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

Novas mensagens à medida que chegam (appmint:sms-received, também window.onAppMintSmsReceived). As mensagens que chegam com o app fechado são guardadas (as últimas 50) e entregues à sua página quando o app abrir de novo.

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

Um controle de despesas que analisa as últimas 200 mensagens da caixa de entrada em busca de débitos bancários e soma os valores:

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

Observações: limit é limitado a 1-500. Qualquer nome de caixa desconhecido lê a caixa de entrada. Valores de reason: not_enabled, permission_denied, foreign_origin. As mensagens recebidas continuam chegando também ao app de SMS normal do usuário. SMS é uma permissão restrita do Google Play: você precisa enviar uma Declaração de Permissões aprovada no Play Console (geralmente só apps de SMS padrão se qualificam), ou o app é rejeitado.

makePhoneCall ponte#

window.WebToApk.makePhoneCall(phoneNumber: String)

Liga para [phoneNumber]. - Permissão de Telefone ATIVADA no build: faz a ligação diretamente. Na primeira vez, o Android pede ao usuário a permissão de Telefone; se ele recusar, o discador abre com o número preenchido para que ele ainda possa ligar por conta própria. - Permissão de Telefone DESATIVADA: abre o discador com o número preenchido (o discador não precisa de permissão) e o usuário toca em Ligar. - Uma página que não é do próprio app (um site externo aberto no app) sempre recebe o discador, nunca uma ligação direta. Um link `tel:` simples no seu HTML também abre o discador e muitas vezes basta.

Exemplo

Inicia uma ligação para um número ou abre o discador com o número preenchido.

Retorna: nada. Requer: nada para abrir o discador. Ative Telefone na Etapa 4 (Acesso) ao compilar se a ligação deve começar direto.

Um botão "Ligue para a loja".

document.getElementById('callShop').addEventListener('click', function () {
  if (window.WebToApk && window.WebToApk.makePhoneCall) {
    WebToApk.makePhoneCall('+911234567890');
  }
});

Sem precisar de chave: um link tel: simples abre o discador com o número, no app e em um navegador. Muitas vezes isso é tudo de que você precisa.

<a href="tel:+911234567890">Call the shop</a>

Um único manipulador para um diretório inteiro de funcionários, em que cada linha carrega o número em um atributo 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
  }
});

Observações: Com Telefone desativado, o discador abre com o número e o usuário toca em ligar. Com Telefone ativado, a ligação começa direto; na primeira vez, o Android pede ao usuário a permissão de chamada telefônica e, se ele recusar, o discador abre para que ele ainda possa ligar. Uma página que não é do próprio app (um site externo aberto dentro do app) sempre recebe o discador. Passe o número como uma string simples. Nada é informado de volta: você não sabe se a ligação foi feita. A chave Telefone adiciona a permissão CALL_PHONE à sua ficha na Play; declare-a no formulário de Segurança dos dados e só ative se precisar.

sendSms ponte#

window.WebToApk.sendSms(

WebToApk.composeSms(number, message) <- SEM permissão, abre o app de SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Resultados de envio: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Mensagens recebidas: `appmint:sms-received` / window.onAppMintSmsReceived.

Descrito por seu grupo, SMS, e não separadamente.

Exemplo

Envia um SMS diretamente do app, sem abrir o app de SMS, e informa o que realmente aconteceu.

Retorna: nada por enquanto. A resposta é o evento appmint:sms (e window.onAppMintSms) com detail = { requestId, status: 'sent' | 'delivered' | 'failed', ok, reason? }. Com relatório de entrega, você recebe DUAS respostas em caso de sucesso: sent e depois delivered. Requer: ative SMS (ler e enviar) na Etapa 4 (Acesso) ao compilar; o usuário é solicitado a dar permissão na primeira chamada.

Envie um código e acompanhe o status dele. Não remova o listener depois da primeira resposta quando você pediu um relatório de entrega.

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
}

Sem relatório de entrega. Passe false como último valor: você recebe exatamente uma resposta, sent ou 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);
}

O mesmo aviso para uma pequena lista de números, um após o outro, coletando cada resultado (e parando quando o SMS está desativado ou foi recusado, porque todos os outros números falhariam do mesmo jeito):

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

Observações: Textos longos são divididos em partes e enviados como uma única mensagem. Se nenhum relatório de entrega chegar em 60 segundos, você recebe status: 'sent' com reason: 'no_delivery_report': a mensagem foi enviada mesmo assim. Valores de reason: not_enabled, permission_denied, foreign_origin, invalid_arguments (número ou texto vazio), no_service, radio_off, generic_failure, null_pdu, not_delivered e outros. Use uma string em requestId. Cada SMS pode custar dinheiro ao usuário. SMS é uma permissão restrita do Google Play: você precisa enviar uma Declaração de Permissões aprovada no Play Console (geralmente só apps de SMS padrão se qualificam), então prefira composeSms quando o usuário puder tocar em enviar.

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.