API jembatan JavaScript
File: dibuka dengan aplikasi ini
Mengumpulkan apa yang diserahkan aplikasi lain lewat "Buka dengan", dalam potongan yang diambil halaman.
AppMint.getOpenedFile helper#
AppMint.getOpenedFile()
Contoh
Memberi halaman Anda file yang dibagikan ke aplikasi, atau dibuka dengannya ("Buka dengan…"), seterlambat apa pun halaman Anda dimulai.
Mengembalikan: Promise<object | null> - null jika aplikasi dibuka secara normal. Objek ini memiliki token, name, mimeType, size, kind, url, file, getFile(), ditambah text (file teks hingga 32 MB) atau base64 (file data lain hingga 8 MB) bila murah. kind adalah 'image' | 'video' | 'audio' | 'document' | 'data'. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build dan centang jenis file yang diterima aplikasi Anda.
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();Atau dengarkan - event (dan hook window.onAppMintFileOpen opsional) menyala sekali per file baru, dengan objek yang sama seperti 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}
});Di aplikasi React, hook kecil mengambil file seterlambat apa pun komponen dipasang, dan terus mengikuti file baru:
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;
}Catatan: Gambar, video, audio, dan file apa pun di atas 64 MB hanya datang sebagai url (file adalah null); panggil f.getFile() jika Anda benar-benar butuh byte-nya. File data dan dokumen juga datang sebagai f.file. File disimpan untuk halaman Anda sampai diminta, sehingga SPA yang dimuat terlambat tetap mendapatkannya; yang terbaru dikembalikan setiap kali. Saat aplikasi sudah terbuka dan file lain tiba, event menyala lagi. Panggil AppMint.releaseOpenedFile(f) setelah selesai untuk membebaskan salinan cache.
AppMint.getOpenedFiles helper#
AppMint.getOpenedFiles()
Contoh
Memberi halaman Anda setiap file yang dibagikan ke, atau dibuka dengan, aplikasi selama sesi ini - berguna saat pengguna membagikan beberapa foto sekaligus.
Mengembalikan: Promise<Array> berisi objek yang sama dengan yang dikembalikan AppMint.getOpenedFile() (token, name, mimeType, size, kind, url, file, getFile(), dan text / base64 bila murah). Array kosong jika tidak ada yang diterima. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build dan centang jenis file yang diterima aplikasi Anda.
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();File baru yang tiba saat halaman terbuka juga memicu appmint:fileopen, satu event per file:
window.addEventListener('appmint:fileopen', function (e) { addToGallery(e.detail); });Mengunggah semua yang dibagikan pengguna dalam satu permintaan, sebagai file sungguhan:
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 });
}Catatan: Daftar ini adalah salinan; bertambah saat lebih banyak file tiba. File yang sudah Anda lepas dengan AppMint.releaseOpenedFile(f) tetap ada di daftar, tetapi url-nya tidak lagi berfungsi.
AppMint.releaseOpenedFile helper#
AppMint.releaseOpenedFile(file)
Contoh
Menghapus salinan cache aplikasi dari file yang dibagikan ke atau dibuka dengan aplikasi, setelah halaman Anda selesai memakainya.
Mengembalikan: tidak ada. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build.
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
}Video yang dibagikan diputar dari f.url, jadi lepaskan hanya setelah pemutaran selesai:
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 });
}Setelah mengimpor sekumpulan foto yang dibagikan, bebaskan semua salinan cache sekaligus:
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);
}
}Catatan: Berikan objek yang Anda dapat (butuh token-nya). File yang sudah Anda ambil tetap valid; url berhenti berfungsi, dan getFile() menolak kecuali sudah mengambil File-nya. Jangan lepas video yang masih Anda putar dari f.url. Aplikasi juga menghapus salinan ini saat layarnya ditutup, jadi melepas lebih awal terkait ruang disk selama sesi panjang.
openedFileChunk jembatan#
window.WebToApk.openedFileChunk(token: String, offset: String, len: Int): String
Internal untuk AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). Halaman menarik, bukan didorong: file yang dibuka saat cold start disimpan sampai diminta, sehingga SPA yang dimuat terlambat tetap mendapatkannya. Metadata tiap file membawa `kind` (image | video | audio | document | data) dan `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, disajikan dari salinan cache dengan byte range, sehingga video yang dibagikan mengalir ke <video src> dan bisa di-seek. File data dan dokumen juga ditarik dalam irisan MAX_IO_CHUNK ke sebuah File; media dan apa pun di atas 64 MB tetap hanya-url (getFile() mengambil sesuai permintaan), sehingga klip 300 MB tidak pernah berada di array JS.
Dijelaskan oleh grupnya, File yang dibagikan ke, atau dibuka dengan, aplikasi, bukan secara tersendiri.
Contoh
Jalur internal di balik AppMint.getOpenedFile(): mengembalikan satu irisan byte file yang dibagikan atau dibuka, yang digabung helper menjadi File. Halaman sebaiknya memakai API publik - f.file, f.getFile() atau f.url.
Mengembalikan: (sinkron) string base64 hingga len byte mulai dari offset (offset dikirim sebagai string, sehingga file di atas 2 GB tetap berfungsi); "" di akhir file, untuk token yang tidak dikenal atau sudah dilepas, atau saat terjadi kesalahan baca. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build.
API publik (pakai ini):
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'); });
});
}Apa yang dilakukan helper dengannya, ditampilkan hanya untuk menjelaskan argumen:
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);
}
}Membaca file bersama yang sangat besar tanpa irisan - alirkan f.url dan tampilkan kemajuan (di sini menghitung baris 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);
}Catatan: len dibatasi dari 1 byte … 4 MB. Untuk file media, mengalirkan f.url jauh lebih murah daripada menarik irisan.
openedFileCollected jembatan#
window.WebToApk.openedFileCollected(token: String)
Internal untuk AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). Halaman menarik, bukan didorong: file yang dibuka saat cold start disimpan sampai diminta, sehingga SPA yang dimuat terlambat tetap mendapatkannya. Metadata tiap file membawa `kind` (image | video | audio | document | data) dan `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, disajikan dari salinan cache dengan byte range, sehingga video yang dibagikan mengalir ke <video src> dan bisa di-seek. File data dan dokumen juga ditarik dalam irisan MAX_IO_CHUNK ke sebuah File; media dan apa pun di atas 64 MB tetap hanya-url (getFile() mengambil sesuai permintaan), sehingga klip 300 MB tidak pernah berada di array JS.
Dijelaskan oleh grupnya, File yang dibagikan ke, atau dibuka dengan, aplikasi, bukan secara tersendiri.
Contoh
Jalur internal di balik AppMint.getOpenedFile(): menandai file yang dibagikan atau dibuka sebagai sudah dikumpulkan, sehingga tidak lagi ditawarkan sebagai "baru" tetapi salinan cache-nya dipertahankan. Halaman sebaiknya memakai API publik; tidak ada alasan memanggil ini langsung.
Mengembalikan: tidak ada. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build.
API publik (pakai ini) - setiap file dikumpulkan sekali dan diserahkan ke halaman Anda:
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); });File yang dikumpulkan saat startup ada di daftar sekaligus diumumkan oleh event, jadi tangani berdasarkan token agar tiap file diproses sekali saja:
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); });
}File yang tidak bisa dibaca tetap dikumpulkan; ia dilaporkan sekali lewat appmint:fileopenerror alih-alih 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);
});Catatan: Memanggilnya sendiri akan menyembunyikan file dari AppMint.getOpenedFile() dan event appmint:fileopen tanpa menyerahkannya kepada siapa pun. Untuk membebaskan salinan cache, gunakan AppMint.releaseOpenedFile(f).
openedFileMaxChunk jembatan#
window.WebToApk.openedFileMaxChunk(): Int
Internal untuk AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). Halaman menarik, bukan didorong: file yang dibuka saat cold start disimpan sampai diminta, sehingga SPA yang dimuat terlambat tetap mendapatkannya. Metadata tiap file membawa `kind` (image | video | audio | document | data) dan `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, disajikan dari salinan cache dengan byte range, sehingga video yang dibagikan mengalir ke <video src> dan bisa di-seek. File data dan dokumen juga ditarik dalam irisan MAX_IO_CHUNK ke sebuah File; media dan apa pun di atas 64 MB tetap hanya-url (getFile() mengambil sesuai permintaan), sehingga klip 300 MB tidak pernah berada di array JS.
Dijelaskan oleh grupnya, File yang dibagikan ke, atau dibuka dengan, aplikasi, bukan secara tersendiri.
Contoh
Jalur internal di balik AppMint.getOpenedFile(): irisan terbesar yang dikembalikan openedFileChunk dalam satu panggilan (4 MB). Halaman sebaiknya memakai API publik, yang sudah membaca dalam irisan berukuran ini.
Mengembalikan: (sinkron) sejumlah byte (4194304). Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build.
API publik (pakai ini):
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');
});
}Panggilan mentah, jika Anda hanya ingin tahu angkanya:
if (window.WebToApk && typeof WebToApk.openedFileMaxChunk === 'function') {
console.log('slice size', WebToApk.openedFileMaxChunk());
}Memperkirakan berapa banyak panggilan jembatan yang akan dilakukan helper untuk tiap file yang menunggu, saat memprofil impor yang lambat:
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)');
});
}Catatan: Batas yang sama dengan getMaxIoChunkSize() untuk jembatan akses file.
openedFileRelease jembatan#
window.WebToApk.openedFileRelease(token: String)
Internal untuk AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). Halaman menarik, bukan didorong: file yang dibuka saat cold start disimpan sampai diminta, sehingga SPA yang dimuat terlambat tetap mendapatkannya. Metadata tiap file membawa `kind` (image | video | audio | document | data) dan `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, disajikan dari salinan cache dengan byte range, sehingga video yang dibagikan mengalir ke <video src> dan bisa di-seek. File data dan dokumen juga ditarik dalam irisan MAX_IO_CHUNK ke sebuah File; media dan apa pun di atas 64 MB tetap hanya-url (getFile() mengambil sesuai permintaan), sehingga klip 300 MB tidak pernah berada di array JS.
Dijelaskan oleh grupnya, File yang dibagikan ke, atau dibuka dengan, aplikasi, bukan secara tersendiri.
Contoh
Jalur internal di balik AppMint.releaseOpenedFile(f): menghapus salinan cache dari file yang dibagikan atau dibuka, berdasarkan token. Halaman sebaiknya memakai API publik.
Mengembalikan: tidak ada. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build.
API publik (pakai ini):
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)
}Pengguna menutup file yang dibagikan tanpa mengimpornya - bebaskan salinan cache segera:
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();
});
}File yang tidak bisa dipakai aplikasi dilepas segera setelah ditolak:
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
});Catatan: Setelah dilepas, f.url mengembalikan kesalahan dan openedFileChunk mengembalikan "" untuk token itu. Objek File yang sudah Anda miliki tidak terpengaruh.
openedFilesPending jembatan#
window.WebToApk.openedFilesPending(): String
Halaman asing tidak mendapat apa-apa: file tetap disimpan untuk halaman milik aplikasi sendiri.
Contoh
Jalur internal di balik AppMint.getOpenedFile() / AppMint.getOpenedFiles(): mendaftar file yang dibagikan atau dibuka yang belum dikumpulkan halaman (hanya metadata, tanpa byte). Halaman sebaiknya memakai API publik.
Mengembalikan: (sinkron) string JSON: array berisi {token, name, mimeType, size, isText, kind, url}. [] jika tidak ada yang menunggu. Membutuhkan: aktifkan Receive Shares From Other Apps saat Anda membuat build (tanpanya daftar selalu kosong dan helper AppMint tidak dipasang).
API publik (pakai ini):
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); });Hanya untuk debugging - intip apa yang menunggu tanpa mengumpulkannya:
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; }));
}Men-debug file yang tidak pernah sampai ke halaman - lihat apa yang masih menunggu dan apakah akan tiba sebagai f.file atau hanya sebagai 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)' : '');
});
}Catatan: Helper mengumpulkan ini secara otomatis setelah load dan setiap kali file baru tiba, jadi halaman jarang melihat apa pun di sini. Setelah dikumpulkan, file keluar dari daftar ini tetapi tetap bisa dibaca sampai dilepas.