fix : mise à jour in-app sans quitter l'application
- @capacitor/filesystem : téléchargement natif de l'APK (OkHttp) - @capawesome-team/capacitor-file-opener : ouverture de l'installateur Android - L'APK est écrit dans le cache de l'app puis ouvert via un Intent natif - Plus de passage par le navigateur — tout se passe dans l'app - file_paths.xml mis à jour pour couvrir le cache
This commit is contained in:
parent
208ece19fc
commit
3ecd57119f
7 changed files with 150 additions and 48 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { Capacitor } from '@capacitor/core'
|
||||
import { Capacitor, CapacitorHttp } from '@capacitor/core'
|
||||
import { Filesystem, Directory } from '@capacitor/filesystem'
|
||||
import { FileOpener } from '@capawesome-team/capacitor-file-opener'
|
||||
import { getBaseUrl } from './config'
|
||||
|
||||
export interface UpdateInfo {
|
||||
|
|
@ -23,8 +25,7 @@ function compareVersions(a: string, b: string): number {
|
|||
|
||||
/**
|
||||
* Vérifie si une mise à jour est disponible sur le serveur.
|
||||
* Ne retourne que sur Android natif (Capacitor) : la PWA iOS se met à jour
|
||||
* en rechargeant simplement la page.
|
||||
* Seulement sur Android natif : la PWA iOS se met à jour en rechargeant.
|
||||
*/
|
||||
export async function checkForUpdate(): Promise<UpdateInfo | null> {
|
||||
if (!Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android') {
|
||||
|
|
@ -40,17 +41,13 @@ export async function checkForUpdate(): Promise<UpdateInfo | null> {
|
|||
if (!response.ok) return null
|
||||
|
||||
const remote = (await response.json()) as UpdateInfo
|
||||
|
||||
// Le serveur est plus vieux ou à jour : rien à faire.
|
||||
if (compareVersions(remote.version, localVersion) <= 0) return null
|
||||
|
||||
// L'utilisateur a déjà ignoré cette version-là.
|
||||
const dismissed = localStorage.getItem('meteo-exam.dismissed-update')
|
||||
if (dismissed === remote.version) return null
|
||||
|
||||
return remote
|
||||
} catch {
|
||||
// Serveur injoignable : on ne bloque pas le démarrage de l'app.
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -62,17 +59,55 @@ export function dismissUpdate(version: string): void {
|
|||
}
|
||||
|
||||
/**
|
||||
* Déclenche le téléchargement de l'APK. Sur Android, le WebView enregistre
|
||||
* le fichier et l'utilisateur ouvre la notification pour installer.
|
||||
* Télécharge l'APK via le réseau natif (OkHttp), l'enregistre dans le cache
|
||||
* de l'app puis ouvre l'installateur Android — sans quitter l'application.
|
||||
*/
|
||||
export function downloadApk(apkUrl: string): void {
|
||||
export async function downloadAndInstallApk(apkUrl: string): Promise<void> {
|
||||
const url = apkUrl.startsWith('http') ? apkUrl : `${getBaseUrl()}${apkUrl}`
|
||||
const anchor = document.createElement('a')
|
||||
anchor.href = url
|
||||
anchor.download = 'meteo-exam.apk'
|
||||
anchor.rel = 'noopener'
|
||||
anchor.style.display = 'none'
|
||||
document.body.append(anchor)
|
||||
anchor.click()
|
||||
anchor.remove()
|
||||
|
||||
// 1. Télécharger le binaire via CapacitorHttp (natif, pas de WebView).
|
||||
const response = await CapacitorHttp.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Téléchargement impossible (HTTP ${response.status})`)
|
||||
}
|
||||
|
||||
// 2. Écrire le fichier dans le cache de l'app.
|
||||
// CapacitorHttp renvoie les données en base64 pour responseType arraybuffer.
|
||||
const data = response.data as string
|
||||
const binary = atob(data)
|
||||
const bytes = new Uint8Array(binary.length)
|
||||
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
|
||||
|
||||
// Convertir en base64 pour Capacitor Filesystem.
|
||||
let base64 = ''
|
||||
const chunk = 0x8000
|
||||
for (let i = 0; i < bytes.length; i += chunk) {
|
||||
base64 += String.fromCharCode(...bytes.subarray(i, i + chunk))
|
||||
}
|
||||
const b64 = btoa(base64)
|
||||
|
||||
const fileName = `meteo-exam-${__APP_VERSION__}-update.apk`
|
||||
await Filesystem.writeFile({
|
||||
path: fileName,
|
||||
data: b64,
|
||||
directory: Directory.Cache,
|
||||
})
|
||||
|
||||
// 3. Lire le chemin réel du fichier sur le système de fichiers.
|
||||
const { uri } = await Filesystem.getUri({
|
||||
path: fileName,
|
||||
directory: Directory.Cache,
|
||||
})
|
||||
|
||||
// 4. Ouvrir l'installateur Android avec ce fichier APK.
|
||||
// Android affiche l'écran d'installation par-dessus l'app.
|
||||
await FileOpener.openFile({
|
||||
path: uri,
|
||||
mimeType: 'application/vnd.android.package-archive',
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue