Crea APK gratis

API del bridge JavaScript

Condivisione, appunti e stampa

Il foglio di condivisione di Android, gli appunti di sistema, il testo che un'altra app ha condiviso nella tua e la finestra di stampa.

AppMint.pdf helper#

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

Esempio

Crea un vero PDF (testo selezionabile, grafica vettoriale) senza finestra di stampa: fromPage salva la pagina cosĂŹ com'Ăš sullo schermo, impaginata con il tuo CSS @media print; fromHtml genera l'HTML che passi in una pagina invisibile all'indirizzo dell'app, quindi immagini, fogli di stile e font relativi si caricano come nella tua pagina, attende i font e ogni immagine, poi lo salva. window.print() apre comunque la normale finestra di stampa.

Restituisce: una Promise di {ok: true, fileName, pages, bytes, saved, uri?, base64?} oppure {ok: false, code, message}: code Ăš busy (un PDF alla volta), bad-options, failed, too-large (base64 oltre 25 MB) o permission-denied (archiviazione rifiutata su Android 7-9). Richiede: niente: ogni build lo include. Salvare in Download non richiede autorizzazioni su Android 10+.

Opzioni: fileName (".pdf" viene aggiunto; predefinito: il titolo della pagina), pageSize 'A4' (predefinito) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins in millimetri (un numero, oppure {top, right, bottom, left}; se omesso = la tua regola @page), save 'downloads' (predefinito) | 'share' | 'base64'.

Salva la pagina corrente in Download:

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

Una ricevuta creata da HTML, direttamente nel foglio di condivisione (immagini e font vengono caricati prima di creare il PDF):

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

Recupera i byte per caricarli tu stesso:

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

Le chiamate dirette prendono le opzioni come testo JSON e un id di callback; usa invece l'helper qui sopra:

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

Note: Nascondi pulsanti e menu dal PDF con @media print { .no-print { display: none } }. Un PDF scaricato mostra "Scaricato <nome> · Apri" come ogni download. fromHtml esegue gli script dell'HTML (i grafici disegnati da script compaiono) ma non ha accesso alle funzioni native dell'app. Le pagine piĂč lunghe di un foglio vengono divise come farebbe Chrome; usa break-before: page per controllare dove.

navigator.clipboard standard web#

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

Esempio

L'API Clipboard standard, servita dagli appunti veri del telefono dentro l'app.

Restituisce: writeText(text) → una Promise che si risolve quando il testo Ăš copiato e viene rifiutata con un NotAllowedError in caso contrario. readText() → una Promise con il testo degli appunti ("" se vuoti). write([item]) / read() → Promise, come nello standard. Richiede: niente. Installato all'avvio del documento, quindi un controllo fatto mentre la pagina viene analizzata lo trova giĂ .

Copia:

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

Incolla, da un tocco:

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

Copia un'immagine (per esempio un codice QR disegnato su un canvas) con 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');
  }
}

Note: Nell'app, writeText/readText chiamano WebToApk.copyToClipboard e WebToApk.readClipboard. write() prende solo il primo ClipboardItem e supporta text/plain, text/html e un'immagine (fino a 16 MB; un'immagine ha la precedenza sul testo nello stesso elemento). read() si risolve in [item] con il testo, l'HTML o l'immagine degli appunti ([] se vuoti). Solo le pagine dell'app possono leggerli: un sito esterno aperto nell'app viene rifiutato (NotAllowedError). Leggili da un tocco dell'utente: Android 12+ mostra un avviso "incollato dagli appunti". In un browser lo stesso codice funziona con le regole di autorizzazione del browser.

navigator.share standard web#

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

Esempio

La Web Share API standard, fatta funzionare nell'app: testo e link aprono il foglio di condivisione di Android, e files vengono condivisi come file veri (WhatsApp, Gmail, Drive…).

Restituisce: navigator.share → una Promise che si risolve quando il foglio si apre (non ti dice quale app Ăš stata scelta). navigator.canShare → true/false. Richiede: niente. Installato all'avvio del documento, quindi un controllo fatto mentre la pagina viene analizzata lo trova giĂ .

Testo e un 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 */ }
}

File: controlla prima con 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); }
}

PiĂč file scelti dall'utente: le foto dello stesso tipo si condividono con quel 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); }
};

Note: Nell'app title diventa l'oggetto, e text + url vengono inviati come un unico testo. Con files, ogni file viene letto nella pagina e inviato come base64 (WebToApk.shareFiles), quindi tieni i file entro pochi MB. Se il foglio di condivisione non si apre, i file vengono invece salvati come download e la promise si risolve comunque. canShare restituisce true per qualsiasi condivisione di testo. Le app create con l'AI possono usare share da @/lib/appmintNative.

window.print standard web#

window.print()

Esempio

La chiamata di stampa standard, fatta funzionare nell'app: apre la schermata di stampa di Android, dove l'utente stampa su carta o salva un PDF.

Restituisce: niente. Richiede: niente.

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

Nascondi i controlli dell'app sulla carta con un foglio di stile di stampa:

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

Stampa un'etichetta di spedizione senza la pagina intorno: un iframe srcdoc dello stesso dominio stampa solo il proprio 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);
}

Note: window.print() stampa la pagina principale cosĂŹ com'Ăš disegnata ora. Un <iframe> dello stesso dominio, l'iframe nascosto che una libreria di stampa riempie con una ricevuta e su cui chiama iframe.contentWindow.print(), o una pagina in iframe che chiama il proprio window.print(), stampa il documento DI QUELL'iframe (vedi __printHtml). Un iframe di un altro dominio non puĂČ essere stampato dalla pagina. La sostituzione esiste prima del primo script della pagina. beforeprint non puĂČ essere annullato (in nessun browser).

__clipboardRead bridge#

window.WebToApk.__clipboardRead(): String

navigator.clipboard.read(): `{parts:[{type, text|base64}]}`. Stessa portata di readClipboard() in questa shell (che non ha controllo sull'origine).

Esempio

Trasporto interno dietro navigator.clipboard.read(). Le pagine usano l'API standard; non chiamare mai questo direttamente: i metodi __ possono cambiare senza preavviso.

Restituisce: {parts:[{type, text|base64}]}, la stessa portata di readClipboard() in questa app. Richiede: niente da attivare.

Usa l'API pubblica:

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

Incolla testo formattato in un editor, mantenendo la formattazione quando gli appunti contengono 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');
  }
}

Incolla qualsiasi immagine copiata (PNG, JPEG, WebP…) come foto del profilo e distingui una lettura rifiutata:

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

Note: Android 12+ mostra un proprio messaggio "incollato dagli appunti" quando un'app legge gli appunti.

__clipboardWrite bridge#

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

navigator.clipboard.write(): [partsJson] Ăš `[{type, text}|{type, base64}]` per un ClipboardItem: text/plain, text/html o un'immagine copiata come content URI.

Esempio

Trasporto interno dietro navigator.clipboard.write([ClipboardItem]). Le pagine usano l'API standard; non chiamare mai questo direttamente: i metodi __ possono cambiare senza preavviso.

Restituisce: true quando il contenuto Ăš stato inserito. Richiede: niente da attivare.

Usa l'API pubblica:

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

Copia il testo formattato e la sua versione semplice in un unico elemento, cosĂŹ gli editor avanzati ricevono la tabella e i campi semplici il testo:

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

Copia un'immagine mostrata dalla pagina, come un codice QR, cosĂŹ da poterla incollare in una chat:

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

Note: Testo, HTML e un'immagine per elemento. L'immagine viene copiata come content URI tramite il FileProvider dell'app, quindi si incolla in WhatsApp, Gmail o Keep.

__printHtml bridge#

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

Stampa [html] (il documento serializzato di un iframe dello stesso dominio, inviato dallo shim `window.print()` ([PrintShim])) tramite una WebView fuori schermo all'indirizzo [baseUrl], perchĂ© l'adattatore di stampa della WebView principale puĂČ stampare solo l'intera pagina. [title] dĂ il nome al lavoro di stampa ("<Nome app> Document" se vuoto).

Esempio

Trasporto interno dietro la stampa di un iframe: iframe.contentWindow.print() (e il window.print() della pagina di un iframe) invia qui il documento di quell'iframe, e l'app lo stampa tramite la schermata di stampa di Android. Le pagine usano le chiamate standard; non chiamare mai questo direttamente.

Restituisce: niente. Richiede: niente.

Lo schema della ricevuta che questo rende possibile: un iframe nascosto riempito solo con la ricevuta:

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

Una pagina biglietto tua, caricata in un iframe nascosto dello stesso dominio: il suo CSS e le sue immagini si caricano e il suo <title> dĂ il nome al lavoro di stampa:

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

Modifica il documento dell'iframe poco prima dell'istantanea con beforeprint sulla finestra dell'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
}

Note: Gli argomenti sono (html, baseUrl, title): il documento serializzato dell'iframe, l'indirizzo rispetto a cui si risolvono i suoi link relativi (l'indirizzo della pagina stessa per un iframe about:blank) e il nome del lavoro di stampa (il <title> dell'iframe, altrimenti quello della pagina). L'istantanea viene scattata quando si chiama print(): gli script al suo interno non vengono rieseguiti, e beforeprint / afterprint scattano sulla finestra dell'iframe attorno ad essa. Un iframe di un altro dominio non puĂČ essere stampato dalla pagina.

clearSharedText bridge#

window.WebToApk.clearSharedText()

Cancella la condivisione conservata una volta che la pagina l'ha consumata.

Esempio

Dimentica il testo o il link che Ăš stato condiviso nell'app, una volta che la tua pagina lo ha usato.

Restituisce: niente. Richiede: niente (la condivisione in sé richiede Ricevi condivisioni da altre app con Testo e link selezionato).

Leggi la condivisione conservata, usala, poi cancellala cosĂŹ non viene gestita di nuovo dopo un ricaricamento:

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

Chiedi prima e cancella la condivisione qualunque pulsante tocchi l'utente:

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

Cancellali solo quando sono stati salvati in sicurezza, cosĂŹ un caricamento fallito puĂČ essere ritentato:

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

Note: La cancellazione riguarda solo getSharedText(). Una nuova condivisione sostituisce quella conservata e fa scattare di nuovo appmint:shared. I file condivisi nell'app vengono rilasciati separatamente con AppMint.releaseOpenedFile().

copyToClipboard bridge#

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

Usa il wrapper basato su promise: const r = await AppMint.requestReview(); // {result, openedStore} const r = await AppMint.requestReview({ openStore: true }); // scheda del negozio se non c'è la finestra r.result: 'shown' | 'not_from_play' | 'quota' | 'error'. Vedi RatePrompt.

Descritto dal suo gruppo, Finestra di recensione in-app di Google Play, e non da solo.

Esempio

Copia testo semplice negli appunti del telefono. Le pagine usano normalmente lo standard navigator.clipboard.writeText(text), che la shell indirizza qui.

Restituisce: true in caso di successo, false in caso di errore (in modo sincrono). Richiede: niente.

Prima l'API standard:

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

La chiamata grezza:

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

Un pulsante "Copia" su ogni blocco di codice, usando la risposta sincrona per aggiornare l'etichetta:

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

Note: Solo testo semplice (niente immagini o HTML). Android 13+ mostra una propria piccola anteprima "Copiato", quindi lĂŹ potresti non aver bisogno di un tuo messaggio.

getSharedText bridge#

window.WebToApk.getSharedText(): String

Testo o link che un'altra app ha condiviso IN questa ("Condividi con <app>": un video YouTube condiviso dall'app YouTube arriva come suo link), come JSON `{text, subject, url, receivedAt}` oppure "" quando non Ăš stato condiviso nulla. Conservato fino alla lettura, cosĂŹ un'app che si avvia dopo la condivisione lo vede comunque; una condivisione che arriva mentre l'app Ăš in esecuzione fa scattare anche `appmint:shared`. Una foto o un video condivisi con una didascalia consegnano il file tramite AppMint.getOpenedFile() e la didascalia qui. Richiede l'opzione della build "Ricevi condivisioni da altre app" con "Testo e link" selezionato, che registra la voce nel foglio di condivisione.

Esempio

Testo o link che un'altra app ha condiviso NELLA tua app ("Condividi → la tua app"), come stringa JSON, oppure "" quando non Ăš stato condiviso nulla.

Restituisce: una stringa JSON {text, subject, url, receivedAt} oppure "" (in modo sincrono). url Ăš il primo link http(s):// trovato in text (o ""); receivedAt Ăš un tempo in millisecondi. Richiede: Ricevi condivisioni da altre app attivato durante la creazione, con Testo e link selezionato: cosĂŹ la tua app compare nel menu Condividi delle altre app.

La condivisione viene conservata finché non la cancelli, quindi leggila all'avvio e ascolta quelle che arrivano mentre l'app Ú in esecuzione:

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

Un hook globale riceve lo stesso oggetto, se preferisci:

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

Apri un video YouTube condiviso nell'app, usando il 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');
  }
}

Note: Un video YouTube condiviso dall'app YouTube arriva come suo link. Una foto o un video condivisi CON una didascalia consegnano il file tramite AppMint.getOpenedFile() e la didascalia qui. Cancella la condivisione dopo averla usata, altrimenti la rivedrai al prossimo avvio. Le app create con l'AI possono usare sharedText() / onShared() da @/lib/appmintNative.

pdfFromHtml bridge#

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

Genera [html] fuori schermo all'origine dell'app e lo salva come PDF: usa `AppMint.pdf.fromHtml(html, options)`.

Esempio

Documentato insieme a AppMint.pdf - l'esempio lì mostra anche questo.

Crea un vero PDF (testo selezionabile, grafica vettoriale) senza finestra di stampa: fromPage salva la pagina cosĂŹ com'Ăš sullo schermo, impaginata con il tuo CSS @media print; fromHtml genera l'HTML che passi in una pagina invisibile all'indirizzo dell'app, quindi immagini, fogli di stile e font relativi si caricano come nella tua pagina, attende i font e ogni immagine, poi lo salva. window.print() apre comunque la normale finestra di stampa.

Restituisce: una Promise di {ok: true, fileName, pages, bytes, saved, uri?, base64?} oppure {ok: false, code, message}: code Ăš busy (un PDF alla volta), bad-options, failed, too-large (base64 oltre 25 MB) o permission-denied (archiviazione rifiutata su Android 7-9). Richiede: niente: ogni build lo include. Salvare in Download non richiede autorizzazioni su Android 10+.

Opzioni: fileName (".pdf" viene aggiunto; predefinito: il titolo della pagina), pageSize 'A4' (predefinito) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins in millimetri (un numero, oppure {top, right, bottom, left}; se omesso = la tua regola @page), save 'downloads' (predefinito) | 'share' | 'base64'.

Salva la pagina corrente in Download:

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

Una ricevuta creata da HTML, direttamente nel foglio di condivisione (immagini e font vengono caricati prima di creare il PDF):

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

Recupera i byte per caricarli tu stesso:

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

Le chiamate dirette prendono le opzioni come testo JSON e un id di callback; usa invece l'helper qui sopra:

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

Note: Nascondi pulsanti e menu dal PDF con @media print { .no-print { display: none } }. Un PDF scaricato mostra "Scaricato <nome> · Apri" come ogni download. fromHtml esegue gli script dell'HTML (i grafici disegnati da script compaiono) ma non ha accesso alle funzioni native dell'app. Le pagine piĂč lunghe di un foglio vengono divise come farebbe Chrome; usa break-before: page per controllare dove.

pdfFromPage bridge#

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

Salva la pagina come PDF senza finestra di stampa: usa `AppMint.pdf.fromPage(options)`. [optionsJson] `{fileName, pageSize, landscape, margins, save}`; risolve [callbackId].

Esempio

Documentato insieme a AppMint.pdf - l'esempio lì mostra anche questo.

Crea un vero PDF (testo selezionabile, grafica vettoriale) senza finestra di stampa: fromPage salva la pagina cosĂŹ com'Ăš sullo schermo, impaginata con il tuo CSS @media print; fromHtml genera l'HTML che passi in una pagina invisibile all'indirizzo dell'app, quindi immagini, fogli di stile e font relativi si caricano come nella tua pagina, attende i font e ogni immagine, poi lo salva. window.print() apre comunque la normale finestra di stampa.

Restituisce: una Promise di {ok: true, fileName, pages, bytes, saved, uri?, base64?} oppure {ok: false, code, message}: code Ăš busy (un PDF alla volta), bad-options, failed, too-large (base64 oltre 25 MB) o permission-denied (archiviazione rifiutata su Android 7-9). Richiede: niente: ogni build lo include. Salvare in Download non richiede autorizzazioni su Android 10+.

Opzioni: fileName (".pdf" viene aggiunto; predefinito: il titolo della pagina), pageSize 'A4' (predefinito) | 'Letter' | 'Legal' | 'A3' | 'A5', landscape (true/false), margins in millimetri (un numero, oppure {top, right, bottom, left}; se omesso = la tua regola @page), save 'downloads' (predefinito) | 'share' | 'base64'.

Salva la pagina corrente in Download:

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

Una ricevuta creata da HTML, direttamente nel foglio di condivisione (immagini e font vengono caricati prima di creare il PDF):

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

Recupera i byte per caricarli tu stesso:

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

Le chiamate dirette prendono le opzioni come testo JSON e un id di callback; usa invece l'helper qui sopra:

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

Note: Nascondi pulsanti e menu dal PDF con @media print { .no-print { display: none } }. Un PDF scaricato mostra "Scaricato <nome> · Apri" come ogni download. fromHtml esegue gli script dell'HTML (i grafici disegnati da script compaiono) ma non ha accesso alle funzioni native dell'app. Le pagine piĂč lunghe di un foglio vengono divise come farebbe Chrome; usa break-before: page per controllare dove.

print bridge#

window.WebToApk.print()

Passa la pagina cosĂŹ com'Ăš ora alla finestra di stampa di Android, che puĂČ stampare su carta o salvare un PDF. Niente da configurare: la WebView genera il documento.

Descritta dal suo gruppo, Stampa, e non singolarmente.

Esempio

Apre la schermata di stampa di Android per la pagina cosĂŹ com'Ăš ora: stampa su carta o "Salva come PDF". Le pagine usano normalmente lo standard window.print(), che la shell indirizza qui.

Restituisce: niente. Richiede: niente.

Prima l'API standard:

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

La chiamata diretta fa la stessa cosa:

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

Stampa una fattura da un elenco contrassegnandola per un foglio di stile di stampa (lascia il contrassegno: la schermata di stampa impagina la pagina dopo che print() Ăš tornato):

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

Note: Stampa l'intera WebView, cioĂš la pagina principale. Il print() di un iframe dello stesso dominio stampa solo quell'iframe (vedi window.print). Usa un foglio di stile @media print per nascondere pulsanti e menu. Il lavoro si chiama "<Nome app> Document". Gli errori vengono solo registrati.

readClipboard bridge#

window.WebToApk.readClipboard(): String

Usa il wrapper basato su promise: const r = await AppMint.requestReview(); // {result, openedStore} const r = await AppMint.requestReview({ openStore: true }); // scheda del negozio se non c'è la finestra r.result: 'shown' | 'not_from_play' | 'quota' | 'error'. Vedi RatePrompt.

Descritto dal suo gruppo, Finestra di recensione in-app di Google Play, e non da solo.

Esempio

Legge il testo presente ora negli appunti del telefono. Le pagine usano normalmente lo standard navigator.clipboard.readText(), che la shell indirizza qui.

Restituisce: il testo, oppure "" quando gli appunti sono vuoti o non leggibili (in modo sincrono). Richiede: niente.

Prima l'API standard, per esempio un pulsante "Incolla codice":

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

La chiamata grezza:

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

Un pulsante "Incolla link" che estrae il primo indirizzo web da ciĂČ che Ăš stato copiato:

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

Note: Solo le pagine della tua app possono leggerli: un sito esterno aperto nell'app ottiene "". Android permette all'app di leggere gli appunti solo mentre Ăš in primo piano, e Android 12+ mostra un avviso "incollato dagli appunti": leggili da un tocco dell'utente, non al caricamento della pagina.

shareFiles bridge#

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

Vera condivisione nativa di file per navigator.share({files:[...]}). La WebView di Android non ha l'API Web Share e un ACTION_SEND text/plain non puĂČ trasportare un file: per questo prima un'immagine condivisa veniva solo SALVATA, non condivisa. filesJson Ăš [{name, mimeType, base64}, ...]; scriviamo ogni file nella cache, lo esponiamo tramite il FileProvider dell'app e lanciamo ACTION_SEND / ACTION_SEND_MULTIPLE cosĂŹ si apre il vero foglio di condivisione di Android (WhatsApp, Gmail, …). Restituisce true se il foglio si Ăš aperto.

Esempio

Condivide uno o piĂč file veri (un'immagine, un PDF…) tramite il foglio di condivisione di Android. Le pagine usano normalmente lo standard navigator.share({ files: [...] }), che la shell indirizza qui.

Restituisce: true se il foglio di condivisione si Ăš aperto, false altrimenti (in modo sincrono). Richiede: niente.

Prima l'API standard:

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

La chiamata diretta prende una stringa JSON di [{name, mimeType, base64}] (base64 senza il prefisso data:), poi 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');
}

Esporta una tabella come file CSV (il testo viene codificato in UTF-8 prima del 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');
  }
}

Note: I file vengono scritti nella cache dell'app e condivisi tramite il suo FileProvider. I caratteri non sicuri in name diventano _. PiĂč file dello stesso tipo si condividono con quel tipo; tipi misti si condividono come */*. L'intero file viaggia come base64 sul bridge, quindi tienilo entro pochi MB. Quando navigator.share({files}) non riesce ad aprire il foglio, la shell salva invece i file come download e la promise si risolve comunque.

shareNative bridge#

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

Il foglio di condivisione di Android, gli appunti di sistema e la chiusura di una finestra aperta dalla pagina. Sono gli equivalenti web standard (navigator.share, navigator.clipboard, window.close) gestiti in modo nativo, cosĂŹ si comportano allo stesso modo dentro l'app.

Descritta dal suo gruppo, Condivisione, appunti e finestra, e non singolarmente.

Esempio

Apre il foglio di condivisione di Android con titolo, testo e link. Le pagine usano normalmente lo standard navigator.share({title, text, url}), che la shell indirizza qui.

Restituisce: nulla (non puĂČ dirti quale app ha scelto l'utente). Richiede: niente.

Prima l'API standard: funziona anche in un browser mobile:

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

La chiamata diretta, tutti e tre gli argomenti sono stringhe (usa '' per ometterne uno):

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

Solo testo, senza link, per esempio il punteggio di un gioco:

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

Note: Il testo condiviso Ăš text + una riga vuota + url (oppure solo uno dei due). title diventa l'oggetto dell'email e l'intestazione del foglio di condivisione. Solo testo e link: per condividere un file usa navigator.share({files}) (vedi shareFiles).

Pulsanti share: nei messaggi in-app. Un pulsante il cui link Ăš share:<text> apre questo stesso foglio di condivisione con quel testo, senza codice nella pagina. Il modello Invita amici usa share:Try {app}: {store}; l'app inserisce il proprio nome e il proprio indirizzo su Play. La tua pagina puĂČ sapere che Ăš successo:

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 disegni tu i messaggi in-app (setInAppPresentation('none')), gestisci allo stesso modo un pulsante share::

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

Un link copy:<text> copia invece di condividere, per esempio un codice sconto da un messaggio promozionale:

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

Generato dal runtime dell'app e dai suoi file di esempio a ogni build della documentazione. Leggilo in Markdown · Tutte le famiglie.

Verificato rispetto al bridge distribuito il 22/10/2026.