File size: 5,969 Bytes
3a08226 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | // 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' },
];
} |