mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-18 20:49:30 +02:00
Switched to WorkManager for reliability (#608)
This commit is contained in:
@@ -12,7 +12,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:easy_localization/src/easy_localization_controller.dart';
|
||||
@@ -23,7 +23,7 @@ const String currentVersion = '0.14.31';
|
||||
const String currentReleaseTag =
|
||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||
|
||||
const int bgUpdateCheckAlarmId = 666;
|
||||
const String bgUpdateTaskId = 'bgUpdate';
|
||||
|
||||
List<MapEntry<Locale, String>> supportedLocales = const [
|
||||
MapEntry(Locale('en'), 'English'),
|
||||
@@ -71,6 +71,17 @@ Future<void> loadTranslations() async {
|
||||
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 {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
try {
|
||||
@@ -88,7 +99,7 @@ void main() async {
|
||||
);
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
}
|
||||
await AndroidAlarmManager.initialize();
|
||||
await Workmanager().initialize(bgTaskDispatcher);
|
||||
runApp(MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (context) => AppsProvider()),
|
||||
@@ -158,7 +169,7 @@ class _ObtainiumState extends State<Obtainium> {
|
||||
var actualUpdateInterval = settingsProvider.updateInterval;
|
||||
if (existingUpdateInterval != actualUpdateInterval) {
|
||||
if (actualUpdateInterval == 0) {
|
||||
AndroidAlarmManager.cancel(bgUpdateCheckAlarmId);
|
||||
Workmanager().cancelByUniqueName(bgUpdateTaskId);
|
||||
} else {
|
||||
var settingChanged = existingUpdateInterval != -1;
|
||||
var lastCheckWasTooLongAgo = actualUpdateInterval != 0 &&
|
||||
@@ -168,12 +179,10 @@ class _ObtainiumState extends State<Obtainium> {
|
||||
if (settingChanged || lastCheckWasTooLongAgo) {
|
||||
logs.add(
|
||||
'Update interval was set to ${actualUpdateInterval.toString()} (reason: ${settingChanged ? 'setting changed' : 'last check was ${settingsProvider.lastBGCheckTime.toLocal().toString()}'}).');
|
||||
AndroidAlarmManager.periodic(
|
||||
Duration(minutes: actualUpdateInterval),
|
||||
bgUpdateCheckAlarmId,
|
||||
bgUpdateCheck,
|
||||
rescheduleOnReboot: true,
|
||||
wakeup: true);
|
||||
Workmanager().registerPeriodicTask(
|
||||
bgUpdateTaskId, "BG Update Main Loop",
|
||||
initialDelay: Duration(minutes: actualUpdateInterval),
|
||||
existingWorkPolicy: ExistingWorkPolicy.replace);
|
||||
}
|
||||
}
|
||||
existingUpdateInterval = actualUpdateInterval;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -591,10 +591,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
height16,
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
AndroidAlarmManager.oneShot(
|
||||
const Duration(seconds: 0),
|
||||
bgUpdateCheckAlarmId + 200,
|
||||
bgUpdateCheck);
|
||||
Workmanager().registerOneOffTask(
|
||||
'$bgUpdateTaskId+Manual', bgUpdateTaskId,
|
||||
existingWorkPolicy:
|
||||
ExistingWorkPolicy.replace);
|
||||
showMessage(tr('bgTaskStarted'), context);
|
||||
},
|
||||
child: Text(tr('runBgCheckNow')))
|
||||
|
@@ -7,7 +7,7 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:android_intent_plus/flag.dart';
|
||||
import 'package:android_package_installer/android_package_installer.dart';
|
||||
import 'package:android_package_manager/android_package_manager.dart';
|
||||
@@ -1356,14 +1356,14 @@ 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 an app repeatedly fails to install up to its retry limit, the user is notified.
|
||||
///
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
Future<bool> bgUpdateTask(String taskId, Map<String, dynamic>? params) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
await AndroidAlarmManager.initialize();
|
||||
await Workmanager().initialize(bgTaskDispatcher);
|
||||
await loadTranslations();
|
||||
|
||||
LogsProvider logs = LogsProvider();
|
||||
try {
|
||||
NotificationsProvider notificationsProvider = NotificationsProvider();
|
||||
AppsProvider appsProvider = AppsProvider(isBg: true);
|
||||
await appsProvider.loadApps();
|
||||
@@ -1375,10 +1375,10 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
appsProvider.settingsProvider.lastBGCheckTime = DateTime.now();
|
||||
}
|
||||
List<MapEntry<String, int>> toCheck = <MapEntry<String, int>>[
|
||||
...(params['toCheck']
|
||||
?.map((entry) => MapEntry<String, int>(
|
||||
entry['key'] as String, entry['value'] as int))
|
||||
.toList() ??
|
||||
...(params['toCheck']?.map((str) {
|
||||
var temp = str.split(',');
|
||||
return MapEntry<String, int>(temp[0], int.parse(temp[1]));
|
||||
}).toList() ??
|
||||
appsProvider
|
||||
.getAppsSortedByUpdateCheckTime(
|
||||
onlyCheckInstalledOrTrackOnlyApps: appsProvider
|
||||
@@ -1386,10 +1386,10 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
.map((e) => MapEntry(e, 0)))
|
||||
];
|
||||
List<MapEntry<String, int>> toInstall = <MapEntry<String, int>>[
|
||||
...(params['toInstall']
|
||||
?.map((entry) => MapEntry<String, int>(
|
||||
entry['key'] as String, entry['value'] as int))
|
||||
.toList() ??
|
||||
...(params['toInstall']?.map((str) {
|
||||
var temp = str.split(',');
|
||||
return MapEntry<String, int>(temp[0], int.parse(temp[1]));
|
||||
}).toList() ??
|
||||
(<List<MapEntry<String, int>>>[]))
|
||||
];
|
||||
|
||||
@@ -1403,18 +1403,17 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
DateTime.now().add(Duration(minutes: networkBasedRetryInterval));
|
||||
var shouldRetry = potentialNetworkRetryCheck.isBefore(nextRegularCheck);
|
||||
logs.add(
|
||||
'BG update task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.');
|
||||
AndroidAlarmManager.oneShot(
|
||||
const Duration(minutes: 15), taskId + 1, bgUpdateCheck,
|
||||
params: {
|
||||
'toCheck': toCheck
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList(),
|
||||
'BG task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.');
|
||||
await Workmanager().registerOneOffTask("$taskId+Retry", taskId,
|
||||
initialDelay: const Duration(minutes: 15),
|
||||
existingWorkPolicy: ExistingWorkPolicy.replace,
|
||||
inputData: {
|
||||
'toCheck':
|
||||
toCheck.map((entry) => '${entry.key},${entry.value}').toList(),
|
||||
'toInstall': toInstall
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${entry.value}')
|
||||
.toList(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var networkRestricted = false;
|
||||
@@ -1475,8 +1474,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
errors = e['errors'];
|
||||
errors!.rawErrors.forEach((key, err) {
|
||||
logs.add(
|
||||
'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
|
||||
var toCheckApp = toCheck.where((element) => element.key == key).first;
|
||||
'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
|
||||
@@ -1494,7 +1494,7 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
});
|
||||
} 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()}');
|
||||
logs.add('Fatal error in BG task: ${e.toString()}');
|
||||
rethrow;
|
||||
}
|
||||
} finally {
|
||||
@@ -1505,7 +1505,8 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
for (var i = 0; i < updates.length; i++) {
|
||||
if (networkRestricted ||
|
||||
!(await appsProvider.canInstallSilently(updates[i]))) {
|
||||
if (updates[i].additionalSettings['skipUpdateNotifications'] != true) {
|
||||
if (updates[i].additionalSettings['skipUpdateNotifications'] !=
|
||||
true) {
|
||||
toNotify.add(updates[i]);
|
||||
}
|
||||
}
|
||||
@@ -1527,28 +1528,29 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
|
||||
// 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: {
|
||||
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) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${entry.value}')
|
||||
.toList(),
|
||||
'toInstall': toInstall
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${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': [],
|
||||
'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) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${entry.value}')
|
||||
.toList()
|
||||
});
|
||||
}
|
||||
@@ -1577,7 +1579,7 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
var retryCount = toInstall[i].value;
|
||||
try {
|
||||
logs.add(
|
||||
'BG install task $taskId: Attempting to update $appId in the background.');
|
||||
'BG task $taskId: Attempting to update $appId in the background.');
|
||||
await appsProvider.downloadAndInstallLatestApps([appId], null,
|
||||
notificationsProvider: notificationsProvider);
|
||||
await Future.delayed(const Duration(
|
||||
@@ -1589,21 +1591,22 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
} 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()}\'.');
|
||||
'BG 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).');
|
||||
'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));
|
||||
AndroidAlarmManager.oneShot(
|
||||
Duration(seconds: remainingSeconds), taskId + 1, bgUpdateCheck,
|
||||
params: {
|
||||
await Workmanager().registerOneOffTask("$taskId+Retry", taskId,
|
||||
initialDelay: Duration(seconds: remainingSeconds),
|
||||
existingWorkPolicy: ExistingWorkPolicy.replace,
|
||||
inputData: {
|
||||
'toCheck': toCheck
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${entry.value}')
|
||||
.toList(),
|
||||
'toInstall': remainingToInstall
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.map((entry) => '${entry.key},${entry.value}')
|
||||
.toList(),
|
||||
});
|
||||
break;
|
||||
@@ -1617,7 +1620,12 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
}
|
||||
}
|
||||
if (didCompleteInstalling || toInstall.isEmpty) {
|
||||
logs.add('BG install task $taskId: Done.');
|
||||
logs.add('BG task $taskId: Done.');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
logs.add(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
18
pubspec.lock
18
pubspec.lock
@@ -1,14 +1,6 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -931,6 +923,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -956,5 +956,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.1.0 <4.0.0"
|
||||
dart: ">=3.1.2 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
|
@@ -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
|
||||
# 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.
|
||||
version: 0.14.31+223 # When changing this, update the tag in main() accordingly
|
||||
version: 0.14.31+224 # When changing this, update the tag in main() accordingly
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
@@ -57,7 +57,6 @@ dependencies:
|
||||
ref: main
|
||||
android_package_manager: ^0.6.0
|
||||
share_plus: ^7.0.0
|
||||
android_alarm_manager_plus: ^3.0.0
|
||||
sqflite: ^2.2.0+3
|
||||
easy_localization: ^3.0.1
|
||||
android_intent_plus: ^4.0.0
|
||||
@@ -66,6 +65,7 @@ dependencies:
|
||||
hsluv: ^1.1.3
|
||||
connectivity_plus: ^5.0.0
|
||||
shared_storage: ^0.8.0
|
||||
workmanager: ^0.5.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user