// App Data Worker - Fetches installed apps from Android let appCache = []; let lastUpdate = 0; self.addEventListener('message', async (e) => { const { action, filter } = e.data; switch (action) { case 'FETCH_ALL': const apps = await fetchInstalledApps(); self.postMessage({ type: 'APPS_LOADED', apps, count: apps.length }); break; case 'FETCH_CATEGORIES': const categories = await fetchAppCategories(); self.postMessage({ type: 'CATEGORIES_LOADED', categories }); break; case 'LAUNCH_APP': await launchApp(filter.package); break; case 'GET_APP_INFO': const info = await getAppInfo(filter.package); self.postMessage({ type: 'APP_INFO', info }); break; case 'SEARCH': const results = searchApps(filter.query); self.postMessage({ type: 'SEARCH_RESULTS', results }); break; } }); async function fetchInstalledApps() { const now = Date.now(); // Return cached if recent (< 5 seconds) if (now - lastUpdate < 5000 && appCache.length > 0) { return appCache; } let apps = []; // Try Android interface first if (self.Android?.PackageManager?.getInstalledApps) { apps = JSON.parse(self.Android.PackageManager.getInstalledApps()); } else { // Fallback: Generate from localStorage or defaults apps = getFallbackApps(); } // Parallel icon fetching const withIcons = await Promise.all( apps.map(async (app) => ({ ...app, hasIcon: await checkIcon(app.package), iconUrl: `/api/android/icon/${app.package}` })) ); appCache = withIcons; lastUpdate = now; return withIcons; } async function fetchAppCategories() { const apps = await fetchInstalledApps(); const categories = {}; apps.forEach(app => { const cat = app.category || 'uncategorized'; if (!categories[cat]) categories[cat] = []; categories[cat].push(app); }); return categories; } function searchApps(query) { const q = query.toLowerCase(); return appCache.filter(app => app.name.toLowerCase().includes(q) || app.package.toLowerCase().includes(q) ); } async function launchApp(packageName) { if (self.Android?.Intent?.launch) { self.Android.Intent.launch(packageName); return true; } // PWA fallback - try to open as URL scheme try { window.open(`android-app://${packageName}`, '_blank'); } catch (e) { self.postMessage({ type: 'LAUNCH_FAILED', package: packageName }); } } async function getAppInfo(packageName) { const app = appCache.find(a => a.package === packageName); if (app) return app; // Fetch fresh if (self.Android?.PackageManager?.getAppInfo) { return JSON.parse(self.Android.PackageManager.getAppInfo(packageName)); } return null; } async function checkIcon(packageName) { // Check if icon exists in cache return true; // Simplified for worker } function getFallbackApps() { // Comprehensive default app list for Samsung S23 Ultra return [ // System { name: 'Settings', package: 'com.android.settings', category: 'system', icon: 'settings' }, { name: 'Files', package: 'com.android.documentsui', category: 'system', icon: 'folder' }, { name: 'Calculator', package: 'com.google.android.calculator', category: 'system', icon: 'hash' }, { name: 'Calendar', package: 'com.samsung.android.calendar', category: 'system', icon: 'calendar' }, { name: 'Clock', package: 'com.sec.android.app.clockpackage', category: 'system', icon: 'clock' }, { name: 'Weather', package: 'com.sec.android.daemonapp', category: 'system', icon: 'cloud' }, { name: 'Samsung Notes', package: 'com.samsung.android.app.notes', category: 'system', icon: 'file-text' }, { name: 'SmartThings', package: 'com.samsung.android.oneconnect', category: 'system', icon: 'home' }, { name: 'Device Care', package: 'com.samsung.android.lool', category: 'system', icon: 'shield' }, { name: 'Gallery', package: 'com.samsung.android.gallery3d', category: 'media', icon: 'image' }, { name: 'Camera', package: 'com.sec.android.app.camera', category: 'media', icon: 'camera' }, { name: 'Voice Recorder', package: 'com.sec.android.app.voicenote', category: 'media', icon: 'mic' }, // Communication { name: 'Phone', package: 'com.samsung.android.dialer', category: 'communication', icon: 'phone' }, { name: 'Messages', package: 'com.samsung.android.messaging', category: 'communication', icon: 'message-square' }, { name: 'Contacts', package: 'com.samsung.android.app.contacts', category: 'communication', icon: 'users' }, { name: 'Gmail', package: 'com.google.android.gm', category: 'communication', icon: 'mail' }, // Internet { name: 'Samsung Internet', package: 'com.sec.android.app.sbrowser', category: 'internet', icon: 'globe' }, { name: 'Chrome', package: 'com.android.chrome', category: 'internet', icon: 'chrome' }, // Media { name: 'Spotify', package: 'com.spotify.music', category: 'media', icon: 'music' }, { name: 'YouTube', package: 'com.google.android.youtube', category: 'media', icon: 'youtube' }, { name: 'Netflix', package: 'com.netflix.mediaclient', category: 'media', icon: 'film' }, { name: 'Samsung Music', package: 'com.sec.android.app.music', category: 'media', icon: 'headphones' }, // Productivity { name: 'Google Drive', package: 'com.google.android.apps.docs', category: 'productivity', icon: 'hard-drive' }, { name: 'Samsung Wallet', package: 'com.samsung.android.spay', category: 'productivity', icon: 'credit-card' }, { name: 'Maps', package: 'com.google.android.apps.maps', category: 'productivity', icon: 'map' }, // Social { name: 'Samsung Members', package: 'com.samsung.android.voc', category: 'social', icon: 'users' }, // Development { name: 'Termux', package: 'com.termux', category: 'dev', icon: 'terminal' }, ]; }