diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index d55a393..64d8465 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -354,7 +354,11 @@ class AppsPageState extends State { SliverFillRemaining( child: Center( child: Text( - appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'), + appsProvider.apps.isEmpty + ? appsProvider.loadingApps + ? tr('pleaseWait') + : tr('noApps') + : tr('noAppsForFilter'), style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ))), @@ -419,7 +423,9 @@ class AppsPageState extends State { child: Image( image: const AssetImage( 'assets/graphics/icon_small.png'), - color: Colors.white.withOpacity(0.3), + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.4) + : Colors.white.withOpacity(0.3), colorBlendMode: BlendMode.modulate, gaplessPlayback: true, ), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 6542f7c..6674557 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -329,6 +329,10 @@ Future> getHeaders(String url, return returnHeaders; } +Future> getAllInstalledInfo() async { + return await pm.getInstalledPackages() ?? []; +} + Future getInstalledInfo(String? packageName, {bool printErr = true}) async { if (packageName != null) { @@ -1160,8 +1164,9 @@ class AppsProvider with ChangeNotifier { : false; } - Future updateInstallStatusInMemory(AppInMemory app) async { - apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id); + Future updateInstallStatusInMemory( + AppInMemory app, PackageInfo? installedInfo) async { + apps[app.app.id]?.installedInfo = installedInfo; apps[app.app.id]?.icon = await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon(); apps[app.app.id]?.app.name = await (apps[app.app.id] @@ -1179,6 +1184,8 @@ class AppsProvider with ChangeNotifier { notifyListeners(); var sp = SourceProvider(); List> errors = []; + var installedAppsData = await getAllInstalledInfo(); + List removedAppIds = []; await Future.wait((await getAppsDir()) // Parse Apps from JSON .listSync() .map((item) async { @@ -1199,43 +1206,57 @@ class AppsProvider with ChangeNotifier { } } if (app != null) { + // Save the app to the in-memory list without grabbing any OS info first + apps.update( + app.id, + (value) => AppInMemory( + app!, value.downloadProgress, value.installedInfo, value.icon), + ifAbsent: () => AppInMemory(app!, null, null, null)); + notifyListeners(); try { + // Try getting the app's source to ensure no invalid apps get loaded sp.getSource(app.url, overrideSource: app.overrideSource); + // If the app is installed, grab its OS data and reconcile install statuses + PackageInfo? installedInfo; + try { + installedInfo = + installedAppsData.firstWhere((i) => i.packageName == app!.id); + } catch (e) { + // If the app isn't installed the above throws an error + } + // Reconcile differences between the installed and recorded install info + var moddedApp = + getCorrectedInstallStatusAppIfPossible(app, installedInfo); + if (moddedApp != null) { + app = moddedApp; + // Note the app ID if it was uninstalled externally + if (moddedApp.installedVersion == null) { + removedAppIds.add(moddedApp.id); + } + } + var icon = await installedInfo?.applicationInfo?.getAppIcon(); + app.name = + await (installedInfo?.applicationInfo?.getAppLabel()) ?? app.name; + // Update the app in memory with install info and corrections apps.update( app.id, - (value) => AppInMemory(app!, value.downloadProgress, - value.installedInfo, value.icon), - ifAbsent: () => AppInMemory(app!, null, null, null)); + (value) => AppInMemory( + app!, value.downloadProgress, installedInfo, icon), + ifAbsent: () => AppInMemory(app!, null, installedInfo, icon)); + notifyListeners(); } catch (e) { - errors.add([app.id, app.finalName, e.toString()]); + errors.add([app!.id, app.finalName, e.toString()]); } } })); - notifyListeners(); + if (errors.isNotEmpty) { removeApps(errors.map((e) => e[0]).toList()); NotificationsProvider().notify( AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList())); } - // Get install status and other OS info for each App (slow) - List modifiedApps = []; - await Future.wait(apps.values.map((app) async { - await updateInstallStatusInMemory(app); - var moddedApp = - getCorrectedInstallStatusAppIfPossible(app.app, app.installedInfo); - if (moddedApp != null) { - modifiedApps.add(moddedApp); - } - })); - notifyListeners(); - // Reconcile version differences - if (modifiedApps.isNotEmpty) { - await saveApps(modifiedApps, attemptToCorrectInstallStatus: false); - var removedAppIds = modifiedApps - .where((a) => a.installedVersion == null) - .map((e) => e.id) - .toList(); - // After reconciliation, delete externally uninstalled Apps if needed + // Delete externally uninstalled Apps if needed + if (removedAppIds.isNotEmpty) { if (removedAppIds.isNotEmpty) { if (settingsProvider.removeOnExternalUninstall) { await removeApps(removedAppIds);