import {validateChannels,validatePreview} from '/airtap/channels.mjs'; // Share the canonical validator with official Android pages. Never manufacture // a public APK URL from a private candidate or a source/package version. const root = document.querySelector('[data-android-release]'); const link = root?.querySelector('[data-android-download]'); const status = root?.querySelector('[data-android-status]'); let release = null; let pending = null; const qrEntry=new URL(location.href),qrPlatform=qrEntry.searchParams.get('airtap'); let autoDownload=qrPlatform==='android'&&qrEntry.searchParams.get('download')==='1'; if(['android','ios'].includes(qrPlatform)){ qrEntry.searchParams.delete('airtap');qrEntry.searchParams.delete('download'); try{history.replaceState(null,'',qrEntry.pathname+qrEntry.search+qrEntry.hash);}catch{/* Browser history is optional. */} } const text = (fr,en)=>document.documentElement.lang==='en'?en:fr; function render(){ if(!link || !status)return; if(qrPlatform==='ios'){ const title=root.querySelector('h3'),platform=root.querySelector('.app-platform'); if(title)title.textContent=text('AirTap pour iPhone / iPad','AirTap for iPhone / iPad'); if(platform)platform.textContent=text('iOS / iPadOS 15+ · Version en attente','iOS / iPadOS 15+ · Version on hold'); link.href=release?.ios.available?release.ios.url:'/airtap/ios/';link.removeAttribute('download'); link.textContent=release?.ios.available?text('Télécharger sur l’App Store','Download on the App Store'):text('Voir la disponibilité iPhone / iPad','Check iPhone / iPad availability'); status.textContent=release?.ios.available?text('Version ','Version ')+release.ios.version:text('Version iPhone / iPad en attente. Aucun téléchargement Apple disponible actuellement.','iPhone / iPad version on hold. No Apple download is available yet.'); return; } link.href='/airtap/android/'; link.removeAttribute('download'); if(release?.android.available){ const item=release.android; link.href=item.url; link.setAttribute('download',`AirTap-${item.version}-arm64.apk`); link.textContent=text('Télécharger l’APK Android','Download Android APK'); status.textContent=text('Version ','Version ')+item.version+' · '+(item.bytes/1048576).toFixed(1)+text(' Mo · Android 10+, ARM64',' MB · Android 10+, ARM64'); if(item.channel==='preview')status.textContent+=text(' · Version d’essai — tests de connexion en cours',' · Preview — connection testing in progress'); }else{ link.textContent=text('Téléchargement Android','Android download'); status.textContent=release?text('La livraison Android est en cours de validation. Retrouvez sa disponibilité sur la page officielle.','The Android release is being validated. Check availability on the official page.'):text('Disponibilité à vérifier sur la page officielle Android.','Check availability on the official Android page.'); } } function handleInstallationQr(){ if(!root||!status)return; if(qrPlatform==='ios'){ status.textContent=text('iPhone / iPad : version en attente. Aucun téléchargement Apple disponible actuellement.','iPhone / iPad: version on hold. No Apple download is available yet.'); return; } if(!autoDownload)return;autoDownload=false; // Only the exact public, qualified first-party APK may be reached. A QR // navigation starts a download on Android; it never starts an installation. if(!/Android/i.test(navigator.userAgent)){ status.textContent=text('Ouvrez ce QR sur votre téléphone Android pour télécharger AirTap.','Open this QR on your Android phone to download AirTap.');return; } if(!release?.android.available)return; const guide=root.querySelector('[data-android-install]');if(guide)guide.hidden=false; location.assign(release.android.url); } async function refresh(){ if(pending)return pending; pending=(async()=>{ const abort=new AbortController(); const timer=setTimeout(()=>abort.abort(),5000); try{ const response=await fetch('/airtap/release.json',{cache:'no-store',credentials:'omit',referrerPolicy:'no-referrer',signal:abort.signal}); if(!response.ok)throw Error('unavailable'); const raw=await response.text(); if(raw.length>4096)throw Error('oversized'); release=validateChannels(JSON.parse(raw)); if(release && !release.android.available){ const previewResponse=await fetch('/airtap/preview.json',{cache:'no-store',credentials:'omit',referrerPolicy:'no-referrer',signal:abort.signal}); if(previewResponse.ok){const previewRaw=await previewResponse.text();if(previewRaw.length<=4096){const preview=validatePreview(JSON.parse(previewRaw));if(preview)release.android=preview;}} } }catch(_){release=null;}finally{clearTimeout(timer);render();pending=null;} })(); return pending; } if(link && status){ link.addEventListener('click',async event=>{ event.preventDefault(); if(link.getAttribute('aria-busy')==='true')return; link.setAttribute('aria-busy','true'); try{ await refresh(); if(qrPlatform==='ios'){location.assign(release?.ios.available?release.ios.url:'/airtap/ios/');return;} const ready=release?.android.available; const guide=root.querySelector('[data-android-install]'); if(guide)guide.hidden=!ready; // Explicit user gesture only. The browser cannot start the package // installer or observe completion; Android keeps its confirmation step. location.assign(ready?release.android.url:'/airtap/android/'); }finally{link.removeAttribute('aria-busy');} }); render();void refresh().then(handleInstallationQr); new MutationObserver(render).observe(document.documentElement,{attributes:true,attributeFilter:['lang']}); window.addEventListener('online',refresh); }