mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-28 04:03:44 +01:00 
			
		
		
		
	Switch back to parallelized BG task (#963) + better logging
This commit is contained in:
		| @@ -101,9 +101,9 @@ class MultiAppMultiError extends ObtainiumError { | |||||||
|       .join('\n\n'); |       .join('\n\n'); | ||||||
| } | } | ||||||
|  |  | ||||||
| showError(dynamic e, BuildContext context) { | showMessage(dynamic e, BuildContext context, {bool isError = false}) { | ||||||
|   Provider.of<LogsProvider>(context, listen: false) |   Provider.of<LogsProvider>(context, listen: false) | ||||||
|       .add(e.toString(), level: LogLevels.error); |       .add(e.toString(), level: isError ? LogLevels.error : LogLevels.info); | ||||||
|   if (e is String || (e is ObtainiumError && !e.unexpected)) { |   if (e is String || (e is ObtainiumError && !e.unexpected)) { | ||||||
|     ScaffoldMessenger.of(context).showSnackBar( |     ScaffoldMessenger.of(context).showSnackBar( | ||||||
|       SnackBar(content: Text(e.toString())), |       SnackBar(content: Text(e.toString())), | ||||||
| @@ -115,8 +115,8 @@ showError(dynamic e, BuildContext context) { | |||||||
|           return AlertDialog( |           return AlertDialog( | ||||||
|             scrollable: true, |             scrollable: true, | ||||||
|             title: Text(e is MultiAppMultiError |             title: Text(e is MultiAppMultiError | ||||||
|                 ? tr('someErrors') |                 ? tr(isError ? 'someErrors' : 'updates') | ||||||
|                 : tr('unexpectedError')), |                 : tr(isError ? 'unexpectedError' : 'unknown')), | ||||||
|             content: GestureDetector( |             content: GestureDetector( | ||||||
|                 onLongPress: () { |                 onLongPress: () { | ||||||
|                   Clipboard.setData(ClipboardData(text: e.toString())); |                   Clipboard.setData(ClipboardData(text: e.toString())); | ||||||
| @@ -137,6 +137,10 @@ showError(dynamic e, BuildContext context) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | showError(dynamic e, BuildContext context) { | ||||||
|  |   showMessage(e, context, isError: true); | ||||||
|  | } | ||||||
|  |  | ||||||
| String list2FriendlyString(List<String> list) { | String list2FriendlyString(List<String> list) { | ||||||
|   return list.length == 2 |   return list.length == 2 | ||||||
|       ? '${list[0]} ${tr('and')} ${list[1]}' |       ? '${list[0]} ${tr('and')} ${list[1]}' | ||||||
|   | |||||||
| @@ -292,7 +292,7 @@ class _AppPageState extends State<AppPage> { | |||||||
|         if (source?.enforceTrackOnly == true) { |         if (source?.enforceTrackOnly == true) { | ||||||
|           app.app.additionalSettings['trackOnly'] = true; |           app.app.additionalSettings['trackOnly'] = true; | ||||||
|           // ignore: use_build_context_synchronously |           // ignore: use_build_context_synchronously | ||||||
|           showError(tr('appsFromSourceAreTrackOnly'), context); |           showMessage(tr('appsFromSourceAreTrackOnly'), context); | ||||||
|         } |         } | ||||||
|         if (app.app.additionalSettings['versionDetection'] == |         if (app.app.additionalSettings['versionDetection'] == | ||||||
|             'releaseDateAsVersion') { |             'releaseDateAsVersion') { | ||||||
| @@ -343,7 +343,7 @@ class _AppPageState extends State<AppPage> { | |||||||
|                   ); |                   ); | ||||||
|                   if (app?.app.installedVersion != null && !trackOnly) { |                   if (app?.app.installedVersion != null && !trackOnly) { | ||||||
|                     // ignore: use_build_context_synchronously |                     // ignore: use_build_context_synchronously | ||||||
|                     showError(tr('appsUpdated'), context); |                     showMessage(tr('appsUpdated'), context); | ||||||
|                   } |                   } | ||||||
|                   if (res.isNotEmpty && mounted) { |                   if (res.isNotEmpty && mounted) { | ||||||
|                     Navigator.of(context).pop(); |                     Navigator.of(context).pop(); | ||||||
|   | |||||||
| @@ -705,7 +705,7 @@ class AppsPageState extends State<AppsPage> { | |||||||
|                     return <String>[]; |                     return <String>[]; | ||||||
|                   }).then((value) { |                   }).then((value) { | ||||||
|                     if (shouldInstallUpdates) { |                     if (shouldInstallUpdates) { | ||||||
|                       showError(tr('appsUpdated'), context); |                       showMessage(tr('appsUpdated'), context); | ||||||
|                     } |                     } | ||||||
|                   }); |                   }); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -81,7 +81,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|           }); |           }); | ||||||
|           appsProvider.addAppsByURL(urls).then((errors) { |           appsProvider.addAppsByURL(urls).then((errors) { | ||||||
|             if (errors.isEmpty) { |             if (errors.isEmpty) { | ||||||
|               showError(tr('importedX', args: [plural('apps', urls.length)]), |               showMessage(tr('importedX', args: [plural('apps', urls.length)]), | ||||||
|                   context); |                   context); | ||||||
|             } else { |             } else { | ||||||
|               showDialog( |               showDialog( | ||||||
| @@ -111,7 +111,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|               sp: settingsProvider) |               sp: settingsProvider) | ||||||
|           .then((String? result) { |           .then((String? result) { | ||||||
|         if (result != null) { |         if (result != null) { | ||||||
|           showError(tr('exportedTo', args: [result]), context); |           showMessage(tr('exportedTo', args: [result]), context); | ||||||
|         } |         } | ||||||
|       }).catchError((e) { |       }).catchError((e) { | ||||||
|         showError(e, context); |         showError(e, context); | ||||||
| @@ -141,7 +141,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|               } |               } | ||||||
|             }); |             }); | ||||||
|             appsProvider.addMissingCategories(settingsProvider); |             appsProvider.addMissingCategories(settingsProvider); | ||||||
|             showError(tr('importedX', args: [plural('apps', value)]), context); |             showMessage(tr('importedX', args: [plural('apps', value)]), context); | ||||||
|           }); |           }); | ||||||
|         } else { |         } else { | ||||||
|           // User canceled the picker |           // User canceled the picker | ||||||
| @@ -216,7 +216,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|               var errors = await appsProvider.addAppsByURL(selectedUrls); |               var errors = await appsProvider.addAppsByURL(selectedUrls); | ||||||
|               if (errors.isEmpty) { |               if (errors.isEmpty) { | ||||||
|                 // ignore: use_build_context_synchronously |                 // ignore: use_build_context_synchronously | ||||||
|                 showError( |                 showMessage( | ||||||
|                     tr('importedX', |                     tr('importedX', | ||||||
|                         args: [plural('apps', selectedUrls.length)]), |                         args: [plural('apps', selectedUrls.length)]), | ||||||
|                     context); |                     context); | ||||||
| @@ -274,7 +274,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|             var errors = await appsProvider.addAppsByURL(selectedUrls); |             var errors = await appsProvider.addAppsByURL(selectedUrls); | ||||||
|             if (errors.isEmpty) { |             if (errors.isEmpty) { | ||||||
|               // ignore: use_build_context_synchronously |               // ignore: use_build_context_synchronously | ||||||
|               showError( |               showMessage( | ||||||
|                   tr('importedX', args: [plural('apps', selectedUrls.length)]), |                   tr('importedX', args: [plural('apps', selectedUrls.length)]), | ||||||
|                   context); |                   context); | ||||||
|             } else { |             } else { | ||||||
|   | |||||||
| @@ -535,7 +535,7 @@ class _SettingsPageState extends State<SettingsPage> { | |||||||
|                         onPressed: () { |                         onPressed: () { | ||||||
|                           context.read<LogsProvider>().get().then((logs) { |                           context.read<LogsProvider>().get().then((logs) { | ||||||
|                             if (logs.isEmpty) { |                             if (logs.isEmpty) { | ||||||
|                               showError(ObtainiumError(tr('noLogs')), context); |                               showMessage(ObtainiumError(tr('noLogs')), context); | ||||||
|                             } else { |                             } else { | ||||||
|                               showDialog( |                               showDialog( | ||||||
|                                   context: context, |                                   context: context, | ||||||
| @@ -577,7 +577,7 @@ class _SettingsPageState extends State<SettingsPage> { | |||||||
|                                     const Duration(seconds: 0), |                                     const Duration(seconds: 0), | ||||||
|                                     bgUpdateCheckAlarmId + 200, |                                     bgUpdateCheckAlarmId + 200, | ||||||
|                                     bgUpdateCheck); |                                     bgUpdateCheck); | ||||||
|                                 showError(tr('bgTaskStarted'), context); |                                 showMessage(tr('bgTaskStarted'), context); | ||||||
|                               }, |                               }, | ||||||
|                               child: Text(tr('runBgCheckNow'))) |                               child: Text(tr('runBgCheckNow'))) | ||||||
|                         ], |                         ], | ||||||
|   | |||||||
| @@ -1325,18 +1325,19 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> { | |||||||
| /// | /// | ||||||
| /// @param List<MapEntry<String, int>>? toCheck: The appIds to check for updates (with the number of previous attempts made per appid) (defaults to all apps) | /// @param List<MapEntry<String, int>>? toCheck: The appIds to check for updates (with the number of previous attempts made per appid) (defaults to all apps) | ||||||
| /// | /// | ||||||
| /// @param List<String>? toInstall: The appIds to attempt to update (defaults to an empty array) | /// @param List<String>? toInstall: The appIds to attempt to update (if empty - which is the default - all pending updates are taken) | ||||||
| /// | /// | ||||||
| /// When toCheck is empty, the function is in "install mode" (else it is in "update mode"). | /// When toCheck is empty, the function is in "install mode" (else it is in "update mode"). | ||||||
| /// In update mode, all apps in toCheck are checked for updates. | /// In update mode, all apps in toCheck are checked for updates (in parallel). | ||||||
| /// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified. | /// If an update is available and it cannot be installed silently, the user is notified of the available update. | ||||||
| /// If there are errors, the task is run again for the remaining apps after a few minutes (duration depends on the errors), up to a maximum of 5 tries for any app. | /// If there are any errors, the task is run again for the remaining apps after a few minutes (based on the error with the longest retry interval). | ||||||
|  | /// Any app that has reached it's retry limit, the user is notified that it could not be checked. | ||||||
| /// | /// | ||||||
| /// Once all update checks are complete, the task is run again in install mode. | /// Once all update checks are complete, the task is run again in install mode. | ||||||
| /// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown). | /// In this mode, all pending silent updates are downloaded and installed in the background (serially - one at a time). | ||||||
| /// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries. | /// If there is an error, the offending app is moved to the back of the line of remaining apps, and the task is retried. | ||||||
|  | /// If an app repeatedly fails to install up to its retry limit, the user is notified. | ||||||
| /// | /// | ||||||
| /// In either mode, if the function fails after the maximum number of tries, the user is notified. |  | ||||||
| @pragma('vm:entry-point') | @pragma('vm:entry-point') | ||||||
| Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | ||||||
|   WidgetsFlutterBinding.ensureInitialized(); |   WidgetsFlutterBinding.ensureInitialized(); | ||||||
| @@ -1405,97 +1406,120 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | |||||||
|   bool installMode = |   bool installMode = | ||||||
|       toCheck.isEmpty; // Task is either in update mode or install mode |       toCheck.isEmpty; // Task is either in update mode or install mode | ||||||
|  |  | ||||||
|   // In install mode, grab all available silent updates unless explicitly told otherwise |  | ||||||
|   if (installMode && toInstall.isEmpty && !networkRestricted) { |  | ||||||
|     var temp = appsProvider.findExistingUpdates(installedOnly: true); |  | ||||||
|     for (var i = 0; i < temp.length; i++) { |  | ||||||
|       if (await appsProvider |  | ||||||
|           .canInstallSilently(appsProvider.apps[temp[i]]!.app)) { |  | ||||||
|         toInstall.add(MapEntry(temp[i], 0)); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   logs.add( |   logs.add( | ||||||
|       'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).'); |       'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).'); | ||||||
|  |  | ||||||
|   if (!installMode) { |   if (!installMode) { | ||||||
|     // If in update mode... |     // If in update mode, we check for updates. | ||||||
|     var didCompleteChecking = false; |     // We divide the results into 4 groups: | ||||||
|     CheckingUpdatesNotification? notif; |     // - toNotify - Apps with updates that the user will be notified about (can't be silently installed) | ||||||
|     // Loop through all updates and check each |     // - toRetry - Apps with update check errors that will be retried in a while | ||||||
|     List<App> toNotify = []; |     // - toThrow - Apps with update check errors that the user will be notified about (no retry) | ||||||
|  |     // After grouping the updates, we take care of toNotify and toThrow first | ||||||
|  |     // Then if toRetry is not empty, we schedule another update task to run in a while | ||||||
|  |     // If toRetry is empty, we take care of schedule another task that will run in install mode (toCheck is empty) | ||||||
|  |  | ||||||
|  |     // Init. vars. | ||||||
|  |     List<App> updates = []; // All updates found (silent and non-silent) | ||||||
|  |     List<App> toNotify = | ||||||
|  |         []; // All non-silent updates that the user will be notified about | ||||||
|  |     List<MapEntry<String, int>> toRetry = | ||||||
|  |         []; // All apps that got errors while checking | ||||||
|  |     var retryAfterXSeconds = | ||||||
|  |         0; // How long to wait until the next attempt (if there are errors) | ||||||
|  |     MultiAppMultiError? | ||||||
|  |         errors; // All errors including those that will lead to a retry | ||||||
|  |     MultiAppMultiError toThrow = | ||||||
|  |         MultiAppMultiError(); // All errors that will not lead to a retry, just a notification | ||||||
|  |     CheckingUpdatesNotification notif = CheckingUpdatesNotification( | ||||||
|  |         plural('apps', toCheck.length)); // The notif. to show while checking | ||||||
|  |  | ||||||
|  |     // Set a bool for when we're no on wifi/wired and the user doesn't want to download apps in that state | ||||||
|  |     var networkRestricted = false; | ||||||
|  |     if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) { | ||||||
|  |       var netResult = await (Connectivity().checkConnectivity()); | ||||||
|  |       networkRestricted = (netResult != ConnectivityResult.wifi) && | ||||||
|  |           (netResult != ConnectivityResult.ethernet); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       for (int i = 0; i < toCheck.length; i++) { |       // Check for updates | ||||||
|         var appId = toCheck[i].key; |       notificationsProvider.notify(notif, cancelExisting: true); | ||||||
|         var attemptCount = toCheck[i].value + 1; |       updates = await appsProvider.checkUpdates( | ||||||
|         AppInMemory? app = appsProvider.apps[appId]; |           specificIds: toCheck.map((e) => e.key).toList()); | ||||||
|         if (app?.app.installedVersion != null) { |  | ||||||
|           try { |  | ||||||
|             notificationsProvider.notify( |  | ||||||
|                 notif = CheckingUpdatesNotification(app?.name ?? appId), |  | ||||||
|                 cancelExisting: true); |  | ||||||
|             App? newApp = await appsProvider.checkUpdate(appId); |  | ||||||
|             if (newApp != null) { |  | ||||||
|               if (networkRestricted || |  | ||||||
|                   !(await appsProvider.canInstallSilently(app!.app))) { |  | ||||||
|                 if (newApp.additionalSettings['skipUpdateNotifications'] != |  | ||||||
|                     true) { |  | ||||||
|                   toNotify.add(newApp); |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|             if (i == (toCheck.length - 1)) { |  | ||||||
|               didCompleteChecking = true; |  | ||||||
|             } |  | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|             // If you got an error, move the offender to the back of the line (increment their fail count) and schedule another task to continue checking shortly |       // If there were errors, group them into toRetry and toThrow based on max retry count per app | ||||||
|  |       if (e is Map) { | ||||||
|  |         updates = e['updates']; | ||||||
|  |         errors = e['errors']; | ||||||
|  |         errors!.rawErrors.forEach((key, err) { | ||||||
|           logs.add( |           logs.add( | ||||||
|                 'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.'); |               'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.'); | ||||||
|             if (attemptCount < maxAttempts) { |           var toCheckApp = toCheck.where((element) => element.key == key).first; | ||||||
|               var remainingSeconds = e is RateLimitError |           if (toCheckApp.value < maxAttempts) { | ||||||
|                   ? (i == 0 ? (e.remainingMinutes * 60) : (5 * 60)) |             toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1)); | ||||||
|  |             // Next task interval is based on the error with the longest retry time | ||||||
|  |             var minRetryIntervalForThisApp = err is RateLimitError | ||||||
|  |                 ? (err.remainingMinutes * 60) | ||||||
|                 : e is ClientException |                 : e is ClientException | ||||||
|                     ? (15 * 60) |                     ? (15 * 60) | ||||||
|                       : pow(attemptCount, 2).toInt(); |                     : pow(toCheckApp.value + 1, 2).toInt(); | ||||||
|               logs.add( |             if (minRetryIntervalForThisApp > retryAfterXSeconds) { | ||||||
|                   'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).'); |               retryAfterXSeconds = minRetryIntervalForThisApp; | ||||||
|               var remainingToCheck = moveStrToEndMapEntryWithCount( |             } | ||||||
|                   toCheck.sublist(i), MapEntry(appId, attemptCount)); |  | ||||||
|               AndroidAlarmManager.oneShot(Duration(seconds: remainingSeconds), |  | ||||||
|                   taskId + 1, bgUpdateCheck, |  | ||||||
|                   params: { |  | ||||||
|                     'toCheck': remainingToCheck |  | ||||||
|                         .map( |  | ||||||
|                             (entry) => {'key': entry.key, 'value': entry.value}) |  | ||||||
|                         .toList(), |  | ||||||
|                     'toInstall': toInstall |  | ||||||
|                         .map( |  | ||||||
|                             (entry) => {'key': entry.key, 'value': entry.value}) |  | ||||||
|                         .toList(), |  | ||||||
|                   }); |  | ||||||
|               break; |  | ||||||
|           } else { |           } else { | ||||||
|               // If the offender has reached its fail limit, notify the user and remove it from the list (task can continue) |             toThrow.add(key, err, appName: errors?.appIdNames[key]); | ||||||
|               toCheck.removeAt(i); |           } | ||||||
|               i--; |         }); | ||||||
|               notificationsProvider |       } else { | ||||||
|                   .notify(ErrorCheckingUpdatesNotification(e.toString())); |         // We don't expect to ever get here in any situation so no need to catch (but log it in case) | ||||||
|  |         logs.add('Fatal error in BG update task: ${e.toString()}'); | ||||||
|  |         rethrow; | ||||||
|       } |       } | ||||||
|     } finally { |     } finally { | ||||||
|             if (notif != null) { |  | ||||||
|       notificationsProvider.cancel(notif.id); |       notificationsProvider.cancel(notif.id); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Filter out updates that will be installed silently (the rest go into toNotify) | ||||||
|  |     for (var i = 0; i < updates.length; i++) { | ||||||
|  |       if (networkRestricted || | ||||||
|  |           !(await appsProvider.canInstallSilently(updates[i]))) { | ||||||
|  |         if (updates[i].additionalSettings['skipUpdateNotifications'] != true) { | ||||||
|  |           toNotify.add(updates[i]); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     } finally { |  | ||||||
|  |     // Send the update notification | ||||||
|     if (toNotify.isNotEmpty) { |     if (toNotify.isNotEmpty) { | ||||||
|       notificationsProvider.notify(UpdateNotification(toNotify)); |       notificationsProvider.notify(UpdateNotification(toNotify)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Send the error notifications (grouped by error string) | ||||||
|  |     if (toThrow.rawErrors.isNotEmpty) { | ||||||
|  |       for (var element in toThrow.idsByErrorString.entries) { | ||||||
|  |         notificationsProvider.notify(ErrorCheckingUpdatesNotification( | ||||||
|  |             errors!.errorsAppsString(element.key, element.value), | ||||||
|  |             id: Random().nextInt(10000))); | ||||||
|       } |       } | ||||||
|     // If you're done checking and found some silently installable updates, schedule another task which will run in install mode |     } | ||||||
|     if (didCompleteChecking) { |  | ||||||
|  |     // if there are update checks to retry, schedule a retry task | ||||||
|  |     if (toRetry.isNotEmpty) { | ||||||
|  |       logs.add( | ||||||
|  |           'BG update task $taskId: Will retry in $retryAfterXSeconds seconds.'); | ||||||
|  |       AndroidAlarmManager.oneShot( | ||||||
|  |           Duration(seconds: retryAfterXSeconds), taskId + 1, bgUpdateCheck, | ||||||
|  |           params: { | ||||||
|  |             'toCheck': toRetry | ||||||
|  |                 .map((entry) => {'key': entry.key, 'value': entry.value}) | ||||||
|  |                 .toList(), | ||||||
|  |             'toInstall': toInstall | ||||||
|  |                 .map((entry) => {'key': entry.key, 'value': entry.value}) | ||||||
|  |                 .toList(), | ||||||
|  |           }); | ||||||
|  |     } else { | ||||||
|  |       // If there are no more update checks, schedule an install task | ||||||
|       logs.add( |       logs.add( | ||||||
|           'BG update task $taskId: Done. Scheduling install task to run immediately.'); |           'BG update task $taskId: Done. Scheduling install task to run immediately.'); | ||||||
|       AndroidAlarmManager.oneShot( |       AndroidAlarmManager.oneShot( | ||||||
| @@ -1506,11 +1530,19 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | |||||||
|                 .map((entry) => {'key': entry.key, 'value': entry.value}) |                 .map((entry) => {'key': entry.key, 'value': entry.value}) | ||||||
|                 .toList() |                 .toList() | ||||||
|           }); |           }); | ||||||
|     } else if (didCompleteChecking) { |  | ||||||
|       logs.add('BG update task $taskId: Done.'); |  | ||||||
|     } |     } | ||||||
|   } else { |   } else { | ||||||
|     // If in install mode... |     // In install mode... | ||||||
|  |     // If you haven't explicitly been given updates to install (which is the case for new tasks), grab all available silent updates | ||||||
|  |     if (toInstall.isEmpty && !networkRestricted) { | ||||||
|  |       var temp = appsProvider.findExistingUpdates(installedOnly: true); | ||||||
|  |       for (var i = 0; i < temp.length; i++) { | ||||||
|  |         if (await appsProvider | ||||||
|  |             .canInstallSilently(appsProvider.apps[temp[i]]!.app)) { | ||||||
|  |           toInstall.add(MapEntry(temp[i], 0)); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|     var didCompleteInstalling = false; |     var didCompleteInstalling = false; | ||||||
|     var tempObtArr = toInstall.where((element) => element.key == obtainiumId); |     var tempObtArr = toInstall.where((element) => element.key == obtainiumId); | ||||||
|     if (tempObtArr.isNotEmpty) { |     if (tempObtArr.isNotEmpty) { | ||||||
| @@ -1562,9 +1594,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | |||||||
|               .notify(ErrorCheckingUpdatesNotification(e.toString())); |               .notify(ErrorCheckingUpdatesNotification(e.toString())); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       if (didCompleteInstalling) { |     } | ||||||
|  |     if (didCompleteInstalling || toInstall.isEmpty) { | ||||||
|       logs.add('BG install task $taskId: Done.'); |       logs.add('BG install task $taskId: Done.'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user