أنشئ APK مجانًا

واجهة JavaScript bridge API

الملفات: المفتوحة بهذا التطبيق

جمع ما سلّمه تطبيق آخر عبر "فتح باستخدام"، على أجزاء تسحبها الصفحة.

AppMint.getOpenedFile دالة مساعدة (helper)#

AppMint.getOpenedFile()

مثال

يعطي صفحتك الملف الذي شُورك إلى التطبيق، أو فُتح به ("فتح باستخدام…")، مهما تأخر بدء صفحتك.

يُرجع: Promise<object | null> - null عندما فُتح التطبيق بشكل عادي. للكائن token, name, mimeType, size, kind, url, file, getFile()، إضافة إلى text (ملفات نصية حتى 32 ميغابايت) أو base64 (ملفات بيانات أخرى حتى 8 ميغابايت) عندما يكون ذلك رخيصًا. kind هو 'image' | 'video' | 'audio' | 'document' | 'data'. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء وحدّد أنواع الملفات التي يقبلها تطبيقك.

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

أو استمع - الحدث (وخطاف window.onAppMintFileOpen الاختياري) يُطلق مرة لكل ملف جديد، بالكائن نفسه مثل 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}
});

في تطبيق React، يلتقط خطاف صغير الملف مهما تأخر تركيب المكوّن، ويواصل متابعة الملفات الجديدة:

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

ملاحظات: الصور والفيديو والصوت وأي ملف يتجاوز 64 ميغابايت تأتي فقط كـ url (file يكون null)؛ استدعِ f.getFile() عندما تحتاج البايتات فعلًا. ملفات البيانات والمستندات تأتي أيضًا كـ f.file. يُحفظ الملف لصفحتك حتى تطلبه، فيحصل عليه تطبيق SPA الذي يُحمَّل متأخرًا؛ ويُرجع في كل مرة أحدث ملف. وعندما يكون التطبيق مفتوحًا بالفعل ويصل ملف آخر، يُطلق الحدث مرة أخرى. استدعِ AppMint.releaseOpenedFile(f) عند الانتهاء لتحرير النسخة المخزّنة.

AppMint.getOpenedFiles دالة مساعدة (helper)#

AppMint.getOpenedFiles()

مثال

يعطي صفحتك كل ملف شُورك إلى التطبيق أو فُتح به خلال هذه الجلسة - مفيد عندما يشارك المستخدم عدة صور دفعة واحدة.

يُرجع: Promise<Array> من الكائنات نفسها التي يُرجعها AppMint.getOpenedFile() (token, name, mimeType, size, kind, url, file, getFile()، وtext / base64 عندما يكون ذلك رخيصًا). مصفوفة فارغة عندما لم يُستقبل شيء. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء وحدّد أنواع الملفات التي يقبلها تطبيقك.

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

الملفات الجديدة التي تصل أثناء فتح الصفحة تُطلق أيضًا appmint:fileopen، حدثًا واحدًا لكل ملف:

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

رفع كل ما شاركه المستخدم في طلب واحد، كملفات حقيقية:

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

ملاحظات: القائمة نسخة؛ وتنمو كلما وصلت ملفات أكثر. الملفات التي حررتها بـ AppMint.releaseOpenedFile(f) تبقى في القائمة، لكن url الخاص بها لا يعمل بعد ذلك.

AppMint.releaseOpenedFile دالة مساعدة (helper)#

AppMint.releaseOpenedFile(file)

مثال

يحذف النسخة المخزّنة مؤقتًا في التطبيق من ملف شُورك إلى التطبيق أو فُتح به، بمجرد انتهاء صفحتك منه.

يُرجع: لا شيء. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء.

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
}

يُشغَّل الفيديو المشترك من f.url، فلا تحرره إلا بعد انتهاء التشغيل:

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

بعد استيراد دفعة من الصور المشتركة، حرّر كل النسخ المخزّنة مؤقتًا دفعة واحدة:

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

ملاحظات: مرّر الكائن الذي حصلت عليه (يحتاج إلى token). كائن File سحبته بالفعل يبقى صالحًا؛ أما url فيتوقف عن العمل، وgetFile() يُرفض ما لم يكن قد جلب الـ File بالفعل. لا تحرر فيديو ما زلت تشغّله من f.url. ويمسح التطبيق هذه النسخ أيضًا عند إغلاق شاشته، فالتحرير المبكر يخص مساحة القرص خلال جلسة طويلة.

openedFileChunk الجسر (bridge)#

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

داخلي لـ AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). تسحب الصفحة بنفسها بدلًا من أن يُدفع إليها: الملف المفتوح عند البدء البارد يُحجز حتى يُطلب، فيحصل عليه تطبيق SPA الذي يُركَّب متأخرًا. تحمل بيانات كل ملف الوصفية `kind` (image | video | audio | document | data) و`url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>، يُقدَّم من النسخة المخزّنة مع نطاقات البايت، فيُبثّ الفيديو المشترك إلى <video src> ويمكن التنقل فيه. تُسحب ملفات البيانات والمستندات أيضًا بشرائح MAX_IO_CHUNK إلى File؛ أما الوسائط وأي شيء فوق 64 ميغابايت فيبقى url فقط (getFile() يجلب عند الطلب)، فلا يقبع مقطع 300 ميغابايت في مصفوفة JS.

موصوفة ضمن مجموعتها، الملفات المشتركة إلى التطبيق أو المفتوحة به، وليس على حدة.

مثال

آلية داخلية خلف AppMint.getOpenedFile(): تُرجع شريحة واحدة من بايتات ملف مشترك أو مفتوح، تضمّها المساعدة في File. على الصفحات استخدام الواجهة العامة - f.file أو f.getFile() أو f.url.

يُرجع: (متزامن) سلسلة base64 بحد أقصى len بايت ابتداءً من offset (تُمرَّر الإزاحة كسلسلة، فتعمل الملفات فوق 2 غيغابايت أيضًا)؛ و"" عند نهاية الملف، أو لرمز غير معروف أو محرَّر، أو عند خطأ قراءة. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء.

الواجهة العامة (استخدم هذه):

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

ما تفعله المساعدة به، يُعرض فقط لشرح الوسائط:

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

قراءة ملف مشترك كبير جدًا دون شرائح - ابثّ f.url واعرض التقدم (هنا عدّ صفوف 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);
}

ملاحظات: يُقيَّد len من 1 بايت إلى 4 ميغابايت. وللملفات الإعلامية، بثّ f.url أرخص بكثير من سحب الشرائح.

openedFileCollected الجسر (bridge)#

window.WebToApk.openedFileCollected(token: String)

داخلي لـ AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). تسحب الصفحة بنفسها بدلًا من أن يُدفع إليها: الملف المفتوح عند البدء البارد يُحجز حتى يُطلب، فيحصل عليه تطبيق SPA الذي يُركَّب متأخرًا. تحمل بيانات كل ملف الوصفية `kind` (image | video | audio | document | data) و`url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>، يُقدَّم من النسخة المخزّنة مع نطاقات البايت، فيُبثّ الفيديو المشترك إلى <video src> ويمكن التنقل فيه. تُسحب ملفات البيانات والمستندات أيضًا بشرائح MAX_IO_CHUNK إلى File؛ أما الوسائط وأي شيء فوق 64 ميغابايت فيبقى url فقط (getFile() يجلب عند الطلب)، فلا يقبع مقطع 300 ميغابايت في مصفوفة JS.

موصوفة ضمن مجموعتها، الملفات المشتركة إلى التطبيق أو المفتوحة به، وليس على حدة.

مثال

آلية داخلية خلف AppMint.getOpenedFile(): تعلّم ملفًا مشتركًا أو مفتوحًا بأنه جُمع، فلا يُعرض بعدها كـ "جديد" لكن تبقى نسخته المخزّنة. على الصفحات استخدام الواجهة العامة؛ ولا سبب لاستدعائها مباشرة.

يُرجع: لا شيء. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء.

الواجهة العامة (استخدم هذه) - يُجمع كل ملف مرة واحدة ويُسلَّم لصفحتك:

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

الملف الذي جُمع عند البدء موجود في القائمة ومُعلَن عنه بالحدث معًا، فاربط معالجتك بـ token لتعالج كل ملف مرة واحدة:

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

الملف الذي تعذّرت قراءته يبقى مجموعًا؛ ويُبلَّغ عنه مرة واحدة عبر appmint:fileopenerror بدلًا من 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);
});

ملاحظات: استدعاؤها بنفسك يخفي ملفًا عن AppMint.getOpenedFile() وحدث appmint:fileopen دون تسليمه لأحد. لتحرير النسخة المخزّنة، استخدم AppMint.releaseOpenedFile(f).

openedFileMaxChunk الجسر (bridge)#

window.WebToApk.openedFileMaxChunk(): Int

داخلي لـ AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). تسحب الصفحة بنفسها بدلًا من أن يُدفع إليها: الملف المفتوح عند البدء البارد يُحجز حتى يُطلب، فيحصل عليه تطبيق SPA الذي يُركَّب متأخرًا. تحمل بيانات كل ملف الوصفية `kind` (image | video | audio | document | data) و`url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>، يُقدَّم من النسخة المخزّنة مع نطاقات البايت، فيُبثّ الفيديو المشترك إلى <video src> ويمكن التنقل فيه. تُسحب ملفات البيانات والمستندات أيضًا بشرائح MAX_IO_CHUNK إلى File؛ أما الوسائط وأي شيء فوق 64 ميغابايت فيبقى url فقط (getFile() يجلب عند الطلب)، فلا يقبع مقطع 300 ميغابايت في مصفوفة JS.

موصوفة ضمن مجموعتها، الملفات المشتركة إلى التطبيق أو المفتوحة به، وليس على حدة.

مثال

آلية داخلية خلف AppMint.getOpenedFile(): أكبر شريحة يُرجعها openedFileChunk في استدعاء واحد (4 ميغابايت). على الصفحات استخدام الواجهة العامة، التي تقرأ فعلًا بشرائح بهذا الحجم.

يُرجع: (متزامن) عدد بايتات (4194304). المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء.

الواجهة العامة (استخدم هذه):

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

الاستدعاء الخام، إذا أردت معرفة الرقم فقط:

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

تقدير عدد استدعاءات الجسر التي ستجريها المساعدة لكل ملف منتظر، عند تحليل أداء استيراد بطيء:

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

ملاحظات: الحد الأقصى نفسه لـ getMaxIoChunkSize() في جسر الوصول إلى الملفات.

openedFileRelease الجسر (bridge)#

window.WebToApk.openedFileRelease(token: String)

داخلي لـ AppMint.getOpenedFile() / getOpenedFiles() / releaseOpenedFile(). تسحب الصفحة بنفسها بدلًا من أن يُدفع إليها: الملف المفتوح عند البدء البارد يُحجز حتى يُطلب، فيحصل عليه تطبيق SPA الذي يُركَّب متأخرًا. تحمل بيانات كل ملف الوصفية `kind` (image | video | audio | document | data) و`url` - https://appassets.androidplatform.net/appmint-shared/<token>/<name>، يُقدَّم من النسخة المخزّنة مع نطاقات البايت، فيُبثّ الفيديو المشترك إلى <video src> ويمكن التنقل فيه. تُسحب ملفات البيانات والمستندات أيضًا بشرائح MAX_IO_CHUNK إلى File؛ أما الوسائط وأي شيء فوق 64 ميغابايت فيبقى url فقط (getFile() يجلب عند الطلب)، فلا يقبع مقطع 300 ميغابايت في مصفوفة JS.

موصوفة ضمن مجموعتها، الملفات المشتركة إلى التطبيق أو المفتوحة به، وليس على حدة.

مثال

آلية داخلية خلف AppMint.releaseOpenedFile(f): تحذف النسخة المخزّنة مؤقتًا من ملف مشترك أو مفتوح، بواسطة الرمز. على الصفحات استخدام الواجهة العامة.

يُرجع: لا شيء. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء.

الواجهة العامة (استخدم هذه):

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

يتجاهل المستخدم ملفًا مشتركًا دون استيراده - حرّر النسخة المخزّنة فورًا:

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

ملف لا يستطيع التطبيق استخدامه يُحرَّر فور رفضه:

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

ملاحظات: بعد التحرير، يُرجع f.url خطأً ويُرجع openedFileChunk القيمة "" لذلك الرمز. كائن File لديك مسبقًا لا يتأثر.

openedFilesPending الجسر (bridge)#

window.WebToApk.openedFilesPending(): String

الصفحة الغريبة لا تحصل على شيء: تبقى الملفات محجوزة لصفحة التطبيق نفسه.

مثال

آلية داخلية خلف AppMint.getOpenedFile() / AppMint.getOpenedFiles(): تسرد الملفات المشتركة أو المفتوحة التي لم تجمعها الصفحة بعد (بيانات وصفية فقط، دون بايتات). على الصفحات استخدام الواجهة العامة.

يُرجع: (متزامن) سلسلة JSON: مصفوفة من {token, name, mimeType, size, isText, kind, url}. [] عندما لا شيء ينتظر. المتطلبات: فعّل استقبال المشاركات من التطبيقات الأخرى عند الإنشاء (بدونه تكون القائمة فارغة دائمًا ولا تُثبَّت مساعدات AppMint).

الواجهة العامة (استخدم هذه):

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

للتصحيح فقط - اطّلع على ما ينتظر دون جمعه:

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

تصحيح أخطاء ملف لم يصل إلى الصفحة - انظر ما لا يزال ينتظر وهل سيصل كـ f.file أم فقط كـ 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)' : '');
  });
}

ملاحظات: تجمع المساعدة هذه الملفات تلقائيًا بعد load وكلما وصل ملف جديد، فنادرًا ما ترى الصفحة شيئًا هنا. بعد الجمع يغادر الملف هذه القائمة لكنه يبقى قابلًا للقراءة حتى يُحرَّر.

يتم إنشاؤه من بيئة تشغيل التطبيق وملفات الأمثلة الخاصة به عند كل بناء للمستندات. اقرأه بصيغة Markdown · جميع العائلات.

تمت المراجعة مقابل الجسر المُرفق بتاريخ 2026-10-22.