Fix bgcheck error reporting (per-app), move code around

This commit is contained in:
Imran Remtulla
2023-08-24 15:05:07 -04:00
parent 5e41d5762b
commit 2504ae24fc
2 changed files with 236 additions and 175 deletions

View File

@@ -2,8 +2,6 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/pages/home.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/logs_provider.dart';
@@ -71,179 +69,6 @@ Future<void> loadTranslations() async {
fallbackTranslations: controller.fallbackTranslations);
}
moveStrToEnd(List<String> arr, String str, {String? strB}) {
String? temp;
arr.removeWhere((element) {
bool res = element == str || element == strB;
if (res) {
temp = element;
}
return res;
});
if (temp != null) {
arr = [...arr, temp!];
}
return arr;
}
/// Background updater function
///
/// @param List<String>? toCheck: The appIds to check for updates (default to all apps sorted by last update check time)
///
/// @param List<String>? toInstall: The appIds to attempt to update (defaults to an empty array)
///
/// @param int? attemptCount: The number of times the function has failed up to this point (defaults to 0)
///
/// 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.
/// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified.
/// 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.
///
/// Once all update checks are complete, the function is called again in install mode.
/// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown).
/// 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.
///
/// In either mode, if the function fails after the maximum number of tries, the user is notified.
@pragma('vm:entry-point')
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
await AndroidAlarmManager.initialize();
await loadTranslations();
LogsProvider logs = LogsProvider();
NotificationsProvider notificationsProvider = NotificationsProvider();
AppsProvider appsProvider = AppsProvider(isBg: true);
await appsProvider.loadApps();
var settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
int maxAttempts = 4;
params ??= {};
if (params['toCheck'] == null) {
settingsProvider.lastBGCheckTime = DateTime.now();
}
int attemptCount = (params['attemptCount'] ?? 0) + 1;
List<String> toCheck = <String>[
...(params['toCheck'] ?? appsProvider.getAppsSortedByUpdateCheckTime())
];
List<String> toInstall = <String>[...(params['toInstall'] ?? (<String>[]))];
bool installMode = toCheck.isEmpty && toInstall.isNotEmpty;
logs.add(
'BG ${installMode ? 'install' : 'update'} task $taskId: Started${attemptCount > 1 ? '. ${attemptCount - 1} consecutive fail(s)' : ''}.');
if (!installMode) {
var didCompleteChecking = false;
CheckingUpdatesNotification? notif;
for (int i = 0; i < toCheck.length; i++) {
var appId = toCheck[i];
AppInMemory? app = appsProvider.apps[appId];
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 (!(await appsProvider.canInstallSilently(
app!.app, settingsProvider))) {
notificationsProvider.notify(
UpdateNotification([newApp], id: newApp.id.hashCode - 1));
} else {
toInstall.add(appId);
}
}
if (i == (toCheck.length - 1)) {
didCompleteChecking = true;
}
} catch (e) {
logs.add(
'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.');
if (attemptCount < maxAttempts) {
var remainingSeconds = e is RateLimitError
? (e.remainingMinutes * 60)
: e is ClientException
? (15 * 60)
: (attemptCount ^ 2);
logs.add(
'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToCheck = moveStrToEnd(toCheck.sublist(i), appId);
AndroidAlarmManager.oneShot(
Duration(seconds: remainingSeconds), taskId + 1, bgUpdateCheck,
params: {
'toCheck': remainingToCheck,
'toInstall': toInstall,
'attemptCount': attemptCount
});
break;
} else {
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
}
} finally {
if (notif != null) {
notificationsProvider.cancel(notif.id);
}
}
}
}
if (didCompleteChecking && toInstall.isNotEmpty) {
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});
} else if (didCompleteChecking) {
logs.add('BG install task $taskId: Done.');
}
} else {
var didCompleteInstalling = false;
toInstall = moveStrToEnd(toInstall, obtainiumId);
for (var i = 0; i < toInstall.length; i++) {
String appId = toInstall[i];
try {
logs.add(
'BG install task $taskId: Attempting to update $appId in the background.');
await appsProvider.downloadAndInstallLatestApps(
[appId], null, settingsProvider,
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) {
logs.add(
'BG install task $taskId: Got error on updating $appId \'${e.toString()}\'.');
if (attemptCount < maxAttempts) {
var remainingSeconds = attemptCount;
logs.add(
'BG install task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToInstall = moveStrToEnd(toInstall.sublist(i), appId);
AndroidAlarmManager.oneShot(
Duration(seconds: remainingSeconds), taskId + 1, bgUpdateCheck,
params: {
'toCheck': toCheck,
'toInstall': remainingToInstall,
'attemptCount': attemptCount
});
break;
} else {
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
}
}
if (didCompleteInstalling) {
logs.add('BG install task $taskId: Done.');
}
}
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
try {