Improve loading time/stability (at the cost of icon flickering)

This commit is contained in:
Imran Remtulla
2024-05-23 20:02:43 -04:00
parent 06a079e452
commit 7808bc5ccb
3 changed files with 66 additions and 91 deletions

View File

@@ -226,18 +226,26 @@ class _AppPageState extends State<AppPage> {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: 20), const SizedBox(height: 20),
app?.icon != null FutureBuilder(
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [ future: app?.installedInfo?.applicationInfo?.getAppIcon(),
builder: (ctx, val) {
return val.data != null
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector( GestureDetector(
onTap: app == null
? null
: () => pm.openApp(app.app.id),
child: Image.memory( child: Image.memory(
app!.icon!, val.data!,
height: 150, height: 150,
gaplessPlayback: true, gaplessPlayback: true,
), ),
onTap: () => pm.openApp(app.app.id),
) )
]) ])
: Container(), : Container();
}),
const SizedBox( const SizedBox(
height: 25, height: 25,
), ),

View File

@@ -406,9 +406,13 @@ class AppsPageState extends State<AppsPage> {
} }
getAppIcon(int appIndex) { getAppIcon(int appIndex) {
return listedApps[appIndex].icon != null return FutureBuilder(
future:
listedApps[appIndex].installedInfo?.applicationInfo?.getAppIcon(),
builder: (ctx, val) {
return val.data != null
? Image.memory( ? Image.memory(
listedApps[appIndex].icon!, val.data!,
gaplessPlayback: true, gaplessPlayback: true,
) )
: Row( : Row(
@@ -423,7 +427,8 @@ class AppsPageState extends State<AppsPage> {
child: Image( child: Image(
image: const AssetImage( image: const AssetImage(
'assets/graphics/icon_small.png'), 'assets/graphics/icon_small.png'),
color: Theme.of(context).brightness == Brightness.dark color: Theme.of(context).brightness ==
Brightness.dark
? Colors.white.withOpacity(0.4) ? Colors.white.withOpacity(0.4)
: Colors.white.withOpacity(0.3), : Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate, colorBlendMode: BlendMode.modulate,
@@ -431,6 +436,7 @@ class AppsPageState extends State<AppsPage> {
), ),
)), )),
]); ]);
});
} }
getVersionText(int appIndex) { getVersionText(int appIndex) {

View File

@@ -42,11 +42,10 @@ class AppInMemory {
late App app; late App app;
double? downloadProgress; double? downloadProgress;
PackageInfo? installedInfo; PackageInfo? installedInfo;
Uint8List? icon;
AppInMemory(this.app, this.downloadProgress, this.installedInfo, this.icon); AppInMemory(this.app, this.downloadProgress, this.installedInfo);
AppInMemory deepCopy() => AppInMemory deepCopy() =>
AppInMemory(app.deepCopy(), downloadProgress, installedInfo, icon); AppInMemory(app.deepCopy(), downloadProgress, installedInfo);
String get name => app.overrideName ?? app.finalName; String get name => app.overrideName ?? app.finalName;
} }
@@ -369,8 +368,7 @@ class AppsProvider with ChangeNotifier {
foregroundSubscription = foregroundStream?.listen((event) async { foregroundSubscription = foregroundStream?.listen((event) async {
isForeground = event == FGBGType.foreground; isForeground = event == FGBGType.foreground;
if (isForeground) { if (isForeground) {
await loadApps(andUpdateSuperficialDetails: false); await loadApps();
updateAppsSuperficialDetails();
} }
}); });
() async { () async {
@@ -387,9 +385,7 @@ class AppsProvider with ChangeNotifier {
} }
if (!isBg) { if (!isBg) {
// Load Apps into memory (in background processes, this is done later instead of in the constructor) // Load Apps into memory (in background processes, this is done later instead of in the constructor)
await loadApps(andUpdateSuperficialDetails: false); await loadApps();
// Update app names/icons asynchronously
updateAppsSuperficialDetails();
// Delete any partial APKs (if safe to do so) // Delete any partial APKs (if safe to do so)
var cutoff = DateTime.now().subtract(const Duration(days: 7)); var cutoff = DateTime.now().subtract(const Duration(days: 7));
APKDir.listSync() APKDir.listSync()
@@ -1125,8 +1121,7 @@ class AppsProvider with ChangeNotifier {
// FOURTH, DISABLE VERSION DETECTION IF ENABLED AND THE REPORTED/REAL INSTALLED VERSIONS ARE NOT STANDARDIZED // FOURTH, DISABLE VERSION DETECTION IF ENABLED AND THE REPORTED/REAL INSTALLED VERSIONS ARE NOT STANDARDIZED
if (installedInfo != null && if (installedInfo != null &&
versionDetectionIsStandard && versionDetectionIsStandard &&
!isVersionDetectionPossible( !isVersionDetectionPossible(AppInMemory(app, null, installedInfo))) {
AppInMemory(app, null, installedInfo, null))) {
app.additionalSettings['versionDetection'] = false; app.additionalSettings['versionDetection'] = false;
logs.add('Could not reconcile version formats for: ${app.id}'); logs.add('Could not reconcile version formats for: ${app.id}');
modded = true; modded = true;
@@ -1169,36 +1164,7 @@ class AppsProvider with ChangeNotifier {
: false; : false;
} }
Future<void> updateInstallStatusInMemory( Future<void> loadApps({String? singleId}) async {
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]
?.installedInfo
?.applicationInfo
?.getAppLabel()) ??
app.name;
}
Future<void> updateAppsSuperficialDetails() async {
await Future.wait(apps.values.map((app) async {
var icon = await app.installedInfo?.applicationInfo?.getAppIcon();
app.app.name =
await (app.installedInfo?.applicationInfo?.getAppLabel()) ??
app.app.name;
// Update the app in memory with install info and corrections
apps.update(
app.app.id,
(value) => AppInMemory(
app.app, value.downloadProgress, app.installedInfo, icon),
ifAbsent: () => AppInMemory(app.app, null, app.installedInfo, icon));
notifyListeners();
}));
}
Future<void> loadApps(
{String? singleId, bool andUpdateSuperficialDetails = true}) async {
while (loadingApps) { while (loadingApps) {
await Future.delayed(const Duration(microseconds: 1)); await Future.delayed(const Duration(microseconds: 1));
} }
@@ -1231,9 +1197,9 @@ class AppsProvider with ChangeNotifier {
// Save the app to the in-memory list without grabbing any OS info first // Save the app to the in-memory list without grabbing any OS info first
apps.update( apps.update(
app.id, app.id,
(value) => AppInMemory( (value) =>
app!, value.downloadProgress, value.installedInfo, value.icon), AppInMemory(app!, value.downloadProgress, value.installedInfo),
ifAbsent: () => AppInMemory(app!, null, null, null)); ifAbsent: () => AppInMemory(app!, null, null));
notifyListeners(); notifyListeners();
try { try {
// Try getting the app's source to ensure no invalid apps get loaded // Try getting the app's source to ensure no invalid apps get loaded
@@ -1259,18 +1225,15 @@ class AppsProvider with ChangeNotifier {
// Update the app in memory with install info and corrections // Update the app in memory with install info and corrections
apps.update( apps.update(
app.id, app.id,
(value) => AppInMemory( (value) =>
app!, value.downloadProgress, installedInfo, value.icon), AppInMemory(app!, value.downloadProgress, installedInfo),
ifAbsent: () => AppInMemory(app!, null, installedInfo, null)); ifAbsent: () => AppInMemory(app!, null, installedInfo));
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
errors.add([app!.id, app.finalName, e.toString()]); errors.add([app!.id, app.finalName, e.toString()]);
} }
} }
})); }));
if (andUpdateSuperficialDetails) {
await updateAppsSuperficialDetails();
}
if (errors.isNotEmpty) { if (errors.isNotEmpty) {
removeApps(errors.map((e) => e[0]).toList()); removeApps(errors.map((e) => e[0]).toList());
NotificationsProvider().notify( NotificationsProvider().notify(
@@ -1295,7 +1258,6 @@ class AppsProvider with ChangeNotifier {
await Future.wait(apps.map((a) async { await Future.wait(apps.map((a) async {
var app = a.deepCopy(); var app = a.deepCopy();
PackageInfo? info = await getInstalledInfo(app.id); PackageInfo? info = await getInstalledInfo(app.id);
var icon = await info?.applicationInfo?.getAppIcon();
app.name = await (info?.applicationInfo?.getAppLabel()) ?? app.name; app.name = await (info?.applicationInfo?.getAppLabel()) ?? app.name;
if (attemptToCorrectInstallStatus) { if (attemptToCorrectInstallStatus) {
app = getCorrectedInstallStatusAppIfPossible(app, info) ?? app; app = getCorrectedInstallStatusAppIfPossible(app, info) ?? app;
@@ -1305,10 +1267,9 @@ class AppsProvider with ChangeNotifier {
.writeAsStringSync(jsonEncode(app.toJson())); .writeAsStringSync(jsonEncode(app.toJson()));
} }
try { try {
this.apps.update(app.id, this.apps.update(
(value) => AppInMemory(app, value.downloadProgress, info, icon), app.id, (value) => AppInMemory(app, value.downloadProgress, info),
ifAbsent: ifAbsent: onlyIfExists ? null : () => AppInMemory(app, null, info));
onlyIfExists ? null : () => AppInMemory(app, null, info, icon));
} catch (e) { } catch (e) {
if (e is! ArgumentError || e.name != 'key') { if (e is! ArgumentError || e.name != 'key') {
rethrow; rethrow;