Criar APK grátis

API da ponte JavaScript

Compartilhar, área de transferência e impressão

A folha de compartilhamento do Android, a área de transferência do sistema, o texto que outro app compartilhou com o seu e o diálogo de impressão.

AppMint.pdf helper#

AppMint.pdf.fromPage(options) / fromHtml(html, options)

Exemplo

Gera um PDF de verdade (texto selecionável, gráficos vetoriais) sem diálogo de impressão: fromPage salva a página como está na tela, com o layout do seu CSS @media print; fromHtml renderiza o HTML que você passar em uma página invisível no endereço do próprio app, de modo que imagens, folhas de estilo e fontes relativas carregam como na sua página, aguarda as fontes e todas as imagens e então salva. window.print() continua abrindo o diálogo de impressão normal.

Retorna: uma Promise de {ok: true, fileName, pages, bytes, saved, uri?, base64?} ou {ok: false, code, message}: code é busy (um PDF por vez), bad-options, failed, too-large (base64 acima de 25 MB) ou permission-denied (armazenamento recusado no Android 7-9). Requer: nada: todo build o tem. Salvar em Downloads não requer permissão no Android 10+.

Opções: fileName (".pdf" é adicionado; padrão: o título da página), pageSize 'A4' (padrão) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins em milímetros (um número ou {top, right, bottom, left}; omitido = sua própria regra @page), save 'downloads' (padrão) | 'share' | 'base64'.

Salvar a página atual em Downloads:

async function saveInvoice() {
  if (!(window.AppMint && AppMint.pdf)) { window.print(); return; }   // in a browser
  var r = await AppMint.pdf.fromPage({ fileName: 'invoice-1042', pageSize: 'A4', margins: 12 });
  if (r.ok) console.log('Saved ' + r.fileName + ' (' + r.pages + ' pages)');
  else alert('Could not make the PDF: ' + r.message);
}

Um recibo montado a partir de HTML, direto para a folha de compartilhamento (imagens e fontes são carregadas antes de o PDF ser gerado):

async function shareReceipt(order) {
  var html = '<style>body{font-family:sans-serif} td{padding:4px 8px}</style>' +
    '<img src="img/logo.png" width="120"><h1>Receipt #' + order.id + '</h1><table>' +
    order.items.map(function (i) { return '<tr><td>' + i.name + '</td><td>' + i.price + '</td></tr>'; }).join('') +
    '</table><p><b>Total ' + order.total + '</b></p>';
  var r = await AppMint.pdf.fromHtml(html, { fileName: 'receipt-' + order.id, pageSize: 'Letter', save: 'share' });
  if (!r.ok) console.warn(r.code, r.message);
}

Recupere os bytes para enviá-los você mesmo:

async function uploadReport() {
  var r = await AppMint.pdf.fromPage({ landscape: true, margins: { top: 10, right: 8, bottom: 10, left: 8 }, save: 'base64' });
  if (!r.ok) return;
  var bin = atob(r.base64), buf = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
  await fetch('/api/reports', { method: 'POST', headers: { 'Content-Type': 'application/pdf' }, body: buf });
}

As chamadas diretas recebem as opções como texto JSON e um id de callback; use o auxiliar acima:

if (window.WebToApk && typeof window.WebToApk.pdfFromPage === 'function') {
  // AppMint.pdf.fromPage wraps: WebToApk.pdfFromPage(JSON.stringify(options), callbackId)
  // and AppMint.pdf.fromHtml wraps: WebToApk.pdfFromHtml(html, JSON.stringify(options), callbackId)
  AppMint.pdf.fromPage({ save: 'downloads' }).then(function (r) { console.log(r); });
}

Observações: Oculte botões e menus do PDF com @media print { .no-print { display: none } }. Um PDF baixado mostra "Baixado <name> · Abrir" como qualquer download. fromHtml executa os scripts do HTML (gráficos desenhados por script aparecem), mas não tem acesso aos recursos nativos do app. Páginas maiores que uma folha são divididas como o Chrome faria; use break-before: page para controlar onde.

navigator.clipboard padrão web#

navigator.clipboard.writeText / readText (and write / read)

Exemplo

A Clipboard API padrão, respondida pela área de transferência real do celular dentro do app.

Retorna: writeText(text) → uma Promise que resolve quando copiado e, caso contrário, rejeita com um NotAllowedError. readText() → uma Promise com o texto da área de transferência ("" quando vazia). write([item]) / read() → Promises, como no padrão. Requer: nada. Instalado no início do documento, então uma verificação feita enquanto a página é lida já o encontra.

Copiar:

async function copyLink(link) {
  if (!(navigator.clipboard && navigator.clipboard.writeText)) { alert('Copy is not available'); return; }
  try {
    await navigator.clipboard.writeText(link);
    showMessage('Link copied');
  } catch (e) {
    showMessage('Could not copy');
  }
}

Colar, a partir de um toque:

document.getElementById('paste').onclick = async function () {
  try {
    var text = await navigator.clipboard.readText();
    document.getElementById('input').value = text;
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
};

Copie uma imagem (por exemplo, um QR code desenhado em um canvas) com ClipboardItem:

async function copyCanvas(canvas) {
  if (!(navigator.clipboard && navigator.clipboard.write && window.ClipboardItem)) {
    alert('Copying images is not available here');
    return;
  }
  var blob = await new Promise(function (r) { canvas.toBlob(r, 'image/png'); });
  try {
    await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
    showMessage('Image copied');
  } catch (e) {
    showMessage('Could not copy the image');
  }
}

Observações: No app, writeText/readText chamam WebToApk.copyToClipboard e WebToApk.readClipboard. write() aceita apenas o primeiro ClipboardItem e suporta text/plain, text/html e uma imagem (até 16 MB; a imagem tem prioridade sobre o texto no mesmo item). read() resolve para [item] com o texto, o HTML ou a imagem da área de transferência ([] quando está vazia). Somente as páginas do próprio app podem lê-la: um site de terceiros aberto dentro do app é recusado (NotAllowedError). Leia em um toque do usuário: o Android 12+ mostra um aviso "colado da área de transferência". Em um navegador, o mesmo código funciona com as regras de permissão do próprio navegador.

navigator.share padrão web#

navigator.share(data) / navigator.canShare(data)

Exemplo

A Web Share API padrão, adaptada para funcionar dentro do app: texto e links abrem a folha de compartilhamento do Android, e files são compartilhados como arquivos de verdade (WhatsApp, Gmail, Drive…).

Retorna: navigator.share → uma Promise que resolve quando a folha abre (não informa qual app foi escolhido). navigator.canShare → true/false. Requer: nada. Instalado no início do documento, então uma verificação feita enquanto a página é lida já o encontra.

Texto e um link:

async function shareItem(item) {
  if (!navigator.share) { alert('Sharing is not available here'); return; }
  try {
    await navigator.share({ title: item.name, text: 'Look at this', url: item.link });
  } catch (e) { /* in a browser: AbortError when the user closes the sheet */ }
}

Arquivos: verifique primeiro com canShare:

async function sharePdf(blob) {
  var file = new File([blob], 'invoice.pdf', { type: 'application/pdf' });
  var data = { files: [file], title: 'Invoice', text: 'Invoice for March' };
  if (!(navigator.canShare && navigator.canShare(data))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share(data);
  } catch (e) { console.log('Share failed', e); }
}

Vários arquivos escolhidos pelo usuário: fotos do mesmo tipo são compartilhadas com esse tipo:

document.getElementById('photos').onchange = async function (e) {
  var files = Array.from(e.target.files);
  if (!files.length) return;
  var data = { files: files, title: 'Trip photos' };
  if (!(navigator.canShare && navigator.canShare(data))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share(data);
  } catch (err) { console.log('Share failed', err); }
};

Observações: No app, title vira o assunto, e text + url são enviados como um único texto. Com files, cada arquivo é lido na página e enviado como base64 (WebToApk.shareFiles), então mantenha os arquivos em alguns MB. Se a folha de compartilhamento não puder abrir, os arquivos são salvos como downloads e a promise ainda resolve. canShare retorna true para qualquer compartilhamento de texto. Apps criados com IA podem usar share de @/lib/appmintNative.

window.print padrão web#

window.print()

Exemplo

A chamada de impressão padrão, adaptada para funcionar dentro do app: abre a tela de impressão do Android, onde o usuário imprime no papel ou salva um PDF.

Retorna: nada. Requer: nada.

document.getElementById('print-receipt').onclick = function () {
  window.print();
};

Oculte os controles do app no papel com uma folha de estilo de impressão:

<style>
  @media print {
    nav, .toolbar, button { display: none !important; }
    body { background: #fff; color: #000; }
  }
</style>

Imprima uma etiqueta de envio sem a página ao redor: um iframe srcdoc da mesma origem imprime apenas o próprio documento:

function esc(s) {
  return String(s).replace(/[&<>"]/g, function (c) {
    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c];
  });
}
function printLabel(name, address) {
  var frame = document.createElement('iframe');
  frame.style.cssText = 'position:fixed;width:0;height:0;border:0';
  frame.srcdoc = '<title>Label</title><style>body{font:18px sans-serif;margin:24px}</style>' +
                 '<b>' + esc(name) + '</b><br>' + esc(address).replace(/\n/g, '<br>');
  frame.onload = function () {
    frame.contentWindow.print();
    setTimeout(function () { frame.remove(); }, 1000);
  };
  document.body.appendChild(frame);
}

Observações: window.print() imprime a página principal como ela está desenhada agora. Um <iframe> da mesma origem (o iframe oculto que uma biblioteca de impressão preenche com um recibo e no qual chama iframe.contentWindow.print(), ou uma página em iframe chamando seu próprio window.print()) imprime o documento DESSE iframe (veja __printHtml). Um iframe de origem diferente não pode ser impresso pela página. A substituição existe antes do primeiro script da página. beforeprint não pode ser cancelado (em nenhum navegador).

__clipboardRead ponte#

window.WebToApk.__clipboardRead(): String

navigator.clipboard.read(): `{parts:[{type, text|base64}]}`. Mesmo alcance de readClipboard() neste shell (que não tem verificação de origem).

Exemplo

Transporte interno por trás de navigator.clipboard.read(). As páginas usam a API padrão; nunca chame isso diretamente - os métodos __ podem mudar sem aviso.

Retorna: {parts:[{type, text|base64}]}, com o mesmo alcance de readClipboard() neste app. Requer: nada para ativar.

Use a API pública:

canvas.toBlob(async (png) => {
  await navigator.clipboard.write([new ClipboardItem({ 'image/png': png })]);
});
const [item] = await navigator.clipboard.read();
if (item && item.types.includes('image/png')) preview(await item.getType('image/png'));

Cole texto formatado em um editor, mantendo a formatação quando a área de transferência tiver HTML:

async function pasteRich(editor) {
  try {
    var items = await navigator.clipboard.read();
    if (!items.length) { showMessage('The clipboard is empty'); return; }
    var item = items[0];
    if (item.types.includes('text/html')) {
      editor.insertHtml(sanitize(await (await item.getType('text/html')).text()));
    } else if (item.types.includes('text/plain')) {
      editor.insertText(await (await item.getType('text/plain')).text());
    }
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
}

Cole qualquer imagem copiada (PNG, JPEG, WebP…) como foto de perfil e identifique uma leitura recusada:

document.getElementById('paste-photo').onclick = async function () {
  try {
    var items = await navigator.clipboard.read();
    var item = items[0];
    var type = item && item.types.find(function (t) { return t.indexOf('image/') === 0; });
    if (!type) { showMessage('Copy a picture first'); return; }
    uploadAvatar(await item.getType(type));   // a Blob of that type
  } catch (e) {
    if (e.name === 'NotAllowedError') showMessage('This page cannot read the clipboard');
    else showMessage('Could not paste the picture');
  }
};

Observações: O Android 12+ mostra sua própria mensagem "colado da sua área de transferência" quando um app lê a área de transferência.

__clipboardWrite ponte#

window.WebToApk.__clipboardWrite(partsJson: String): Boolean

navigator.clipboard.write(): [partsJson] é `[{type, text}|{type, base64}]` para um ClipboardItem: text/plain, text/html ou uma imagem copiada como URI de conteúdo.

Exemplo

Transporte interno por trás de navigator.clipboard.write([ClipboardItem]). As páginas usam a API padrão; nunca chame isso diretamente - os métodos __ podem mudar sem aviso.

Retorna: true quando o conteúdo foi colocado. Requer: nada para ativar.

Use a API pública:

canvas.toBlob(async (png) => {
  await navigator.clipboard.write([new ClipboardItem({ 'image/png': png })]);
});
const [item] = await navigator.clipboard.read();
if (item && item.types.includes('image/png')) preview(await item.getType('image/png'));

Copie o texto formatado e a versão simples dele em um único item, para que editores avançados recebam a tabela e campos simples recebam o texto:

async function copyTable(table) {
  if (!(navigator.clipboard && navigator.clipboard.write && window.ClipboardItem)) return;
  try {
    await navigator.clipboard.write([new ClipboardItem({
      'text/html': new Blob([table.outerHTML], { type: 'text/html' }),
      'text/plain': new Blob([table.innerText], { type: 'text/plain' })
    })]);
    showMessage('Table copied');
  } catch (e) {
    showMessage('Could not copy (' + e.name + ')');   // NotAllowedError when the write fails
  }
}

Copie uma imagem que a página mostra, como um QR code, para poder colá-la em uma conversa:

async function copyImage(img) {
  try {
    var blob = await (await fetch(img.src)).blob();   // an image/* type, up to 16 MB
    await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
    showMessage('Image copied');
  } catch (e) {
    showMessage('Could not copy the image');
  }
}

Observações: Texto, HTML e uma imagem por item. A imagem é copiada como um URI de conteúdo pelo FileProvider do app, então pode ser colada no WhatsApp, no Gmail ou no Keep.

__printHtml ponte#

window.WebToApk.__printHtml(html: String, baseUrl: String, title: String)

Imprime [html] (o documento serializado de um iframe da mesma origem, enviado pelo shim de `window.print()` ([PrintShim])) por meio de uma WebView fora da tela em [baseUrl], porque o adaptador de impressão da WebView principal só consegue imprimir a página inteira. [title] dá nome ao trabalho de impressão ("<App name> Document" quando vazio).

Exemplo

Transporte interno por trás da impressão de um iframe: iframe.contentWindow.print() (e o window.print() da própria página do iframe) envia o documento desse iframe para cá, e o app o imprime pela tela de impressão do Android. As páginas usam as chamadas padrão; nunca chame isto diretamente.

Retorna: nada. Requer: nada.

O padrão de recibo que isso viabiliza: um iframe oculto preenchido apenas com o recibo:

function printReceipt(order) {
  var frame = document.createElement('iframe');
  frame.style.display = 'none';
  document.body.appendChild(frame);
  var doc = frame.contentWindow.document;
  doc.open();
  doc.write('<html><head><title>Receipt ' + order.id + '</title>' +
            '<style>body{font:14px sans-serif} td{padding:2px 8px}</style></head><body>' +
            '<h2>Receipt ' + order.id + '</h2><table>' +
            order.lines.map(function (l) { return '<tr><td>' + l.name + '</td><td>' + l.price + '</td></tr>'; }).join('') +
            '</table></body></html>');
  doc.close();
  frame.contentWindow.print();   // prints the RECEIPT, not the page behind it
  setTimeout(function () { frame.remove(); }, 1000);
}

Uma página de ingresso sua, carregada em um iframe oculto da mesma origem: o CSS e as imagens carregam, e o <title> dela dá nome ao trabalho de impressão:

function printTicket(id) {
  var frame = document.createElement('iframe');
  frame.style.cssText = 'position:fixed;width:0;height:0;border:0';
  frame.src = 'ticket.html?id=' + encodeURIComponent(id);
  frame.onload = function () {
    frame.contentWindow.print();   // prints ticket.html's DOM as it stands now
    setTimeout(function () { frame.remove(); }, 1000);
  };
  document.body.appendChild(frame);
}

Ajuste o documento do iframe logo antes do instantâneo com beforeprint na janela do iframe:

function printWithTimestamp(frame) {
  var w = frame.contentWindow;
  w.addEventListener('beforeprint', function () {
    var p = w.document.createElement('p');
    p.textContent = 'Printed ' + new Date().toLocaleString();
    w.document.body.appendChild(p);
  }, { once: true });
  w.print();   // the printed copy includes the line added above
}

Observações: Os argumentos são (html, baseUrl, title): o documento serializado do iframe, o endereço em relação ao qual seus links relativos são resolvidos (o endereço da própria página para um iframe about:blank) e o nome do trabalho de impressão (o <title> do iframe, ou o da página). O instantâneo é tirado quando print() é chamado (os scripts dentro dele não rodam de novo), e beforeprint / afterprint disparam na janela do iframe ao redor dele. Um iframe de origem diferente não pode ser impresso pela página.

clearSharedText ponte#

window.WebToApk.clearSharedText()

Limpa o compartilhamento guardado depois que a página o consumiu.

Exemplo

Esquece o texto ou link que foi compartilhado com o app, depois que sua página o usou.

Retorna: nada. Requer: nada (o compartilhamento em si requer Receber compartilhamentos de outros apps com Texto e links marcado).

Leia o compartilhamento guardado, use-o e depois limpe-o para que não seja tratado de novo após um recarregamento:

if (window.WebToApk && typeof window.WebToApk.getSharedText === 'function') {
  var raw = window.WebToApk.getSharedText();
  if (raw) {
    var shared = JSON.parse(raw);          // { text, subject, url, receivedAt }
    saveNote(shared.subject, shared.text);
    window.WebToApk.clearSharedText();     // getSharedText() now returns ""
  }
}

window.addEventListener('appmint:shared', function (e) {
  saveNote(e.detail.subject, e.detail.text);
  if (window.WebToApk && window.WebToApk.clearSharedText) window.WebToApk.clearSharedText();
});

Pergunte primeiro e limpe o compartilhamento seja qual for o botão que o usuário tocar:

function askAboutShare(s) {
  var dialog = document.getElementById('share-dialog');
  document.getElementById('share-preview').textContent = s.text;
  dialog.hidden = false;
  function done() {
    dialog.hidden = true;
    if (window.WebToApk && typeof window.WebToApk.clearSharedText === 'function') {
      window.WebToApk.clearSharedText();
    }
  }
  document.getElementById('share-save').onclick = function () { saveNote(s.subject, s.text); done(); };
  document.getElementById('share-discard').onclick = done;
}

Limpe somente depois que estiver salvo com segurança, para que um envio que falhou possa ser tentado de novo:

async function importShare() {
  if (!(window.WebToApk && typeof window.WebToApk.getSharedText === 'function')) return;
  var raw = window.WebToApk.getSharedText();
  if (!raw) return;
  var s = JSON.parse(raw);
  try {
    var res = await fetch('/api/bookmarks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url: s.url || s.text, title: s.subject })
    });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    window.WebToApk.clearSharedText();
  } catch (e) {
    showMessage('Could not save the link — tap Retry');   // Retry calls importShare() again
  }
}

Observações: Limpar afeta apenas getSharedText(). Um novo compartilhamento substitui o guardado e dispara appmint:shared de novo. Os arquivos compartilhados com o app são liberados separadamente com AppMint.releaseOpenedFile().

copyToClipboard ponte#

window.WebToApk.copyToClipboard(text: String): Boolean

Use o wrapper de promessa: const r = await AppMint.requestReview(); // {result, openedStore} const r = await AppMint.requestReview({ openStore: true }); // página da loja se não houver janela r.result: 'shown' | 'not_from_play' | 'quota' | 'error'. Veja RatePrompt.

Descrito pelo seu grupo, Janela de avaliação no app do Google Play, e não isoladamente.

Exemplo

Copia texto simples para a área de transferência do celular. As páginas normalmente usam o navigator.clipboard.writeText(text) padrão, que o shell encaminha para cá.

Retorna: true em caso de sucesso, false em caso de falha (de forma síncrona). Requer: nada.

Primeiro a API padrão:

document.getElementById('copy-btn').onclick = async function () {
  try {
    await navigator.clipboard.writeText('PROMO-2026');
    showMessage('Copied');
  } catch (e) {
    showMessage('Could not copy');
  }
};

A chamada bruta:

if (window.WebToApk && typeof window.WebToApk.copyToClipboard === 'function') {
  var ok = window.WebToApk.copyToClipboard('PROMO-2026');
  showMessage(ok ? 'Copied' : 'Could not copy');
}

Um botão "Copiar" em cada bloco de código, usando a resposta síncrona para atualizar o rótulo:

document.querySelectorAll('pre').forEach(function (pre) {
  var btn = document.createElement('button');
  btn.textContent = 'Copy';
  btn.onclick = function () {
    var ok = !!(window.WebToApk && typeof window.WebToApk.copyToClipboard === 'function' &&
                window.WebToApk.copyToClipboard(pre.innerText));
    btn.textContent = ok ? 'Copied' : 'Copy failed';
    setTimeout(function () { btn.textContent = 'Copy'; }, 1500);
  };
  pre.parentNode.insertBefore(btn, pre);
});

Observações: Somente texto simples (sem imagens nem HTML). O Android 13+ mostra a própria pequena prévia "Copiado", então talvez você não precise de uma mensagem sua ali.

getSharedText ponte#

window.WebToApk.getSharedText(): String

Texto ou link que outro app compartilhou COM este ("Compartilhar para <app>": um vídeo do YouTube compartilhado pelo app do YouTube chega como o link dele), como JSON `{text, subject, url, receivedAt}` ou "" quando nada foi compartilhado. Mantido até ser lido, de modo que um app que carregue depois do compartilhamento ainda o veja; um compartilhamento que chegue com o app em uso também dispara `appmint:shared`. Uma foto ou vídeo compartilhado com legenda entrega o arquivo por AppMint.getOpenedFile() e a legenda aqui. Requer a opção "Receber compartilhamentos de outros apps" do build com "Texto e links" marcado, que registra a entrada na folha de compartilhamento.

Exemplo

Texto ou link que outro app compartilhou COM o seu app ("Compartilhar → seu app"), como uma string JSON, ou "" quando nada foi compartilhado.

Retorna: uma string JSON {text, subject, url, receivedAt} ou "" (de forma síncrona). url é o primeiro link http(s):// encontrado em text (ou ""); receivedAt é um instante em milissegundos. Requer: Receber compartilhamentos de outros apps ativado ao compilar, com Texto e links marcado: isso coloca seu app no menu Compartilhar de outros apps.

O compartilhamento é mantido até você limpá-lo, então leia-o na inicialização e escute os compartilhamentos que chegarem enquanto o app estiver rodando:

function handleShare(s) {
  addBookmark(s.url || s.text, s.subject);
  if (window.WebToApk && window.WebToApk.clearSharedText) window.WebToApk.clearSharedText();
}

// 1. On start (the app may have been opened by the share).
if (window.WebToApk && typeof window.WebToApk.getSharedText === 'function') {
  var raw = window.WebToApk.getSharedText();
  if (raw) handleShare(JSON.parse(raw));
}

// 2. While running: detail has the same keys { text, subject, url, receivedAt }.
window.addEventListener('appmint:shared', function (e) {
  handleShare(e.detail);
});

Um hook global recebe o mesmo objeto, se você preferir um:

window.onAppMintShared = function (s) { handleShare(s); };

Abra um vídeo do YouTube que foi compartilhado com o app, usando o campo url:

function openSharedVideo() {
  if (!(window.WebToApk && typeof window.WebToApk.getSharedText === 'function')) return;
  var raw = window.WebToApk.getSharedText();
  if (!raw) return;
  var s = JSON.parse(raw);
  var m = /(?:youtu\.be\/|[?&]v=)([\w-]{11})/.exec(s.url || s.text);
  if (m) {
    showVideo(m[1]);
    window.WebToApk.clearSharedText();
  } else {
    showMessage('That share is not a video link');
  }
}

Observações: Um vídeo do YouTube compartilhado pelo app do YouTube chega como o link dele. Uma foto ou vídeo compartilhado COM legenda entrega o arquivo por AppMint.getOpenedFile() e a legenda aqui. Limpe o compartilhamento depois de usá-lo, ou você o verá de novo na próxima inicialização. Apps criados com IA podem usar sharedText() / onShared() de @/lib/appmintNative.

pdfFromHtml ponte#

window.WebToApk.pdfFromHtml(html: String, optionsJson: String, callbackId: String)

Renderiza [html] fora da tela na origem do app e o salva como PDF: use `AppMint.pdf.fromHtml(html, options)`.

Exemplo

Documentado junto com AppMint.pdf - o exemplo de lá mostra este também.

Gera um PDF de verdade (texto selecionável, gráficos vetoriais) sem diálogo de impressão: fromPage salva a página como está na tela, com o layout do seu CSS @media print; fromHtml renderiza o HTML que você passar em uma página invisível no endereço do próprio app, de modo que imagens, folhas de estilo e fontes relativas carregam como na sua página, aguarda as fontes e todas as imagens e então salva. window.print() continua abrindo o diálogo de impressão normal.

Retorna: uma Promise de {ok: true, fileName, pages, bytes, saved, uri?, base64?} ou {ok: false, code, message}: code é busy (um PDF por vez), bad-options, failed, too-large (base64 acima de 25 MB) ou permission-denied (armazenamento recusado no Android 7-9). Requer: nada: todo build o tem. Salvar em Downloads não requer permissão no Android 10+.

Opções: fileName (".pdf" é adicionado; padrão: o título da página), pageSize 'A4' (padrão) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins em milímetros (um número ou {top, right, bottom, left}; omitido = sua própria regra @page), save 'downloads' (padrão) | 'share' | 'base64'.

Salvar a página atual em Downloads:

async function saveInvoice() {
  if (!(window.AppMint && AppMint.pdf)) { window.print(); return; }   // in a browser
  var r = await AppMint.pdf.fromPage({ fileName: 'invoice-1042', pageSize: 'A4', margins: 12 });
  if (r.ok) console.log('Saved ' + r.fileName + ' (' + r.pages + ' pages)');
  else alert('Could not make the PDF: ' + r.message);
}

Um recibo montado a partir de HTML, direto para a folha de compartilhamento (imagens e fontes são carregadas antes de o PDF ser gerado):

async function shareReceipt(order) {
  var html = '<style>body{font-family:sans-serif} td{padding:4px 8px}</style>' +
    '<img src="img/logo.png" width="120"><h1>Receipt #' + order.id + '</h1><table>' +
    order.items.map(function (i) { return '<tr><td>' + i.name + '</td><td>' + i.price + '</td></tr>'; }).join('') +
    '</table><p><b>Total ' + order.total + '</b></p>';
  var r = await AppMint.pdf.fromHtml(html, { fileName: 'receipt-' + order.id, pageSize: 'Letter', save: 'share' });
  if (!r.ok) console.warn(r.code, r.message);
}

Recupere os bytes para enviá-los você mesmo:

async function uploadReport() {
  var r = await AppMint.pdf.fromPage({ landscape: true, margins: { top: 10, right: 8, bottom: 10, left: 8 }, save: 'base64' });
  if (!r.ok) return;
  var bin = atob(r.base64), buf = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
  await fetch('/api/reports', { method: 'POST', headers: { 'Content-Type': 'application/pdf' }, body: buf });
}

As chamadas diretas recebem as opções como texto JSON e um id de callback; use o auxiliar acima:

if (window.WebToApk && typeof window.WebToApk.pdfFromPage === 'function') {
  // AppMint.pdf.fromPage wraps: WebToApk.pdfFromPage(JSON.stringify(options), callbackId)
  // and AppMint.pdf.fromHtml wraps: WebToApk.pdfFromHtml(html, JSON.stringify(options), callbackId)
  AppMint.pdf.fromPage({ save: 'downloads' }).then(function (r) { console.log(r); });
}

Observações: Oculte botões e menus do PDF com @media print { .no-print { display: none } }. Um PDF baixado mostra "Baixado <name> · Abrir" como qualquer download. fromHtml executa os scripts do HTML (gráficos desenhados por script aparecem), mas não tem acesso aos recursos nativos do app. Páginas maiores que uma folha são divididas como o Chrome faria; use break-before: page para controlar onde.

pdfFromPage ponte#

window.WebToApk.pdfFromPage(optionsJson: String, callbackId: String)

Salva a página como PDF sem diálogo de impressão: use `AppMint.pdf.fromPage(options)`. [optionsJson] `{fileName, pageSize, landscape, margins, save}`; resolve [callbackId].

Exemplo

Documentado junto com AppMint.pdf - o exemplo de lá mostra este também.

Gera um PDF de verdade (texto selecionável, gráficos vetoriais) sem diálogo de impressão: fromPage salva a página como está na tela, com o layout do seu CSS @media print; fromHtml renderiza o HTML que você passar em uma página invisível no endereço do próprio app, de modo que imagens, folhas de estilo e fontes relativas carregam como na sua página, aguarda as fontes e todas as imagens e então salva. window.print() continua abrindo o diálogo de impressão normal.

Retorna: uma Promise de {ok: true, fileName, pages, bytes, saved, uri?, base64?} ou {ok: false, code, message}: code é busy (um PDF por vez), bad-options, failed, too-large (base64 acima de 25 MB) ou permission-denied (armazenamento recusado no Android 7-9). Requer: nada: todo build o tem. Salvar em Downloads não requer permissão no Android 10+.

Opções: fileName (".pdf" é adicionado; padrão: o título da página), pageSize 'A4' (padrão) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins em milímetros (um número ou {top, right, bottom, left}; omitido = sua própria regra @page), save 'downloads' (padrão) | 'share' | 'base64'.

Salvar a página atual em Downloads:

async function saveInvoice() {
  if (!(window.AppMint && AppMint.pdf)) { window.print(); return; }   // in a browser
  var r = await AppMint.pdf.fromPage({ fileName: 'invoice-1042', pageSize: 'A4', margins: 12 });
  if (r.ok) console.log('Saved ' + r.fileName + ' (' + r.pages + ' pages)');
  else alert('Could not make the PDF: ' + r.message);
}

Um recibo montado a partir de HTML, direto para a folha de compartilhamento (imagens e fontes são carregadas antes de o PDF ser gerado):

async function shareReceipt(order) {
  var html = '<style>body{font-family:sans-serif} td{padding:4px 8px}</style>' +
    '<img src="img/logo.png" width="120"><h1>Receipt #' + order.id + '</h1><table>' +
    order.items.map(function (i) { return '<tr><td>' + i.name + '</td><td>' + i.price + '</td></tr>'; }).join('') +
    '</table><p><b>Total ' + order.total + '</b></p>';
  var r = await AppMint.pdf.fromHtml(html, { fileName: 'receipt-' + order.id, pageSize: 'Letter', save: 'share' });
  if (!r.ok) console.warn(r.code, r.message);
}

Recupere os bytes para enviá-los você mesmo:

async function uploadReport() {
  var r = await AppMint.pdf.fromPage({ landscape: true, margins: { top: 10, right: 8, bottom: 10, left: 8 }, save: 'base64' });
  if (!r.ok) return;
  var bin = atob(r.base64), buf = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
  await fetch('/api/reports', { method: 'POST', headers: { 'Content-Type': 'application/pdf' }, body: buf });
}

As chamadas diretas recebem as opções como texto JSON e um id de callback; use o auxiliar acima:

if (window.WebToApk && typeof window.WebToApk.pdfFromPage === 'function') {
  // AppMint.pdf.fromPage wraps: WebToApk.pdfFromPage(JSON.stringify(options), callbackId)
  // and AppMint.pdf.fromHtml wraps: WebToApk.pdfFromHtml(html, JSON.stringify(options), callbackId)
  AppMint.pdf.fromPage({ save: 'downloads' }).then(function (r) { console.log(r); });
}

Observações: Oculte botões e menus do PDF com @media print { .no-print { display: none } }. Um PDF baixado mostra "Baixado <name> · Abrir" como qualquer download. fromHtml executa os scripts do HTML (gráficos desenhados por script aparecem), mas não tem acesso aos recursos nativos do app. Páginas maiores que uma folha são divididas como o Chrome faria; use break-before: page para controlar onde.

print ponte#

window.WebToApk.print()

Entrega a página como ela está agora ao diálogo de impressão do Android, que pode imprimir no papel ou salvar um PDF. Nada a configurar: a WebView renderiza o documento.

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

Exemplo

Abre a tela de impressão do Android para a página como ela está agora: imprima no papel ou "Salvar como PDF". As páginas normalmente usam o window.print() padrão, que o shell encaminha para cá.

Retorna: nada. Requer: nada.

Primeiro a API padrão:

document.getElementById('print-btn').onclick = function () {
  window.print();   // in the app: the Android print screen
};

A chamada direta faz o mesmo:

if (window.WebToApk && typeof window.WebToApk.print === 'function') {
  window.WebToApk.print();
}

Imprima uma fatura de uma lista marcando-a para uma folha de estilo de impressão (deixe a marca: a tela de impressão monta a página depois que print() retorna):

// CSS: @media print { .invoice:not(.print-me), nav, button { display: none !important; } }
function printInvoice(id) {
  document.querySelectorAll('.invoice.print-me').forEach(function (el) {
    el.classList.remove('print-me');
  });
  document.getElementById('invoice-' + id).classList.add('print-me');
  if (window.WebToApk && typeof window.WebToApk.print === 'function') {
    window.WebToApk.print();
  } else {
    window.print();   // a normal browser
  }
}

Observações: Imprime a WebView inteira, ou seja, a página principal. O print() próprio de um iframe da mesma origem imprime só aquele iframe (veja window.print). Use uma folha de estilo @media print para ocultar botões e menus. O trabalho se chama "<App name> Document". Os erros só são registrados no log.

readClipboard ponte#

window.WebToApk.readClipboard(): String

Use o wrapper de promessa: const r = await AppMint.requestReview(); // {result, openedStore} const r = await AppMint.requestReview({ openStore: true }); // página da loja se não houver janela r.result: 'shown' | 'not_from_play' | 'quota' | 'error'. Veja RatePrompt.

Descrito pelo seu grupo, Janela de avaliação no app do Google Play, e não isoladamente.

Exemplo

Lê o texto que está agora na área de transferência do celular. As páginas normalmente usam o navigator.clipboard.readText() padrão, que o shell encaminha para cá.

Retorna: o texto, ou "" quando a área de transferência está vazia ou não pode ser lida (de forma síncrona). Requer: nada.

Primeiro a API padrão, por exemplo um botão "Colar código":

document.getElementById('paste-btn').onclick = async function () {
  try {
    var text = await navigator.clipboard.readText();
    if (text) document.getElementById('code').value = text.trim();
    else showMessage('The clipboard is empty');
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
};

A chamada bruta:

if (window.WebToApk && typeof window.WebToApk.readClipboard === 'function') {
  var text = window.WebToApk.readClipboard();
  if (text) document.getElementById('code').value = text;
}

Um botão "Colar link" que pega o primeiro endereço web do que foi copiado:

document.getElementById('paste-link').onclick = function () {
  if (!(window.WebToApk && typeof window.WebToApk.readClipboard === 'function')) return;
  var text = window.WebToApk.readClipboard();
  var m = /https?:\/\/\S+/.exec(text);
  if (m) document.getElementById('link').value = m[0];
  else showMessage('There is no link on the clipboard');
};

Observações: Somente as páginas do próprio app podem lê-la: um site de terceiros aberto dentro do app recebe "". O Android só deixa o app ler a área de transferência enquanto ele está na tela, e o Android 12+ mostra um aviso "colado da área de transferência": leia em um toque do usuário, não ao carregar a página.

shareFiles ponte#

window.WebToApk.shareFiles(filesJson: String, title: String, text: String): Boolean

Compartilhamento NATIVO de arquivo de verdade para navigator.share({files:[...]}). A WebView do Android não tem a Web Share API, e um ACTION_SEND text/plain não consegue carregar um arquivo, então antes disso uma imagem compartilhada era apenas SALVA, não compartilhada. filesJson é [{name, mimeType, base64}, ...]; gravamos cada um no cache, o expomos pelo FileProvider do app e disparamos ACTION_SEND / ACTION_SEND_MULTIPLE para que a folha de compartilhamento real do Android (WhatsApp, Gmail, …) abra. Retorna true se a folha abriu.

Exemplo

Compartilha um ou mais arquivos de verdade (uma imagem, um PDF…) pela folha de compartilhamento do Android. As páginas normalmente usam o navigator.share({ files: [...] }) padrão, que o shell encaminha para cá.

Retorna: true se a folha de compartilhamento abriu, false caso contrário (de forma síncrona). Requer: nada.

Primeiro a API padrão:

async function shareCanvas(canvas) {
  var blob = await new Promise(function (r) { canvas.toBlob(r, 'image/png'); });
  var file = new File([blob], 'drawing.png', { type: 'image/png' });
  if (!(navigator.canShare && navigator.canShare({ files: [file] }))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share({ files: [file], title: 'My drawing', text: 'Made in my app' });
  } catch (e) { /* in a browser: the user closed the sheet */ }
}

A chamada direta recebe uma string JSON de [{name, mimeType, base64}] (base64 sem o prefixo data:), depois title e text:

function shareBase64Png(base64) {
  if (!(window.WebToApk && typeof window.WebToApk.shareFiles === 'function')) return false;
  var files = [{ name: 'receipt.png', mimeType: 'image/png', base64: base64 }];
  return window.WebToApk.shareFiles(JSON.stringify(files), 'Receipt', 'Your receipt');
}

Exporte uma tabela como arquivo CSV (o texto é codificado em UTF-8 antes do base64):

function shareCsv(rows) {
  if (!(window.WebToApk && typeof window.WebToApk.shareFiles === 'function')) return;
  var csv = rows.map(function (r) {
    return r.map(function (c) { return '"' + String(c).replace(/"/g, '""') + '"'; }).join(',');
  }).join('\n');
  var base64 = btoa(unescape(encodeURIComponent(csv)));
  var files = [{ name: 'expenses.csv', mimeType: 'text/csv', base64: base64 }];
  if (!window.WebToApk.shareFiles(JSON.stringify(files), 'Expenses', '')) {
    showMessage('Could not open the share sheet');
  }
}

Observações: Os arquivos são gravados no cache do app e compartilhados pelo FileProvider dele. Caracteres inseguros em name viram _. Vários arquivos do mesmo tipo são compartilhados com esse tipo; tipos mistos são compartilhados como */*. O arquivo inteiro trafega como base64 pela ponte, então mantenha-o em alguns MB. Quando navigator.share({files}) não consegue abrir a folha, o shell salva os arquivos como downloads e a promise ainda resolve.

shareNative ponte#

window.WebToApk.shareNative(title: String, text: String, url: String)

A folha de compartilhamento do Android, a área de transferência do sistema e o fechamento de uma janela aberta pela página. São os equivalentes da web comum (navigator.share, navigator.clipboard, window.close) respondidos nativamente, para que se comportem igual dentro do app.

Descrito por seu grupo, Compartilhar, área de transferência e janela, e não separadamente.

Exemplo

Abre a folha de compartilhamento do Android com um título, um texto e um link. As páginas normalmente usam o navigator.share({title, text, url}) padrão, que o shell encaminha para cá.

Retorna: nada (não informa qual app o usuário escolheu). Requer: nada.

Primeiro a API padrão: ela também funciona em um navegador de celular:

document.getElementById('share-btn').onclick = async function () {
  if (!navigator.share) { alert('Sharing is not available here'); return; }
  try {
    await navigator.share({ title: 'My app', text: 'Look at this', url: 'https://mysite.com/item/42' });
  } catch (e) {
    // in a browser: AbortError when the user closes the sheet
  }
};

A chamada direta, com os três argumentos como strings (use '' para omitir um):

if (window.WebToApk && typeof window.WebToApk.shareNative === 'function') {
  window.WebToApk.shareNative('My app', 'Look at this', 'https://mysite.com/item/42');
}

Somente texto, sem link, por exemplo a pontuação de um jogo:

function shareScore(score) {
  if (!(window.WebToApk && typeof window.WebToApk.shareNative === 'function')) return;
  window.WebToApk.shareNative('My score', 'I scored ' + score + ' points. Can you beat it?', '');
}

Observações: O texto compartilhado é text + uma nova linha + url (ou apenas um deles). title vira o assunto do e-mail e o título da folha de compartilhamento. Somente texto e links: para compartilhar um arquivo, use navigator.share({files}) (veja shareFiles).

Links de botão share: em mensagens dentro do app. Um botão cujo link é share:<text> abre esta mesma folha de compartilhamento com esse texto, sem código na página. O modelo Convidar amigos usa share:Try {app}: {store}; o app preenche o próprio nome e o endereço dele na Play. Sua página pode saber que isso aconteceu:

window.addEventListener('appmint:inapp', function (e) {
  var d = e.detail;
  if (d.type === 'clicked' && d.link && d.link.indexOf('share:') === 0) analytics('invite_shared', d.name);
});

Se você desenha as mensagens dentro do app por conta própria (setInAppPresentation('none')), execute um botão share: da mesma forma:

function runInAppButton(link) {
  if (link.indexOf('share:') === 0 && window.WebToApk && WebToApk.shareNative) {
    WebToApk.shareNative('', link.slice(6), '');   // text only
  }
}

Um link copy:<text> copia em vez de compartilhar, por exemplo um código de cupom de uma mensagem promocional:

window.addEventListener('appmint:inapp', function (e) {
  var d = e.detail;
  if (d.type === 'clicked' && d.link === 'copy:SAVE20') showToast('Code SAVE20 copied');
});

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.