useAnimation drives shader uniforms via Reanimated frame callbacks. useGyroscope accumulates device rotation with spring damping. useWallpaperExport bridges to native modules with platform detection and Expo Go fallback.
148 lines
4.2 KiB
TypeScript
148 lines
4.2 KiB
TypeScript
import { useState, useCallback } from "react";
|
|
import { Platform, Alert } from "react-native";
|
|
import type { WallpaperConfig } from "@/types/wallpaper";
|
|
|
|
type ExportStatus = "idle" | "preparing" | "exporting" | "success" | "error";
|
|
|
|
interface ExportResult {
|
|
status: ExportStatus;
|
|
progress: number;
|
|
error: string | null;
|
|
exportAsWallpaper: (config: WallpaperConfig) => Promise<void>;
|
|
}
|
|
|
|
export function useWallpaperExport(): ExportResult {
|
|
const [status, setStatus] = useState<ExportStatus>("idle");
|
|
const [progress, setProgress] = useState(0);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const exportAsWallpaper = useCallback(async (config: WallpaperConfig) => {
|
|
try {
|
|
setStatus("preparing");
|
|
setProgress(0);
|
|
setError(null);
|
|
|
|
if (Platform.OS === "android") {
|
|
await exportAndroid(config, setProgress, setStatus);
|
|
} else if (Platform.OS === "ios") {
|
|
await exportIOS(config, setProgress, setStatus);
|
|
} else {
|
|
throw new Error("Platform non supportée. Utilisez Android ou iOS.");
|
|
}
|
|
|
|
setStatus("success");
|
|
setProgress(1);
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : "Échec de l'export";
|
|
setError(message);
|
|
setStatus("error");
|
|
Alert.alert("Erreur d'export", message);
|
|
}
|
|
}, []);
|
|
|
|
return { status, progress, error, exportAsWallpaper };
|
|
}
|
|
|
|
async function exportAndroid(
|
|
config: WallpaperConfig,
|
|
onProgress: (p: number) => void,
|
|
onStatus: (s: ExportStatus) => void
|
|
) {
|
|
// Lazy-load native module (only available on Android device builds)
|
|
let WallpaperAndroid: typeof import("../../modules/wallpaper-android/src/WallpaperAndroidModule").WallpaperAndroid;
|
|
try {
|
|
WallpaperAndroid =
|
|
require("../../modules/wallpaper-android/src/WallpaperAndroidModule").WallpaperAndroid;
|
|
} catch {
|
|
Alert.alert(
|
|
"Module non disponible",
|
|
"Le module natif Android n'est pas disponible dans Expo Go. Créez un development build avec `npx expo run:android`.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check support
|
|
const supported = WallpaperAndroid.isLiveWallpaperSupported();
|
|
if (!supported) {
|
|
throw new Error("Les live wallpapers ne sont pas supportés sur cet appareil.");
|
|
}
|
|
|
|
onProgress(0.2);
|
|
onStatus("exporting");
|
|
|
|
// Save config to SharedPreferences (read by LiveWallpaperService)
|
|
const configJson = JSON.stringify({
|
|
sourceUri: config.sourceUri,
|
|
depthMapUri: config.depthMapUri ?? "",
|
|
animation: config.animation,
|
|
uniforms: config.uniforms,
|
|
fps: config.fps,
|
|
});
|
|
|
|
await WallpaperAndroid.saveConfig(configJson);
|
|
onProgress(0.6);
|
|
|
|
// Launch system wallpaper picker
|
|
await WallpaperAndroid.setLiveWallpaper();
|
|
onProgress(1.0);
|
|
}
|
|
|
|
async function exportIOS(
|
|
config: WallpaperConfig,
|
|
onProgress: (p: number) => void,
|
|
onStatus: (s: ExportStatus) => void
|
|
) {
|
|
let LivePhoto: typeof import("../../modules/livephoto-ios/src/LivePhotoModule").LivePhoto;
|
|
try {
|
|
LivePhoto =
|
|
require("../../modules/livephoto-ios/src/LivePhotoModule").LivePhoto;
|
|
} catch {
|
|
Alert.alert(
|
|
"Module non disponible",
|
|
"Le module natif iOS n'est pas disponible dans Expo Go. Créez un development build avec `npx expo run:ios`.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check/request photo library permission
|
|
let permission = await LivePhoto.checkPermission();
|
|
if (permission === "undetermined") {
|
|
permission = await LivePhoto.requestPermission();
|
|
}
|
|
if (permission !== "granted") {
|
|
throw new Error(
|
|
"Accès à la photothèque refusé. Activez-le dans Réglages > Lively > Photos."
|
|
);
|
|
}
|
|
|
|
onProgress(0.1);
|
|
onStatus("exporting");
|
|
|
|
const configJson = JSON.stringify({
|
|
sourceUri: config.sourceUri,
|
|
depthMapUri: config.depthMapUri ?? "",
|
|
animation: config.animation,
|
|
uniforms: config.uniforms,
|
|
fps: config.fps,
|
|
});
|
|
|
|
onProgress(0.3);
|
|
|
|
// Generate Live Photo (HEIC + MOV) and save to photo library
|
|
await LivePhoto.exportLivePhoto(configJson);
|
|
|
|
onProgress(0.9);
|
|
|
|
// Guide user to apply the wallpaper
|
|
Alert.alert(
|
|
"Live Photo sauvegardée",
|
|
"Pour l'appliquer comme fond d'écran :\n\n" +
|
|
"Réglages > Fond d'écran > Ajouter\n" +
|
|
"> Photos > Sélectionnez votre Live Photo\n" +
|
|
"> Activez « Live Photo »",
|
|
[{ text: "OK" }]
|
|
);
|
|
|
|
onProgress(1.0);
|
|
}
|