Ücretsiz APK oluştur

JavaScript köprü API'si

Arka planda çalışma

Sayfanın başlatıp durdurduğu bir ön plan hizmeti, uygulama arka plandayken gelmeye devam eden konum, uygulama kapalıyken tetiklenen coğrafi çitler ve yeniden başlatmadan sonra periyodik olarak çalıştırılan kendi kodunuz. Her biri derlemede kendi anahtarını ve bir Play Console beyanını gerektirir.

AppMint.background yardımcı#

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

Örnek

Sayfanızın KENDİ kodunu uygulama kapalıyken periyodik olarak çalıştırır - veriyi yenileyin, bir fiyatı kontrol edin, bildirim gönderin. Her çalıştırma sayfanızı görünmez şekilde yükler (aynı origin, aynı localStorage/IndexedDB, ekran yok, reklam yok) ?appmint_background=1&appmint_task=<name>&appmint_run=<runId> ile, yüklendikten sonra bir kez appmint:background { name, runId } tetikler ve done(runId, { ok }) bekler. Bir çalıştırma yaklaşık 9 dakika sonra kesilir.

Döndürür: her yöntem bir Promise döndürür. register → { ok: true, name, intervalMinutes } veya { 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 } (koşullar izin verir vermez bir çalıştırma - test için). done → çalıştırma sonlandırıldığında true. isBackgroundRun() ve currentRun() eşzamanlıdır. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin (Erişim adımı → Arka plan). Web sitesi ve çevrimdışı/ZIP uygulamaları; belge ve medya uygulamalarında çalıştırılacak sayfa kodu yoktur.

Tek sayfa, iki rol:

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

Geç bağlanan sayfalar (React, tembel rota) çalıştırma zaten başladıysa da tetiklenen onRun veya sayfa kancasını kullanır:

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

Yönet:

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

Notlar: Tam anı Android belirler; 15 dakika en kısa aralıktır ve pil tasarrufu çalıştırmaları geciktirebilir. Telefon yeniden başlatıldıktan sonra başlat ile her görev yeniden başlatmadan sonra bir kez de çalışır. Bir çalıştırmanın WebToApk nesnesinde yalnızca notify, showNotification, konum tamponu, coğrafi çitler, workEnqueueOnline, isBackgroundServiceRunning ve bu görev yöntemleri bulunur; başka her şey yoktur (özelliği algılayın).

addGeofence köprü#

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

Bir coğrafi çit ekler {id, lat, lng, radiusM, events, dwellMs?, notify?} → JSON {ok, status:'adding'} veya {ok:false, reason}; sonuç şudur appmint:geofence-added.

Örnek

Uygulama kapalıyken enter, exit ve/veya dwell tetikleyen, isteğe bağlı olarak bildirim gönderen bir daire. Coğrafi çitler telefon yeniden başlatıldığında da korunur (otomatik olarak geri eklenirler).

Döndürür: addGeofence → bir JSON dizesi { ok: true, status: 'adding', id } veya { ok: false, reason } - not_enabled, invalid_id, invalid_position, invalid_radius (1 m … 100 km), invalid_events, location_permission_required, background_permission_required, foreign_origin. Play hizmetlerinin cevabı appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off, too_many_geofences - uygulama başına 100, play_services_unavailable) olarak gelir. Bir geçiş appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } olarak gelir - uygulama kapalıyken gerçekleştiyse ve bir sonraki başlangıçta teslim edildiyse queued: true. removeGeofence → varsa true; getGeofences → eklediklerinizin JSON dizisi. Gerekenler: derlemede Coğrafi çitler işaretleyin ve "Her zaman izin ver" (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');

Özelliği algılayın ve her reddi bildirin:

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

Kayıtlı olanları listeleyin ve birini kaldırın:

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 köprü#

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

AppMint.background.done(runId, result): bir arka plan çalıştırmasını bitirir; uygulamanın kendisinde her zaman false.

Örnek

WebToApk.backgroundRegister ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

window.WebToApk.backgroundList(): String

AppMint.background.list(): {name, intervalMinutes, requiresNetwork, requiresCharging, registeredAt, lastRunAt, lastResult} JSON dizisi.

Örnek

WebToApk.backgroundRegister ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

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

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

Örnek

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

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

AppMint.background.runNow(name): koşullar izin verir vermez tek bir arka plan çalıştırması.

Örnek

WebToApk.backgroundRegister ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

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

AppMint.background.unregister(name); bu adda görev yoksa false.

Örnek

WebToApk.backgroundRegister ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

window.WebToApk.clearBufferedLocations(): Boolean

getBufferedLocations işlevinin okuduğu tamponu boşaltır (noktaları kaydettikten sonra çağırın).

Örnek

WebToApk.getBufferedLocations ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Sayfanız yokken (uygulama arka planda veya kapalı) kaydedilen konumlar, en eskiden başlayarak, 10.000'e kadar.

Döndürür: getBufferedLocations() → { type: 'fix', lat, lng, accuracy, time, …, background: true } değerlerinin JSON dize dizisi; Arka plan konumu kapalıyken '[]'. clearBufferedLocations() → boşaltıldığında true. Gerekenler: derlemede Arka plan konumu.

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

Takibi başlatın, sonra bir reddi ele alın. Başlatma çağrısı bir JSON dizesiyle cevap verir.

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, ...
}

Sayfa açıkken canlı konumlar appmint:location olarak gelir; kalanını tampon tutar.

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 köprü#

window.WebToApk.clearGeofenceEvents(): Boolean

getGeofenceEvents işlevinin okuduğu geçiş geçmişini boşaltır.

Örnek

WebToApk.getGeofenceEvents ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Son 100 coğrafi çit geçişi, en eskiden başlayarak - uygulama kapalıyken gerçekleşenler dahil. Sayfanız geç bağlanıyorsa ve kuyruğa alınan appmint:geofence olaylarını kaçırabilirse bunu başlangıçta okuyun.

Döndürür: getGeofenceEvents() → { id, event, time, lat, lng } değerlerinin JSON dize dizisi; clearGeofenceEvents() → true. Gerekenler: derlemede Coğrafi çitler.

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

Bir coğrafi çit ekleyin, sonra geçişleri izleyin (sayfa açıkken).

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

Kullanıcı bir özellikten ayrıldığında kayıtlı olanları listeleyin ve birini kaldırın.

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 köprü#

window.WebToApk.getBufferedLocations(): String

Sayfa yokken kaydedilen konumların JSON dizisi, en eskiden başlayarak (10.000'e kadar).

Örnek

Sayfanız yokken (uygulama arka planda veya kapalı) kaydedilen konumlar, en eskiden başlayarak, 10.000'e kadar.

Döndürür: getBufferedLocations() → { type: 'fix', lat, lng, accuracy, time, …, background: true } değerlerinin JSON dize dizisi; Arka plan konumu kapalıyken '[]'. clearBufferedLocations() → boşaltıldığında true. Gerekenler: derlemede Arka plan konumu.

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

Takibi başlatın, sonra bir reddi ele alın. Başlatma çağrısı bir JSON dizesiyle cevap verir.

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, ...
}

Sayfa açıkken canlı konumlar appmint:location olarak gelir; kalanını tampon tutar.

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 köprü#

window.WebToApk.getGeofenceEvents(): String

Son 100 coğrafi çit geçişinin {id, event, time, lat, lng} JSON dizisi, en eskiden başlayarak.

Örnek

Son 100 coğrafi çit geçişi, en eskiden başlayarak - uygulama kapalıyken gerçekleşenler dahil. Sayfanız geç bağlanıyorsa ve kuyruğa alınan appmint:geofence olaylarını kaçırabilirse bunu başlangıçta okuyun.

Döndürür: getGeofenceEvents() → { id, event, time, lat, lng } değerlerinin JSON dize dizisi; clearGeofenceEvents() → true. Gerekenler: derlemede Coğrafi çitler.

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

Bir coğrafi çit ekleyin, sonra geçişleri izleyin (sayfa açıkken).

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

Kullanıcı bir özellikten ayrıldığında kayıtlı olanları listeleyin ve birini kaldırın.

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 köprü#

window.WebToApk.getGeofences(): String

Bu uygulamanın eklediği coğrafi çitlerin JSON dizisi (yeniden başlatmalarda korunurlar).

Örnek

WebToApk.addGeofence ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Uygulama kapalıyken enter, exit ve/veya dwell tetikleyen, isteğe bağlı olarak bildirim gönderen bir daire. Coğrafi çitler telefon yeniden başlatıldığında da korunur (otomatik olarak geri eklenirler).

Döndürür: addGeofence → bir JSON dizesi { ok: true, status: 'adding', id } veya { ok: false, reason } - not_enabled, invalid_id, invalid_position, invalid_radius (1 m … 100 km), invalid_events, location_permission_required, background_permission_required, foreign_origin. Play hizmetlerinin cevabı appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off, too_many_geofences - uygulama başına 100, play_services_unavailable) olarak gelir. Bir geçiş appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } olarak gelir - uygulama kapalıyken gerçekleştiyse ve bir sonraki başlangıçta teslim edildiyse queued: true. removeGeofence → varsa true; getGeofences → eklediklerinizin JSON dizisi. Gerekenler: derlemede Coğrafi çitler işaretleyin ve "Her zaman izin ver" (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');

Özelliği algılayın ve her reddi bildirin:

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

Kayıtlı olanları listeleyin ve birini kaldırın:

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 köprü#

window.WebToApk.getLocationPermissionState(): String

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

Örnek

WebToApk.requestBackgroundLocation ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Konum ister, sonra "Her zaman izin ver" ister (Android 10+ bunları ayrı ayrı sorar; Android 11+ üzerinde ikinci adım uygulamanın konum ayarları sayfasını açar). Coğrafi çitler buna ihtiyaç duyar; telefon yeniden başlatıldıktan sonra konum takibini yeniden başlatmak da öyle.

Döndürür: doğrudan hiçbir şey; cevap appmint:location-permission (ve window.onAppMintLocationPermission(detail)) = { foreground: 'granted' | 'denied', background: 'granted' | 'denied', precise: boolean, error?: 'not_enabled' } olarak gelir. getLocationPermissionState() → aynı nesne, sormadan JSON dizesi olarak. Gerekenler: derlemede Arka plan konumu veya Coğrafi çitler.

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

Önce durumu kontrol edin ve kullanıcının neye dokunması gerektiğini açıklayın:

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

İzni isteyin, sonra verildiğinde bir coğrafi çit (geofence) ekleyin:

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 köprü#

window.WebToApk.isBackgroundRun(): Boolean

Burada false: uygulamanın kendi sayfası asla bir arka plan çalıştırması değildir (başsız bir çalıştırma true döndürür).

Örnek

WebToApk.backgroundRegister ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

AppMint.background arkasındaki ham yöntemler - AppMint.background.register(...) kullanın (promise döndürür ve çalıştırma olayını yönetir). Her köprü yönteminin bir örneği olsun diye burada listelenmiştir.

Döndürür: backgroundRegister → JSON { ok: true, name, intervalMinutes } veya { ok: false, reason } (not_enabled, invalid_name - harfler, rakamlar, _, -, en fazla 64 - interval_below_15_minutes, invalid_interval, schedule_failed). backgroundUnregister → varsa true. backgroundList → JSON dizisi. backgroundRunNow → JSON { ok, name } veya { ok: false, reason: 'not_registered' }. backgroundDone(runId, resultJson) → arka plan çalıştırmasında true, uygulamada her zaman false. isBackgroundRun() → uygulamada false, arka plan çalıştırmasında true. Gerekenler: Kapalıyken sayfa kodunu çalıştır işaretleyin.

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

Özelliği algılayın ve uygulama çalıştırmasını arka plan çalıştırmasından ayırt edin:

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

Bir görevi test edin ve temizleyin:

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 köprü#

window.WebToApk.isBackgroundServiceRunning(): Boolean

Uygulamanın ön plan hizmetinin çalışıp çalışmadığı (çalışmaya devam etme veya arka plan konumu için).

Örnek

WebToApk.startBackgroundService ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Kullanıcı uzaktayken uygulamanızı çalışır tutar: bildirimli gerçek bir Android ön plan hizmeti. Çalışırken sayfa duraklatılmaz, bu yüzden JavaScript'i, <audio> ve uygulamanın arka plan müziği devam eder (Android hâlâ kimsenin göremediği bir sayfanın zamanlayıcılarını yavaşlatır).

Döndürür: startBackgroundService → bir JSON dizesi { ok: true, status: 'starting', type, notificationShown } veya { ok: false, reason } - neden not_enabled, invalid_options, type_not_declared, invalid_progress, location_permission_required, start_not_allowed. updateBackgroundService → { ok: true } veya { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning → true / false. Durum değişiklikleri appmint:background-service (ve window.onAppMintBackgroundService(detail)) olarak gelir: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. Gerekenler: Arka planda çalışmaya devam et işaretleyin (Erişim adımı → Arka plan) ve hizmet türünü seçin. Android 13+ üzerinde bildirim yalnızca kullanıcı bildirimlere izin verdikten sonra görünür (aksi halde notificationShown: false - hizmet yine de çalışır).

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

Derlemenin veya telefonun bunu yapamayacağı anlamına gelen cevapları ele alın:

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

Bir yükleme döngüsüyle birleştirin: yalnızca kuyrukta iş varken çalışmaya devam edin.

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

Notlar: tür, derlemenin bildirdiği türlerden biri olmalıdır (sihirbazda seçilen, ayrıca Arka plan konumu açıksa location). Uygulamayı kaydırarak kapatmak, keepAfterSwipe: true vermediğiniz sürece çalışmaya devam eden bir hizmeti durdurur (o zaman sayfanın kendisi gitmiştir). Play Console, bildirilen her tür için kısa bir videoyla ön plan hizmeti beyanı ister.

isIgnoringBatteryOptimizations köprü#

window.WebToApk.isIgnoringBatteryOptimizations(): Boolean

Kullanıcının bu uygulamayı pil optimizasyonundan (Doze uygulama beklemesi) muaf tutup tutmadığı.

Örnek

Pil optimizasyonu muafiyetini kontrol eder ve ister; böylece Doze arka plan işinizi ertelemez.

Döndürür: isIgnoringBatteryOptimizations() → true / false. requestIgnoreBatteryOptimizations() → 'already_ignoring', 'dialog_opened' (sistemin İzin Ver/Reddet iletişim kutusu - yalnızca derleme Pil optimizasyonunu atlamak için doğrudan sor seçeneğini işaretlediyse), 'settings_opened' (pil optimizasyonu listesi; izin yok, Play beyanı yok) veya 'unavailable'. Gerekenler: ayarlar listesi için hiçbir şey. Doğrudan iletişim kutusu REQUEST_IGNORE_BATTERY_OPTIMIZATIONS bildirir; Play bunu yalnızca uygulamanın temel işlevi olduğunda kabul eder.

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

Her cevap için ne olduğunu söyleyin.

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

Arka plan takibiyle eşleştirin, çünkü Doze onu duraklatırdı.

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 köprü#

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

Bu kimliğe sahip coğrafi çiti kaldırır; yoksa false.

Örnek

WebToApk.addGeofence ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Uygulama kapalıyken enter, exit ve/veya dwell tetikleyen, isteğe bağlı olarak bildirim gönderen bir daire. Coğrafi çitler telefon yeniden başlatıldığında da korunur (otomatik olarak geri eklenirler).

Döndürür: addGeofence → bir JSON dizesi { ok: true, status: 'adding', id } veya { ok: false, reason } - not_enabled, invalid_id, invalid_position, invalid_radius (1 m … 100 km), invalid_events, location_permission_required, background_permission_required, foreign_origin. Play hizmetlerinin cevabı appmint:geofence-added (window.onAppMintGeofenceAdded) = { id, ok, reason? } (location_off, too_many_geofences - uygulama başına 100, play_services_unavailable) olarak gelir. Bir geçiş appmint:geofence (window.onAppMintGeofence) = { id, event: 'enter' | 'exit' | 'dwell', time, lat, lng, queued? } olarak gelir - uygulama kapalıyken gerçekleştiyse ve bir sonraki başlangıçta teslim edildiyse queued: true. removeGeofence → varsa true; getGeofences → eklediklerinizin JSON dizisi. Gerekenler: derlemede Coğrafi çitler işaretleyin ve "Her zaman izin ver" (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');

Özelliği algılayın ve her reddi bildirin:

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

Kayıtlı olanları listeleyin ve birini kaldırın:

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 köprü#

window.WebToApk.requestBackgroundLocation()

Konum ister, sonra "Her zaman izin ver" ister; sonuç şu olarak gelir appmint:location-permission.

Örnek

Konum ister, sonra "Her zaman izin ver" ister (Android 10+ bunları ayrı ayrı sorar; Android 11+ üzerinde ikinci adım uygulamanın konum ayarları sayfasını açar). Coğrafi çitler buna ihtiyaç duyar; telefon yeniden başlatıldıktan sonra konum takibini yeniden başlatmak da öyle.

Döndürür: doğrudan hiçbir şey; cevap appmint:location-permission (ve window.onAppMintLocationPermission(detail)) = { foreground: 'granted' | 'denied', background: 'granted' | 'denied', precise: boolean, error?: 'not_enabled' } olarak gelir. getLocationPermissionState() → aynı nesne, sormadan JSON dizesi olarak. Gerekenler: derlemede Arka plan konumu veya Coğrafi çitler.

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

Önce durumu kontrol edin ve kullanıcının neye dokunması gerektiğini açıklayın:

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

İzni isteyin, sonra verildiğinde bir coğrafi çit (geofence) ekleyin:

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 köprü#

window.WebToApk.requestIgnoreBatteryOptimizations(): String

Pil muafiyetini ister: 'already_ignoring' | 'dialog_opened' | 'settings_opened' | 'unavailable'.

Örnek

WebToApk.isIgnoringBatteryOptimizations ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Pil optimizasyonu muafiyetini kontrol eder ve ister; böylece Doze arka plan işinizi ertelemez.

Döndürür: isIgnoringBatteryOptimizations() → true / false. requestIgnoreBatteryOptimizations() → 'already_ignoring', 'dialog_opened' (sistemin İzin Ver/Reddet iletişim kutusu - yalnızca derleme Pil optimizasyonunu atlamak için doğrudan sor seçeneğini işaretlediyse), 'settings_opened' (pil optimizasyonu listesi; izin yok, Play beyanı yok) veya 'unavailable'. Gerekenler: ayarlar listesi için hiçbir şey. Doğrudan iletişim kutusu REQUEST_IGNORE_BATTERY_OPTIMIZATIONS bildirir; Play bunu yalnızca uygulamanın temel işlevi olduğunda kabul eder.

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

Her cevap için ne olduğunu söyleyin.

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

Arka plan takibiyle eşleştirin, çünkü Doze onu duraklatırdı.

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 köprü#

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

Uygulamanın ön plan hizmetini başlatır: {title, text, type?, progress?, stopLabel?, keepAfterSwipe?} → JSON {ok, status, type, notificationShown} veya {ok:false, reason}.

Örnek

Kullanıcı uzaktayken uygulamanızı çalışır tutar: bildirimli gerçek bir Android ön plan hizmeti. Çalışırken sayfa duraklatılmaz, bu yüzden JavaScript'i, <audio> ve uygulamanın arka plan müziği devam eder (Android hâlâ kimsenin göremediği bir sayfanın zamanlayıcılarını yavaşlatır).

Döndürür: startBackgroundService → bir JSON dizesi { ok: true, status: 'starting', type, notificationShown } veya { ok: false, reason } - neden not_enabled, invalid_options, type_not_declared, invalid_progress, location_permission_required, start_not_allowed. updateBackgroundService → { ok: true } veya { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning → true / false. Durum değişiklikleri appmint:background-service (ve window.onAppMintBackgroundService(detail)) olarak gelir: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. Gerekenler: Arka planda çalışmaya devam et işaretleyin (Erişim adımı → Arka plan) ve hizmet türünü seçin. Android 13+ üzerinde bildirim yalnızca kullanıcı bildirimlere izin verdikten sonra görünür (aksi halde notificationShown: false - hizmet yine de çalışır).

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

Derlemenin veya telefonun bunu yapamayacağı anlamına gelen cevapları ele alın:

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

Bir yükleme döngüsüyle birleştirin: yalnızca kuyrukta iş varken çalışmaya devam edin.

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

Notlar: tür, derlemenin bildirdiği türlerden biri olmalıdır (sihirbazda seçilen, ayrıca Arka plan konumu açıksa location). Uygulamayı kaydırarak kapatmak, keepAfterSwipe: true vermediğiniz sürece çalışmaya devam eden bir hizmeti durdurur (o zaman sayfanın kendisi gitmiştir). Play Console, bildirilen her tür için kısa bir videoyla ön plan hizmeti beyanı ister.

startLocationUpdates köprü#

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

Konum güncellemeleri {intervalMs, minDistanceM, accuracy, background, uploadUrl?, headers?} → JSON {ok, background, backgroundPermission, precise} veya {ok:false, reason}; konumlar şu olarak gelir appmint:location.

Örnek

Uygulama arka plandayken gelmeye devam eden konum güncellemeleri (koşu takipçisi, teslimat uygulaması). background: true ile (varsayılan) uygulamanın ön plan hizmetinin içinde çalışırlar. Her konum, sayfa oradayken appmint:location olarak sayfaya, değilken bir tampona (getBufferedLocations()) ve - bir uploadUrl verirseniz - yerel koddan JSON POST olarak sunucunuza gider; böylece sayfa gitmiş olsa da takip çalışır.

Döndürür: bir JSON dizesi { ok: true, background, backgroundPermission, precise } veya { ok: false, reason } - not_enabled, invalid_options, invalid_interval (1 sn … 24 sa), invalid_distance, invalid_accuracy, invalid_upload_url, location_permission_required, start_not_allowed, foreign_origin. stopLocationUpdates() → bir şey çalışıyorsa true. Konumlar: appmint:location / window.onAppMintLocation(detail) = { type: 'fix', lat, lng, accuracy, altitude?, speed?, bearing?, time, provider, background } veya { type: 'error', reason: 'play_services_unavailable' | 'location_unavailable' | 'location_permission_required' }. Gerekenler: Arka plan konumu işaretleyin (Erişim adımı → Arka plan) ve önce kullanıcının konum izni.

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

Hatalı bir seçeneği veya eksik bir izni cevabın şu alanıyla bildirin 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.');
}

Tamponla doğal bir çift: vardiya sonunda durun ve sayfa yokken kaydedilenleri yükleyin.

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

Notlar: uygulamadan başlatılan bir konum hizmeti "Her zaman izin ver" olmadan arka planda konum almaya devam eder; telefon yeniden başlatıldıktan sonra yeniden başlatmak buna ihtiyaç duyar (requestBackgroundLocation()). Başarısız olan bir POST WorkManager'a devredilir ve ağ geri geldiğinde gönderilir. Google Play hizmetleri olmayan telefonlar play_services_unavailable döndürür. Play Console bir arka plan konumu beyanı gerektirir.

stopBackgroundService köprü#

window.WebToApk.stopBackgroundService(): Boolean

startBackgroundService ile başlatılan hizmeti durdurur (arka plan konumu çalışıyorsa devam eder).

Örnek

WebToApk.startBackgroundService ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Kullanıcı uzaktayken uygulamanızı çalışır tutar: bildirimli gerçek bir Android ön plan hizmeti. Çalışırken sayfa duraklatılmaz, bu yüzden JavaScript'i, <audio> ve uygulamanın arka plan müziği devam eder (Android hâlâ kimsenin göremediği bir sayfanın zamanlayıcılarını yavaşlatır).

Döndürür: startBackgroundService → bir JSON dizesi { ok: true, status: 'starting', type, notificationShown } veya { ok: false, reason } - neden not_enabled, invalid_options, type_not_declared, invalid_progress, location_permission_required, start_not_allowed. updateBackgroundService → { ok: true } veya { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning → true / false. Durum değişiklikleri appmint:background-service (ve window.onAppMintBackgroundService(detail)) olarak gelir: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. Gerekenler: Arka planda çalışmaya devam et işaretleyin (Erişim adımı → Arka plan) ve hizmet türünü seçin. Android 13+ üzerinde bildirim yalnızca kullanıcı bildirimlere izin verdikten sonra görünür (aksi halde notificationShown: false - hizmet yine de çalışır).

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

Derlemenin veya telefonun bunu yapamayacağı anlamına gelen cevapları ele alın:

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

Bir yükleme döngüsüyle birleştirin: yalnızca kuyrukta iş varken çalışmaya devam edin.

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

Notlar: tür, derlemenin bildirdiği türlerden biri olmalıdır (sihirbazda seçilen, ayrıca Arka plan konumu açıksa location). Uygulamayı kaydırarak kapatmak, keepAfterSwipe: true vermediğiniz sürece çalışmaya devam eden bir hizmeti durdurur (o zaman sayfanın kendisi gitmiştir). Play Console, bildirilen her tür için kısa bir videoyla ön plan hizmeti beyanı ister.

stopLocationUpdates köprü#

window.WebToApk.stopLocationUpdates(): Boolean

startLocationUpdates ile başlatılan konum güncellemelerini durdurur; hiçbiri çalışmıyorsa false.

Örnek

WebToApk.startLocationUpdates ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Uygulama arka plandayken gelmeye devam eden konum güncellemeleri (koşu takipçisi, teslimat uygulaması). background: true ile (varsayılan) uygulamanın ön plan hizmetinin içinde çalışırlar. Her konum, sayfa oradayken appmint:location olarak sayfaya, değilken bir tampona (getBufferedLocations()) ve - bir uploadUrl verirseniz - yerel koddan JSON POST olarak sunucunuza gider; böylece sayfa gitmiş olsa da takip çalışır.

Döndürür: bir JSON dizesi { ok: true, background, backgroundPermission, precise } veya { ok: false, reason } - not_enabled, invalid_options, invalid_interval (1 sn … 24 sa), invalid_distance, invalid_accuracy, invalid_upload_url, location_permission_required, start_not_allowed, foreign_origin. stopLocationUpdates() → bir şey çalışıyorsa true. Konumlar: appmint:location / window.onAppMintLocation(detail) = { type: 'fix', lat, lng, accuracy, altitude?, speed?, bearing?, time, provider, background } veya { type: 'error', reason: 'play_services_unavailable' | 'location_unavailable' | 'location_permission_required' }. Gerekenler: Arka plan konumu işaretleyin (Erişim adımı → Arka plan) ve önce kullanıcının konum izni.

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

Hatalı bir seçeneği veya eksik bir izni cevabın şu alanıyla bildirin 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.');
}

Tamponla doğal bir çift: vardiya sonunda durun ve sayfa yokken kaydedilenleri yükleyin.

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

Notlar: uygulamadan başlatılan bir konum hizmeti "Her zaman izin ver" olmadan arka planda konum almaya devam eder; telefon yeniden başlatıldıktan sonra yeniden başlatmak buna ihtiyaç duyar (requestBackgroundLocation()). Başarısız olan bir POST WorkManager'a devredilir ve ağ geri geldiğinde gönderilir. Google Play hizmetleri olmayan telefonlar play_services_unavailable döndürür. Play Console bir arka plan konumu beyanı gerektirir.

updateBackgroundService köprü#

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

Çalışan hizmetin bildirimini değiştirir (startBackgroundService ile aynı seçenekler) → JSON {ok} veya {ok:false, reason:'not_running'}.

Örnek

WebToApk.startBackgroundService ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

Kullanıcı uzaktayken uygulamanızı çalışır tutar: bildirimli gerçek bir Android ön plan hizmeti. Çalışırken sayfa duraklatılmaz, bu yüzden JavaScript'i, <audio> ve uygulamanın arka plan müziği devam eder (Android hâlâ kimsenin göremediği bir sayfanın zamanlayıcılarını yavaşlatır).

Döndürür: startBackgroundService → bir JSON dizesi { ok: true, status: 'starting', type, notificationShown } veya { ok: false, reason } - neden not_enabled, invalid_options, type_not_declared, invalid_progress, location_permission_required, start_not_allowed. updateBackgroundService → { ok: true } veya { ok: false, reason: 'not_running' }. stopBackgroundService / isBackgroundServiceRunning → true / false. Durum değişiklikleri appmint:background-service (ve window.onAppMintBackgroundService(detail)) olarak gelir: { state: 'running' | 'stopped' | 'failed' | 'not_restarted', reason?, types?, restored?, queued? }. Gerekenler: Arka planda çalışmaya devam et işaretleyin (Erişim adımı → Arka plan) ve hizmet türünü seçin. Android 13+ üzerinde bildirim yalnızca kullanıcı bildirimlere izin verdikten sonra görünür (aksi halde notificationShown: false - hizmet yine de çalışır).

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

Derlemenin veya telefonun bunu yapamayacağı anlamına gelen cevapları ele alın:

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

Bir yükleme döngüsüyle birleştirin: yalnızca kuyrukta iş varken çalışmaya devam edin.

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

Notlar: tür, derlemenin bildirdiği türlerden biri olmalıdır (sihirbazda seçilen, ayrıca Arka plan konumu açıksa location). Uygulamayı kaydırarak kapatmak, keepAfterSwipe: true vermediğiniz sürece çalışmaya devam eden bir hizmeti durdurur (o zaman sayfanın kendisi gitmiştir). Play Console, bildirilen her tür için kısa bir videoyla ön plan hizmeti beyanı ister.

Her belge derlemesinde uygulama çalışma zamanından ve örnek dosyalarından oluşturulur. Markdown olarak okuyun · Tüm aileler.

22.10.2026 tarihinde yayınlanan köprüyle karşılaştırılarak kontrol edildi.