Revert "Switched to WorkManager for reliability (#608)"

This reverts commit d3247a9ec1.
This commit is contained in:
Imran Remtulla
2023-10-22 12:50:54 -04:00
parent d3247a9ec1
commit a34a447164
5 changed files with 268 additions and 285 deletions

View File

@@ -12,7 +12,7 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:workmanager/workmanager.dart'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
// ignore: implementation_imports // ignore: implementation_imports
import 'package:easy_localization/src/easy_localization_controller.dart'; import 'package:easy_localization/src/easy_localization_controller.dart';
@@ -23,7 +23,7 @@ const String currentVersion = '0.14.31';
const String currentReleaseTag = const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
const String bgUpdateTaskId = 'bgUpdate'; const int bgUpdateCheckAlarmId = 666;
List<MapEntry<Locale, String>> supportedLocales = const [ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('en'), 'English'), MapEntry(Locale('en'), 'English'),
@@ -71,17 +71,6 @@ Future<void> loadTranslations() async {
fallbackTranslations: controller.fallbackTranslations); fallbackTranslations: controller.fallbackTranslations);
} }
@pragma('vm:entry-point')
void bgTaskDispatcher() {
Workmanager().executeTask((taskId, params) {
if (taskId == bgUpdateTaskId) {
return bgUpdateTask(taskId, params);
} else {
return Future.value(true);
}
});
}
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
try { try {
@@ -99,7 +88,7 @@ void main() async {
); );
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
} }
await Workmanager().initialize(bgTaskDispatcher); await AndroidAlarmManager.initialize();
runApp(MultiProvider( runApp(MultiProvider(
providers: [ providers: [
ChangeNotifierProvider(create: (context) => AppsProvider()), ChangeNotifierProvider(create: (context) => AppsProvider()),
@@ -169,7 +158,7 @@ class _ObtainiumState extends State<Obtainium> {
var actualUpdateInterval = settingsProvider.updateInterval; var actualUpdateInterval = settingsProvider.updateInterval;
if (existingUpdateInterval != actualUpdateInterval) { if (existingUpdateInterval != actualUpdateInterval) {
if (actualUpdateInterval == 0) { if (actualUpdateInterval == 0) {
Workmanager().cancelByUniqueName(bgUpdateTaskId); AndroidAlarmManager.cancel(bgUpdateCheckAlarmId);
} else { } else {
var settingChanged = existingUpdateInterval != -1; var settingChanged = existingUpdateInterval != -1;
var lastCheckWasTooLongAgo = actualUpdateInterval != 0 && var lastCheckWasTooLongAgo = actualUpdateInterval != 0 &&
@@ -179,10 +168,12 @@ class _ObtainiumState extends State<Obtainium> {
if (settingChanged || lastCheckWasTooLongAgo) { if (settingChanged || lastCheckWasTooLongAgo) {
logs.add( logs.add(
'Update interval was set to ${actualUpdateInterval.toString()} (reason: ${settingChanged ? 'setting changed' : 'last check was ${settingsProvider.lastBGCheckTime.toLocal().toString()}'}).'); 'Update interval was set to ${actualUpdateInterval.toString()} (reason: ${settingChanged ? 'setting changed' : 'last check was ${settingsProvider.lastBGCheckTime.toLocal().toString()}'}).');
Workmanager().registerPeriodicTask( AndroidAlarmManager.periodic(
bgUpdateTaskId, "BG Update Main Loop", Duration(minutes: actualUpdateInterval),
initialDelay: Duration(minutes: actualUpdateInterval), bgUpdateCheckAlarmId,
existingWorkPolicy: ExistingWorkPolicy.replace); bgUpdateCheck,
rescheduleOnReboot: true,
wakeup: true);
} }
} }
existingUpdateInterval = actualUpdateInterval; existingUpdateInterval = actualUpdateInterval;

View File

@@ -1,4 +1,4 @@
import 'package:workmanager/workmanager.dart'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -591,10 +591,10 @@ class _SettingsPageState extends State<SettingsPage> {
height16, height16,
TextButton( TextButton(
onPressed: () { onPressed: () {
Workmanager().registerOneOffTask( AndroidAlarmManager.oneShot(
'$bgUpdateTaskId+Manual', bgUpdateTaskId, const Duration(seconds: 0),
existingWorkPolicy: bgUpdateCheckAlarmId + 200,
ExistingWorkPolicy.replace); bgUpdateCheck);
showMessage(tr('bgTaskStarted'), context); showMessage(tr('bgTaskStarted'), context);
}, },
child: Text(tr('runBgCheckNow'))) child: Text(tr('runBgCheckNow')))

View File

@@ -7,7 +7,7 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:workmanager/workmanager.dart'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:android_intent_plus/flag.dart'; import 'package:android_intent_plus/flag.dart';
import 'package:android_package_installer/android_package_installer.dart'; import 'package:android_package_installer/android_package_installer.dart';
import 'package:android_package_manager/android_package_manager.dart'; import 'package:android_package_manager/android_package_manager.dart';
@@ -1356,276 +1356,268 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> {
/// 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 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. /// If an app repeatedly fails to install up to its retry limit, the user is notified.
/// ///
Future<bool> bgUpdateTask(String taskId, Map<String, dynamic>? params) async { @pragma('vm:entry-point')
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
await Workmanager().initialize(bgTaskDispatcher); await AndroidAlarmManager.initialize();
await loadTranslations(); await loadTranslations();
LogsProvider logs = LogsProvider(); LogsProvider logs = LogsProvider();
try { NotificationsProvider notificationsProvider = NotificationsProvider();
NotificationsProvider notificationsProvider = NotificationsProvider(); AppsProvider appsProvider = AppsProvider(isBg: true);
AppsProvider appsProvider = AppsProvider(isBg: true); await appsProvider.loadApps();
await appsProvider.loadApps();
int maxAttempts = 4; int maxAttempts = 4;
params ??= {}; params ??= {};
if (params['toCheck'] == null) { if (params['toCheck'] == null) {
appsProvider.settingsProvider.lastBGCheckTime = DateTime.now(); appsProvider.settingsProvider.lastBGCheckTime = DateTime.now();
} }
List<MapEntry<String, int>> toCheck = <MapEntry<String, int>>[ List<MapEntry<String, int>> toCheck = <MapEntry<String, int>>[
...(params['toCheck']?.map((str) { ...(params['toCheck']
var temp = str.split(','); ?.map((entry) => MapEntry<String, int>(
return MapEntry<String, int>(temp[0], int.parse(temp[1])); entry['key'] as String, entry['value'] as int))
}).toList() ?? .toList() ??
appsProvider appsProvider
.getAppsSortedByUpdateCheckTime( .getAppsSortedByUpdateCheckTime(
onlyCheckInstalledOrTrackOnlyApps: appsProvider onlyCheckInstalledOrTrackOnlyApps: appsProvider
.settingsProvider.onlyCheckInstalledOrTrackOnlyApps) .settingsProvider.onlyCheckInstalledOrTrackOnlyApps)
.map((e) => MapEntry(e, 0))) .map((e) => MapEntry(e, 0)))
]; ];
List<MapEntry<String, int>> toInstall = <MapEntry<String, int>>[ List<MapEntry<String, int>> toInstall = <MapEntry<String, int>>[
...(params['toInstall']?.map((str) { ...(params['toInstall']
var temp = str.split(','); ?.map((entry) => MapEntry<String, int>(
return MapEntry<String, int>(temp[0], int.parse(temp[1])); entry['key'] as String, entry['value'] as int))
}).toList() ?? .toList() ??
(<List<MapEntry<String, int>>>[])) (<List<MapEntry<String, int>>>[]))
]; ];
var netResult = await (Connectivity().checkConnectivity()); var netResult = await (Connectivity().checkConnectivity());
if (netResult == ConnectivityResult.none) { if (netResult == ConnectivityResult.none) {
var networkBasedRetryInterval = 15; var networkBasedRetryInterval = 15;
var nextRegularCheck = appsProvider.settingsProvider.lastBGCheckTime var nextRegularCheck = appsProvider.settingsProvider.lastBGCheckTime
.add(Duration(minutes: appsProvider.settingsProvider.updateInterval)); .add(Duration(minutes: appsProvider.settingsProvider.updateInterval));
var potentialNetworkRetryCheck = var potentialNetworkRetryCheck =
DateTime.now().add(Duration(minutes: networkBasedRetryInterval)); DateTime.now().add(Duration(minutes: networkBasedRetryInterval));
var shouldRetry = potentialNetworkRetryCheck.isBefore(nextRegularCheck); var shouldRetry = potentialNetworkRetryCheck.isBefore(nextRegularCheck);
logs.add( logs.add(
'BG task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.'); 'BG update task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.');
await Workmanager().registerOneOffTask("$taskId+Retry", taskId, AndroidAlarmManager.oneShot(
initialDelay: const Duration(minutes: 15), const Duration(minutes: 15), taskId + 1, bgUpdateCheck,
existingWorkPolicy: ExistingWorkPolicy.replace, params: {
inputData: { 'toCheck': toCheck
'toCheck': .map((entry) => {'key': entry.key, 'value': entry.value})
toCheck.map((entry) => '${entry.key},${entry.value}').toList(), .toList(),
'toInstall': toInstall 'toInstall': toInstall
.map((entry) => '${entry.key},${entry.value}') .map((entry) => {'key': entry.key, 'value': entry.value})
.toList(), .toList(),
}); });
} return;
}
var networkRestricted = false;
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
networkRestricted = (netResult != ConnectivityResult.wifi) &&
(netResult != ConnectivityResult.ethernet);
}
bool installMode =
toCheck.isEmpty; // Task is either in update mode or install mode
logs.add(
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
if (!installMode) {
// If in update mode, we check for updates.
// We divide the results into 4 groups:
// - toNotify - Apps with updates that the user will be notified about (can't be silently installed)
// - toRetry - Apps with update check errors that will be retried in a while
// - 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; var networkRestricted = false;
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) { if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
var netResult = await (Connectivity().checkConnectivity());
networkRestricted = (netResult != ConnectivityResult.wifi) && networkRestricted = (netResult != ConnectivityResult.wifi) &&
(netResult != ConnectivityResult.ethernet); (netResult != ConnectivityResult.ethernet);
} }
bool installMode = try {
toCheck.isEmpty; // Task is either in update mode or install mode // Check for updates
notificationsProvider.notify(notif, cancelExisting: true);
logs.add( updates = await appsProvider.checkUpdates(
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).'); specificIds: toCheck.map((e) => e.key).toList(),
sp: appsProvider.settingsProvider);
if (!installMode) { } catch (e) {
// If in update mode, we check for updates. // If there were errors, group them into toRetry and toThrow based on max retry count per app
// We divide the results into 4 groups: if (e is Map) {
// - toNotify - Apps with updates that the user will be notified about (can't be silently installed) updates = e['updates'];
// - toRetry - Apps with update check errors that will be retried in a while errors = e['errors'];
// - toThrow - Apps with update check errors that the user will be notified about (no retry) errors!.rawErrors.forEach((key, err) {
// After grouping the updates, we take care of toNotify and toThrow first logs.add(
// Then if toRetry is not empty, we schedule another update task to run in a while 'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
// If toRetry is empty, we take care of schedule another task that will run in install mode (toCheck is empty) var toCheckApp = toCheck.where((element) => element.key == key).first;
if (toCheckApp.value < maxAttempts) {
// Init. vars. toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1));
List<App> updates = []; // All updates found (silent and non-silent) // Next task interval is based on the error with the longest retry time
List<App> toNotify = var minRetryIntervalForThisApp = err is RateLimitError
[]; // All non-silent updates that the user will be notified about ? (err.remainingMinutes * 60)
List<MapEntry<String, int>> toRetry = : e is ClientException
[]; // All apps that got errors while checking ? (15 * 60)
var retryAfterXSeconds = : pow(toCheckApp.value + 1, 2).toInt();
0; // How long to wait until the next attempt (if there are errors) if (minRetryIntervalForThisApp > retryAfterXSeconds) {
MultiAppMultiError? retryAfterXSeconds = minRetryIntervalForThisApp;
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 {
// Check for updates
notificationsProvider.notify(notif, cancelExisting: true);
updates = await appsProvider.checkUpdates(
specificIds: toCheck.map((e) => e.key).toList(),
sp: appsProvider.settingsProvider);
} catch (e) {
// 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(
'BG task $taskId: Got error on checking for $key \'${err.toString()}\'.');
var toCheckApp =
toCheck.where((element) => element.key == key).first;
if (toCheckApp.value < maxAttempts) {
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
? (15 * 60)
: pow(toCheckApp.value + 1, 2).toInt();
if (minRetryIntervalForThisApp > retryAfterXSeconds) {
retryAfterXSeconds = minRetryIntervalForThisApp;
}
} else {
toThrow.add(key, err, appName: errors?.appIdNames[key]);
} }
});
} else {
// 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 task: ${e.toString()}');
rethrow;
}
} finally {
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]);
}
}
}
// Send the update notification
if (toNotify.isNotEmpty) {
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 there are update checks to retry, schedule a retry task
if (toRetry.isNotEmpty) {
logs.add('BG task $taskId: Will retry in $retryAfterXSeconds seconds.');
await Workmanager().registerOneOffTask("$taskId+Retry", taskId,
initialDelay: Duration(seconds: retryAfterXSeconds),
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: {
'toCheck': toRetry
.map((entry) => '${entry.key},${entry.value}')
.toList(),
'toInstall': toInstall
.map((entry) => '${entry.key},${entry.value}')
.toList(),
});
} else {
// If there are no more update checks, schedule an install task
logs.add(
'BG task $taskId: Done. Scheduling install task to run immediately.');
await Workmanager().registerOneOffTask(
"$bgUpdateTaskId+Install", taskId,
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: {
'toCheck': <String>[],
'toInstall': toInstall
.map((entry) => '${entry.key},${entry.value}')
.toList()
});
}
} else {
// 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 tempObtArr = toInstall.where((element) => element.key == obtainiumId);
if (tempObtArr.isNotEmpty) {
// Move obtainium to the end of the list as it must always install last
var obt = tempObtArr.first;
toInstall = moveStrToEndMapEntryWithCount(toInstall, obt);
}
// Loop through all updates and install each
for (var i = 0; i < toInstall.length; i++) {
var appId = toInstall[i].key;
var retryCount = toInstall[i].value;
try {
logs.add(
'BG task $taskId: Attempting to update $appId in the background.');
await appsProvider.downloadAndInstallLatestApps([appId], null,
notificationsProvider: notificationsProvider);
await Future.delayed(const Duration(
seconds:
5)); // Just in case task ending causes install fail (not clear)
if (i == (toCheck.length - 1)) {
didCompleteInstalling = true;
}
} 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 installing shortly
logs.add(
'BG task $taskId: Got error on updating $appId \'${e.toString()}\'.');
if (retryCount < maxAttempts) {
var remainingSeconds = retryCount;
logs.add(
'BG task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToInstall = moveStrToEndMapEntryWithCount(
toInstall.sublist(i), MapEntry(appId, retryCount + 1));
await Workmanager().registerOneOffTask("$taskId+Retry", taskId,
initialDelay: Duration(seconds: remainingSeconds),
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: {
'toCheck': toCheck
.map((entry) => '${entry.key},${entry.value}')
.toList(),
'toInstall': remainingToInstall
.map((entry) => '${entry.key},${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]);
toInstall.removeAt(i);
i--;
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
} }
} });
} else {
// 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;
} }
if (didCompleteInstalling || toInstall.isEmpty) { } finally {
logs.add('BG task $taskId: Done.'); 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]);
}
} }
} }
return true;
} catch (e) { // Send the update notification
logs.add(e.toString()); if (toNotify.isNotEmpty) {
return false; 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 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(
'BG update task $taskId: Done. Scheduling install task to run immediately.');
AndroidAlarmManager.oneShot(
const Duration(minutes: 0), taskId + 1, bgUpdateCheck,
params: {
'toCheck': [],
'toInstall': toInstall
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList()
});
}
} else {
// 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 tempObtArr = toInstall.where((element) => element.key == obtainiumId);
if (tempObtArr.isNotEmpty) {
// Move obtainium to the end of the list as it must always install last
var obt = tempObtArr.first;
toInstall = moveStrToEndMapEntryWithCount(toInstall, obt);
}
// Loop through all updates and install each
for (var i = 0; i < toInstall.length; i++) {
var appId = toInstall[i].key;
var retryCount = toInstall[i].value;
try {
logs.add(
'BG install task $taskId: Attempting to update $appId in the background.');
await appsProvider.downloadAndInstallLatestApps([appId], null,
notificationsProvider: notificationsProvider);
await Future.delayed(const Duration(
seconds:
5)); // Just in case task ending causes install fail (not clear)
if (i == (toCheck.length - 1)) {
didCompleteInstalling = true;
}
} 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 installing shortly
logs.add(
'BG install task $taskId: Got error on updating $appId \'${e.toString()}\'.');
if (retryCount < maxAttempts) {
var remainingSeconds = retryCount;
logs.add(
'BG install task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToInstall = moveStrToEndMapEntryWithCount(
toInstall.sublist(i), MapEntry(appId, retryCount + 1));
AndroidAlarmManager.oneShot(
Duration(seconds: remainingSeconds), taskId + 1, bgUpdateCheck,
params: {
'toCheck': toCheck
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
'toInstall': remainingToInstall
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
});
break;
} else {
// If the offender has reached its fail limit, notify the user and remove it from the list (task can continue)
toInstall.removeAt(i);
i--;
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
}
}
}
if (didCompleteInstalling || toInstall.isEmpty) {
logs.add('BG install task $taskId: Done.');
}
} }
} }

View File

@@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
android_alarm_manager_plus:
dependency: "direct main"
description:
name: android_alarm_manager_plus
sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
android_intent_plus: android_intent_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -923,14 +931,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.2"
workmanager:
dependency: "direct main"
description:
name: workmanager
sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00
url: "https://pub.dev"
source: hosted
version: "0.5.2"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@@ -956,5 +956,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.1.2 <4.0.0" dart: ">=3.1.0 <4.0.0"
flutter: ">=3.13.0" flutter: ">=3.13.0"

View File

@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.14.31+224 # When changing this, update the tag in main() accordingly version: 0.14.31+223 # When changing this, update the tag in main() accordingly
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
@@ -57,6 +57,7 @@ dependencies:
ref: main ref: main
android_package_manager: ^0.6.0 android_package_manager: ^0.6.0
share_plus: ^7.0.0 share_plus: ^7.0.0
android_alarm_manager_plus: ^3.0.0
sqflite: ^2.2.0+3 sqflite: ^2.2.0+3
easy_localization: ^3.0.1 easy_localization: ^3.0.1
android_intent_plus: ^4.0.0 android_intent_plus: ^4.0.0
@@ -65,7 +66,6 @@ dependencies:
hsluv: ^1.1.3 hsluv: ^1.1.3
connectivity_plus: ^5.0.0 connectivity_plus: ^5.0.0
shared_storage: ^0.8.0 shared_storage: ^0.8.0
workmanager: ^0.5.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: