# Files: opened with this app - JavaScript bridge API

> Collecting what another app handed over through "Open with", in chunks the page pulls.

- **Applies to:** AppMint
- **Source:** extracted from the app runtime and its per-method example files; this page is generated from them.
- **HTML:** https://freewebtoapk.com/docs/api/files-opened

### `AppMint.getOpenedFile`

```js
AppMint.getOpenedFile()
```

**Example**

Gives your page the file that was shared to the app, or opened with it ("Open with…"), however late your page starts.

**Returns:** `Promise<object | null>` - `null` when the app was opened normally. The object has `token, name, mimeType, size, kind, url, file, getFile()`, plus `text` (text files up to 32 MB) or `base64` (other data files up to 8 MB) when cheap. `kind` is `'image' | 'video' | 'audio' | 'document' | 'data'`. **Needs:** turn on **Receive Shares From Other Apps** when you build and tick the kinds of files your app accepts.

```js
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();
```

Or listen - the event (and the optional `window.onAppMintFileOpen` hook) fires once per new file, with the same object as `detail`:

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

**Notes:** Images, videos, audio and any file over 64 MB come only as `url` (`file` is `null`); call `f.getFile()` when you really need the bytes. Data files and documents also come as `f.file`. The file is kept for your page until you ask, so an SPA that mounts late still gets it; the latest one is returned each time. When the app is already open and another file arrives, the event fires again. Call `AppMint.releaseOpenedFile(f)` when done to free the cached copy.

### `AppMint.getOpenedFiles`

```js
AppMint.getOpenedFiles()
```

**Example**

Gives your page every file shared to, or opened with, the app during this session - useful when the user shares several photos at once.

**Returns:** `Promise<Array>` of the same objects `AppMint.getOpenedFile()` returns (`token, name, mimeType, size, kind, url, file, getFile()`, and `text` / `base64` when cheap). An empty array when nothing was received. **Needs:** turn on **Receive Shares From Other Apps** when you build and tick the kinds of files your app accepts.

```js
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();
```

New files that arrive while the page is open also fire `appmint:fileopen`, one event per file:

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

**Notes:** The list is a copy; it grows as more files arrive. Files you have released with `AppMint.releaseOpenedFile(f)` stay in the list, but their `url` no longer works.

### `AppMint.releaseOpenedFile`

```js
AppMint.releaseOpenedFile(file)
```

**Example**

Deletes the app's cached copy of a file that was shared to or opened with the app, once your page is done with it.

**Returns:** nothing. **Needs:** turn on **Receive Shares From Other Apps** when you build.

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

**Notes:** Pass the object you got (it needs its `token`). A `File` you already pulled stays valid; the `url` stops working, and `getFile()` rejects unless it had already fetched the File. Do not release a video you are still playing from `f.url`. The app also clears these copies when its screen is closed, so releasing early is about disk space during a long session.

### `openedFileChunk`

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

Internal to AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Each file's metadata carries `kind` (image | video | audio | document | data) and `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, served from the cache copy with byte ranges, so a shared video streams into <video src> and seeks. Data files and documents are also pulled in MAX_IO_CHUNK slices into a File; media and anything over 64 MB stay url-only (getFile() fetches on demand), so a 300 MB clip never sits in a JS array.

_Described by its group, Files shared to, or opened with, the app, rather than on its own._

**Example**

Internal plumbing behind `AppMint.getOpenedFile()`: returns one slice of a shared or opened file's bytes, which the helper joins into a `File`. Pages should use the public API - `f.file`, `f.getFile()` or `f.url`.

**Returns:** (sync) a base64 **string** of up to `len` bytes starting at `offset` (the offset is passed as a string, so files over 2 GB still work); `""` at the end of the file, for an unknown or released token, or on a read error. **Needs:** turn on **Receive Shares From Other Apps** when you build.

The public API (use this):

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

What the helper does with it, shown only to explain the arguments:

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

**Notes:** `len` is clamped to 1 byte … 4 MB. For media files, streaming `f.url` is far cheaper than pulling slices.

### `openedFileCollected`

```js
window.WebToApk.openedFileCollected(token: String)
```

Internal to AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Each file's metadata carries `kind` (image | video | audio | document | data) and `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, served from the cache copy with byte ranges, so a shared video streams into <video src> and seeks. Data files and documents are also pulled in MAX_IO_CHUNK slices into a File; media and anything over 64 MB stay url-only (getFile() fetches on demand), so a 300 MB clip never sits in a JS array.

_Described by its group, Files shared to, or opened with, the app, rather than on its own._

**Example**

Internal plumbing behind `AppMint.getOpenedFile()`: marks a shared or opened file as collected, so it is no longer offered as "new" but its cached copy is kept. Pages should use the public API; there is no reason to call this directly.

**Returns:** nothing. **Needs:** turn on **Receive Shares From Other Apps** when you build.

The public API (use this) - each file is collected once and handed to your page:

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

**Notes:** Calling it yourself would hide a file from `AppMint.getOpenedFile()` and the `appmint:fileopen` event without giving it to anyone. To free the cached copy, use `AppMint.releaseOpenedFile(f)`.

### `openedFileMaxChunk`

```js
window.WebToApk.openedFileMaxChunk(): Int
```

Internal to AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Each file's metadata carries `kind` (image | video | audio | document | data) and `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, served from the cache copy with byte ranges, so a shared video streams into <video src> and seeks. Data files and documents are also pulled in MAX_IO_CHUNK slices into a File; media and anything over 64 MB stay url-only (getFile() fetches on demand), so a 300 MB clip never sits in a JS array.

_Described by its group, Files shared to, or opened with, the app, rather than on its own._

**Example**

Internal plumbing behind `AppMint.getOpenedFile()`: the largest slice `openedFileChunk` returns in one call (4 MB). Pages should use the public API, which already reads in slices of this size.

**Returns:** (sync) a number of bytes (4194304). **Needs:** turn on **Receive Shares From Other Apps** when you build.

The public API (use this):

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

The raw call, if you only want to know the number:

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

**Notes:** Same cap as `getMaxIoChunkSize()` for the file-access bridge.

### `openedFileRelease`

```js
window.WebToApk.openedFileRelease(token: String)
```

Internal to AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Each file's metadata carries `kind` (image | video | audio | document | data) and `url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>, served from the cache copy with byte ranges, so a shared video streams into <video src> and seeks. Data files and documents are also pulled in MAX_IO_CHUNK slices into a File; media and anything over 64 MB stay url-only (getFile() fetches on demand), so a 300 MB clip never sits in a JS array.

_Described by its group, Files shared to, or opened with, the app, rather than on its own._

**Example**

Internal plumbing behind `AppMint.releaseOpenedFile(f)`: deletes the cached copy of a shared or opened file, by token. Pages should use the public API.

**Returns:** nothing. **Needs:** turn on **Receive Shares From Other Apps** when you build.

The public API (use this):

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

**Notes:** After release, `f.url` returns an error and `openedFileChunk` returns `""` for that token. A `File` object you already have is not affected.

### `openedFilesPending`

```js
window.WebToApk.openedFilesPending(): String
```

A foreign page gets nothing: the files stay parked for the app's own page.

**Example**

Internal plumbing behind `AppMint.getOpenedFile()` / `AppMint.getOpenedFiles()`: lists the shared or opened files the page has not collected yet (metadata only, no bytes). Pages should use the public API.

**Returns:** (sync) a JSON **string**: an array of `{token, name, mimeType, size, isText, kind, url}`. `[]` when nothing is waiting. **Needs:** turn on **Receive Shares From Other Apps** when you build (without it the list is always empty and the `AppMint` helpers are not installed).

The public API (use this):

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

Only for debugging - peek at what is waiting without collecting it:

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

**Notes:** The helper collects these automatically after `load` and whenever a new file arrives, so a page rarely sees anything here. Once collected, a file leaves this list but stays readable until released.

