ساخت APK رایگان

API پل جاوااسکریپت

اجرا در پس‌زمینه

سرویس پیش‌زمینه‌ای که صفحه شروع و متوقف می‌کند، مکانی که با اپلیکیشن در پس‌زمینه به رسیدن ادامه می‌دهد، geofenceهایی که با بسته بودن اپلیکیشن اجرا می‌شوند، و کد خودتان که پس از راه‌اندازی مجدد به‌صورت دوره‌ای اجرا می‌شود. هر کدام کلیدش را در ساخت و یک اعلامیه در Play Console لازم دارد.

AppMint.background کمک‌کننده#

AppMint.background.register(name, options) / unregister / list / runNow / done / isBackgroundRun / currentRun / onRun

نمونه

کد خودِ صفحهٔ شما را به‌صورت دوره‌ای و با بسته بودن اپلیکیشن اجرا می‌کند - به‌روزرسانی داده، بررسی یک قیمت، ارسال یک اعلان. هر اجرا صفحهٔ شما را نامرئی بارگذاری می‌کند (همان مبدأ، همان localStorage/IndexedDB، بدون صفحه و بدون تبلیغ) با ?appmint_background=1&appmint_task=<name>&appmint_run=<runId>، پس از بارگذاری appmint:background { name, runId } را اجرا می‌کند و منتظر done(runId, { ok }) می‌ماند. هر اجرا پس از حدود ۹ دقیقه قطع می‌شود.

مقدار بازگشتی: هر متد یک Promise برمی‌گرداند. register ← { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name، interval_below_15_minutes، invalid_interval، schedule_failed). unregister ← true / false. list ← [{ name, intervalMinutes, requiresNetwork, requiresCharging, registeredAt, lastRunAt, lastResult }] (lastResult: ok، retry، failed، timeout، unsupported_mode، page_load_failed، …). runNow ← { ok } (یک اجرا به‌محض اینکه شرایط اجازه دهد - برای آزمایش). done ← true وقتی اجرا پایان یافت. isBackgroundRun() و currentRun() همگام‌اند. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه). اپلیکیشن‌های وب‌سایت و آفلاین/ZIP؛ اپلیکیشن‌های سند و رسانه کد صفحه‌ای برای اجرا ندارند.

یک صفحه، دو نقش:

function startApp() {
  const bg = window.AppMint && AppMint.background;
  if (bg && bg.isBackgroundRun()) return;          // a background run: no UI work
  renderUi();
  if (bg) bg.register('prices', { intervalMinutes: 60, requiresNetwork: true }).then(function (r) {
    if (!r.ok) console.warn('background task not scheduled:', r.reason);
  });
}

// Runs only in the background copy of the page.
window.addEventListener('appmint:background', async function (e) {
  const run = e.detail;                              // { name: 'prices', runId: '…' }
  try {
    const price = await fetch('https://api.example.com/price').then(function (r) { return r.json(); });
    const last = Number(localStorage.getItem('lastPrice') || 0);
    if (price.value < last) {
      WebToApk.notify(JSON.stringify({ title: 'Price dropped', body: 'Now ' + price.value, tag: 'price' }));
    }
    localStorage.setItem('lastPrice', String(price.value));
    AppMint.background.done(run.runId, { ok: true });
  } catch (err) {
    AppMint.background.done(run.runId, { ok: false, retry: true });   // retried with backoff
  }
});
startApp();

صفحه‌هایی که دیر mount می‌شوند (React، مسیر lazy) از onRun استفاده می‌کنند که اگر اجرا از قبل شروع شده باشد هم اجرا می‌شود، یا hook صفحه:

if (window.AppMint && AppMint.background) {
  AppMint.background.onRun(function (run) { syncInbox().then(function () { AppMint.background.done(run.runId, { ok: true }); }); });
}
window.onAppMintBackground = function (run) { console.log('background run', run.name, run.runId, AppMint.background.currentRun()); };

مدیریت:

AppMint.background.list().then(function (tasks) { console.table(tasks); });
AppMint.background.runNow('prices');
AppMint.background.unregister('prices');

نکته‌ها: Android لحظهٔ دقیق را تعیین می‌کند؛ ۱۵ دقیقه کوتاه‌ترین فاصله است و صرفه‌جویی باتری می‌تواند اجراها را به تأخیر بیندازد. با شروع پس از راه‌اندازی مجدد گوشی هر وظیفه پس از راه‌اندازی مجدد هم یک‌بار اجرا می‌شود. WebToApk در یک اجرا فقط notify، showNotification، بافر مکان، geofenceها، workEnqueueOnline، isBackgroundServiceRunning و این متدهای وظیفه را دارد؛ هر چیز دیگر وجود ندارد (پشتیبانی را تشخیص دهید).

addGeofence پل#

window.WebToApk.addGeofence(json: String): String

یک geofence اضافه می‌کند {id, lat, lng, radiusM, events, dwellMs?, notify?} ← JSON {ok, status:'adding'} یا {ok:false, reason}؛ نتیجه appmint:geofence-added.

نمونه

دایره‌ای که با بسته بودن اپلیکیشن enter، exit و/یا dwell را اجرا می‌کند و در صورت تمایل یک اعلان می‌فرستد. Geofenceها از راه‌اندازی مجدد گوشی جان سالم به در می‌برند (خودکار دوباره اضافه می‌شوند).

مقدار بازگشتی: addGeofence ← یک رشتهٔ JSON { ok: true, status: 'adding', id } یا { ok: false, reason } - not_enabled، invalid_id، invalid_position، invalid_radius (۱ متر … ۱۰۰ کیلومتر)، invalid_events، location_permission_required، background_permission_required، foreign_origin. پاسخ Play services به‌صورت appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off، too_many_geofences - ۱۰۰ برای هر اپلیکیشن، play_services_unavailable) می‌رسد. یک گذار به‌صورت appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } می‌رسد - queued: true وقتی اتفاق افتاده که اپلیکیشن بسته بوده و در شروع بعدی تحویل داده می‌شود. removeGeofence ← true اگر وجود داشت؛ getGeofences ← یک آرایهٔ JSON از آنچه اضافه کرده‌اید. نیازها: Geofenceها را در ساخت تیک بزنید و «همیشه اجازه بده» (requestBackgroundLocation()).

function watchOffice() {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) return;
  const r = JSON.parse(b.addGeofence(JSON.stringify({
    id: 'office', lat: 51.5033, lng: -0.1196, radiusM: 150,
    events: ['enter', 'exit', 'dwell'], dwellMs: 5 * 60000,
    notify: {
      enter: { title: 'At the office', body: 'Tap to check in' },
      exit: { title: 'Left the office', body: 'Tap to check out' }
    }
  })));
  if (!r.ok && r.reason === 'background_permission_required') b.requestBackgroundLocation();
}

window.addEventListener('appmint:geofence-added', function (e) {
  if (!e.detail.ok) showMessage('Geofence ' + e.detail.id + ' not added: ' + e.detail.reason);
});
window.addEventListener('appmint:geofence', function (e) {
  logVisit(e.detail.id, e.detail.event, e.detail.time);
});

console.log(JSON.parse(WebToApk.getGeofences()).map(function (g) { return g.id; }));
WebToApk.removeGeofence('office');

پشتیبانی را تشخیص دهید و هر رد شدن را گزارش کنید:

function addCircle(id, lat, lng, radiusM) {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) { showMessage('Place alerts need the app'); return false; }
  const r = JSON.parse(b.addGeofence(JSON.stringify({ id: id, lat: lat, lng: lng, radiusM: radiusM, events: ['enter'] })));
  if (r.ok) return true;
  if (r.reason === 'not_enabled') showMessage('Geofences are not switched on in this build');
  else if (r.reason === 'background_permission_required') b.requestBackgroundLocation();
  else if (r.reason === 'invalid_radius') showMessage('Pick a radius between 1 m and 100 km');
  else showMessage('Could not add the place: ' + r.reason);
  return false;
}

آنچه ثبت شده را فهرست کنید و یکی را بردارید:

const mine = JSON.parse(WebToApk.getGeofences());          // [] when none
mine.forEach(function (g) { addRow(g.id); });
document.getElementById('forget').onclick = function () {
  const existed = WebToApk.removeGeofence('office');       // true if it was there
  if (!existed) showMessage('That place was not registered');
};

backgroundDone پل#

window.WebToApk.backgroundDone(runId: String, resultJson: String): Boolean

AppMint.background.done(runId, result): یک اجرای پس‌زمینه را پایان می‌دهد؛ در خود اپلیکیشن همیشه false است.

نمونه

همراه با WebToApk.backgroundRegister مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

backgroundList پل#

window.WebToApk.backgroundList(): String

AppMint.background.list(): آرایهٔ JSON {name, intervalMinutes, requiresNetwork, requiresCharging, registeredAt, lastRunAt, lastResult}.

نمونه

همراه با WebToApk.backgroundRegister مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

backgroundRegister پل#

window.WebToApk.backgroundRegister(name: String, optionsJson: String): String

AppMint.background.register(name, {intervalMinutes >= 15, requiresNetwork, requiresCharging}) ← JSON {ok, name, intervalMinutes} یا {ok:false, reason}.

نمونه

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

backgroundRunNow پل#

window.WebToApk.backgroundRunNow(name: String): String

AppMint.background.runNow(name): یک اجرای پس‌زمینه به‌محض اینکه شرایطش اجازه دهد.

نمونه

همراه با WebToApk.backgroundRegister مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

backgroundUnregister پل#

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

AppMint.background.unregister(name)؛ اگر وظیفه‌ای با آن نام نبود false.

نمونه

همراه با WebToApk.backgroundRegister مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

clearBufferedLocations پل#

window.WebToApk.clearBufferedLocations(): Boolean

بافری را که getBufferedLocations می‌خواند خالی می‌کند (پس از ذخیرهٔ نقطه‌ها فراخوانی کنید).

نمونه

همراه با WebToApk.getBufferedLocations مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

مکان‌هایی که در غیاب صفحه‌تان ثبت شده‌اند (اپلیکیشن در پس‌زمینه یا بسته)، قدیمی‌ترین اول، تا ۱۰٬۰۰۰.

مقدار بازگشتی: getBufferedLocations() ← یک آرایهٔ رشته‌ای JSON از { type: 'fix', lat, lng, accuracy, time, …, background: true }؛ وقتی مکان‌یابی پس‌زمینه خاموش باشد '[]'. clearBufferedLocations() ← وقتی خالی شد true. نیازها: مکان‌یابی پس‌زمینه در ساخت.

function syncTrack() {
  if (!(window.WebToApk && WebToApk.getBufferedLocations)) return;
  const points = JSON.parse(WebToApk.getBufferedLocations());
  if (!points.length) return;
  points.forEach(function (p) { drawPoint(p.lat, p.lng); });
  saveTrack(points).then(function () { WebToApk.clearBufferedLocations(); });
}
document.addEventListener('visibilitychange', function () { if (!document.hidden) syncTrack(); });

ردیابی را شروع کنید، سپس رد شدن را مدیریت کنید. فراخوانی شروع با یک رشتهٔ JSON پاسخ می‌دهد.

function startTracking() {
  if (!(window.WebToApk && WebToApk.startLocationUpdates)) return;
  const r = JSON.parse(WebToApk.startLocationUpdates(JSON.stringify({
    intervalMs: 15000, accuracy: 'balanced', background: true
  })));
  if (!r.ok) showMessage('Tracking not started: ' + r.reason);   // not_enabled, location_permission_required, ...
}

مکان‌های زنده وقتی صفحه باز است به‌صورت appmint:location می‌رسند؛ بافر بقیه را نگه می‌دارد.

window.addEventListener('appmint:location', function (e) {
  drawPoint(e.detail.lat, e.detail.lng);
});
document.getElementById('stop').addEventListener('click', function () {
  if (window.WebToApk && WebToApk.stopLocationUpdates) WebToApk.stopLocationUpdates();
  if (WebToApk.getBufferedLocations) {
    const left = JSON.parse(WebToApk.getBufferedLocations());
    console.log(left.length + ' fixes still buffered');
  }
});

clearGeofenceEvents پل#

window.WebToApk.clearGeofenceEvents(): Boolean

تاریخچهٔ گذارهایی را که getGeofenceEvents می‌خواند خالی می‌کند.

نمونه

همراه با WebToApk.getGeofenceEvents مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

۱۰۰ گذار آخر geofence، قدیمی‌ترین اول - از جمله آن‌هایی که وقتی اپلیکیشن بسته بود رخ دادند. اگر صفحه‌تان دیر بالا می‌آید و ممکن است رویدادهای appmint:geofence در صف را از دست بدهد، در شروع آن را بخوانید.

مقدار بازگشتی: getGeofenceEvents() ← یک آرایهٔ رشته‌ای JSON از { id, event, time, lat, lng }؛ clearGeofenceEvents() ← true. نیازها: Geofenceها در ساخت.

if (window.WebToApk && WebToApk.getGeofenceEvents) {
  JSON.parse(WebToApk.getGeofenceEvents()).forEach(function (ev) {
    logVisit(ev.id, ev.event, ev.time);
  });
  WebToApk.clearGeofenceEvents();
}

یک geofence اضافه کنید، سپس گذارها را زیر نظر بگیرید تا وقتی صفحه باز است.

if (window.WebToApk && WebToApk.addGeofence) {
  const r = JSON.parse(WebToApk.addGeofence(JSON.stringify({
    id: 'shop-1', lat: 12.9716, lng: 77.5946, radiusM: 150, events: ['enter', 'exit']
  })));
  if (!r.ok) showMessage('Geofence refused: ' + r.reason);   // not_enabled, invalid_radius, ...
}
window.addEventListener('appmint:geofence', function (e) {
  logVisit(e.detail.id, e.detail.event, e.detail.time);
});

آنچه ثبت شده را فهرست کنید و یکی را بردارید وقتی کاربر یک قابلیت را ترک می‌کند.

function dropGeofence(id) {
  if (!(window.WebToApk && WebToApk.removeGeofence)) return false;
  const known = JSON.parse(WebToApk.getGeofences()).length;
  const removed = WebToApk.removeGeofence(id);
  console.log(removed ? 'removed' : 'not found', 'of', known, 'registered');
  return removed;
}

getBufferedLocations پل#

window.WebToApk.getBufferedLocations(): String

آرایهٔ JSON از مکان‌هایی که در غیاب صفحه ثبت شده‌اند، قدیمی‌ترین اول (تا ۱۰٬۰۰۰).

نمونه

مکان‌هایی که در غیاب صفحه‌تان ثبت شده‌اند (اپلیکیشن در پس‌زمینه یا بسته)، قدیمی‌ترین اول، تا ۱۰٬۰۰۰.

مقدار بازگشتی: getBufferedLocations() ← یک آرایهٔ رشته‌ای JSON از { type: 'fix', lat, lng, accuracy, time, …, background: true }؛ وقتی مکان‌یابی پس‌زمینه خاموش باشد '[]'. clearBufferedLocations() ← وقتی خالی شد true. نیازها: مکان‌یابی پس‌زمینه در ساخت.

function syncTrack() {
  if (!(window.WebToApk && WebToApk.getBufferedLocations)) return;
  const points = JSON.parse(WebToApk.getBufferedLocations());
  if (!points.length) return;
  points.forEach(function (p) { drawPoint(p.lat, p.lng); });
  saveTrack(points).then(function () { WebToApk.clearBufferedLocations(); });
}
document.addEventListener('visibilitychange', function () { if (!document.hidden) syncTrack(); });

ردیابی را شروع کنید، سپس رد شدن را مدیریت کنید. فراخوانی شروع با یک رشتهٔ JSON پاسخ می‌دهد.

function startTracking() {
  if (!(window.WebToApk && WebToApk.startLocationUpdates)) return;
  const r = JSON.parse(WebToApk.startLocationUpdates(JSON.stringify({
    intervalMs: 15000, accuracy: 'balanced', background: true
  })));
  if (!r.ok) showMessage('Tracking not started: ' + r.reason);   // not_enabled, location_permission_required, ...
}

مکان‌های زنده وقتی صفحه باز است به‌صورت appmint:location می‌رسند؛ بافر بقیه را نگه می‌دارد.

window.addEventListener('appmint:location', function (e) {
  drawPoint(e.detail.lat, e.detail.lng);
});
document.getElementById('stop').addEventListener('click', function () {
  if (window.WebToApk && WebToApk.stopLocationUpdates) WebToApk.stopLocationUpdates();
  if (WebToApk.getBufferedLocations) {
    const left = JSON.parse(WebToApk.getBufferedLocations());
    console.log(left.length + ' fixes still buffered');
  }
});

getGeofenceEvents پل#

window.WebToApk.getGeofenceEvents(): String

آرایهٔ JSON از ۱۰۰ گذار آخر geofence {id, event, time, lat, lng}، قدیمی‌ترین اول.

نمونه

۱۰۰ گذار آخر geofence، قدیمی‌ترین اول - از جمله آن‌هایی که وقتی اپلیکیشن بسته بود رخ دادند. اگر صفحه‌تان دیر بالا می‌آید و ممکن است رویدادهای appmint:geofence در صف را از دست بدهد، در شروع آن را بخوانید.

مقدار بازگشتی: getGeofenceEvents() ← یک آرایهٔ رشته‌ای JSON از { id, event, time, lat, lng }؛ clearGeofenceEvents() ← true. نیازها: Geofenceها در ساخت.

if (window.WebToApk && WebToApk.getGeofenceEvents) {
  JSON.parse(WebToApk.getGeofenceEvents()).forEach(function (ev) {
    logVisit(ev.id, ev.event, ev.time);
  });
  WebToApk.clearGeofenceEvents();
}

یک geofence اضافه کنید، سپس گذارها را زیر نظر بگیرید تا وقتی صفحه باز است.

if (window.WebToApk && WebToApk.addGeofence) {
  const r = JSON.parse(WebToApk.addGeofence(JSON.stringify({
    id: 'shop-1', lat: 12.9716, lng: 77.5946, radiusM: 150, events: ['enter', 'exit']
  })));
  if (!r.ok) showMessage('Geofence refused: ' + r.reason);   // not_enabled, invalid_radius, ...
}
window.addEventListener('appmint:geofence', function (e) {
  logVisit(e.detail.id, e.detail.event, e.detail.time);
});

آنچه ثبت شده را فهرست کنید و یکی را بردارید وقتی کاربر یک قابلیت را ترک می‌کند.

function dropGeofence(id) {
  if (!(window.WebToApk && WebToApk.removeGeofence)) return false;
  const known = JSON.parse(WebToApk.getGeofences()).length;
  const removed = WebToApk.removeGeofence(id);
  console.log(removed ? 'removed' : 'not found', 'of', known, 'registered');
  return removed;
}

getGeofences پل#

window.WebToApk.getGeofences(): String

آرایهٔ JSON از geofenceهایی که این اپلیکیشن اضافه کرده (از راه‌اندازی مجدد جان سالم به در می‌برند).

نمونه

همراه با WebToApk.addGeofence مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

دایره‌ای که با بسته بودن اپلیکیشن enter، exit و/یا dwell را اجرا می‌کند و در صورت تمایل یک اعلان می‌فرستد. Geofenceها از راه‌اندازی مجدد گوشی جان سالم به در می‌برند (خودکار دوباره اضافه می‌شوند).

مقدار بازگشتی: addGeofence ← یک رشتهٔ JSON { ok: true, status: 'adding', id } یا { ok: false, reason } - not_enabled، invalid_id، invalid_position، invalid_radius (۱ متر … ۱۰۰ کیلومتر)، invalid_events، location_permission_required، background_permission_required، foreign_origin. پاسخ Play services به‌صورت appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off، too_many_geofences - ۱۰۰ برای هر اپلیکیشن، play_services_unavailable) می‌رسد. یک گذار به‌صورت appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } می‌رسد - queued: true وقتی اتفاق افتاده که اپلیکیشن بسته بوده و در شروع بعدی تحویل داده می‌شود. removeGeofence ← true اگر وجود داشت؛ getGeofences ← یک آرایهٔ JSON از آنچه اضافه کرده‌اید. نیازها: Geofenceها را در ساخت تیک بزنید و «همیشه اجازه بده» (requestBackgroundLocation()).

function watchOffice() {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) return;
  const r = JSON.parse(b.addGeofence(JSON.stringify({
    id: 'office', lat: 51.5033, lng: -0.1196, radiusM: 150,
    events: ['enter', 'exit', 'dwell'], dwellMs: 5 * 60000,
    notify: {
      enter: { title: 'At the office', body: 'Tap to check in' },
      exit: { title: 'Left the office', body: 'Tap to check out' }
    }
  })));
  if (!r.ok && r.reason === 'background_permission_required') b.requestBackgroundLocation();
}

window.addEventListener('appmint:geofence-added', function (e) {
  if (!e.detail.ok) showMessage('Geofence ' + e.detail.id + ' not added: ' + e.detail.reason);
});
window.addEventListener('appmint:geofence', function (e) {
  logVisit(e.detail.id, e.detail.event, e.detail.time);
});

console.log(JSON.parse(WebToApk.getGeofences()).map(function (g) { return g.id; }));
WebToApk.removeGeofence('office');

پشتیبانی را تشخیص دهید و هر رد شدن را گزارش کنید:

function addCircle(id, lat, lng, radiusM) {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) { showMessage('Place alerts need the app'); return false; }
  const r = JSON.parse(b.addGeofence(JSON.stringify({ id: id, lat: lat, lng: lng, radiusM: radiusM, events: ['enter'] })));
  if (r.ok) return true;
  if (r.reason === 'not_enabled') showMessage('Geofences are not switched on in this build');
  else if (r.reason === 'background_permission_required') b.requestBackgroundLocation();
  else if (r.reason === 'invalid_radius') showMessage('Pick a radius between 1 m and 100 km');
  else showMessage('Could not add the place: ' + r.reason);
  return false;
}

آنچه ثبت شده را فهرست کنید و یکی را بردارید:

const mine = JSON.parse(WebToApk.getGeofences());          // [] when none
mine.forEach(function (g) { addRow(g.id); });
document.getElementById('forget').onclick = function () {
  const existed = WebToApk.removeGeofence('office');       // true if it was there
  if (!existed) showMessage('That place was not registered');
};

getLocationPermissionState پل#

window.WebToApk.getLocationPermissionState(): String

JSON {foreground:'granted'|'denied', background:'granted'|'denied', precise: boolean}.

نمونه

همراه با WebToApk.requestBackgroundLocation مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

مکان را می‌خواهد، سپس «همیشه اجازه بده» (Android 10+ آن‌ها را جداگانه می‌خواهد؛ در Android 11+ مرحلهٔ دوم صفحهٔ تنظیمات مکان اپلیکیشن را باز می‌کند). Geofenceها به آن نیاز دارند؛ راه‌اندازی مجدد ردیابی مکان پس از راه‌اندازی مجدد گوشی هم.

مقدار بازگشتی: مستقیماً هیچ؛ پاسخ به‌صورت appmint:location-permission (و window.onAppMintLocationPermission(detail)) = { foreground: 'granted' | 'denied', background: 'granted' | 'denied', precise: boolean, error?: 'not_enabled' } می‌رسد. getLocationPermissionState() ← همان شیء به‌صورت رشتهٔ JSON، بدون پرسیدن. نیازها: مکان‌یابی پس‌زمینه یا Geofenceها در ساخت.

function askAlways() {
  const b = window.WebToApk;
  if (!(b && b.requestBackgroundLocation)) return;
  const now = JSON.parse(b.getLocationPermissionState());
  if (now.background === 'granted') { enableGeofences(); return; }
  // Explain first: Play requires an in-app disclosure before this request.
  if (!confirm('Allow location "all the time" so we can remind you when you arrive?')) return;
  b.requestBackgroundLocation();
}

window.addEventListener('appmint:location-permission', function (e) {
  if (e.detail.background === 'granted') enableGeofences();
  else showMessage('Choose "Allow all the time" in the location settings to use arrival reminders.');
});

ابتدا وضعیت را بررسی کنید و توضیح دهید کاربر باید چه چیزی را لمس کند:

const b = window.WebToApk;
if (b && b.getLocationPermissionState) {
  const s = JSON.parse(b.getLocationPermissionState());
  if (s.foreground !== 'granted') showMessage('Location is off for this app.');
  else if (s.background !== 'granted') showMessage('Tap "Allow all the time" to enable arrival reminders.');
  else if (!s.precise) showMessage('Precise location is off; arrival alerts may be late.');
}

مجوز را درخواست کنید، سپس پس از اعطا یک geofence اضافه کنید:

window.addEventListener('appmint:location-permission', function (e) {
  if (e.detail.error === 'not_enabled') return showMessage('This build has no background location.');
  if (e.detail.background !== 'granted') return;
  const r = JSON.parse(WebToApk.addGeofence(JSON.stringify({
    id: 'home', lat: 51.5033, lng: -0.1196, radiusM: 120, events: ['enter']
  })));
  if (!r.ok) showMessage('Could not add the place: ' + r.reason);
}, { once: true });
WebToApk.requestBackgroundLocation();

isBackgroundRun پل#

window.WebToApk.isBackgroundRun(): Boolean

اینجا false: صفحهٔ خود اپلیکیشن هرگز اجرای پس‌زمینه نیست (اجرای headless با true پاسخ می‌دهد).

نمونه

همراه با WebToApk.backgroundRegister مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

متدهای خام پشت AppMint.background - از AppMint.background.register(...) استفاده کنید (promise برمی‌گرداند و رویداد اجرا را مدیریت می‌کند). اینجا فهرست شده‌اند تا هر متد پل یک نمونه داشته باشد.

مقدار بازگشتی: backgroundRegister ← JSON { ok: true, name, intervalMinutes } یا { ok: false, reason } (not_enabled، invalid_name - حروف، ارقام، _، -، تا ۶۴ - interval_below_15_minutes، invalid_interval، schedule_failed). backgroundUnregister ← true اگر وجود داشت. backgroundList ← آرایهٔ JSON. backgroundRunNow ← JSON { ok, name } یا { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) ← true در اجرای پس‌زمینه، در اپلیکیشن همیشه false. isBackgroundRun() ← false در اپلیکیشن، true در اجرای پس‌زمینه. نیازها: اجرای کد صفحه با بسته بودن اپلیکیشن را تیک بزنید.

const b = window.WebToApk;
if (b && b.backgroundRegister) {
  const r = JSON.parse(b.backgroundRegister('refresh', JSON.stringify({ intervalMinutes: 60, requiresNetwork: true })));
  if (!r.ok) console.warn('not scheduled:', r.reason);
  console.log(JSON.parse(b.backgroundList()));
  console.log(b.isBackgroundRun());               // false here: this is the app on screen
  // b.backgroundRunNow('refresh');  b.backgroundUnregister('refresh');
  // In a run: b.backgroundDone(runId, JSON.stringify({ ok: true }))
}

پشتیبانی را تشخیص دهید و اجرای اپلیکیشن را از اجرای پس‌زمینه تشخیص دهید:

const b = window.WebToApk;
if (!(b && b.backgroundRegister)) {
  console.log('background tasks are only in the app');
} else if (b.isBackgroundRun()) {
  // the invisible copy: do the work, no UI
} else {
  const r = JSON.parse(b.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 5 })));
  if (!r.ok) console.warn(r.reason);                // 'interval_below_15_minutes'
}

یک وظیفه را آزمایش کنید و پاک‌سازی کنید:

const run = JSON.parse(WebToApk.backgroundRunNow('sync'));
if (!run.ok && run.reason === 'not_registered') WebToApk.backgroundRegister('sync', JSON.stringify({ intervalMinutes: 30 }));
JSON.parse(WebToApk.backgroundList()).forEach(function (t) { console.log(t.name, t.intervalMinutes, t.lastResult); });
WebToApk.backgroundUnregister('old-task');           // true if it existed

isBackgroundServiceRunning پل#

window.WebToApk.isBackgroundServiceRunning(): Boolean

آیا سرویس پیش‌زمینهٔ اپلیکیشن در حال اجراست (چه نگه‌دارنده، چه مکان‌یابی پس‌زمینه).

نمونه

همراه با WebToApk.startBackgroundService مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

اپلیکیشن شما را در غیاب کاربر در حال اجرا نگه می‌دارد: یک سرویس پیش‌زمینهٔ واقعی Android با یک اعلان. تا وقتی اجرا می‌شود صفحه متوقف نمی‌شود، پس جاوااسکریپتش، <audio> و موسیقی پس‌زمینهٔ اپلیکیشن ادامه می‌یابد (Android همچنان تایمرهای صفحه‌ای را که کسی نمی‌بیند کند می‌کند).

مقدار بازگشتی: startBackgroundService ← یک رشتهٔ JSON { ok: true, status: 'starting', type, notificationShown } یا { ok: false, reason } - دلیل not_enabled، invalid_options، type_not_declared، invalid_progress، location_permission_required، start_not_allowed. updateBackgroundService ← { ok: true } یا { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning ← true / false. تغییرهای وضعیت به‌صورت appmint:background-service (و window.onAppMintBackgroundService(detail)) می‌رسند: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. نیازها: نگه داشتن اجرا در پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و نوع سرویس را انتخاب کنید. در Android 13+ اعلان فقط وقتی دیده می‌شود که کاربر اعلان‌ها را مجاز کرده باشد (وگرنه notificationShown: false - سرویس همچنان اجرا می‌شود).

function startWorkout() {
  const b = window.WebToApk;
  if (!(b && b.startBackgroundService)) return;             // browser
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({
    title: 'Workout in progress',
    text: '00:00 elapsed',
    stopLabel: 'Stop',                  // a Stop button on the notification
    progress: { value: 0, max: 100 }
  })));
  if (!r.ok) showMessage('Could not keep running: ' + r.reason);
}

function tick(seconds) {
  if (window.WebToApk && WebToApk.isBackgroundServiceRunning()) {
    WebToApk.updateBackgroundService(JSON.stringify({
      title: 'Workout in progress', text: seconds + ' s elapsed', stopLabel: 'Stop',
      progress: { value: Math.min(100, seconds / 18), max: 100 }
    }));
  }
}

function finishWorkout() {
  if (window.WebToApk && WebToApk.stopBackgroundService) WebToApk.stopBackgroundService();
}

window.addEventListener('appmint:background-service', function (e) {
  // reason: 'user' (Stop button), 'page', 'timeout' (Android 15 dataSync 6-hour limit),
  // 'task_removed' (app swiped away); after a phone restart, state 'not_restarted' with
  // 'android_forbids_type_at_boot' | 'background_permission_required' | 'start_not_allowed'
  if (e.detail.state === 'stopped') showMessage('Stopped (' + e.detail.reason + ')');
});

پاسخ‌هایی را مدیریت کنید که یعنی ساخت یا گوشی نمی‌تواند این کار را انجام دهد:

function startKeepAlive() {
  const b = window.WebToApk;
  if (!(b && typeof b.startBackgroundService === 'function')) return;
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Uploading photos' })));
  if (r.ok) return;
  if (r.reason === 'not_enabled') showMessage('Keep running in background is not on in this build.');
  else if (r.reason === 'type_not_declared') showMessage('This build declares no service type.');
  else showMessage('Could not start: ' + r.reason);
}

آن را با یک حلقهٔ بارگذاری ترکیب کنید: فقط تا وقتی صف کار دارد در حال اجرا بمانید.

async function uploadAll(files) {
  const b = window.WebToApk;
  const keep = b && b.startBackgroundService &&
    JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Uploading', text: '0 of ' + files.length }))).ok;
  for (let i = 0; i < files.length; i++) {
    await upload(files[i]);
    if (keep) b.updateBackgroundService(JSON.stringify({ title: 'Uploading', text: (i + 1) + ' of ' + files.length }));
  }
  if (keep) b.stopBackgroundService();
}

نکته‌ها: نوع باید یکی از انواعی باشد که ساخت اعلام کرده (همان که در جادوگر انتخاب شد، به‌علاوهٔ location وقتی مکان‌یابی پس‌زمینه روشن است). کنار زدن اپلیکیشن، سرویس نگه‌دارنده را متوقف می‌کند مگر keepAfterSwipe: true بدهید (آن‌وقت خود صفحه رفته است). Play Console برای هر نوع اعلام‌شده یک اعلامیهٔ سرویس پیش‌زمینه با یک ویدیوی کوتاه می‌خواهد.

isIgnoringBatteryOptimizations پل#

window.WebToApk.isIgnoringBatteryOptimizations(): Boolean

آیا کاربر این اپلیکیشن را از بهینه‌سازی باتری (آماده‌به‌کار Doze) معاف کرده است.

نمونه

معافیت از بهینه‌سازی باتری را بررسی و درخواست می‌کند، تا Doze کار پس‌زمینهٔ شما را به تعویق نیندازد.

مقدار بازگشتی: isIgnoringBatteryOptimizations() ← true / false. requestIgnoreBatteryOptimizations() ← 'already_ignoring'، 'dialog_opened' (کادر گفتگوی اجازه/رد سیستم - فقط وقتی ساخت درخواست مستقیم برای رد کردن بهینه‌سازی باتری را تیک زده باشد)، 'settings_opened' (فهرست بهینه‌سازی باتری؛ بدون مجوز و بدون اعلامیهٔ Play) یا 'unavailable'. نیازها: برای فهرست تنظیمات هیچ. کادر گفتگوی مستقیم REQUEST_IGNORE_BATTERY_OPTIMIZATIONS را اعلام می‌کند که Play فقط وقتی می‌پذیرد که عملکرد اصلی اپلیکیشن باشد.

document.getElementById('reliable').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.isIgnoringBatteryOptimizations)) return;
  if (b.isIgnoringBatteryOptimizations()) { showMessage('Already unrestricted'); return; }
  const how = b.requestIgnoreBatteryOptimizations();
  if (how === 'settings_opened') showMessage('Find this app in the list and choose "Not optimised".');
});

بگویید چه اتفاقی افتاد برای هر پاسخ.

function askBatteryExemption() {
  const b = window.WebToApk;
  if (!(b && b.requestIgnoreBatteryOptimizations)) { showMessage('Only available in the app'); return; }
  const how = b.requestIgnoreBatteryOptimizations();
  const text = {
    already_ignoring: 'Already unrestricted',
    dialog_opened: 'Please tap Allow',
    settings_opened: 'Choose "Not optimised" for this app',
    unavailable: 'This phone has no such setting'
  };
  showMessage(text[how] || how);
}

آن را با ردیابی پس‌زمینه جفت کنید، چون Doze آن را متوقف می‌کند.

if (window.WebToApk && WebToApk.startLocationUpdates) {
  const r = JSON.parse(WebToApk.startLocationUpdates(JSON.stringify({ intervalMs: 30000 })));
  if (r.ok && !WebToApk.isIgnoringBatteryOptimizations()) {
    showMessage('For steady tracking, turn off battery optimisation for this app.');
  }
}

removeGeofence پل#

window.WebToApk.removeGeofence(id: String): Boolean

geofence با این شناسه را برمی‌دارد؛ اگر چیزی نبود false.

نمونه

همراه با WebToApk.addGeofence مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

دایره‌ای که با بسته بودن اپلیکیشن enter، exit و/یا dwell را اجرا می‌کند و در صورت تمایل یک اعلان می‌فرستد. Geofenceها از راه‌اندازی مجدد گوشی جان سالم به در می‌برند (خودکار دوباره اضافه می‌شوند).

مقدار بازگشتی: addGeofence ← یک رشتهٔ JSON { ok: true, status: 'adding', id } یا { ok: false, reason } - not_enabled، invalid_id، invalid_position، invalid_radius (۱ متر … ۱۰۰ کیلومتر)، invalid_events، location_permission_required، background_permission_required، foreign_origin. پاسخ Play services به‌صورت appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off، too_many_geofences - ۱۰۰ برای هر اپلیکیشن، play_services_unavailable) می‌رسد. یک گذار به‌صورت appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } می‌رسد - queued: true وقتی اتفاق افتاده که اپلیکیشن بسته بوده و در شروع بعدی تحویل داده می‌شود. removeGeofence ← true اگر وجود داشت؛ getGeofences ← یک آرایهٔ JSON از آنچه اضافه کرده‌اید. نیازها: Geofenceها را در ساخت تیک بزنید و «همیشه اجازه بده» (requestBackgroundLocation()).

function watchOffice() {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) return;
  const r = JSON.parse(b.addGeofence(JSON.stringify({
    id: 'office', lat: 51.5033, lng: -0.1196, radiusM: 150,
    events: ['enter', 'exit', 'dwell'], dwellMs: 5 * 60000,
    notify: {
      enter: { title: 'At the office', body: 'Tap to check in' },
      exit: { title: 'Left the office', body: 'Tap to check out' }
    }
  })));
  if (!r.ok && r.reason === 'background_permission_required') b.requestBackgroundLocation();
}

window.addEventListener('appmint:geofence-added', function (e) {
  if (!e.detail.ok) showMessage('Geofence ' + e.detail.id + ' not added: ' + e.detail.reason);
});
window.addEventListener('appmint:geofence', function (e) {
  logVisit(e.detail.id, e.detail.event, e.detail.time);
});

console.log(JSON.parse(WebToApk.getGeofences()).map(function (g) { return g.id; }));
WebToApk.removeGeofence('office');

پشتیبانی را تشخیص دهید و هر رد شدن را گزارش کنید:

function addCircle(id, lat, lng, radiusM) {
  const b = window.WebToApk;
  if (!(b && b.addGeofence)) { showMessage('Place alerts need the app'); return false; }
  const r = JSON.parse(b.addGeofence(JSON.stringify({ id: id, lat: lat, lng: lng, radiusM: radiusM, events: ['enter'] })));
  if (r.ok) return true;
  if (r.reason === 'not_enabled') showMessage('Geofences are not switched on in this build');
  else if (r.reason === 'background_permission_required') b.requestBackgroundLocation();
  else if (r.reason === 'invalid_radius') showMessage('Pick a radius between 1 m and 100 km');
  else showMessage('Could not add the place: ' + r.reason);
  return false;
}

آنچه ثبت شده را فهرست کنید و یکی را بردارید:

const mine = JSON.parse(WebToApk.getGeofences());          // [] when none
mine.forEach(function (g) { addRow(g.id); });
document.getElementById('forget').onclick = function () {
  const existed = WebToApk.removeGeofence('office');       // true if it was there
  if (!existed) showMessage('That place was not registered');
};

requestBackgroundLocation پل#

window.WebToApk.requestBackgroundLocation()

مکان را می‌خواهد، سپس «همیشه اجازه بده»؛ نتیجه به‌صورت appmint:location-permission.

نمونه

مکان را می‌خواهد، سپس «همیشه اجازه بده» (Android 10+ آن‌ها را جداگانه می‌خواهد؛ در Android 11+ مرحلهٔ دوم صفحهٔ تنظیمات مکان اپلیکیشن را باز می‌کند). Geofenceها به آن نیاز دارند؛ راه‌اندازی مجدد ردیابی مکان پس از راه‌اندازی مجدد گوشی هم.

مقدار بازگشتی: مستقیماً هیچ؛ پاسخ به‌صورت appmint:location-permission (و window.onAppMintLocationPermission(detail)) = { foreground: 'granted' | 'denied', background: 'granted' | 'denied', precise: boolean, error?: 'not_enabled' } می‌رسد. getLocationPermissionState() ← همان شیء به‌صورت رشتهٔ JSON، بدون پرسیدن. نیازها: مکان‌یابی پس‌زمینه یا Geofenceها در ساخت.

function askAlways() {
  const b = window.WebToApk;
  if (!(b && b.requestBackgroundLocation)) return;
  const now = JSON.parse(b.getLocationPermissionState());
  if (now.background === 'granted') { enableGeofences(); return; }
  // Explain first: Play requires an in-app disclosure before this request.
  if (!confirm('Allow location "all the time" so we can remind you when you arrive?')) return;
  b.requestBackgroundLocation();
}

window.addEventListener('appmint:location-permission', function (e) {
  if (e.detail.background === 'granted') enableGeofences();
  else showMessage('Choose "Allow all the time" in the location settings to use arrival reminders.');
});

ابتدا وضعیت را بررسی کنید و توضیح دهید کاربر باید چه چیزی را لمس کند:

const b = window.WebToApk;
if (b && b.getLocationPermissionState) {
  const s = JSON.parse(b.getLocationPermissionState());
  if (s.foreground !== 'granted') showMessage('Location is off for this app.');
  else if (s.background !== 'granted') showMessage('Tap "Allow all the time" to enable arrival reminders.');
  else if (!s.precise) showMessage('Precise location is off; arrival alerts may be late.');
}

مجوز را درخواست کنید، سپس پس از اعطا یک geofence اضافه کنید:

window.addEventListener('appmint:location-permission', function (e) {
  if (e.detail.error === 'not_enabled') return showMessage('This build has no background location.');
  if (e.detail.background !== 'granted') return;
  const r = JSON.parse(WebToApk.addGeofence(JSON.stringify({
    id: 'home', lat: 51.5033, lng: -0.1196, radiusM: 120, events: ['enter']
  })));
  if (!r.ok) showMessage('Could not add the place: ' + r.reason);
}, { once: true });
WebToApk.requestBackgroundLocation();

requestIgnoreBatteryOptimizations پل#

window.WebToApk.requestIgnoreBatteryOptimizations(): String

معافیت از باتری را می‌خواهد: 'already_ignoring' | 'dialog_opened' | 'settings_opened' | 'unavailable'.

نمونه

همراه با WebToApk.isIgnoringBatteryOptimizations مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

معافیت از بهینه‌سازی باتری را بررسی و درخواست می‌کند، تا Doze کار پس‌زمینهٔ شما را به تعویق نیندازد.

مقدار بازگشتی: isIgnoringBatteryOptimizations() ← true / false. requestIgnoreBatteryOptimizations() ← 'already_ignoring'، 'dialog_opened' (کادر گفتگوی اجازه/رد سیستم - فقط وقتی ساخت درخواست مستقیم برای رد کردن بهینه‌سازی باتری را تیک زده باشد)، 'settings_opened' (فهرست بهینه‌سازی باتری؛ بدون مجوز و بدون اعلامیهٔ Play) یا 'unavailable'. نیازها: برای فهرست تنظیمات هیچ. کادر گفتگوی مستقیم REQUEST_IGNORE_BATTERY_OPTIMIZATIONS را اعلام می‌کند که Play فقط وقتی می‌پذیرد که عملکرد اصلی اپلیکیشن باشد.

document.getElementById('reliable').addEventListener('click', function () {
  const b = window.WebToApk;
  if (!(b && b.isIgnoringBatteryOptimizations)) return;
  if (b.isIgnoringBatteryOptimizations()) { showMessage('Already unrestricted'); return; }
  const how = b.requestIgnoreBatteryOptimizations();
  if (how === 'settings_opened') showMessage('Find this app in the list and choose "Not optimised".');
});

بگویید چه اتفاقی افتاد برای هر پاسخ.

function askBatteryExemption() {
  const b = window.WebToApk;
  if (!(b && b.requestIgnoreBatteryOptimizations)) { showMessage('Only available in the app'); return; }
  const how = b.requestIgnoreBatteryOptimizations();
  const text = {
    already_ignoring: 'Already unrestricted',
    dialog_opened: 'Please tap Allow',
    settings_opened: 'Choose "Not optimised" for this app',
    unavailable: 'This phone has no such setting'
  };
  showMessage(text[how] || how);
}

آن را با ردیابی پس‌زمینه جفت کنید، چون Doze آن را متوقف می‌کند.

if (window.WebToApk && WebToApk.startLocationUpdates) {
  const r = JSON.parse(WebToApk.startLocationUpdates(JSON.stringify({ intervalMs: 30000 })));
  if (r.ok && !WebToApk.isIgnoringBatteryOptimizations()) {
    showMessage('For steady tracking, turn off battery optimisation for this app.');
  }
}

startBackgroundService پل#

window.WebToApk.startBackgroundService(optionsJson: String): String

سرویس پیش‌زمینهٔ اپلیکیشن را شروع می‌کند: {title, text, type?, progress?, stopLabel?, keepAfterSwipe?} ← JSON {ok, status, type, notificationShown} یا {ok:false, reason}.

نمونه

اپلیکیشن شما را در غیاب کاربر در حال اجرا نگه می‌دارد: یک سرویس پیش‌زمینهٔ واقعی Android با یک اعلان. تا وقتی اجرا می‌شود صفحه متوقف نمی‌شود، پس جاوااسکریپتش، <audio> و موسیقی پس‌زمینهٔ اپلیکیشن ادامه می‌یابد (Android همچنان تایمرهای صفحه‌ای را که کسی نمی‌بیند کند می‌کند).

مقدار بازگشتی: startBackgroundService ← یک رشتهٔ JSON { ok: true, status: 'starting', type, notificationShown } یا { ok: false, reason } - دلیل not_enabled، invalid_options، type_not_declared، invalid_progress، location_permission_required، start_not_allowed. updateBackgroundService ← { ok: true } یا { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning ← true / false. تغییرهای وضعیت به‌صورت appmint:background-service (و window.onAppMintBackgroundService(detail)) می‌رسند: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. نیازها: نگه داشتن اجرا در پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و نوع سرویس را انتخاب کنید. در Android 13+ اعلان فقط وقتی دیده می‌شود که کاربر اعلان‌ها را مجاز کرده باشد (وگرنه notificationShown: false - سرویس همچنان اجرا می‌شود).

function startWorkout() {
  const b = window.WebToApk;
  if (!(b && b.startBackgroundService)) return;             // browser
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({
    title: 'Workout in progress',
    text: '00:00 elapsed',
    stopLabel: 'Stop',                  // a Stop button on the notification
    progress: { value: 0, max: 100 }
  })));
  if (!r.ok) showMessage('Could not keep running: ' + r.reason);
}

function tick(seconds) {
  if (window.WebToApk && WebToApk.isBackgroundServiceRunning()) {
    WebToApk.updateBackgroundService(JSON.stringify({
      title: 'Workout in progress', text: seconds + ' s elapsed', stopLabel: 'Stop',
      progress: { value: Math.min(100, seconds / 18), max: 100 }
    }));
  }
}

function finishWorkout() {
  if (window.WebToApk && WebToApk.stopBackgroundService) WebToApk.stopBackgroundService();
}

window.addEventListener('appmint:background-service', function (e) {
  // reason: 'user' (Stop button), 'page', 'timeout' (Android 15 dataSync 6-hour limit),
  // 'task_removed' (app swiped away); after a phone restart, state 'not_restarted' with
  // 'android_forbids_type_at_boot' | 'background_permission_required' | 'start_not_allowed'
  if (e.detail.state === 'stopped') showMessage('Stopped (' + e.detail.reason + ')');
});

پاسخ‌هایی را مدیریت کنید که یعنی ساخت یا گوشی نمی‌تواند این کار را انجام دهد:

function startKeepAlive() {
  const b = window.WebToApk;
  if (!(b && typeof b.startBackgroundService === 'function')) return;
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Uploading photos' })));
  if (r.ok) return;
  if (r.reason === 'not_enabled') showMessage('Keep running in background is not on in this build.');
  else if (r.reason === 'type_not_declared') showMessage('This build declares no service type.');
  else showMessage('Could not start: ' + r.reason);
}

آن را با یک حلقهٔ بارگذاری ترکیب کنید: فقط تا وقتی صف کار دارد در حال اجرا بمانید.

async function uploadAll(files) {
  const b = window.WebToApk;
  const keep = b && b.startBackgroundService &&
    JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Uploading', text: '0 of ' + files.length }))).ok;
  for (let i = 0; i < files.length; i++) {
    await upload(files[i]);
    if (keep) b.updateBackgroundService(JSON.stringify({ title: 'Uploading', text: (i + 1) + ' of ' + files.length }));
  }
  if (keep) b.stopBackgroundService();
}

نکته‌ها: نوع باید یکی از انواعی باشد که ساخت اعلام کرده (همان که در جادوگر انتخاب شد، به‌علاوهٔ location وقتی مکان‌یابی پس‌زمینه روشن است). کنار زدن اپلیکیشن، سرویس نگه‌دارنده را متوقف می‌کند مگر keepAfterSwipe: true بدهید (آن‌وقت خود صفحه رفته است). Play Console برای هر نوع اعلام‌شده یک اعلامیهٔ سرویس پیش‌زمینه با یک ویدیوی کوتاه می‌خواهد.

startLocationUpdates پل#

window.WebToApk.startLocationUpdates(optionsJson: String): String

به‌روزرسانی‌های مکان {intervalMs, minDistanceM, accuracy, background, uploadUrl?, headers?} ← JSON {ok, background, backgroundPermission, precise} یا {ok:false, reason}؛ مکان‌ها به‌صورت appmint:location.

نمونه

به‌روزرسانی‌های مکان که وقتی اپلیکیشن در پس‌زمینه است ادامه می‌یابند (ردیاب دویدن، اپلیکیشن تحویل). با background: true (پیش‌فرض) درون سرویس پیش‌زمینهٔ اپلیکیشن اجرا می‌شوند. هر مکان تا وقتی صفحه هست به‌صورت appmint:location به صفحه می‌رود، وقتی نیست در یک بافر (getBufferedLocations()) می‌رود، و - وقتی uploadUrl بدهید - به‌صورت یک POST با JSON از کد بومی به سرورتان می‌رود، پس ردیابی با نبودن صفحه هم کار می‌کند.

مقدار بازگشتی: یک رشتهٔ JSON { ok: true, background, backgroundPermission, precise } یا { ok: false, reason } - not_enabled، invalid_options، invalid_interval (۱ ثانیه … ۲۴ ساعت)، invalid_distance، invalid_accuracy، invalid_upload_url، location_permission_required، start_not_allowed، foreign_origin. stopLocationUpdates() ← true اگر چیزی در جریان بود. مکان‌ها: appmint:location / window.onAppMintLocation(detail) = { type: 'fix', lat, lng, accuracy, altitude?, speed?, bearing?, time, provider, background }، یا { type: 'error', reason: 'play_services_unavailable' | 'location_unavailable' | 'location_permission_required' }. نیازها: مکان‌یابی پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و ابتدا مجوز مکان کاربر.

function startTracking(token) {
  const b = window.WebToApk;
  if (!(b && b.startLocationUpdates)) return;
  navigator.geolocation.getCurrentPosition(function () {       // asks for location first
    const r = JSON.parse(b.startLocationUpdates(JSON.stringify({
      intervalMs: 10000, minDistanceM: 10, accuracy: 'high', background: true,
      uploadUrl: 'https://api.example.com/track',
      headers: { Authorization: 'Bearer ' + token }
    })));
    if (!r.ok) showMessage('Tracking not started: ' + r.reason);
  });
}

window.addEventListener('appmint:location', function (e) {
  if (e.detail.type === 'fix') drawPoint(e.detail.lat, e.detail.lng);
  else showMessage('Location stopped: ' + e.detail.reason);
});

function stopTracking() { if (window.WebToApk) WebToApk.stopLocationUpdates(); }

یک گزینهٔ بد یا مجوز نبودن را با reason:

function tryTrack() {
  const b = window.WebToApk;
  if (!(b && typeof b.startLocationUpdates === 'function')) {
    showMessage('Background tracking needs the installed app.');
    return;
  }
  const r = JSON.parse(b.startLocationUpdates(JSON.stringify({ intervalMs: 500 })));
  if (!r.ok && r.reason === 'invalid_interval') showMessage('Interval must be 1 second or more.');
  if (!r.ok && r.reason === 'location_permission_required') showMessage('Allow location first.');
}

یک جفت طبیعی با بافر: در پایان شیفت متوقف کنید و آنچه را در غیاب صفحه ثبت شده بارگذاری کنید.

async function endShift() {
  if (!window.WebToApk) return;
  WebToApk.stopLocationUpdates();
  const points = JSON.parse(WebToApk.getBufferedLocations());
  if (points.length) {
    await fetch('/api/shift/points', { method: 'POST', body: JSON.stringify(points) });
    WebToApk.clearBufferedLocations();
  }
}

نکته‌ها: سرویس مکانی که از اپلیکیشن شروع شده بدون «همیشه اجازه بده» هم در پس‌زمینه مکان می‌گیرد؛ راه‌اندازی مجدد آن پس از راه‌اندازی مجدد گوشی به آن نیاز دارد (requestBackgroundLocation()). POSTی که شکست بخورد به WorkManager سپرده می‌شود و وقتی شبکه برگشت ارسال می‌شود. گوشی‌های بدون Google Play services با play_services_unavailable پاسخ می‌دهند. Play Console به یک اعلامیهٔ مکان پس‌زمینه نیاز دارد.

stopBackgroundService پل#

window.WebToApk.stopBackgroundService(): Boolean

سرویسی را که startBackgroundService شروع کرده متوقف می‌کند (اگر مکان‌یابی پس‌زمینه در جریان باشد، ادامه می‌دهد).

نمونه

همراه با WebToApk.startBackgroundService مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

اپلیکیشن شما را در غیاب کاربر در حال اجرا نگه می‌دارد: یک سرویس پیش‌زمینهٔ واقعی Android با یک اعلان. تا وقتی اجرا می‌شود صفحه متوقف نمی‌شود، پس جاوااسکریپتش، <audio> و موسیقی پس‌زمینهٔ اپلیکیشن ادامه می‌یابد (Android همچنان تایمرهای صفحه‌ای را که کسی نمی‌بیند کند می‌کند).

مقدار بازگشتی: startBackgroundService ← یک رشتهٔ JSON { ok: true, status: 'starting', type, notificationShown } یا { ok: false, reason } - دلیل not_enabled، invalid_options، type_not_declared، invalid_progress، location_permission_required، start_not_allowed. updateBackgroundService ← { ok: true } یا { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning ← true / false. تغییرهای وضعیت به‌صورت appmint:background-service (و window.onAppMintBackgroundService(detail)) می‌رسند: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. نیازها: نگه داشتن اجرا در پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و نوع سرویس را انتخاب کنید. در Android 13+ اعلان فقط وقتی دیده می‌شود که کاربر اعلان‌ها را مجاز کرده باشد (وگرنه notificationShown: false - سرویس همچنان اجرا می‌شود).

function startWorkout() {
  const b = window.WebToApk;
  if (!(b && b.startBackgroundService)) return;             // browser
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({
    title: 'Workout in progress',
    text: '00:00 elapsed',
    stopLabel: 'Stop',                  // a Stop button on the notification
    progress: { value: 0, max: 100 }
  })));
  if (!r.ok) showMessage('Could not keep running: ' + r.reason);
}

function tick(seconds) {
  if (window.WebToApk && WebToApk.isBackgroundServiceRunning()) {
    WebToApk.updateBackgroundService(JSON.stringify({
      title: 'Workout in progress', text: seconds + ' s elapsed', stopLabel: 'Stop',
      progress: { value: Math.min(100, seconds / 18), max: 100 }
    }));
  }
}

function finishWorkout() {
  if (window.WebToApk && WebToApk.stopBackgroundService) WebToApk.stopBackgroundService();
}

window.addEventListener('appmint:background-service', function (e) {
  // reason: 'user' (Stop button), 'page', 'timeout' (Android 15 dataSync 6-hour limit),
  // 'task_removed' (app swiped away); after a phone restart, state 'not_restarted' with
  // 'android_forbids_type_at_boot' | 'background_permission_required' | 'start_not_allowed'
  if (e.detail.state === 'stopped') showMessage('Stopped (' + e.detail.reason + ')');
});

پاسخ‌هایی را مدیریت کنید که یعنی ساخت یا گوشی نمی‌تواند این کار را انجام دهد:

function startKeepAlive() {
  const b = window.WebToApk;
  if (!(b && typeof b.startBackgroundService === 'function')) return;
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Uploading photos' })));
  if (r.ok) return;
  if (r.reason === 'not_enabled') showMessage('Keep running in background is not on in this build.');
  else if (r.reason === 'type_not_declared') showMessage('This build declares no service type.');
  else showMessage('Could not start: ' + r.reason);
}

آن را با یک حلقهٔ بارگذاری ترکیب کنید: فقط تا وقتی صف کار دارد در حال اجرا بمانید.

async function uploadAll(files) {
  const b = window.WebToApk;
  const keep = b && b.startBackgroundService &&
    JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Uploading', text: '0 of ' + files.length }))).ok;
  for (let i = 0; i < files.length; i++) {
    await upload(files[i]);
    if (keep) b.updateBackgroundService(JSON.stringify({ title: 'Uploading', text: (i + 1) + ' of ' + files.length }));
  }
  if (keep) b.stopBackgroundService();
}

نکته‌ها: نوع باید یکی از انواعی باشد که ساخت اعلام کرده (همان که در جادوگر انتخاب شد، به‌علاوهٔ location وقتی مکان‌یابی پس‌زمینه روشن است). کنار زدن اپلیکیشن، سرویس نگه‌دارنده را متوقف می‌کند مگر keepAfterSwipe: true بدهید (آن‌وقت خود صفحه رفته است). Play Console برای هر نوع اعلام‌شده یک اعلامیهٔ سرویس پیش‌زمینه با یک ویدیوی کوتاه می‌خواهد.

stopLocationUpdates پل#

window.WebToApk.stopLocationUpdates(): Boolean

به‌روزرسانی‌های مکان را که startLocationUpdates شروع کرده بود متوقف می‌کند؛ اگر چیزی در جریان نبود false.

نمونه

همراه با WebToApk.startLocationUpdates مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

به‌روزرسانی‌های مکان که وقتی اپلیکیشن در پس‌زمینه است ادامه می‌یابند (ردیاب دویدن، اپلیکیشن تحویل). با background: true (پیش‌فرض) درون سرویس پیش‌زمینهٔ اپلیکیشن اجرا می‌شوند. هر مکان تا وقتی صفحه هست به‌صورت appmint:location به صفحه می‌رود، وقتی نیست در یک بافر (getBufferedLocations()) می‌رود، و - وقتی uploadUrl بدهید - به‌صورت یک POST با JSON از کد بومی به سرورتان می‌رود، پس ردیابی با نبودن صفحه هم کار می‌کند.

مقدار بازگشتی: یک رشتهٔ JSON { ok: true, background, backgroundPermission, precise } یا { ok: false, reason } - not_enabled، invalid_options، invalid_interval (۱ ثانیه … ۲۴ ساعت)، invalid_distance، invalid_accuracy، invalid_upload_url، location_permission_required، start_not_allowed، foreign_origin. stopLocationUpdates() ← true اگر چیزی در جریان بود. مکان‌ها: appmint:location / window.onAppMintLocation(detail) = { type: 'fix', lat, lng, accuracy, altitude?, speed?, bearing?, time, provider, background }، یا { type: 'error', reason: 'play_services_unavailable' | 'location_unavailable' | 'location_permission_required' }. نیازها: مکان‌یابی پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و ابتدا مجوز مکان کاربر.

function startTracking(token) {
  const b = window.WebToApk;
  if (!(b && b.startLocationUpdates)) return;
  navigator.geolocation.getCurrentPosition(function () {       // asks for location first
    const r = JSON.parse(b.startLocationUpdates(JSON.stringify({
      intervalMs: 10000, minDistanceM: 10, accuracy: 'high', background: true,
      uploadUrl: 'https://api.example.com/track',
      headers: { Authorization: 'Bearer ' + token }
    })));
    if (!r.ok) showMessage('Tracking not started: ' + r.reason);
  });
}

window.addEventListener('appmint:location', function (e) {
  if (e.detail.type === 'fix') drawPoint(e.detail.lat, e.detail.lng);
  else showMessage('Location stopped: ' + e.detail.reason);
});

function stopTracking() { if (window.WebToApk) WebToApk.stopLocationUpdates(); }

یک گزینهٔ بد یا مجوز نبودن را با reason:

function tryTrack() {
  const b = window.WebToApk;
  if (!(b && typeof b.startLocationUpdates === 'function')) {
    showMessage('Background tracking needs the installed app.');
    return;
  }
  const r = JSON.parse(b.startLocationUpdates(JSON.stringify({ intervalMs: 500 })));
  if (!r.ok && r.reason === 'invalid_interval') showMessage('Interval must be 1 second or more.');
  if (!r.ok && r.reason === 'location_permission_required') showMessage('Allow location first.');
}

یک جفت طبیعی با بافر: در پایان شیفت متوقف کنید و آنچه را در غیاب صفحه ثبت شده بارگذاری کنید.

async function endShift() {
  if (!window.WebToApk) return;
  WebToApk.stopLocationUpdates();
  const points = JSON.parse(WebToApk.getBufferedLocations());
  if (points.length) {
    await fetch('/api/shift/points', { method: 'POST', body: JSON.stringify(points) });
    WebToApk.clearBufferedLocations();
  }
}

نکته‌ها: سرویس مکانی که از اپلیکیشن شروع شده بدون «همیشه اجازه بده» هم در پس‌زمینه مکان می‌گیرد؛ راه‌اندازی مجدد آن پس از راه‌اندازی مجدد گوشی به آن نیاز دارد (requestBackgroundLocation()). POSTی که شکست بخورد به WorkManager سپرده می‌شود و وقتی شبکه برگشت ارسال می‌شود. گوشی‌های بدون Google Play services با play_services_unavailable پاسخ می‌دهند. Play Console به یک اعلامیهٔ مکان پس‌زمینه نیاز دارد.

updateBackgroundService پل#

window.WebToApk.updateBackgroundService(optionsJson: String): String

اعلان سرویس در حال اجرا را تغییر می‌دهد (همان گزینه‌های startBackgroundService) ← JSON {ok} یا {ok:false, reason:'not_running'}.

نمونه

همراه با WebToApk.startBackgroundService مستند شده - نمونهٔ آنجا این یکی را هم نشان می‌دهد.

اپلیکیشن شما را در غیاب کاربر در حال اجرا نگه می‌دارد: یک سرویس پیش‌زمینهٔ واقعی Android با یک اعلان. تا وقتی اجرا می‌شود صفحه متوقف نمی‌شود، پس جاوااسکریپتش، <audio> و موسیقی پس‌زمینهٔ اپلیکیشن ادامه می‌یابد (Android همچنان تایمرهای صفحه‌ای را که کسی نمی‌بیند کند می‌کند).

مقدار بازگشتی: startBackgroundService ← یک رشتهٔ JSON { ok: true, status: 'starting', type, notificationShown } یا { ok: false, reason } - دلیل not_enabled، invalid_options، type_not_declared، invalid_progress، location_permission_required، start_not_allowed. updateBackgroundService ← { ok: true } یا { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning ← true / false. تغییرهای وضعیت به‌صورت appmint:background-service (و window.onAppMintBackgroundService(detail)) می‌رسند: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. نیازها: نگه داشتن اجرا در پس‌زمینه را تیک بزنید (مرحلهٔ دسترسی ← پس‌زمینه) و نوع سرویس را انتخاب کنید. در Android 13+ اعلان فقط وقتی دیده می‌شود که کاربر اعلان‌ها را مجاز کرده باشد (وگرنه notificationShown: false - سرویس همچنان اجرا می‌شود).

function startWorkout() {
  const b = window.WebToApk;
  if (!(b && b.startBackgroundService)) return;             // browser
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({
    title: 'Workout in progress',
    text: '00:00 elapsed',
    stopLabel: 'Stop',                  // a Stop button on the notification
    progress: { value: 0, max: 100 }
  })));
  if (!r.ok) showMessage('Could not keep running: ' + r.reason);
}

function tick(seconds) {
  if (window.WebToApk && WebToApk.isBackgroundServiceRunning()) {
    WebToApk.updateBackgroundService(JSON.stringify({
      title: 'Workout in progress', text: seconds + ' s elapsed', stopLabel: 'Stop',
      progress: { value: Math.min(100, seconds / 18), max: 100 }
    }));
  }
}

function finishWorkout() {
  if (window.WebToApk && WebToApk.stopBackgroundService) WebToApk.stopBackgroundService();
}

window.addEventListener('appmint:background-service', function (e) {
  // reason: 'user' (Stop button), 'page', 'timeout' (Android 15 dataSync 6-hour limit),
  // 'task_removed' (app swiped away); after a phone restart, state 'not_restarted' with
  // 'android_forbids_type_at_boot' | 'background_permission_required' | 'start_not_allowed'
  if (e.detail.state === 'stopped') showMessage('Stopped (' + e.detail.reason + ')');
});

پاسخ‌هایی را مدیریت کنید که یعنی ساخت یا گوشی نمی‌تواند این کار را انجام دهد:

function startKeepAlive() {
  const b = window.WebToApk;
  if (!(b && typeof b.startBackgroundService === 'function')) return;
  const r = JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Uploading photos' })));
  if (r.ok) return;
  if (r.reason === 'not_enabled') showMessage('Keep running in background is not on in this build.');
  else if (r.reason === 'type_not_declared') showMessage('This build declares no service type.');
  else showMessage('Could not start: ' + r.reason);
}

آن را با یک حلقهٔ بارگذاری ترکیب کنید: فقط تا وقتی صف کار دارد در حال اجرا بمانید.

async function uploadAll(files) {
  const b = window.WebToApk;
  const keep = b && b.startBackgroundService &&
    JSON.parse(b.startBackgroundService(JSON.stringify({ title: 'Uploading', text: '0 of ' + files.length }))).ok;
  for (let i = 0; i < files.length; i++) {
    await upload(files[i]);
    if (keep) b.updateBackgroundService(JSON.stringify({ title: 'Uploading', text: (i + 1) + ' of ' + files.length }));
  }
  if (keep) b.stopBackgroundService();
}

نکته‌ها: نوع باید یکی از انواعی باشد که ساخت اعلام کرده (همان که در جادوگر انتخاب شد، به‌علاوهٔ location وقتی مکان‌یابی پس‌زمینه روشن است). کنار زدن اپلیکیشن، سرویس نگه‌دارنده را متوقف می‌کند مگر keepAfterSwipe: true بدهید (آن‌وقت خود صفحه رفته است). Play Console برای هر نوع اعلام‌شده یک اعلامیهٔ سرویس پیش‌زمینه با یک ویدیوی کوتاه می‌خواهد.