Criar APK grátis

API da ponte JavaScript

Arquivos: abertos com este app

Coletando o que outro app entregou por "Abrir com", em blocos que a página busca.

AppMint.getOpenedFile helper#

AppMint.getOpenedFile()

Exemplo

Entrega à sua página o arquivo que foi compartilhado com o app ou aberto com ele ("Abrir com…"), por mais tarde que sua página inicie.

Retorna: Promise<object | null> - null quando o app foi aberto normalmente. O objeto tem token, name, mimeType, size, kind, url, file, getFile(), mais text (arquivos de texto de até 32 MB) ou base64 (outros arquivos de dados de até 8 MB) quando é barato. kind é 'image' | 'video' | 'audio' | 'document' | 'data'. Requer: ative Receber compartilhamentos de outros apps ao compilar e marque os tipos de arquivo que seu app aceita.

async function handleStartFile() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;  // not in the app, or feature off
  const f = await AppMint.getOpenedFile();
  if (!f) return;                                  // opened normally
  console.log(f.name, f.mimeType, f.size, f.kind);
  if (f.kind === 'video') {
    document.querySelector('video').src = f.url;   // streams from disk, can seek
  } else if (f.kind === 'image') {
    document.querySelector('img').src = f.url;
  } else if (f.kind === 'document') {
    const file = await f.getFile();                // a real File
    await upload(file);
  } else {
    const text = typeof f.text === 'string' ? f.text : await f.file.text();
    importData(text);
  }
}
handleStartFile();

Ou escute - o evento (e o gancho opcional window.onAppMintFileOpen) dispara uma vez por novo arquivo, com o mesmo objeto que detail:

window.onAppMintFileOpen = function (f) { console.log('hook:', f.name); };

window.addEventListener('appmint:fileopen', function (e) {
  const f = e.detail;             // {token, name, mimeType, size, kind, url, file, getFile, text?, base64?}
  showFile(f);
});
window.addEventListener('appmint:fileopenerror', function (e) {
  alert('Could not read ' + e.detail.name + ': ' + e.detail.error);   // detail: {name, error}
});

Em um app React, um pequeno hook captura o arquivo por mais tarde que o componente monte e continua acompanhando os novos:

import { useEffect, useState } from 'react';

export function useOpenedFile() {
  const [file, setFile] = useState(null);
  useEffect(function () {
    const A = window.AppMint;
    if (!A || typeof A.getOpenedFile !== 'function') return;
    let alive = true;
    A.getOpenedFile().then(function (f) { if (alive && f) setFile(f); });
    function onOpen(e) { setFile(e.detail); }
    window.addEventListener('appmint:fileopen', onOpen);
    return function () { alive = false; window.removeEventListener('appmint:fileopen', onOpen); };
  }, []);
  return file;
}

Observações: Imagens, vídeos, áudios e qualquer arquivo acima de 64 MB vêm só como url (file é null); chame f.getFile() quando realmente precisar dos bytes. Arquivos de dados e documentos também vêm como f.file. O arquivo fica guardado para sua página até você pedir, então um SPA que monta tarde ainda o recebe; o mais recente é retornado a cada vez. Quando o app já está aberto e chega outro arquivo, o evento dispara de novo. Chame AppMint.releaseOpenedFile(f) ao terminar para liberar a cópia em cache.

AppMint.getOpenedFiles helper#

AppMint.getOpenedFiles()

Exemplo

Entrega à sua página todos os arquivos compartilhados com o app ou abertos com ele durante esta sessão - útil quando o usuário compartilha várias fotos de uma vez.

Retorna: Promise<Array> dos mesmos objetos que AppMint.getOpenedFile() retorna (token, name, mimeType, size, kind, url, file, getFile(), e text / base64 quando é barato). Um array vazio quando nada foi recebido. Requer: ative Receber compartilhamentos de outros apps ao compilar e marque os tipos de arquivo que seu app aceita.

async function importShared() {
  if (!window.AppMint || typeof AppMint.getOpenedFiles !== 'function') return;
  const files = await AppMint.getOpenedFiles();
  const gallery = document.getElementById('gallery');
  files.forEach(function (f) {
    if (f.kind === 'image') {
      const img = document.createElement('img');
      img.src = f.url;
      img.alt = f.name;
      gallery.appendChild(img);
    } else {
      console.log('received', f.name, f.size + ' bytes');
    }
  });
}
importShared();

Novos arquivos que chegam enquanto a página está aberta também disparam appmint:fileopen, um evento por arquivo:

window.addEventListener('appmint:fileopen', function (e) { addToGallery(e.detail); });

Enviando tudo o que o usuário compartilhou em uma única requisição, como arquivos de verdade:

async function uploadShared() {
  if (!window.AppMint || typeof AppMint.getOpenedFiles !== 'function') return;
  const files = await AppMint.getOpenedFiles();
  if (!files.length) return;
  const form = new FormData();
  for (const f of files) {
    try {
      form.append('files', await f.getFile(), f.name);   // url-only media is fetched here
    } catch (e) {
      console.warn('skipped', f.name, e.message);        // released earlier
    }
  }
  await fetch('/api/upload', { method: 'POST', body: form });
}

Observações: A lista é uma cópia; ela cresce conforme chegam mais arquivos. Arquivos que você liberou com AppMint.releaseOpenedFile(f) continuam na lista, mas o url deles deixa de funcionar.

AppMint.releaseOpenedFile helper#

AppMint.releaseOpenedFile(file)

Exemplo

Exclui a cópia em cache do app de um arquivo que foi compartilhado com o app ou aberto com ele, quando sua página terminar de usá-lo.

Retorna: nada. Requer: ative Receber compartilhamentos de outros apps ao compilar.

async function importAndForget() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;
  const f = await AppMint.getOpenedFile();
  if (!f) return;
  const file = await f.getFile();        // take your own copy of the bytes first
  await saveToMyDatabase(file);
  AppMint.releaseOpenedFile(f);          // then free the cache on disk
}

Um vídeo compartilhado é reproduzido a partir de f.url, então libere-o só quando a reprodução terminar:

async function playShared() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;
  const f = await AppMint.getOpenedFile();
  if (!f || f.kind !== 'video') return;
  const video = document.querySelector('video');
  video.src = f.url;
  video.play();
  video.addEventListener('ended', function () {
    video.removeAttribute('src');
    video.load();
    AppMint.releaseOpenedFile(f);        // the url stops working from here
  }, { once: true });
}

Depois de importar um lote de fotos compartilhadas, libere todas as cópias em cache de uma vez:

async function importAllPhotos() {
  if (!window.AppMint || typeof AppMint.getOpenedFiles !== 'function') return;
  const files = await AppMint.getOpenedFiles();
  for (const f of files) {
    if (f.kind !== 'image') continue;
    const file = await f.getFile();      // fetched from f.url
    await savePhoto(file);
    AppMint.releaseOpenedFile(f);
  }
}

Observações: Passe o objeto que você recebeu (ele precisa do token). Um File que você já obteve continua válido; o url deixa de funcionar, e getFile() rejeita, a menos que já tenha buscado o File. Não libere um vídeo que você ainda está reproduzindo a partir de f.url. O app também limpa essas cópias quando a tela é fechada, então liberar cedo serve para economizar espaço em disco durante uma sessão longa.

openedFileChunk ponte#

window.WebToApk.openedFileChunk(token: String, offset: String, len: Int): String

Interno a AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). A página busca, em vez de receber à força: um arquivo aberto em uma inicialização a frio fica guardado até ser pedido, então um SPA que monta tarde ainda o recebe. Os metadados de cada arquivo trazem `kind` (image | video | audio | document | data) e `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, servida a partir da cópia em cache com ranges de bytes, então um vídeo compartilhado é transmitido para <video src> e permite avançar. Arquivos de dados e documentos também são buscados em fatias de MAX_IO_CHUNK para um File; mídia e qualquer coisa acima de 64 MB ficam só com url (getFile() busca sob demanda), então um clipe de 300 MB nunca fica em um array JS.

Descrito pelo seu grupo, Arquivos compartilhados com o app ou abertos com ele, e não separadamente.

Exemplo

Estrutura interna por trás de AppMint.getOpenedFile(): retorna uma fatia dos bytes de um arquivo compartilhado ou aberto, que o auxiliar junta em um File. As páginas devem usar a API pública - f.file, f.getFile() ou f.url.

Retorna: (síncrono) uma string base64 de até len bytes a partir de offset (o deslocamento é passado como string, então arquivos acima de 2 GB também funcionam); "" no fim do arquivo, para um token desconhecido ou liberado, ou em caso de erro de leitura. Requer: ative Receber compartilhamentos de outros apps ao compilar.

A API pública (use esta):

if (window.AppMint && typeof AppMint.getOpenedFile === 'function') {
  AppMint.getOpenedFile().then(function (f) {
    if (!f) return;
    return f.getFile().then(function (file) { console.log('got', file.size, 'bytes'); });
  });
}

O que o auxiliar faz com ele, mostrado apenas para explicar os argumentos:

if (window.WebToApk && typeof WebToApk.openedFileChunk === 'function') {
  const meta = JSON.parse(WebToApk.openedFilesPending() || '[]')[0];
  if (meta) {
    const b64 = WebToApk.openedFileChunk(meta.token, '0', WebToApk.openedFileMaxChunk());
    console.log('first slice is', atob(b64).length, 'bytes of', meta.size);
  }
}

Lendo um arquivo compartilhado muito grande sem fatias - transmita f.url e mostre o progresso (aqui contando linhas de CSV):

async function countRows() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;
  const f = await AppMint.getOpenedFile();
  if (!f) return;
  const res = await fetch(f.url);                // served from disk, no base64 round trips
  if (!res.ok) throw new Error('shared file unavailable (' + res.status + ')');
  const reader = res.body.getReader();
  let seen = 0, rows = 0;
  for (;;) {
    const step = await reader.read();
    if (step.done) break;
    seen += step.value.length;
    for (let i = 0; i < step.value.length; i++) if (step.value[i] === 10) rows++;
    showProgress(seen / f.size);
  }
  console.log(rows + ' rows in ' + f.name);
}

Observações: len é limitado de 1 byte a 4 MB. Para arquivos de mídia, transmitir f.url é bem mais barato do que buscar fatias.

openedFileCollected ponte#

window.WebToApk.openedFileCollected(token: String)

Interno a AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). A página busca, em vez de receber à força: um arquivo aberto em uma inicialização a frio fica guardado até ser pedido, então um SPA que monta tarde ainda o recebe. Os metadados de cada arquivo trazem `kind` (image | video | audio | document | data) e `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, servida a partir da cópia em cache com ranges de bytes, então um vídeo compartilhado é transmitido para <video src> e permite avançar. Arquivos de dados e documentos também são buscados em fatias de MAX_IO_CHUNK para um File; mídia e qualquer coisa acima de 64 MB ficam só com url (getFile() busca sob demanda), então um clipe de 300 MB nunca fica em um array JS.

Descrito pelo seu grupo, Arquivos compartilhados com o app ou abertos com ele, e não separadamente.

Exemplo

Estrutura interna por trás de AppMint.getOpenedFile(): marca um arquivo compartilhado ou aberto como coletado, para que não seja mais oferecido como "novo", mas sua cópia em cache é mantida. As páginas devem usar a API pública; não há motivo para chamar isto diretamente.

Retorna: nada. Requer: ative Receber compartilhamentos de outros apps ao compilar.

A API pública (use esta) - cada arquivo é coletado uma vez e entregue à sua página:

if (window.AppMint && typeof AppMint.getOpenedFiles === 'function') {
  AppMint.getOpenedFiles().then(function (files) {
    files.forEach(function (f) { console.log(f.name, f.kind); });
  });
}
window.addEventListener('appmint:fileopen', function (e) { console.log('new file', e.detail.name); });

Um arquivo coletado na inicialização está na lista e também é anunciado pelo evento, então use token como chave no seu tratamento para processar cada arquivo uma só vez:

const handled = new Set();
function handleOnce(f) {
  if (handled.has(f.token)) return;
  handled.add(f.token);
  addToInbox(f);
}
window.addEventListener('appmint:fileopen', function (e) { handleOnce(e.detail); });
if (window.AppMint && typeof AppMint.getOpenedFiles === 'function') {
  AppMint.getOpenedFiles().then(function (files) { files.forEach(handleOnce); });
}

Um arquivo que não pôde ser lido ainda assim é coletado; ele é informado uma vez por appmint:fileopenerror em vez de appmint:fileopen:

window.addEventListener('appmint:fileopenerror', function (e) {
  const d = e.detail;             // {name, error}
  showBanner('Could not open "' + d.name + '". Try sharing it again.');
  console.warn('opened file failed:', d.error);
});

Observações: Chamá-lo você mesmo esconderia um arquivo de AppMint.getOpenedFile() e do evento appmint:fileopen sem entregá-lo a ninguém. Para liberar a cópia em cache, use AppMint.releaseOpenedFile(f).

openedFileMaxChunk ponte#

window.WebToApk.openedFileMaxChunk(): Int

Interno a AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). A página busca, em vez de receber à força: um arquivo aberto em uma inicialização a frio fica guardado até ser pedido, então um SPA que monta tarde ainda o recebe. Os metadados de cada arquivo trazem `kind` (image | video | audio | document | data) e `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, servida a partir da cópia em cache com ranges de bytes, então um vídeo compartilhado é transmitido para <video src> e permite avançar. Arquivos de dados e documentos também são buscados em fatias de MAX_IO_CHUNK para um File; mídia e qualquer coisa acima de 64 MB ficam só com url (getFile() busca sob demanda), então um clipe de 300 MB nunca fica em um array JS.

Descrito pelo seu grupo, Arquivos compartilhados com o app ou abertos com ele, e não separadamente.

Exemplo

Estrutura interna por trás de AppMint.getOpenedFile(): a maior fatia que openedFileChunk retorna em uma chamada (4 MB). As páginas devem usar a API pública, que já lê em fatias desse tamanho.

Retorna: (síncrono) um número de bytes (4194304). Requer: ative Receber compartilhamentos de outros apps ao compilar.

A API pública (use esta):

if (window.AppMint && typeof AppMint.getOpenedFile === 'function') {
  AppMint.getOpenedFile().then(function (f) {
    if (f && f.file) console.log('pulled in 4 MB slices:', f.file.size, 'bytes');
  });
}

A chamada bruta, se você só quer saber o número:

if (window.WebToApk && typeof WebToApk.openedFileMaxChunk === 'function') {
  console.log('slice size', WebToApk.openedFileMaxChunk());
}

Estimando quantas chamadas de ponte o auxiliar fará para cada arquivo em espera, ao analisar o desempenho de uma importação lenta:

if (window.WebToApk && typeof WebToApk.openedFileMaxChunk === 'function') {
  const slice = WebToApk.openedFileMaxChunk();
  JSON.parse(WebToApk.openedFilesPending() || '[]').forEach(function (m) {
    console.log(m.name, (m.size / 1048576).toFixed(1) + ' MB =', Math.ceil(m.size / slice), 'slice(s)');
  });
}

Observações: Mesmo limite de getMaxIoChunkSize() da ponte de acesso a arquivos.

openedFileRelease ponte#

window.WebToApk.openedFileRelease(token: String)

Interno a AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). A página busca, em vez de receber à força: um arquivo aberto em uma inicialização a frio fica guardado até ser pedido, então um SPA que monta tarde ainda o recebe. Os metadados de cada arquivo trazem `kind` (image | video | audio | document | data) e `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, servida a partir da cópia em cache com ranges de bytes, então um vídeo compartilhado é transmitido para <video src> e permite avançar. Arquivos de dados e documentos também são buscados em fatias de MAX_IO_CHUNK para um File; mídia e qualquer coisa acima de 64 MB ficam só com url (getFile() busca sob demanda), então um clipe de 300 MB nunca fica em um array JS.

Descrito pelo seu grupo, Arquivos compartilhados com o app ou abertos com ele, e não separadamente.

Exemplo

Estrutura interna por trás de AppMint.releaseOpenedFile(f): exclui a cópia em cache de um arquivo compartilhado ou aberto, pelo token. As páginas devem usar a API pública.

Retorna: nada. Requer: ative Receber compartilhamentos de outros apps ao compilar.

A API pública (use esta):

async function useAndRelease() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;
  const f = await AppMint.getOpenedFile();
  if (!f) return;
  const file = await f.getFile();       // keep your own copy first
  await store(file);
  AppMint.releaseOpenedFile(f);         // calls WebToApk.openedFileRelease(f.token)
}

O usuário descarta um arquivo compartilhado sem importá-lo - libere a cópia em cache na hora:

async function offerImport() {
  if (!window.AppMint || typeof AppMint.getOpenedFile !== 'function') return;
  const f = await AppMint.getOpenedFile();
  if (!f) return;
  const box = showImportPrompt(f.name, f.size);   // your own "Import / Discard" UI
  box.querySelector('.discard').addEventListener('click', function () {
    AppMint.releaseOpenedFile(f);
    box.remove();
  });
  box.querySelector('.import').addEventListener('click', async function () {
    await store(await f.getFile());
    AppMint.releaseOpenedFile(f);
    box.remove();
  });
}

Um arquivo que o app não consegue usar é liberado assim que é recusado:

window.addEventListener('appmint:fileopen', function (e) {
  const f = e.detail;
  if (!/\.(gpx|tcx)$/i.test(f.name)) {
    alert(f.name + ' is not a GPX or TCX activity file.');
    AppMint.releaseOpenedFile(f);
    return;
  }
  importActivity(f.text);                // gpx/tcx arrive as text
});

Observações: Depois da liberação, f.url retorna um erro e openedFileChunk retorna "" para esse token. Um objeto File que você já tem não é afetado.

openedFilesPending ponte#

window.WebToApk.openedFilesPending(): String

Uma página de terceiros não recebe nada: os arquivos ficam guardados para a página do próprio app.

Exemplo

Estrutura interna por trás de AppMint.getOpenedFile() / AppMint.getOpenedFiles(): lista os arquivos compartilhados ou abertos que a página ainda não coletou (só metadados, sem bytes). As páginas devem usar a API pública.

Retorna: (síncrono) uma string JSON: um array de {token, name, mimeType, size, isText, kind, url}. [] quando nada está esperando. Requer: ative Receber compartilhamentos de outros apps ao compilar (sem isso a lista fica sempre vazia e os auxiliares AppMint não são instalados).

A API pública (use esta):

if (window.AppMint && typeof AppMint.getOpenedFile === 'function') {
  AppMint.getOpenedFile().then(function (f) {
    if (f) console.log('opened with the app:', f.name, f.kind);
  });
}
window.addEventListener('appmint:fileopen', function (e) { console.log('arrived:', e.detail.name); });

Só para depuração - espie o que está esperando sem coletar:

if (window.WebToApk && typeof WebToApk.openedFilesPending === 'function') {
  const waiting = JSON.parse(WebToApk.openedFilesPending() || '[]');
  console.log(waiting.length + ' file(s) waiting', waiting.map(function (m) { return m.name; }));
}

Depurando um arquivo que nunca chegou à página - veja o que ainda está esperando e se ele vai chegar como f.file ou só como f.url:

if (window.WebToApk && typeof WebToApk.openedFilesPending === 'function') {
  const STREAM_LIMIT = 64 * 1024 * 1024;   // the helper's url-only threshold
  JSON.parse(WebToApk.openedFilesPending() || '[]').forEach(function (m) {
    const urlOnly = m.kind === 'image' || m.kind === 'video' || m.kind === 'audio' || m.size > STREAM_LIMIT;
    console.log(m.name, m.mimeType, m.size + ' bytes',
      urlOnly ? 'will come as url only' : 'will be pulled into f.file',
      m.isText ? '(text)' : '');
  });
}

Observações: O auxiliar coleta esses arquivos automaticamente após load e sempre que chega um novo arquivo, então uma página raramente vê algo aqui. Depois de coletado, um arquivo sai desta lista, mas continua legível até ser liberado.

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.