Ücretsiz APK oluştur

JavaScript köprü API'si

Cihaz ve uygulama bilgileri

Bu telefonun ne olduΔu, hangi derleme olduΔu, kurulum baΕΔ±na kararlΔ± bir tanΔ±mlayΔ±cΔ± ve standart konum ile kamera API'leri.

getUserMedia web standardı#

navigator.mediaDevices.getUserMedia — camera and microphone

Örnek

Standart web kamera ve mikrofon API'si uygulamanΔ±n iΓ§inde Γ§alΔ±ΕΔ±r. Uygulama Android'in kendi izin penceresini gΓΆsterir ve akΔ±ΕΔ± sayfaya verir.

DΓΆner deΔer: bir MediaStream Promise'i veya bir ret (kullanΔ±cΔ± reddettiΔinde ya da anahtar derlemede kapalΔ±ysa NotAllowedError, bΓΆyle bir cihaz yoksa NotFoundError). Gerekenler: AdΔ±m 4'te (EriΕim) Kamera ve/veya Mikrofon. ZIP veya HTML sayfasΔ±ndan derleme yaparsanΔ±z ve kodunuz getUserMedia({ video: … }) veya { audio: … } Γ§aΔΔ±rΔ±yorsa (ya da <input capture>, BarcodeDetector, SpeechRecognition kullanΔ±yorsa) derleme Γ§aΔrΔ±yΔ± bulur ve ilgili izni sizin iΓ§in ekler; { video: false } Kamera eklemez. Web sitesi (URL) derlemesinde kendiniz iΕaretleyin.

Arka kamerayΔ± bir video ΓΆΔesinde gΓΆsterin.

<video id="preview" autoplay playsinline muted></video>
<button id="startCam">Start camera</button>
<script>
document.getElementById('startCam').addEventListener('click', async function () {
  if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {
    showMessage('Camera is not available here.');
    return;
  }
  try {
    var stream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: 'environment' },   // 'user' = front camera
      audio: false
    });
    document.getElementById('preview').srcObject = stream;
  } catch (err) {
    if (err.name === 'NotAllowedError') showMessage('Camera permission was refused.');
    else showMessage('Camera error: ' + err.name);
  }
});
</script>

YalnΔ±zca mikrofonla bir sesli not kaydedin.

async function recordVoice(seconds) {
  var stream;
  try {
    stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
  } catch (err) {
    showMessage('Microphone permission was refused.');
    return;
  }
  var chunks = [];
  var rec = new MediaRecorder(stream);
  rec.ondataavailable = function (e) { chunks.push(e.data); };
  rec.onstop = function () {
    stream.getTracks().forEach(function (t) { t.stop(); });   // turn the mic off
    var blob = new Blob(chunks, { type: rec.mimeType });
    document.getElementById('player').src = URL.createObjectURL(blob);
  };
  rec.start();
  setTimeout(function () { rec.stop(); }, seconds * 1000);
}

Kamera akΔ±ΕΔ±ndan fotoΔraf Γ§ekin ve kamerayΔ± serbest bΔ±rakΔ±n.

async function snapshot() {
  if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) return null;
  let stream;
  try {
    stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' } });
  } catch (err) {
    showMessage(err.name === 'NotFoundError' ? 'No camera on this device.' : 'Camera permission was refused.');
    return null;
  }
  const video = document.createElement('video');
  video.srcObject = stream;
  await video.play();
  const canvas = document.createElement('canvas');
  canvas.width = video.videoWidth; canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);
  stream.getTracks().forEach(function (t) { t.stop(); });
  return canvas.toDataURL('image/jpeg', 0.9);
}

Notlar: Kamera ve mikrofonu birlikte istediΔinizde uygulama kullanΔ±cΔ±dan ikisini birden ister. Derleme sΔ±rasΔ±nda anahtar kapalΔ±ysa istek hiΓ§ izin penceresi aΓ§Δ±lmadan reddedilir; anahtarΔ± aΓ§Δ±p yeniden derleyin. Δ°Εiniz bitince parΓ§alarΔ± her zaman durdurun, yoksa kamera/mikrofon ΔΕΔ±ΔΔ± yanΔ±k kalΔ±r. DRM'li video (Widevine) herhangi bir anahtar olmadan Γ§alΔ±ΕΔ±r. Kamera ve mikrofon hassastΔ±r: bunlarΔ± Play Veri gΓΌvenliΔi formunuzda belirtin.

IdleDetector web standardı#

new IdleDetector() — Idle Detection

Örnek

Standart BoΕta Kalma AlgΔ±lama API'si: kullanΔ±cΔ±nΔ±n telefonu ne zaman kullanmayΔ± bΔ±raktΔ±ΔΔ±nΔ± (bir sΓΌre girdi yok) ya da ekranΔ± kilitlediΔini ve ne zaman geri dΓΆndΓΌΔΓΌnΓΌ ΓΆΔrenin; "uzakta" durumu, canlΔ± bir oturumu duraklatma veya bir kasayΔ± kilitleme iΓ§in.

DΓΆner deΔer: IdleDetector.requestPermission() β 'granted' veya 'denied' (dokunuΕ gerekir); detector.start({threshold, signal}) β Promise (izin yoksa NotAllowedError ile, 60000 ms altΔ±nda TypeError ile reddedilir); ardΔ±ndan userState ('active'/'idle'), screenState ('locked'/'unlocked') ve bir change olayΔ±. Gerekenler: aΓ§Δ±lacak bir anahtar ve Android izni yok.

document.getElementById('away').addEventListener('click', async () => {
  if (!('IdleDetector' in window)) return;
  if ((await IdleDetector.requestPermission()) !== 'granted') return;
  const ac = new AbortController();
  const idle = new IdleDetector();
  idle.addEventListener('change', () => {
    setStatus(idle.userState === 'idle' || idle.screenState === 'locked' ? 'Away' : 'Online');
  });
  await idle.start({ threshold: 5 * 60000, signal: ac.signal });
  stopButton.onclick = () => ac.abort();
});

KullanΔ±cΔ± bir kez evet dedikten sonra, yeni bir dokunuΕ olmadan bir sonraki aΓ§Δ±lΔ±Εta izlemeye devam edin:

async function watchAwayIfAllowed() {
  if (!('IdleDetector' in window)) return;
  const perm = await navigator.permissions.query({ name: 'idle-detection' });
  if (perm.state !== 'granted') return;          // 'prompt' until requestPermission() ran from a tap
  const idle = new IdleDetector();
  idle.onchange = () => {
    if (idle.screenState === 'locked') pauseLiveSession();
    else if (idle.userState === 'active') resumeLiveSession();
  };
  await idle.start({ threshold: 60000 });         // 1 minute is the smallest threshold
}
watchAwayIfAllowed();

KullanΔ±cΔ±nΔ±n otomatik kilit gecikmesini deΔiΕtirmesine izin verin: Γ§alΔ±Εan algΔ±layΔ±cΔ±yΔ± durdurun, yenisini baΕlatΔ±n ve gerΓ§ek hatalarΔ± bildirin:

let vaultWatch = null;
async function setAutoLock(minutes) {
  if (vaultWatch) vaultWatch.abort();             // aborting stops the old detector
  vaultWatch = new AbortController();
  const idle = new IdleDetector();
  idle.addEventListener('change', () => {
    if (idle.userState === 'idle' || idle.screenState === 'locked') lockVault();
  });
  try {
    await idle.start({ threshold: minutes * 60000, signal: vaultWatch.signal });
  } catch (err) {
    if (err.name === 'NotAllowedError') showMessage('Tap "Enable auto-lock" first.');
    else if (err instanceof TypeError) showMessage('Pick at least 1 minute.');
    else throw err;
  }
}

Notlar: uygulamada "girdi", uygulama ΓΆndeyken kullanΔ±cΔ±nΔ±n BU uygulamaya verdiΔi girdidir; ekran aΓ§Δ±kken baΕka bir uygulama ΓΆndeyse kullanΔ±cΔ± etkin sayΔ±lΔ±r (bir uygulama diΔer uygulamalarΔ±n girdisini gΓΆremez; Android'de Chrome de aynΔ± Εekilde yanΔ±tlar); ekran kapandΔ±ΔΔ±nda veya kilitlendiΔinde boΕta kalma sΓΌresi son dokunuΕtan itibaren iΕler. Δ°zin hatΔ±rlanΔ±r ve navigator.permissions.query({name:'idle-detection'}) onu yanΔ±tlar. Dahili aktarΔ±mlar: __idleState, __idlePermission; kilit/kilit aΓ§ma deΔiΕiklikleri appmint:idle olarak gelir. Yapay zekΓ’yla oluΕturulan uygulamalar Εunu kullanΔ±r: watchIdle().

navigator.geolocation web standardı#

navigator.geolocation

Örnek

Standart web konum API'si uygulamanΔ±n iΓ§inde Γ§alΔ±ΕΔ±r. Uygulama Android'in kendi konum penceresini gΓΆsterir ve yanΔ±tΔ± sayfaya iletir.

DΓΆner deΔer: normal W3C geri Γ§aΔrΔ±larΔ±: bir GeolocationPosition (coords.latitude, coords.longitude, coords.accuracy, β¦) veya bir GeolocationPositionError (code 1 = izin reddedildi, 2 = kullanΔ±lamΔ±yor, 3 = zaman aΕΔ±mΔ±). Gerekenler: AdΔ±m 4'te (EriΕim) GPS (hassas) ve/veya Konum (yaklaΕΔ±k). ZIP veya HTML sayfasΔ±ndan derleme yaparsanΔ±z ve kodunuz navigator.geolocation, getCurrentPosition veya watchPosition Γ§aΔΔ±rΔ±yorsa derleme bu Γ§aΔrΔ±yΔ± bulur ve konumu sizin iΓ§in ekler. Web sitesi (URL) derlemesinde kendiniz iΕaretleyin.

Konumu bir kez alΔ±n.

function whereAmI() {
  if (!('geolocation' in navigator)) { showMessage('Location is not available here.'); return; }

  navigator.geolocation.getCurrentPosition(
    function (pos) {
      showMessage('You are at ' + pos.coords.latitude.toFixed(5) + ', ' +
                  pos.coords.longitude.toFixed(5) + ' (±' + Math.round(pos.coords.accuracy) + ' m)');
    },
    function (err) {
      if (err.code === 1) showMessage('Location permission was refused.');
      else if (err.code === 3) showMessage('Location timed out. Try again outside.');
      else showMessage('Location is unavailable. Is location turned on in phone settings?');
    },
    { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }
  );
}

KullanΔ±cΔ± hareket ederken onu takip edin (koΕu takipΓ§isi, teslimat haritasΔ±). Ekran kapanΔ±nca izlemeyi durdurun.

var watchId = null;

function startTracking() {
  if (!('geolocation' in navigator)) return;
  watchId = navigator.geolocation.watchPosition(
    function (pos) { addPointToMap(pos.coords.latitude, pos.coords.longitude); },
    function (err) { showMessage('Tracking stopped: ' + err.message); },
    { enableHighAccuracy: true }
  );
}

function stopTracking() {
  if (watchId !== null) { navigator.geolocation.clearWatch(watchId); watchId = null; }
}

Γnce izni kontrol edin, sonra yalnΔ±zca yanΔ±tlanabiliyorsa bir konum isteyin:

async function locateIfAllowed() {
  if (!('geolocation' in navigator)) return;
  if (navigator.permissions) {
    const s = await navigator.permissions.query({ name: 'geolocation' });
    if (s.state === 'denied') { showMessage('Location is off for this app. Turn it on in Settings.'); return; }
  }
  navigator.geolocation.getCurrentPosition(
    (pos) => centerMap(pos.coords.latitude, pos.coords.longitude),
    (err) => showMessage('Location error ' + err.code + ': ' + err.message),
    { timeout: 10000 }
  );
}

Notlar: GPS aΓ§Δ±kken uygulama hassas ve yaklaΕΔ±k konumu birlikte ister; Android 12+ sΓΌrΓΌmΓΌnde kullanΔ±cΔ± "YaklaΕΔ±k" seΓ§ebilir, bu da yine Γ§alΔ±ΕΔ±r (daha dΓΌΕΓΌk doΔruluk). YalnΔ±zca Konum aΓ§Δ±ksa yalnΔ±zca yaklaΕΔ±k konum istenir. Ikisi de kapalΔ±ysa her Γ§aΔrΔ± 1 koduyla ve izin penceresi olmadan baΕarΔ±sΔ±z olur. KullanΔ±cΔ± bir kez izin verdikten sonra sonraki Γ§aΔrΔ±lar pencere olmadan yanΔ±tlanΔ±r. Telefonun kendi konum anahtarΔ± da aΓ§Δ±k olmalΔ±dΔ±r. Konum kiΕisel veridir: Play Veri gΓΌvenliΔi formunuzda belirtin; arka planda konum saΔlanmaz.

navigator.getInstalledRelatedApps web standardı#

navigator.getInstalledRelatedApps()

Örnek

Standart ilgili uygulamalar API'si: sayfaya, kendi Android uygulamalarΔ±ndan (web uygulamasΔ± manifestinde listelenenler) hangilerinin yΓΌklΓΌ olduΔunu sΓΆyler; bΓΆylece "DiΔer uygulamamΔ±zΔ± yΓΌkleyin" afiΕini gizleyebilir veya uygulamaya derin baΔlantΔ± verebilir.

DΓΆner deΔer: manifestin related_applications girdilerinden yΓΌklΓΌ olanlarΔ±n dizisini iΓ§eren Promise: { platform: 'play', id, url?, version? }. Gerekenler: uygulamalarΔ± "platform": "play" ve "id": "<package name>" ile listeleyen bir manifest iΓ§eren <link rel="manifest">; uygulamayla birlikte paketlenir ki derleme bunlarΔ± bildirebilsin (Android, bir uygulamanΔ±n yalnΔ±zca adΔ±nΔ± verdiΔi paketleri gΓΆrmesine izin verir).

// manifest.webmanifest: { "related_applications": [{ "platform": "play", "id": "com.example.scanner" }] }
const apps = await navigator.getInstalledRelatedApps();
const hasScanner = apps.some((a) => a.id === 'com.example.scanner');
document.getElementById('get-scanner').hidden = hasScanner;

EΕlik eden uygulama yΓΌklΓΌyse aΓ§Δ±n (girdinin url deΔeri manifestinizden gelir), deΔilse kullanΔ±cΔ±yΔ± maΔaza sayfasΔ±na yΓΆnlendirin:

// manifest: { "platform": "play", "id": "com.example.scanner", "url": "https://example.com/scan" }
document.getElementById('open-scanner').addEventListener('click', async () => {
  const apps = typeof navigator.getInstalledRelatedApps === 'function'
    ? await navigator.getInstalledRelatedApps() : [];
  const scanner = apps.find((a) => a.id === 'com.example.scanner');
  location.href = scanner && scanner.url
    ? scanner.url
    : 'https://play.google.com/store/apps/details?id=com.example.scanner';
});

"Bizden daha fazlasΔ±" ekranΔ±nda, uygulamalarΔ±nΔ±zdan hangilerinin telefonda zaten yΓΌklΓΌ olduΔunu listeleyin:

async function renderMoreApps(catalog) {         // [{ id, name }] matching your manifest entries
  if (typeof navigator.getInstalledRelatedApps !== 'function') return; // e.g. inside an iframe
  const installed = new Set((await navigator.getInstalledRelatedApps()).map((a) => a.id));
  document.getElementById('more-apps').innerHTML = catalog.map((app) =>
    `<li>${app.name} — ${installed.has(app.id) ? 'Installed' : '<a href="https://play.google.com/store/apps/details?id=' + app.id + '">Get it</a>'}</li>`
  ).join('');
}

Notlar: derleme paketinizdeki manifesti okur ve listelenen her paketi bildirir; orada listelenmeyen bir uygulama gΓΆrΓΌlemez ve asla bildirilmez. Web sitesi modundaki uygulamalar manifestlerini sitede tutar; bu yΓΌzden hiΓ§bir Εey bildirilmez ve liste boΕ olur. Dahili aktarΔ±m: __relatedAppsInstalled.

navigator.permissions web standardı#

navigator.permissions.query({ name })

Örnek

Standart Δ°zinler API'si, telefonun gerΓ§ek Android izin durumundan yanΔ±tlanΔ±r. BΓΆylece bir sayfa "KamerayΔ± etkinleΕtir" seΓ§eneΔini yalnΔ±zca hΓ’lΓ’ istenebiliyorsa, "Ayarlar'dan aΓ§Δ±n" mesajΔ±nΔ± ise istenemiyorsa gΓΆsterebilir.

DΓΆner deΔer: bir PermissionStatus {name, state, onchange} Promise'i; state, 'granted', 'denied' veya 'prompt' olur ve change, bir izin penceresi kapandΔ±ktan hemen sonra ya da kullanΔ±cΔ± Ayarlar'dan dΓΆndΓΌΔΓΌnde tetiklenir. Gerekenler: aΓ§Δ±lacak bir anahtar yok.

async function cameraButton() {
  const btn = document.getElementById('scan');
  const status = await navigator.permissions.query({ name: 'camera' });
  function render() {
    if (status.state === 'denied') {
      btn.textContent = 'Camera is off — enable it in Settings';
      btn.disabled = true;
    } else {
      btn.textContent = status.state === 'granted' ? 'Scan' : 'Allow camera and scan';
      btn.disabled = false;
    }
  }
  render();
  status.onchange = render;   // e.g. after the Android dialog, or back from Settings
}
cameraButton();

// The other names the app answers the same way:
for (const name of ['microphone', 'geolocation', 'notifications']) {
  navigator.permissions.query({ name }).then((s) => console.log(name, s.state));
}

YalnΔ±zca sayfa hΓ’lΓ’ isteyebiliyorsa sorun, aksi halde kullanΔ±cΔ±yΔ± Ayarlar'a yΓΆnlendirin:

async function ensureMic() {
  if (!(navigator.permissions && navigator.permissions.query)) return true;   // a browser without it
  const s = await navigator.permissions.query({ name: 'microphone' });
  if (s.state === 'denied') {
    showMessage('Microphone is off for this app. Turn it on in Settings.');
    return false;
  }
  return true;   // 'granted', or 'prompt' (Android asks on first use)
}

document.getElementById('record').onclick = async () => {
  if (await ensureMic()) startRecording();
};

KullanΔ±cΔ± Ayarlar'da bir izni deΔiΕtirdiΔinde tepki verin ve bunu gerΓ§ek bir istekle birleΕtirin:

async function watchLocationSwitch() {
  const status = await navigator.permissions.query({ name: 'geolocation' });
  status.addEventListener('change', () => {
    if (status.state === 'granted') {
      navigator.geolocation.getCurrentPosition((p) => showNearby(p.coords.latitude, p.coords.longitude));
    } else {
      hideNearby();
    }
  });
}
watchLocationSwitch();

Notlar: denied, uygulamanΔ±n olmadan derlendiΔi bir yeteneΔi de kapsar (derlemede anahtarΔ± kapalΔ±ydΔ±, bu yΓΌzden Android bunu asla veremez) ve "Bir daha sorma" reddini de. screen-wake-lock, clipboard-read ve clipboard-write 'granted' dΓΆner (uygulama bunlarΔ± izin penceresi olmadan saΔlar). DiΔer adlar (midi, accelerometer, β¦) daha ΓΆnce olduΔu gibi WebView tarafΔ±ndan yanΔ±tlanΔ±r. ArkasΔ±ndaki olay: appmint:permissions-changed.

TextDetector web standardı#

new TextDetector().detect(image) — text recognition (OCR)

Örnek

Bir resimdeki metni telefonun kendisinde okur: bir fiΕ toplamΔ±, bir seri numarasΔ±, bir kartvizit. Google'Δ±n ML Kit tanΔ±yΔ±cΔ±sΔ±nΔ± kullanΔ±r. YapΔ±sΔ±, Chrome'un bir bayrak arkasΔ±nda sunduΔu Shape Detection API'sinin TextDetector yapΔ±sΔ±dΔ±r.

DΓΆner deΔer: bir dizi iΓ§eren Promise, her satΔ±r iΓ§in bir girdi: gΓΆrΓΌntΓΌnΓΌn piksellerinde { rawValue, boundingBox, cornerPoints }. Google Play hizmetleri yoksa NotSupportedError ile, tanΔ±ma baΕarΔ±sΔ±z olursa OperationError ile reddedilir. Gerekenler: aΓ§Δ±lacak bir anahtar ve izin yok (kameradan okumak iΓ§in fotoΔrafΔ± <input type="file" accept="image/*" capture> ile veya bir kamera akΔ±ΕΔ±yla Γ§ekin).

document.getElementById('photo').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (!file || !('TextDetector' in window)) return;
  status.textContent = 'Reading…';
  try {
    const lines = await new TextDetector().detect(file);   // a Blob, <img>, <canvas>, <video> or ImageBitmap
    const text = lines.map((l) => l.rawValue).join('\n');
    const total = /total\s*[:$€£]?\s*([\d.,]+)/i.exec(text);
    status.textContent = total ? 'Total: ' + total[1] : text;
  } catch (err) {
    status.textContent = 'Could not read the text (' + err.name + ')';
  }
});

CanlΔ± kamera ΓΆnizlemesinden bir seri numarasΔ± okuyun (oynatΔ±lan bir <video> olduΔu gibi geΓ§ilir, mevcut karesi okunur):

const video = document.querySelector('video');
async function startPreview() {
  video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
  await video.play();
}

document.getElementById('read-serial').addEventListener('click', async () => {
  if (!('TextDetector' in window)) return;
  const lines = await new TextDetector().detect(video);
  const hit = lines.map((l) => l.rawValue).find((t) => /S\/?N[:\s]*[A-Z0-9-]{6,}/i.test(t));
  serialField.value = hit ? hit.replace(/^.*S\/?N[:\s]*/i, '') : '';
  if (!hit) showMessage('No serial number in view — move closer and try again.');
});

boundingBox (gΓΆrΓΌntΓΌ pikselleri) kullanarak tanΔ±nan her satΔ±rΔ± fotoΔrafΔ±n ΓΌstΓΌnde Γ§erΓ§eveleyin:

async function outlineText(img, canvas) {
  await img.decode();                             // detect() rejects InvalidStateError before it has loaded
  canvas.width = img.naturalWidth;
  canvas.height = img.naturalHeight;
  const g = canvas.getContext('2d');
  g.drawImage(img, 0, 0);
  const lines = await new TextDetector().detect(img);
  g.strokeStyle = '#ff3b30';
  g.lineWidth = 3;
  for (const { boundingBox: b, rawValue } of lines) {
    g.strokeRect(b.x, b.y, b.width, b.height);
    console.log(rawValue);
  }
  return lines.length;
}

Metin tanΔ±ma yoksa (Google Play hizmetleri yok) tarama dΓΌΔmesini gizleyin ve bir kartvizitten iletiΕim bilgilerini alΔ±n:

const scanBtn = document.getElementById('scan-card');
scanBtn.hidden = !('TextDetector' in window);
document.getElementById('card-photo').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (!file) return;
  const text = (await new TextDetector().detect(file)).map((l) => l.rawValue).join('\n');
  fillContact({
    email: (text.match(/[\w.+-]+@[\w-]+\.[\w.]+/) || [''])[0],
    phone: (text.match(/\+?\d[\d\s().-]{7,}\d/) || [''])[0],
  });
});

Notlar: YalnΔ±zca Latin alfabeleri (Ingilizce, ΔΓ°spanyolca, FransΔ±zca, Almanca, Endonezce, Vietnamca, TΓΌrkΓ§e ve diΔerleri); Γinceyi, Devanagari, Japonca, Korece ve ArapΓ§a tanΔ±nmaz. Model Google Play hizmetlerinden gelir: kodunuz kullanΔ±yorsa derleme kurulumda onu ister, aksi halde yeni bir telefonda ilk Γ§aΔrΔ± indirilirken birkaΓ§ saniye bekler. BaΕka bir siteden gelen gΓΆrΓΌntΓΌler CORS'a izin vermelidir (kirlenmiΕ bir canvas SecurityError hatasΔ± verir). Dahili aktarΔ±mlar: __ocrAvailable, __ocrRecognize (appmint:ocr olarak yanΔ±tlanΔ±r). Yapay zekΓ’yla oluΕturulan uygulamalar Εunu kullanΔ±r: recognizeText().

__idlePermission köprü#

window.WebToApk.__idlePermission(request: Boolean): String

IdleDetector.requestPermission() ([request] true, kullanΔ±cΔ± hareketi altΔ±nda Γ§aΔrΔ±lΔ±r) izni kaydeder; false yalnΔ±zca okur. "granted" | "prompt".

Örnek

IdleDetector.requestPermission() ve 'idle-detection' izin durumunun arkasΔ±ndaki dahili aktarΔ±m. Sayfalar standart API'yi kullanΔ±r; bunu asla doΔrudan Γ§aΔΔ±rmayΔ±n; __ yΓΆntemleri haber verilmeden deΔiΕebilir.

DΓΆner deΔer: 'granted' veya 'prompt'. Gerekenler: istek iΓ§in bir kullanΔ±cΔ± hareketi.

Genel API'yi kullanın:

button.onclick = async () => {
  const state = await IdleDetector.requestPermission();
  console.log(state); // 'granted'
};

ΓzelliΔi kontrol edin ve bir reddi "izin verilmedi" olarak ele alΔ±n:

async function askIdlePermission() {
  if (!('IdleDetector' in window)) return false;
  try {
    return (await IdleDetector.requestPermission()) === 'granted';
  } catch (err) {
    return false;
  }
}

Bir dokunuΕtan isteyin, sonra algΔ±lamayΔ± baΕlatΔ±n:

startBtn.addEventListener('click', async () => {
  if (!(await askIdlePermission())) { showMessage('Idle detection was not allowed.'); return; }
  const idle = new IdleDetector();
  idle.addEventListener('change', () => console.log(idle.userState));
  await idle.start({ threshold: 60000 });
});

Notlar: Uygulama kabuΔunda uygulanmΔ±ΕtΔ±r (IdleAndRelatedApps.kt).

__idleState köprü#

window.WebToApk.__idleState(): String

IdleDetector: uygulamanΔ±n kendi girdisinden ve tuΕ kilidinden `{idleMs, locked}`.

Örnek

IdleDetector arkasΔ±ndaki dahili aktarΔ±m: kullanΔ±cΔ±nΔ±n en son girdi vermesinden bu yana geΓ§en sΓΌre ve ekranΔ±n kilitli olup olmadΔ±ΔΔ±. Sayfalar standart API'yi kullanΔ±r; bunu asla doΔrudan Γ§aΔΔ±rmayΔ±n; __ yΓΆntemleri haber verilmeden deΔiΕebilir.

DΓΆner deΔer: JSON {idleMs, locked}. Ekran ve uygulama deΔiΕiklikleri aynΔ± yapΔ±yla appmint:idle olayΔ± olarak iletilir. Gerekenler: hiΓ§bir Εey.

Genel API'yi kullanın:

const idle = new IdleDetector();
idle.onchange = () => console.log(idle.userState, idle.screenState);
await idle.start({ threshold: 60000 });

Ekran kapanΔ±nca izlemeyi bir AbortController ile durdurun:

const ctl = new AbortController();
const idle = new IdleDetector();
idle.addEventListener('change', () => {
  if (idle.userState === 'idle') showAwayBanner();
});
await idle.start({ threshold: 60000, signal: ctl.signal });
// later: ctl.abort();

Ekran kilitlendiΔinde uygulamayΔ± kilitleyin:

if ('IdleDetector' in window && (await IdleDetector.requestPermission()) === 'granted') {
  const d = new IdleDetector();
  d.onchange = () => { if (d.screenState === 'locked') showPinScreen(); };
  await d.start({ threshold: 60000 });
}

Notlar: Uygulama kabuΔunda uygulanmΔ±ΕtΔ±r (IdleAndRelatedApps.kt).

__permissionState köprü#

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

Bir W3C izin adΔ±nΔ±n Android'in gerΓ§ek izinlerinden Δ°zinler API'si durumu: "granted" | "denied" | "prompt"; ya da kabuk o adΔ± yanΔ±tlamΔ±yorsa "" (sayfanΔ±n sorgusu o zaman WebView'in kendi uygulamasΔ±na gider).

Örnek

navigator.permissions.query({ name }) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

DΓΆner deΔer: 'granted', 'denied', 'prompt' veya uygulamanΔ±n yanΔ±tlamadΔ±ΔΔ± bir ad iΓ§in '' (sorgu o zaman WebView'in kendisine gider). Gerekenler: aΓ§Δ±lacak bir anahtar yok.

Genel API'yi kullanın:

const status = await navigator.permissions.query({ name: 'camera' });
showCameraHint(status.state);                       // 'granted' | 'denied' | 'prompt'
status.addEventListener('change', () => showCameraHint(status.state));

Birden Γ§ok izni aynΔ± anda kontrol edin:

const names = ['camera', 'microphone', 'geolocation', 'notifications'];
const states = await Promise.all(names.map((name) =>
  navigator.permissions.query({ name }).then((s) => s.state, () => 'unknown')));
names.forEach((name, i) => console.log(name, states[i]));

YalnΔ±zca durum denied deΔilse isteyin ve olduΔunda ne yapΔ±lacaΔΔ±nΔ± sΓΆyleyin:

async function ensureLocation() {
  const s = await navigator.permissions.query({ name: 'geolocation' });
  if (s.state === 'denied') { showMessage('Turn location on in the phone settings.'); return null; }
  return new Promise((ok, fail) => navigator.geolocation.getCurrentPosition(ok, fail));
}

Notlar: camera, microphone, geolocation, notifications, screen-wake-lock, clipboard-read, clipboard-write adlarΔ±nΔ± yanΔ±tlar. Derlemede kapalΔ± bΔ±rakΔ±lan (veya izni manifestte olmayan) bir yetenek denied olur. CanlΔ± durumlar appmint:permissions-changed olayΔ±nda (her izin penceresinden sonra tetiklenir) ve sayfa yeniden gΓΆrΓΌnΓΌr olduΔunda yeniden okunur.

__relatedAppsInstalled köprü#

window.WebToApk.__relatedAppsInstalled(idsJson: String): String

navigator.getInstalledRelatedApps(): [idsJson] iΓ§inden (sayfanΔ±n manifestindeki paket adlarΔ±) hangileri yΓΌklΓΌ ve bu uygulamaya gΓΆrΓΌnΓΌr.

Örnek

navigator.getInstalledRelatedApps() arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

DΓΆner deΔer: verilen paket adlarΔ±ndan yΓΌklΓΌ ve bu uygulamaya gΓΆrΓΌnΓΌr olanlarΔ±n JSON dizisi. Gerekenler: derleme sΔ±rasΔ±nda bildirilen paketler (paketlenmiΕ web uygulamasΔ± manifestinden).

Genel API'yi kullanın:

const apps = await navigator.getInstalledRelatedApps();
console.log(apps.map((a) => a.id));

"EΕlik eden uygulamayΔ± edinin" dΓΌΔmesini yalnΔ±zca yΓΌklΓΌ deΔilse gΓΆsterin:

async function showCompanionLink() {
  if (!navigator.getInstalledRelatedApps) return;
  const apps = await navigator.getInstalledRelatedApps();
  document.getElementById('get-companion').hidden = apps.length > 0;
}

Paketler, sayfanΔ±n baΔladΔ±ΔΔ± web uygulamasΔ± manifestinden gelir:

<link rel="manifest" href="/manifest.webmanifest">
<!-- manifest.webmanifest: "related_applications": [{ "platform": "play", "id": "com.example.companion" }] -->

Notlar: Uygulama kabuΔunda uygulanmΔ±ΕtΔ±r (IdleAndRelatedApps.kt).

getAppName köprü#

window.WebToApk.getAppName(): String

OluΕturucunun sihirbazda seΓ§tiΔi uygulama adΔ±; baΕlatΔ±cΔ±nΔ±n gΓΆsterdiΔi ad.

Örnek

Derleme sihirbazΔ±nda seΓ§tiΔiniz uygulama adΔ±nΔ± verir; baΕlatΔ±cΔ± simgesinin altΔ±ndaki ad.

DΓΆner deΔer: hemen bir dize. Gerekenler: hiΓ§bir Εey.

GerΓ§ek uygulama adΔ±nΔ± bir baΕlΔ±kta ve bir paylaΕΔ±m metninde kullanΔ±n.

var appName = (window.WebToApk && window.WebToApk.getAppName)
  ? WebToApk.getAppName()
  : document.title;                       // in a browser: the page title

document.getElementById('welcome').textContent = 'Welcome to ' + appName;
document.getElementById('shareBtn').addEventListener('click', function () {
  if (navigator.share) navigator.share({ text: 'Try ' + appName + '!' });
});

Uygulama adΔ±nΔ± sayfa baΕlΔ±ΔΔ±na ve bir baΕlΔ±Δa koyun:

if (window.WebToApk && WebToApk.getAppName) {
  const name = WebToApk.getAppName();
  document.title = name;
  document.querySelector('h1').textContent = name;
}

Uygulama adΔ±nΔ± ve sΓΌrΓΌmΓΌnΓΌ belirten bir destek e-postasΔ± oluΕturun:

function supportMailto() {
  const b = window.WebToApk;
  const name = b && b.getAppName ? b.getAppName() : 'our app';
  const version = b && b.getAppVersion ? b.getAppVersion() : 'web';
  const subject = encodeURIComponent('Help with ' + name + ' ' + version);
  return 'mailto:[email protected]?subject=' + subject;
}
document.getElementById('contact').href = supportMailto();

Notlar: Bu, sayfanΔ±n <title> deΔil, sihirbazdaki addΔ±r. Γalışma sΔ±rasΔ±nda deΔiΕmez. SΓΌrΓΌm iΓ§in Εunu kullanΔ±n: getAppVersion().

getAppVersion köprü#

window.WebToApk.getAppVersion(): String

YΓΌklΓΌ paketin versionName deΔeri, ΓΆr. "1.4.2"; derleme sΔ±rasΔ±nda oluΕturucunun belirlediΔi dizedir, versionCode deΔil. Paket okunamazsa "1.0" deΔerine dΓΆner; bΓΆylece bir sayfa onu null kontrolΓΌ olmadan yazdΔ±rabilir.

Örnek

YΓΌklΓΌ uygulamanΔ±n sΓΌrΓΌm adΔ±nΔ± verir, ΓΆrneΔin "1.4.2".

DΓΆner deΔer: hemen bir dize. Gerekenler: hiΓ§bir Εey.

SΓΌrΓΌmΓΌ HakkΔ±nda ekranΔ±nda gΓΆsterin.

var version = (window.WebToApk && window.WebToApk.getAppVersion)
  ? WebToApk.getAppVersion()
  : 'web';

document.getElementById('about').textContent = 'Version ' + version;

YΓΌklΓΌ sΓΌrΓΌm eskiyse gΓΌncelleme isteyin:

function isOlder(a, b) {            // "1.4.2" < "1.10.0"
  const x = a.split('.').map(Number), y = b.split('.').map(Number);
  for (let i = 0; i < Math.max(x.length, y.length); i++) {
    if ((x[i] || 0) !== (y[i] || 0)) return (x[i] || 0) < (y[i] || 0);
  }
  return false;
}

if (window.WebToApk && WebToApk.getAppVersion && isOlder(WebToApk.getAppVersion(), '1.5.0')) {
  document.getElementById('update-banner').hidden = false;
}

Bir hata raporuna getDeviceInfo() iΓ§indeki sΓΌrΓΌmΓΌ ve sΓΌrΓΌm kodunu ekleyin:

function reportInfo() {
  const b = window.WebToApk;
  if (!(b && b.getAppVersion)) return 'web';
  let text = 'v' + b.getAppVersion();
  if (b.getDeviceInfo) {
    const info = JSON.parse(b.getDeviceInfo());
    text += ' (build ' + info.app.versionCode + ')';
  }
  return text;
}
document.getElementById('report').value = reportInfo();

Notlar: Bu, derleme sΔ±rasΔ±nda belirlediΔiniz sΓΌrΓΌm ADIDIR, sΓΌrΓΌm kodu deΔil. Paket okunamazsa "1.0" dΓΆner; bu yΓΌzden asla boΕ deΔildir. SΓΌrΓΌm kodu ve kurulum tarihleri getDeviceInfo() iΓ§inde, Εu baΕlΔ±k altΔ±ndadΔ±r: app.

getDeviceInfo köprü#

window.WebToApk.getDeviceInfo(): String

WebToApk.getDeviceInfo() -> JSON dizesi, izin GEREKMEZ WebToApk.getPhoneIdentity() -> operatΓΆr/SIM, Telefon izni gerekir WebToApk.getInstallId() -> kurulum baΕΔ±na kararlΔ± UUID

Kendi baΕΔ±na deΔil, grubuyla birlikte anlatΔ±lΔ±r: Cihaz / uygulama bilgileri.

Örnek

Telefon, Android, ekran, uygulamanΔ±z ve mevcut durum (pil, bellek, aΔ) hakkΔ±ndaki bilgileri tek Γ§aΔrΔ±da verir.

DΓΆner deΔer: hemen bir JSON dizesi; JSON.parse kullanΔ±n. ΓΓΆst dΓΌzey anahtarlar: android, hardware, screen, app, runtime, installId. Gerekenler: hiΓ§bir Εey; izin yok.

Telefon bilgilerini gΓΆsterin.

function showPhoneInfo() {
  if (!(window.WebToApk && window.WebToApk.getDeviceInfo)) {
    document.getElementById('out').textContent = navigator.userAgent;   // browser: only the user agent
    return;
  }
  var info = JSON.parse(WebToApk.getDeviceInfo());

  document.getElementById('out').innerHTML =
    'Android: ' + info.android.release + ' (SDK ' + info.android.sdkInt + ')<br>' +
    'Version ID: ' + info.android.buildId + '<br>' +
    'Build number: ' + info.android.incremental + '<br>' +
    'Phone: ' + info.hardware.manufacturer + ' ' + info.hardware.model + '<br>' +
    'Screen: ' + info.screen.widthPx + 'x' + info.screen.heightPx + ' @' + info.screen.refreshRate + 'Hz<br>' +
    'Battery: ' + info.runtime.batteryLevel + '%' + (info.runtime.charging ? ' (charging)' : '') + '<br>' +
    'Network: ' + info.runtime.networkType + '<br>' +
    'App: ' + info.app.versionName + ' (' + info.app.versionCode + ')<br>' +
    'WebView: ' + info.app.webViewPackage;
}

TΓΌm anahtarlar:

{
  "android":  { "release": "14", "sdkInt": 34, "codename": "REL", "buildId": "UP1A.231005.007", "incremental": "R.1a2b3c", "display": "…", "fingerprint": "…", "securityPatch": "2026-08-05" },
  "hardware": { "manufacturer": "OPPO", "brand": "OPPO", "model": "CPH2269", "device": "…", "product": "…", "board": "…", "hardware": "…", "abis": ["arm64-v8a"], "socManufacturer": "…", "socModel": "…", "isEmulator": false },
  "screen":   { "widthPx": 720, "heightPx": 1600, "density": 2, "densityDpi": 320, "refreshRate": 90, "nightMode": true },
  "app":      { "packageName": "com.example.app", "versionName": "1.0", "versionCode": 1, "targetSdk": 35, "firstInstallTime": 1737000000000, "lastUpdateTime": 1737000000000, "installer": "com.android.vending", "webViewPackage": "com.google.android.webview 131.0" },
  "runtime":  { "totalMemoryBytes": 4000000000, "availableMemoryBytes": 1500000000, "lowMemory": false, "storageFreeBytes": 20000000000, "storageTotalBytes": 64000000000, "batteryLevel": 80, "charging": false, "locale": "en-IN", "timezone": "Asia/Kolkata", "networkType": "wifi" },
  "installId": "3f2b8c1e-…"
}

Depolama veya belleΔi az olan bir telefonda uyarΔ±n; bΓΌyΓΌk bir indirmeden ΓΆnce.

function canDownload(bytesNeeded) {
  if (!(window.WebToApk && WebToApk.getDeviceInfo)) return true;   // browser: let the download try
  const r = JSON.parse(WebToApk.getDeviceInfo()).runtime;
  if (r.storageFreeBytes < bytesNeeded * 2) { showMessage('Not enough free storage.'); return false; }
  if (r.lowMemory) showMessage('The phone is low on memory; this may be slow.');
  return true;
}

Notlar: socManufacturer / socModel yalnΔ±zca Android 12+ sΓΌrΓΌmΓΌnde vardΔ±r. networkType, wifi, cellular, ethernet, other, none veya unknown olur. installer, Play kurulumlarΔ± iΓ§in com.android.vending, yandan yΓΌklemelerde boΕtur. DeΔerler Γ§aΔΔ±rdΔ±ΔΔ±nΔ±z anda okunur; pili veya aΔΔ± yenilemek iΓ§in yeniden Γ§aΔΔ±rΔ±n. IMEI veya seri numarasΔ± yoktur: Android bunlarΔ± tΓΌm uygulamalara kapatΔ±r; Εunu kullanΔ±n: getInstallId().

getInstallId köprü#

window.WebToApk.getInstallId(): String

WebToApk.getDeviceInfo() -> JSON dizesi, izin GEREKMEZ WebToApk.getPhoneIdentity() -> operatΓΆr/SIM, Telefon izni gerekir WebToApk.getInstallId() -> kurulum baΕΔ±na kararlΔ± UUID

Kendi baΕΔ±na deΔil, grubuyla birlikte anlatΔ±lΔ±r: Cihaz / uygulama bilgileri.

Örnek

UygulamanΔ±zΔ±n bu kurulumu iΓ§in aynΔ± kalan rastgele bir kimlik verir.

DΓΆner deΔer: hemen bir dize ("3f2b…-…" gibi bir UUID). Gerekenler: hiΓ§bir Εey.

Sunucunuz iΓ§in veya "cihaz baΕΔ±na bir ΓΌcretsiz deneme" kontrolΓΌ iΓ§in bir cihaz anahtarΔ± olarak kullanΔ±n.

function deviceKey() {
  if (window.WebToApk && window.WebToApk.getInstallId) {
    return WebToApk.getInstallId();
  }
  // In a browser: keep your own random ID in localStorage (per browser, cleared with site data).
  var k = localStorage.getItem('installId');
  if (!k) {
    k = 'w-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
    localStorage.setItem('installId', k);
  }
  return k;
}

fetch('https://api.example.com/register', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ device: deviceKey() })
});

KimliΔi sunucunuza bir baΕlΔ±kla gΓΆnderin ve URL'nin dΔ±ΕΔ±nda tutun.

async function apiGet(path) {
  const id = (window.WebToApk && WebToApk.getInstallId) ? WebToApk.getInstallId() : null;
  const res = await fetch('https://api.example.com' + path, {
    headers: id ? { 'X-Install-Id': id } : {}
  });
  return res.json();
}

ΓΓΆkme raporlarΔ±na ekleyin; bΓΆylece bir kurulumun sorunlarΔ± bulunabilir.

if (window.WebToApk && WebToApk.setCrashUserId && WebToApk.getInstallId) {
  // returns false when Crash reports are off or the id has other characters
  const ok = WebToApk.setCrashUserId(WebToApk.getInstallId());
  if (!ok) console.log('crash user id not set');
}

Notlar: Kimlik ilk kullanΔ±mda oluΕturulur ve uygulamanΔ±n depolama alanΔ±nda tutulur. Uygulama gΓΌncellemelerinden sonra aynΔ± kalΔ±r. KullanΔ±cΔ± uygulamayΔ± kaldΔ±rΔ±rsa veya verilerini temizlerse deΔiΕir. Bir donanΔ±m kimliΔi deΔildir; Android uygulamalara IMEI veya seri numarasΔ± vermez. AynΔ± deΔer getDeviceInfo() iΓ§inde de installId.

getPhoneIdentity köprü#

window.WebToApk.getPhoneIdentity(): String

WebToApk.getDeviceInfo() -> JSON dizesi, izin GEREKMEZ WebToApk.getPhoneIdentity() -> operatΓΆr/SIM, Telefon izni gerekir WebToApk.getInstallId() -> kurulum baΕΔ±na kararlΔ± UUID

Kendi baΕΔ±na deΔil, grubuyla birlikte anlatΔ±lΔ±r: Cihaz / uygulama bilgileri.

Örnek

Mobil operatΓΆr ve SIM bilgilerini verir: operatΓΆr adΔ±, SIM ΓΌlkesi, operatΓΆr kodu, telefon tΓΌrΓΌ ve SIM durumu.

DΓΆner deΔer: hemen bir JSON dizesi; JSON.parse kullanΔ±n: { carrierName, simCountryIso, networkCountryIso, simOperator, phoneType, simState, hardwareIdAvailable: false } veya { error }. Gerekenler: derleme yaparken AdΔ±m 4'te (EriΕim) Telefon seΓ§eneΔini aΓ§Δ±n; aksi halde Εunu dΓΆndΓΌrΓΌr: { "error": "not_enabled" }.

OperatΓΆrΓΌ gΓΆsterin ve ΓΌlke kodunu seΓ§in.

function showCarrier() {
  if (!(window.WebToApk && window.WebToApk.getPhoneIdentity)) return;
  var p = JSON.parse(WebToApk.getPhoneIdentity());

  if (p.error === 'not_enabled') { showMessage('Turn on Phone when you build the app'); return; }
  if (p.error) { showMessage('Could not read SIM info: ' + p.error); return; }

  if (p.simState !== 'ready') {
    showMessage('No SIM ready (' + p.simState + ')');   // absent | pin_required | network_locked | unknown
    return;
  }
  document.getElementById('carrier').textContent = p.carrierName;                 // e.g. "Jio 4G"
  document.getElementById('country').value = p.simCountryIso.toUpperCase();        // e.g. "IN"
}

Bir kayΔ±t formu iΓ§in SIM'in ΓΌlkesinden bir arama kodu seΓ§in.

const DIAL = { IN: '+91', US: '+1', GB: '+44', DE: '+49' };
function defaultDialCode() {
  if (!(window.WebToApk && WebToApk.getPhoneIdentity)) return '+1';
  const p = JSON.parse(WebToApk.getPhoneIdentity());
  if (p.error || p.simState !== 'ready') return '+1';
  return DIAL[p.simCountryIso.toUpperCase()] || '+1';
}
document.getElementById('dial').value = defaultDialCode();

YalnΔ±zca Wi-Fi'li bir tableti telefondan ayΔ±rt edin: phoneType.

if (window.WebToApk && WebToApk.getPhoneIdentity) {
  const p = JSON.parse(WebToApk.getPhoneIdentity());
  if (!p.error && p.phoneType === 'none') {
    document.getElementById('sms-login').hidden = true;   // no cellular radio: hide SMS sign-in
  }
}

Notlar: phoneType, gsm, cdma, sip veya none olur (yalnΔ±zca Wi-Fi'li tabletler). simOperator, MCC+MNC kodudur (ΓΆr. "40445"). IMEI, MEID veya seri numarasΔ± yoktur; hardwareIdAvailable her zaman false olur, Γ§ΓΌnkΓΌ Android 10'dan beri tΓΌm uygulamalardan gizler; kararlΔ± bir kimlik iΓ§in getInstallId() kullanΔ±n. Telefon anahtarΔ± Play sayfanΔ±za hassas bir izin ekler; ne iΓ§in kullandΔ±ΔΔ±nΔ±zΔ± Veri gΓΌvenliΔi formunda belirtin.

isTv köprü#

window.WebToApk.isTv(): Boolean

Uygulama bir televizyonda (Android TV, Google TV, Fire TV) Γ§alΔ±ΕΔ±yorsa doΔrudur.

Örnek

Sayfaya uygulamanΔ±n bir televizyonda (Android TV, Google TV, Fire TV) Γ§alΔ±Εtattigini bildirir; bΓΆylece uzaktan kumandaya uygun bir dΓΌzen gΓΆsterebilir.

DΓΆner deΔer: true veya false (eΕzamanlΔ± olarak). Gerekenler: hiΓ§bir Εey. SihirbazdΔ± Android TV desteΔi aΓ§Δ±kken uygulama TV baΕlatΔ±cΔ±sΔ±nda bir TV afiΕi de alΔ±r ve TV'de sayfa net bir odak halkasΔ± alΔ±r; baΔlantΔ±lar, dΓΌΔmeler, alanlar ve onclick ya da dΓΌΔme rolΓΌyle baΔlanmΔ±Ε ΓΆΔeler kumandanΔ±n oklarΔ±yla eriΕilebilir olur; OK odaktaki ΓΆΔeye tΔ±klar ve Geri telefondaki gibi Γ§alΔ±ΕΔ±r.

var onTv = !!(window.WebToApk && typeof window.WebToApk.isTv === 'function' && window.WebToApk.isTv());
if (onTv) {
  document.documentElement.classList.add('tv');     /* bigger text, no hover-only menus */
  document.querySelector('#play').focus();           /* start the remote on the main action */
}
html.tv body { font-size: 22px; }
html.tv :focus-visible { transform: scale(1.05); }

TV iΓ§in farklΔ± bir ana ekran seΓ§in ve telefon dΓΌzenini varsayΔ±lan olarak koruyun.

function isTv() {
  return !!(window.WebToApk && typeof WebToApk.isTv === 'function' && WebToApk.isTv());
}
history.replaceState({}, '', isTv() ? '/tv-home' : '/home');
if (isTv()) document.querySelectorAll('.touch-only').forEach(function (el) { el.hidden = true; });

Notlar: Android TV emΓΌlatΓΆrΓΌ ve D-pad ile test edin. TV'lerde Google Play'de yayΔ±nlamak ayrΔ± bir adΔ±mdΔ±r: Play Console'da uygulamayΔ± Android TV form faktΓΆrΓΌne dahil edin (SΓΌrΓΌm β GeliΕmiΕ ayarlar β Form faktΓΆrleri) ve bir TV ekran gΓΆrΓΌntΓΌsΓΌ ekleyin.

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.