أنشئ APK مجانًا

واجهة JavaScript bridge API

التشغيل والاختصارات وزر الرجوع

كيف فُتح التطبيق، واختصارات الضغط المطوّل على الأيقونة، ومطالبة التقييم داخل التطبيق، وإخبار الغلاف بأن الصفحة تريد ضغطة الرجوع.

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

AppMint.requestReview(options)

مثال

يطلب من Google Play عرض ورقة التقييم الخاصة به داخل التطبيق (Play In-App Review) ويخبرك بما حدث.

القيمة المُعادة: وعد (Promise) يُحَلّ دائمًا بـ {result, openedStore, message?}. تكون result هي 'shown' (عرضت Play ورقتها - ولا تخبر أبدًا إن كان المستخدم قد قيّم)، أو 'not_from_play' (هذا التثبيت لم يأتِ من Google Play: APK مثبّت يدويًا أو تثبيت تجريبي، حيث لا تعرض Play الورقة أبدًا)، أو 'quota' (اختارت Play ألّا تعرضها الآن: بسبب حصة هذا المستخدم أو لأنه قيّم سابقًا)، أو 'error' (تعذّر سؤال Play: لا يوجد Play Store أو لا يوجد اتصال). مع {openStore: true} تُفتح صفحة التطبيق في Play عندما لا تظهر الورقة، وتكون openedStore هي true. المتطلبات: لا شيء يحتاج إلى تفعيل.

أي كود:

// A "Rate us" button: the sheet when Play shows it, the Play listing otherwise.
document.getElementById('rate-btn').onclick = async function () {
  if (!(window.AppMint && AppMint.requestReview)) { location.href = 'https://play.google.com/store/apps/details?id=com.example.app'; return; }
  var r = await AppMint.requestReview({ openStore: true });
  if (r.result === 'shown' || r.openedStore) showToast('Thank you!');
};

اسأل بعد لحظة نجاح بدلًا من زر، دون مغادرة التطبيق أبدًا:

async function onOrderDelivered(count) {
  if (count !== 3 || !(window.AppMint && AppMint.requestReview)) return;
  var r = await AppMint.requestReview();      // no openStore: nothing opens if Play says no
  if (r.result === 'not_from_play') console.log('Test install: Play only shows the sheet for Play installs');
}

عندما يفعّل المنشئ اطلب من المستخدمين تقييم التطبيق (خطوة العرض)، يسأل التطبيق بنفسه في لحظة هادئة ويخبر الصفحة بما حدث:

window.addEventListener('appmint:review', function (e) {
  // e.detail = { result, openedStore: false, source: 'auto' }
  analytics.track('rate_prompt', { result: e.detail.result });
});

ملاحظات: تحدّ Google Play معدل ظهور الورقة وتقرر بنفسها ما إذا كانت تظهر؛ فلا تكافئ على تقييم أبدًا ولا تعد بالورقة على زر. اختبرها بتثبيت من مسار الاختبار الداخلي في Play - فملف APK المثبّت يدويًا يجيب دائمًا not_from_play.

بدون كود: الرابط appmint://review. زر أو رابط بهذا العنوان يطلب الورقة نفسها (ويفتح صفحة Play عندما لا تعرضها Play، لأن المستخدم طلب التقييم). في رسالة داخل التطبيق ضعه على زر في مركز الإرسال (قالب قيّمنا يفعل ذلك)؛ وفي صفحة استخدمه كرابط عادي:

<a href="appmint://review" class="rate-link">Rate us on Google Play</a>

من سكربت، مثلًا بعد الطلب الثالث المكتمل:

function maybeAskForRating(ordersDone) {
  if (ordersDone === 3) location.href = 'appmint://review';   // the app runs it; nothing is loaded
}

اسمع ما اختاره المستخدم في رسالة "قيّمنا" داخل التطبيق قبل أن تُفتح الورقة:

window.addEventListener('appmint:inapp', function (e) {
  var d = e.detail;
  if (d.type === 'answered' && d.question === 'stars' && d.value <= 3) showFeedbackForm();
  if (d.type === 'clicked' && d.link === 'appmint://review') console.log('Opening the Play review sheet');
});

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

AppMint.setBackHandler(handler)

مثال

يعطي صفحتك الكلمة الأولى عندما يضغط المستخدم زر الرجوع في الهاتف (أو يستخدم إيماءة الرجوع). يحتاجها التطبيقات التي تغيّر الشاشات بإظهار العناصر وإخفائها، حيث يكون سجل أندرويد فارغًا وسيخرج الرجوع من التطبيق.

القيمة المُرجعة: لا شيء. يجب أن تُرجع handler الخاصة بك true (تم التعامل، ابقَ هنا) أو false (دع التطبيق يرجع / يخرج). المتطلبات: لا شيء. يُثبَّت عند بدء المستند، فيمكنك تسجيله في أول سكربت لك.

if (window.AppMint && AppMint.setBackHandler) {
  AppMint.setBackHandler(function () {
    if (isMenuOpen)               { closeMenu();  return true; }   // consumed
    if (currentScreen !== 'home') { goTo('home'); return true; }
    return false;   // nothing to undo: normal Back (history back, exit dialog, exit)
  });
}

// Remove it again, for example when the screen that needed it closes:
// AppMint.setBackHandler(null);

حدث appmint:back - قابل للإلغاء. استدعِ e.preventDefault() لاستهلاك الضغطة. يعمل قبل المعالج، ويمكن لأي عدد من المستمعين استخدامه:

window.addEventListener('appmint:back', function (e) {
  var sheet = document.querySelector('.bottom-sheet.open');
  if (sheet) {
    sheet.classList.remove('open');
    e.preventDefault();   // consumed: the handler and the shell do nothing more
  }
});

امنع ضياع ملاحظة نصف مكتوبة: يعرض الرجوع شريط "تجاهل التغييرات؟" الخاص بك بدلًا من المغادرة (فـ confirm() الحاجبة ستفوّت نافذة الإجابة البالغة 350 مللي ثانية، فيرجع الغلاف على أي حال):

if (window.AppMint && AppMint.setBackHandler) {
  var noteDirty = false;
  document.getElementById('note').addEventListener('input', function () { noteDirty = true; });

  AppMint.setBackHandler(function () {
    if (!noteDirty) return false;        // nothing to lose: normal Back
    showDiscardBar();                    // your in-page "Discard changes? / Keep editing" bar
    return true;                         // stay here while the user decides
  });

  document.getElementById('discard-btn').onclick = function () {
    noteDirty = false;                   // the next Back press passes straight through
    hideDiscardBar();
    closeEditor();
  };
}

ملاحظات: يجب أن تكون الإجابة متزامنة: إرجاع Promise يُحتسب "غير معالَج". أجب بسرعة - فإذا لم تُجب الصفحة خلال نحو 350 مللي ثانية يرجع الغلاف بنفسه. الترتيب عند كل ضغطة: تُغلق القائمة الجانبية المفتوحة (درج التطبيق نفسه) أولًا ولا تُسأل الصفحة؛ ثم مستمعو appmint:back ← معالجك ← مُغلِق مربعات الحوار المدمج في الغلاف (يغلق <dialog> مفتوحًا، أو حوار ARIA، أو نافذة منبثقة بملء الشاشة) ← الرجوع في السجل / تأكيد الخروج / الخروج. التطبيقات التي تستخدم history.pushState لكل شاشة (React Router وما شابه) لا تحتاج إلى هذا. يمكن للتطبيقات المبنية بالذكاء الاصطناعي استخدام onBackGesture من @/lib/appmintNative.

__pullRefreshEligible الجسر (bridge)#

window.WebToApk.__pullRefreshEligible(eligible: Boolean)

انظر [pullPageEligible] / [pullProbeJs]. تُستدعى عند كل touchstart.

مثال

آلية داخلية خلف السحب للتحديث: يستدعيها مسبار اللمس الخاص بالغلاف عند كل لمسة ليحدد ما إذا كان السحب لأسفل هناك يجب أن يحدّث الصفحة. لا تستدعيها الصفحات أبدًا - فهي تتحكم في السحب للتحديث بـ CSS وأحداث اللمس القياسية.

القيمة المُرجعة: لا شيء. المتطلبات: تفعيل تمكين السحب للتحديث في الخطوة 3 (التكامل)، تحت خيارات العرض، عند الإنشاء (بدونه لا يُثبَّت المسبار).

الطريقة العامة لمنع السحب من التحديث في جزء من صفحتك - استحوذ على الإيماءة:

<style>
  /* A map, drawing canvas or slider: this element owns vertical drags. */
  #map, #canvas { touch-action: none; }
  /* A horizontal carousel: pan-x also tells the shell "not a refresh". */
  .carousel { touch-action: pan-x; }
</style>

أو طالب بها من معالج لمس، بالطريقة القياسية:

document.getElementById('canvas').addEventListener('touchstart', function (e) {
  e.preventDefault();   // the page took this gesture: no pull-to-refresh
}, { passive: false });

امنع سطح التحريك والتكبير من تحديث الصفحة بالمطالبة بالسحب العمودي بـ CSS فقط:

<div id="board" style="touch-action: none; height: 60vh"></div>
<script>
  // Dragging on #board moves the board; a pull from the top of the rest of the page still refreshes.
  document.getElementById('board').addEventListener('pointermove', function (e) { moveBoard(e); });
</script>

ملاحظات: لا يحدّث السحب الصفحة إلا عندما يكون أقرب صندوق قابل للتمرير تحت الإصبع (أو الصفحة نفسها) ممرَّرًا إلى الأعلى، ولا يوجد إلا إصبع واحد على الشاشة، ولا ينطبق أيٌّ من الادعاءين أعلاه. اللمس داخل iframe من أصل مختلف لا يحدّث الصفحة أبدًا. لا تستدعِ هذه الدالة بنفسك.

getAppIcon الجسر (bridge)#

window.WebToApk.getAppIcon(): String

أيقونة المشغّل المستخدمة: "default" أو أحد الأسماء من getAvailableIcons().

مثال

أيقونة المشغّل المستخدمة: 'default' أو أحد الأسماء من getAvailableIcons().

القيمة المُرجعة: سلسلة. 'default' في إنشاء بدون أيقونات بديلة.

if (window.WebToApk && WebToApk.getAppIcon) {
  const current = WebToApk.getAppIcon();          // 'default', 'Dark', …
  document.querySelectorAll('[data-icon]').forEach(el => {
    el.classList.toggle('selected', el.dataset.icon === current);
  });
}

انتقل إلى الأيقونة التالية من شاشة الإعدادات:

function nextIcon() {
  if (!(window.WebToApk && WebToApk.getAvailableIcons && WebToApk.setAppIcon)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  if (names.length < 2) return;                                  // no alternate icons in this build
  const next = names[(names.indexOf(WebToApk.getAppIcon()) + 1) % names.length];
  if (!WebToApk.setAppIcon(next)) alert('Could not change the icon.');
}

اعرض اسم الأيقونة في تسمية، أو أخفِه خارج التطبيق:

const label = document.getElementById('icon-label');
if (window.WebToApk && WebToApk.getAppIcon) {
  const name = WebToApk.getAppIcon();
  label.textContent = name === 'default' ? 'Classic icon' : name + ' icon';
} else {
  label.hidden = true;           // in a browser there is no launcher icon
}

getAvailableIcons الجسر (bridge)#

window.WebToApk.getAvailableIcons(): String

مصفوفة JSON بأسماء الأيقونات التي يقبلها setAppIcon: "default" أولًا، ثم بدائل الإنشاء.

مثال

أسماء الأيقونات التي تقبلها setAppIcon، كسلسلة مصفوفة JSON: 'default' أولًا، ثم الأيقونات البديلة من معالج الإنشاء بالترتيب الذي أضفتها به.

القيمة المُرجعة: '["default","Dark","Gold"]'، أو '["default"]' في إنشاء بدون أيقونات بديلة.

أنشئ منتقي أيقونات:

function iconPicker(container) {
  if (!(window.WebToApk && WebToApk.getAvailableIcons)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  if (names.length < 2) { container.hidden = true; return; }   // nothing to choose
  const current = WebToApk.getAppIcon();
  container.innerHTML = '';
  names.forEach(name => {
    const b = document.createElement('button');
    b.textContent = name === 'default' ? 'Classic' : name;
    b.className = name === current ? 'selected' : '';
    b.onclick = () => { if (WebToApk.setAppIcon(name)) iconPicker(container); };
    container.appendChild(b);
  });
}
iconPicker(document.querySelector('#iconPicker'));

أخفِ المنتقي في إنشاء بدون أيقونات بديلة:

const picker = document.getElementById('iconPicker');
let names = ['default'];
if (window.WebToApk && WebToApk.getAvailableIcons) names = JSON.parse(WebToApk.getAvailableIcons());
picker.hidden = names.length < 2;      // only 'default' means there is nothing to choose

بدّل الأيقونة مع السمة:

function matchIconToTheme(dark) {
  if (!(window.WebToApk && WebToApk.getAvailableIcons && WebToApk.setAppIcon)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  const wanted = dark ? 'Dark' : 'default';
  if (names.includes(wanted) && WebToApk.getAppIcon() !== wanted) WebToApk.setAppIcon(wanted);
}
matchIconToTheme(matchMedia('(prefers-color-scheme: dark)').matches);

getLaunchUrl الجسر (bridge)#

window.WebToApk.getLaunchUrl(): String

الرابط العميق الذي شغّل هذا التطبيق أو استأنفه، أو "" إذا فُتح بشكل عادي. اقرأه كلما حُمّل تطبيقك ووجّه بناءً عليه بنفسك: const u = window.WebToApk?.getLaunchUrl?.(); if (u) router.navigate(new URL(u).pathname); القيمة تُحفظ ولا تُستهلك، فيراها حتى تطبيق يُحمَّل متأخرًا؛ والرابط اللاحق الذي يصل أثناء عمل التطبيق يستبدلها ويُطلق أيضًا 'appmint:deep-link'.

مثال

الرابط العميق الذي فتح (أو أعاد فتح) التطبيق، كسلسلة، أو "" عندما فُتح التطبيق بشكل عادي.

القيمة المُرجعة: سلسلة، بشكل متزامن. المتطلبات: لا شيء. القيمة تُحفظ ولا تُستهلك، فمن الآمن قراءتها كلما حُمّل تطبيقك.

قد يصل الرابط بثلاثة أشكال: https://yoursite.com/path، أو appmint-shortcut://<package>/<route> الخاص بالغلاف (اختصارات المشغّل، وودجت الشاشة الرئيسية)، أو مخططك المخصص myapp://segment/path. تحوّل هذه الدالة المساعدة الثلاثة إلى مسار:

function routeOf(link) {
  var m = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]*)([^#]*)/i.exec(link || '');
  if (!m) return '/';
  var scheme = m[1].toLowerCase(), host = m[2], rest = m[3] || '';
  if (scheme === 'http' || scheme === 'https' || scheme === 'appmint-shortcut') return rest || '/';
  return '/' + host + rest;               // myapp://product/42 -> /product/42
}

// 1. On start: the link that launched the app.
if (window.WebToApk && typeof window.WebToApk.getLaunchUrl === 'function') {
  var link = window.WebToApk.getLaunchUrl();
  if (link) goTo(routeOf(link));
}

// 2. While running: a new link replaces the old one and fires 'appmint:deep-link'.
window.addEventListener('appmint:deep-link', function (e) {
  goTo(routeOf(e.detail.url));            // detail: { url }
});

يُمرَّر الإعلان نفسه أيضًا إلى خطاف عام، إن فضّلت واحدًا:

window.onAppMintDeepLink = function (detail) { goTo(routeOf(detail.url)); };

اقرأ رمز إحالة من رابط مثل https://yoursite.com/invite?code=AB12 أو myapp://invite?code=AB12، مرة واحدة لكل رابط (يستخدم routeOf من الأعلى):

function applyInvite(link) {
  if (!link) return;
  var done = '';
  try { done = sessionStorage.getItem('handledLink') || ''; } catch (e) {}
  if (link === done) return;                      // the same url is announced again after a reload
  try { sessionStorage.setItem('handledLink', link); } catch (e) {}
  var code = null;
  try { code = new URL(link).searchParams.get('code'); } catch (e) { return; }
  if (code && routeOf(link).indexOf('/invite') === 0) showInviteBanner(code);
}

if (window.WebToApk && typeof window.WebToApk.getLaunchUrl === 'function') {
  applyInvite(window.WebToApk.getLaunchUrl());
  window.addEventListener('appmint:deep-link', function (e) { applyInvite(e.detail.url); });
}

ملاحظات: يُطلَق appmint:deep-link أيضًا بعد كل تحميل للصفحة ما دام الرابط محفوظًا، لذا قد يصل url نفسه أكثر من مرة. تجاهل الرابط الذي عالجته من قبل.

تطبيقات وضع الموقع مختلفة: يحمّل الغلاف نفسه رابط https:// الوارد (عند البدء البارد وأثناء العمل)، ويُعاد كتابة رابط المخطط المخصص myapp://product/42 أولًا على عنوان موقعك (https://yoursite.com/product/42)، لذا تُرجع getLaunchUrl() صيغة https:// وليس لدى صفحتك عادةً ما توجّه عليه. نقرة اختصار مشغّل أو ودجت تفتح مسارها على موقعك (/today يحمّل https://yoursite.com/today)، وما زالت getLaunchUrl() تُرجع صيغة appmint-shortcut:// (انظر setAppShortcuts). في تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي (التي تُقدَّم من داخل التطبيق) لا ينتقل الغلاف أبدًا: تحصل صفحتك على الرابط وتوجّه بناءً عليه. ويبقى رابط المخطط المخصص كما وصل هناك، لأنه لا يوجد عنوان موقع يُحوَّل إليه. يمكن للتطبيقات المبنية بالذكاء الاصطناعي استخدام launchUrl() من @/lib/appmintNative.

rateApp الجسر (bridge)#

window.WebToApk.rateApp(): String

يفتح تدفق التقييم في صفحة Play. كانت `rateApp()` موجودة لكن لم يكن يصل إليها إلا عنصر الدرج الأصلي، فلم يكن لدى التطبيق الذي يرسم واجهته بنفسه - كل إنشاء بالذكاء الاصطناعي، حيث يكون الدرج مغلقًا - أي طريقة لطلب تقييم. وهي إجراء المتجر الوحيد الذي لا تستطيع الصفحة تنفيذه بنفسها حقًا: `market://` هو intent وليس رابطًا قابلًا للتنقل. مقيّدة بمفتاح "زر تقييم التطبيق" الخاص بالمنشئ، تمامًا مثل عنصر الدرج وعنصر المشاركة في شريط التنقل السفلي: فإيقاف التقييم يجب أن يعني عدم وجود أي سطح تقييم في أي مكان، وإلا يبدو المفتاح معطلًا.

مثال

يفتح صفحة التطبيق على Google Play ليتمكن المستخدم من تقييمه.

القيمة المُرجعة: 'opened' عند فتح الصفحة، 'off' عندما أوقف المنشئ تقييم التطبيق (عندها لا يحدث شيء). المتطلبات: مفتاح زر تقييم التطبيق في الخطوة 5 (القائمة) عند الإنشاء (مفعّل افتراضيًا؛ ويضيف أيضًا عنصر "قيّم التطبيق" إلى القائمة الجانبية). وإذا كان متوقفًا لا يفعل الاستدعاء شيئًا ويُرجع 'off'. لورقة التقييم داخل التطبيق من Google Play استخدم AppMint.requestReview().

var rateBtn = document.getElementById('rate-btn');
if (window.WebToApk && typeof window.WebToApk.rateApp === 'function') {
  rateBtn.onclick = function () { window.WebToApk.rateApp(); };
} else {
  rateBtn.hidden = true;   // in a browser
}

اعرض التقييم مرة واحدة، بعد أن ينهي المستخدم خمس تمارين، من بطاقة ينقرها:

function maybeOfferRating(completedCount) {
  if (!window.WebToApk || typeof window.WebToApk.rateApp !== 'function') return;
  var asked = false;
  try { asked = localStorage.getItem('rateAsked') === '1'; } catch (e) {}
  if (asked || completedCount < 5) return;

  var card = document.getElementById('rate-card');   // "Enjoying the app? Rate it on Google Play"
  card.hidden = false;
  card.querySelector('.rate-yes').onclick = function () {
    window.WebToApk.rateApp();                         // opens the Play listing
    try { localStorage.setItem('rateAsked', '1'); } catch (e) {}
    card.hidden = true;
  };
  card.querySelector('.rate-later').onclick = function () { card.hidden = true; };
}

صف "قيّمنا" في شاشة إعداداتك يقول شكرًا عندما يعود المستخدم من Google Play (عودة الصفحة إلى المقدمة هي الإشارة الوحيدة إلى أن المستخدم عاد):

document.getElementById('settings-rate').onclick = function () {
  if (!window.WebToApk || typeof window.WebToApk.rateApp !== 'function') return;
  window.WebToApk.rateApp();
  document.addEventListener('visibilitychange', function onBack() {
    if (document.visibilityState !== 'visible') return;
    document.removeEventListener('visibilitychange', onBack);
    showToast('Thanks for taking a look!');           // your own toast; the user may not have rated
  });
};

ملاحظات: يفتح صفحة التطبيق في Play Store (market://details?id=<package>)، أو موقع Play على الويب عندما يكون تطبيق Play Store غير موجود. وهو ليس بطاقة التقييم داخل التطبيق. لا تستطيع الصفحة فتح market:// بنفسها - فهو Android intent وليس عنوان ويب. اطلب التقييم فقط بعد أن يفعل المستخدم شيئًا أعجبه، ومن خلال نقرة.

requestReview الجسر (bridge)#

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

يطلب من Play ورقة التقييم الخاصة بها؛ [optionsJson] هو `{openStore?}`. يُحَلّ AppMint.requestReview [callbackId].

مثال

آلية داخلية خلف AppMint.requestReview() - تستخدم الصفحات تلك الدالة المساعدة التي تعيد Promise، وتطلب من Google Play ورقة التقييم داخل التطبيق وتُحلّ بما حدث.

القيمة المُرجعة: لا شيء مباشرة؛ وتحدد النتيجة وعد AppMint.requestReview(). المتطلبات: لا شيء لتفعيله. لا تظهر الورقة إلا لتطبيق مثبّت من Google Play.

الواجهة العامة:

async function askForRating() {
  if (!(window.AppMint && AppMint.requestReview)) return;          // a browser: no store here
  var r = await AppMint.requestReview();
  console.log(r.result);   // 'shown' | 'not_from_play' | 'quota' | 'error'
}

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

async function afterHappyMoment() {
  if (!(window.AppMint && AppMint.requestReview)) return;
  const r = await AppMint.requestReview({ openStore: true });   // {result, openedStore, message?}
  if (r.result === 'shown') thankUser();
  else if (r.openedStore) console.log('Opened the Play listing instead');
}

استمع إلى المطالبة التلقائية وأبلغ عن خطأ:

window.addEventListener('appmint:review', (e) => {
  if (e.detail.source === 'auto') console.log('Rate prompt result:', e.detail.result);
});

AppMint.requestReview().then((r) => {
  if (r.result === 'error') console.warn(r.message);
  if (r.result === 'not_from_play') showMessage('Rating is available once the app is installed from Google Play.');
});

setAppIcon الجسر (bridge)#

window.WebToApk.setAppIcon(name: String): Boolean

يغيّر أيقونة مشغّل التطبيق إلى إحدى الأيقونات البديلة المضافة في معالج الإنشاء، بالاسم المعطى هناك ("default" = الأيقونة العادية). يُرجع false لاسم لا يملكه هذا الإنشاء. يعيد المشغّل رسم نفسه في وقته (بضع ثوانٍ في بعض الهواتف)، وبعض المشغّلات تغلق التطبيق أو تنقل أيقونته على الشاشة الرئيسية عند تغييرها - بدّل من شاشة إعدادات، وليس في منتصف مهمة.

مثال

يغيّر أيقونة مشغّل التطبيق إلى إحدى الأيقونات البديلة التي أضفتها في معالج الإنشاء (خطوة الأساسيات، "أيقونات التطبيق البديلة")، بالاسم الذي أعطيته لها هناك. 'default' هي أيقونتك العادية.

القيمة المُرجعة: true عند إجراء التبديل (أو إذا كانت هي الأيقونة بالفعل)، false لاسم لا يملكه هذا الإنشاء. المتطلبات: أيقونات بديلة مضافة في المعالج (حتى 4). تحتاج بعض المشغّلات بضع ثوانٍ لإعادة رسم الأيقونة، وبعضها يغلق التطبيق أو ينقل اختصاره على الشاشة الرئيسية عند تغييرها: بدّل من شاشة إعدادات، وليس في منتصف مهمة. وتُعاد نشر اختصارات مشغّلك (setAppShortcuts) تحت الأيقونة الجديدة تلقائيًا.

function useIcon(name) {
  if (!(window.WebToApk && window.WebToApk.setAppIcon)) return false;
  const ok = WebToApk.setAppIcon(name);
  if (!ok) console.warn('no icon called', name);
  return ok;
}

document.querySelector('#darkIcon').addEventListener('click', () => useIcon('Dark'));
document.querySelector('#normalIcon').addEventListener('click', () => useIcon('default'));

أنشئ المنتقي من الأيقونات التي يملكها هذا الإنشاء:

if (window.WebToApk && WebToApk.getAvailableIcons) {
  const names = JSON.parse(WebToApk.getAvailableIcons());   // 'default' first, then your alternates
  const current = WebToApk.getAppIcon();
  names.forEach((n) => addRadio('icon', n, n === current, () => WebToApk.setAppIcon(n)));
}

تحقق من الإجابة لاسم لا يملكه الإنشاء:

function pickIcon(name) {
  if (!(window.WebToApk && WebToApk.setAppIcon)) return showMessage('Icons can be changed in the app only');
  if (!WebToApk.setAppIcon(name)) {
    showMessage('Unknown icon "' + name + '". Choose one of: ' + JSON.parse(WebToApk.getAvailableIcons()).join(', '));
  } else {
    showMessage('Done. Your launcher may take a few seconds to redraw the icon.');
  }
}

ملاحظات: أول تحديث يضيف أيقونات بديلة ينقل إدخال المشغّل إلى مكوّن جديد، لذا قد يلزم وضع اختصار الشاشة الرئيسية المصنوع قبل ذلك التحديث من جديد.

setAppShortcuts الجسر (bridge)#

window.WebToApk.setAppShortcuts(itemsJson: String)

اختصارات المشغّل (الضغط المطوّل على أيقونة التطبيق). حتى أربعة إدخالات: `[{id, title, route}]`، حيث `route` مسار في التطبيق ('/new-note'). تفتح النقرة التطبيق بذلك المسار كرابط عميق للتشغيل (`appmint-shortcut://<package><route>`)، وتقرؤه الصفحة عبر `getLaunchUrl()` وتوجّه بناءً عليه تمامًا كأي رابط عميق آخر. في تطبيق بوضع الموقع يفتح الغلاف نفسه المسار على الموقع (`/new-note` ← `https://<site>/new-note`). مرّر `[]` لإزالتها. أندرويد 7.1+؛ ولا تفعل شيئًا دون ذلك.

مثال

يضبط اختصارات المشغّل التي تظهر عندما يلمس المستخدم أيقونة التطبيق ويضغط مطولًا.

القيمة المُرجعة: لا شيء. المتطلبات: لا شيء. أندرويد 7.1+ (لا تفعل شيئًا دون ذلك).

itemsJson هو سلسلة JSON: مصفوفة من أربعة كائنات {id, title, route} على الأكثر. route مسار داخل تطبيقك. تفتح النقرة التطبيق بـ appmint-shortcut://<package><route> كرابط تشغيله:

if (window.WebToApk && typeof window.WebToApk.setAppShortcuts === 'function') {
  window.WebToApk.setAppShortcuts(JSON.stringify([
    { id: 'new-note', title: 'New note', route: '/new-note' },
    { id: 'search',   title: 'Search',   route: '/search' }
  ]));
}

اقرأ النقرة بالطريقة نفسها كأي رابط عميق آخر، عند البدء وأثناء العمل:

function shortcutRoute(link) {
  var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(link || '');
  return m ? (m[1] || '/') : null;
}
if (window.WebToApk && window.WebToApk.getLaunchUrl) {
  var r = shortcutRoute(window.WebToApk.getLaunchUrl());
  if (r) goTo(r);
}
window.addEventListener('appmint:deep-link', function (e) {
  var r = shortcutRoute(e.detail.url);
  if (r) goTo(r);
});

أزل كل الاختصارات:

if (window.WebToApk && window.WebToApk.setAppShortcuts) window.WebToApk.setAppShortcuts('[]');

ملاحظات: كل استدعاء يستبدل القائمة كلها. تُتخطى العناصر التي ليس لها title؛ ويُقصّ الوسم إلى 25 حرفًا (50 للوسم الطويل). القيمة الافتراضية لـ id هي appmint-<index>. المسار الذي لا يبدأ بـ / تتم إضافتها له. الأيقونة هي أيقونة التطبيق. القائمة غير الصحيحة تُتجاهل (وتُسجَّل) ولا يُطلَق خطأ. يمكن للتطبيقات المبنية بالذكاء الاصطناعي استخدام shortcuts من @/lib/appmintNative. تطبيقات وضع الموقع: تفتح النقرة المسار على موقعك - فـ /new-note يحمّل https://<your site>/new-note - ويظل getLaunchUrl() يعيد الرابط appmint-shortcut://، فتستمر الصفحة التي توجّه نفسها بالعمل.

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

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