Créer un APK gratuit

API du pont JavaScript

Téléphone, SMS et journal d'appels

Passer un appel, envoyer ou préparer un message, et lire l'historique. Chacune de ces actions est soumise à une autorisation et Play pose des questions à ce sujet.

composeSms pont#

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

Ouvre l'application SMS de l'utilisateur avec le message prérempli. Ne nécessite aucune autorisation et ne peut pas être rejeté par Play : le bon choix par défaut pour tout ce qui n'est pas automatisé. Renvoie false quand le téléphone n'a pas d'application SMS (ou qu'elle n'a pas pu s'ouvrir).

Exemple

Ouvre l'application SMS de l'utilisateur avec le numéro et le texte déjà remplis. L'utilisateur appuie sur envoyer.

Renvoie : true quand l'application SMS s'est ouverte, false quand le téléphone n'a pas d'application SMS ou qu'elle n'a pas pu s'ouvrir (synchrone). Nécessite : rien : aucun réglage de compilation, aucune autorisation.

Un bouton « Envoyez-nous un SMS ».

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

Un bouton « Rappeler » sur chaque ligne de rendez-vous, préremplissant un rappel vers le numéro de ce client :

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

Un formulaire « Envoyer par SMS » : quand le téléphone n'a pas d'application SMS (false), copier le texte pour que l'utilisateur puisse le coller ailleurs :

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

Remarques : Rien n'est envoyé tant que l'utilisateur n'appuie pas sur envoyer dans sa propre application SMS ; cette méthode ne nécessite donc aucune autorisation SMS. Utilisez-la pour chaque message que l'utilisateur écrit ou approuve ; n'utilisez sendSms que lorsque l'application doit vraiment envoyer toute seule. Pour les applications créées avec l'IA et les imports Google AI Studio, la compilation analyse votre code ; un appel composeSms( ne coche pas SMS (lecture et envoi) : seuls sendSms et listSms le font, donc un bouton « Envoyez-nous un SMS » n'ajoute aucune autorisation restreinte par Play.

listCallLog pont#

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

WebToApk.listCallLog(id, limit, offset, sinceMillis) Transmis sous forme de `appmint:calllog` / window.onAppMintCallLog.

Décrit par son groupe, Journal d'appels, plutôt que seul.

Exemple

Lit l'historique des appels du téléphone, du plus récent au plus ancien.

Renvoie : rien pour l'instant. La réponse est l'événement appmint:calllog (et window.onAppMintCallLog) avec detail = { requestId, calls: [{ number, name, type, date, duration }] } ou { requestId, error }. date est en millisecondes depuis 1970 ; duration est en secondes. Nécessite : activez Journal d'appels (lecture) à l'étape 4 (Accès) lors de la compilation ; l'utilisateur est invité à donner l'autorisation au premier appel.

Afficher les 50 derniers appels.

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

Seulement les appels des dernières 24 heures, avec le hook de page. sinceMillis est une CHAÎNE car le nombre est trop grand pour l'entier du pont.

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

Un écran d'historique d'appels avec un bouton « Charger plus », qui récupère 25 appels à la fois avec 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();

Remarques : limit est limité à 1-2000. Erreurs : not_enabled, permission_denied, foreign_origin. Le journal d'appels est une autorisation restreinte de Google Play : vous devez déposer une déclaration d'autorisations approuvée dans Play Console, sinon l'application est rejetée.

listSms pont#

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

WebToApk.composeSms(number, message) <- AUCUNE autorisation, ouvre l'application SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Résultats d'envoi : `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Messages entrants : `appmint:sms-received` / window.onAppMintSmsReceived.

Décrit par son groupe, SMS, plutôt que seul.

Exemple

Lit les SMS de la boîte de réception, des messages envoyés ou des brouillons du téléphone, du plus récent au plus ancien. Ce fichier montre aussi comment recevoir les nouveaux messages à leur arrivée.

Renvoie : rien pour l'instant. La réponse est l'événement appmint:sms (et window.onAppMintSms) avec detail = { requestId, messages: [{ id, address, body, date, read, box }] }. En cas d'échec : { requestId, status: 'failed', ok: false, reason }. Nécessite : activez SMS (lecture et envoi) à l'étape 4 (Accès) lors de la compilation ; l'utilisateur est invité à donner l'autorisation au premier appel.

Lire les 30 derniers messages de la boîte de réception.

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

Nouveaux messages à leur arrivée (appmint:sms-received, aussi window.onAppMintSmsReceived). Les messages qui arrivent pendant que l'application est fermée sont conservés (les 50 derniers) et transmis à votre page à la réouverture de l'application.

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

Un suivi de dépenses qui analyse les 200 derniers messages de la boîte de réception à la recherche de débits bancaires et additionne les montants :

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

Remarques : limit est limité à 1-500. Tout nom de boîte inconnu lit la boîte de réception. Valeurs de reason : not_enabled, permission_denied, foreign_origin. Les messages reçus parviennent aussi à l'application SMS habituelle de l'utilisateur. Le SMS est une autorisation restreinte de Google Play : vous devez déposer une déclaration d'autorisations approuvée dans Play Console (généralement seules les applications SMS par défaut y sont admises), sinon l'application est rejetée.

makePhoneCall pont#

window.WebToApk.makePhoneCall(phoneNumber: String)

Appelle [phoneNumber]. - Autorisation Téléphone ACTIVÉE dans la compilation : passe l'appel directement. La première fois, Android demande l'autorisation Téléphone à l'utilisateur ; s'il refuse, le numéroteur s'ouvre avec le numéro rempli pour qu'il puisse quand même passer l'appel lui-même. - Autorisation Téléphone DÉSACTIVÉE : ouvre le numéroteur avec le numéro rempli (le numéroteur ne nécessite aucune autorisation) et l'utilisateur touche Appeler. - Une page qui n'est pas celle de l'application (un site externe ouvert dans l'application) obtient toujours le numéroteur, jamais un appel direct. Un simple lien `tel:` dans votre HTML ouvre aussi le numéroteur et suffit souvent.

Exemple

Lance un appel téléphonique vers un numéro, ou ouvre le numéroteur avec le numéro rempli.

Renvoie : rien. Nécessite : rien pour ouvrir le numéroteur. Activez Téléphone à l'étape 4 (Accès) lors de la compilation si l'appel doit démarrer directement.

Un bouton « Appeler la boutique ».

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

Aucun réglage nécessaire : un simple lien tel: ouvre le numéroteur avec le numéro, dans l'application comme dans un navigateur. C'est souvent tout ce qu'il vous faut.

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

Un seul gestionnaire pour tout un annuaire du personnel, où chaque ligne porte son numéro dans un attribut 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
  }
});

Remarques : Avec Téléphone désactivé, le numéroteur s'ouvre avec le numéro et l'utilisateur appuie sur appeler. Avec Téléphone activé, l'appel démarre directement ; la première fois, Android demande à l'utilisateur l'autorisation d'appel téléphonique, et s'il refuse, le numéroteur s'ouvre à la place pour qu'il puisse quand même passer l'appel. Une page qui n'est pas celle de l'application (un site externe ouvert dans l'application) obtient toujours le numéroteur. Transmettez le numéro sous forme de chaîne simple. Rien n'est signalé en retour : vous ne pouvez pas savoir si l'appel a été passé. Le réglage Téléphone ajoute l'autorisation CALL_PHONE à votre fiche Play ; déclarez-la dans le formulaire de sécurité des données et n'activez-la que si vous en avez besoin.

sendSms pont#

window.WebToApk.sendSms(

WebToApk.composeSms(number, message) <- AUCUNE autorisation, ouvre l'application SMS WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Résultats d'envoi : `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Messages entrants : `appmint:sms-received` / window.onAppMintSmsReceived.

Décrit par son groupe, SMS, plutôt que seul.

Exemple

Envoie un SMS directement depuis l'application, sans ouvrir l'application SMS, et signale ce qui s'est réellement passé.

Renvoie : rien pour l'instant. La réponse est l'événement appmint:sms (et window.onAppMintSms) avec detail = { requestId, status: 'sent' | 'delivered' | 'failed', ok, reason? }. Avec un accusé de livraison, vous recevez DEUX réponses en cas de réussite : sent, puis delivered. Nécessite : activez SMS (lecture et envoi) à l'étape 4 (Accès) lors de la compilation ; l'utilisateur est invité à donner l'autorisation au premier appel.

Envoyez un code et suivez son statut. Ne retirez pas l'écouteur après la première réponse quand vous avez demandé un accusé de livraison.

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
}

Sans accusé de livraison. Passez false comme dernière valeur : vous obtenez exactement une réponse, 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);
}

Le même avis envoyé à une courte liste de numéros, l'un après l'autre, en recueillant chaque résultat (et en s'arrêtant quand le SMS est désactivé ou refusé, car tous les autres numéros échoueraient de la même façon) :

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

Remarques : Un texte long est découpé en parties et envoyé comme un seul message. Si aucun accusé de livraison n'arrive en 60 secondes, vous recevez status: 'sent' avec reason: 'no_delivery_report' : le message a bien été envoyé. Valeurs de reason : not_enabled, permission_denied, foreign_origin, invalid_arguments (numéro ou texte vide), no_service, radio_off, generic_failure, null_pdu, not_delivered, et d'autres. Utilisez un requestId sous forme de chaîne. Chaque SMS peut coûter de l'argent à l'utilisateur. Le SMS est une autorisation restreinte de Google Play : vous devez déposer une déclaration d'autorisations approuvée dans Play Console (généralement seules les applications SMS par défaut y sont admises) ; préférez donc composeSms quand l'utilisateur peut appuyer lui-même sur envoyer.

Généré à partir de l'environnement d'exécution de l'application et de ses fichiers d'exemple à chaque compilation de la documentation. Lire en Markdown · Toutes les familles.

Vérifié par rapport au pont publié le 22/10/2026.