mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-21 08:39:42 +02:00
Compare commits
2 Commits
v1.2.0
...
foreground
Author | SHA1 | Date | |
---|---|---|---|
|
2c6e95f902 | ||
|
7dbf3ac102 |
2
.flutter
2
.flutter
Submodule .flutter updated: 6fba2447e9...fcf2c11572
@@ -61,6 +61,13 @@
|
|||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||||
|
<service
|
||||||
|
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
|
android:exported="false" />
|
||||||
|
<meta-data
|
||||||
|
android:name="dev.imranr.obtainium.service.NOTIFICATION_ICON"
|
||||||
|
android:resource="@drawable/ic_notification" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
@@ -74,6 +81,8 @@
|
|||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="29" />
|
android:maxSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<!-- Required to query activities that can process text, see:
|
<!-- Required to query activities that can process text, see:
|
||||||
https://developer.android.com/training/package-visibility and
|
https://developer.android.com/training/package-visibility and
|
||||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
BIN
android/app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
BIN
android/app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 965 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
BIN
android/app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 843 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
BIN
android/app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
BIN
android/app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
@@ -330,6 +330,8 @@
|
|||||||
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
||||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||||
|
"foregroundService": "Obtainium foreground service",
|
||||||
|
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove app?",
|
"one": "Remove app?",
|
||||||
"other": "Remove apps?"
|
"other": "Remove apps?"
|
||||||
|
147
lib/main.dart
147
lib/main.dart
@@ -9,16 +9,15 @@ import 'package:obtainium/providers/native_provider.dart';
|
|||||||
import 'package:obtainium/providers/notifications_provider.dart';
|
import 'package:obtainium/providers/notifications_provider.dart';
|
||||||
import 'package:obtainium/providers/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
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:background_fetch/background_fetch.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';
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||||
|
|
||||||
List<MapEntry<Locale, String>> supportedLocales = const [
|
List<MapEntry<Locale, String>> supportedLocales = const [
|
||||||
MapEntry(Locale('en'), 'English'),
|
MapEntry(Locale('en'), 'English'),
|
||||||
@@ -86,16 +85,31 @@ Future<void> loadTranslations() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
void backgroundFetchHeadlessTask(HeadlessTask task) async {
|
void startCallback() {
|
||||||
String taskId = task.taskId;
|
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
|
||||||
bool isTimeout = task.timeout;
|
|
||||||
if (isTimeout) {
|
|
||||||
print('BG update task timed out.');
|
|
||||||
BackgroundFetch.finish(taskId);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await bgUpdateCheck(taskId, null);
|
|
||||||
BackgroundFetch.finish(taskId);
|
class MyTaskHandler extends TaskHandler {
|
||||||
|
static const String incrementCountCommand = 'incrementCount';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {
|
||||||
|
print('onStart(starter: ${starter.name})');
|
||||||
|
bgUpdateCheck('bg_check', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRepeatEvent(DateTime timestamp) {
|
||||||
|
bgUpdateCheck('bg_check', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onDestroy(DateTime timestamp, bool isTimeout) async {
|
||||||
|
print('Foreground service onDestroy(isTimeout: $isTimeout)');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReceiveData(Object data) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
@@ -119,6 +133,7 @@ void main() async {
|
|||||||
}
|
}
|
||||||
final np = NotificationsProvider();
|
final np = NotificationsProvider();
|
||||||
await np.initialize();
|
await np.initialize();
|
||||||
|
FlutterForegroundTask.initCommunicationPort();
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
@@ -136,7 +151,6 @@ void main() async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Obtainium extends StatefulWidget {
|
class Obtainium extends StatefulWidget {
|
||||||
@@ -152,32 +166,81 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
initPlatformState();
|
// FlutterForegroundTask.addTaskDataCallback(onReceiveForegroundServiceData);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
requestNonOptionalPermissions();
|
||||||
|
initForegroundService();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initPlatformState() async {
|
Future<void> requestNonOptionalPermissions() async {
|
||||||
await BackgroundFetch.configure(
|
final NotificationPermission notificationPermission =
|
||||||
BackgroundFetchConfig(
|
await FlutterForegroundTask.checkNotificationPermission();
|
||||||
minimumFetchInterval: 15,
|
if (notificationPermission != NotificationPermission.granted) {
|
||||||
stopOnTerminate: false,
|
await FlutterForegroundTask.requestNotificationPermission();
|
||||||
startOnBoot: true,
|
}
|
||||||
enableHeadless: true,
|
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
|
||||||
requiresBatteryNotLow: false,
|
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
|
||||||
requiresCharging: false,
|
}
|
||||||
requiresStorageNotLow: false,
|
}
|
||||||
requiresDeviceIdle: false,
|
|
||||||
requiredNetworkType: NetworkType.ANY,
|
void initForegroundService() {
|
||||||
|
FlutterForegroundTask.init(
|
||||||
|
androidNotificationOptions: AndroidNotificationOptions(
|
||||||
|
channelId: 'bg_update',
|
||||||
|
channelName: tr('foregroundService'),
|
||||||
|
channelDescription: tr('foregroundService'),
|
||||||
|
onlyAlertOnce: true,
|
||||||
|
),
|
||||||
|
iosNotificationOptions: const IOSNotificationOptions(
|
||||||
|
showNotification: false,
|
||||||
|
playSound: false,
|
||||||
|
),
|
||||||
|
foregroundTaskOptions: ForegroundTaskOptions(
|
||||||
|
eventAction: ForegroundTaskEventAction.repeat(900000),
|
||||||
|
autoRunOnBoot: true,
|
||||||
|
autoRunOnMyPackageReplaced: true,
|
||||||
|
allowWakeLock: true,
|
||||||
|
allowWifiLock: true,
|
||||||
),
|
),
|
||||||
(String taskId) async {
|
|
||||||
await bgUpdateCheck(taskId, null);
|
|
||||||
BackgroundFetch.finish(taskId);
|
|
||||||
},
|
|
||||||
(String taskId) async {
|
|
||||||
context.read<LogsProvider>().add('BG update task timed out.');
|
|
||||||
BackgroundFetch.finish(taskId);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
}
|
||||||
|
|
||||||
|
Future<ServiceRequestResult?> startForegroundService(bool restart) async {
|
||||||
|
if (await FlutterForegroundTask.isRunningService) {
|
||||||
|
if (restart) {
|
||||||
|
return FlutterForegroundTask.restartService();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return FlutterForegroundTask.startService(
|
||||||
|
serviceTypes: [ForegroundServiceTypes.specialUse],
|
||||||
|
serviceId: 666,
|
||||||
|
notificationTitle: tr('foregroundService'),
|
||||||
|
notificationText: tr('fgServiceNotice'),
|
||||||
|
notificationIcon: NotificationIcon(
|
||||||
|
metaDataName: 'dev.imranr.obtainium.service.NOTIFICATION_ICON',
|
||||||
|
),
|
||||||
|
callback: startCallback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopForegroundService() async {
|
||||||
|
if (await FlutterForegroundTask.isRunningService) {
|
||||||
|
return FlutterForegroundTask.stopService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void onReceiveForegroundServiceData(Object data) {
|
||||||
|
// print('onReceiveTaskData: $data');
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// Remove a callback to receive data sent from the TaskHandler.
|
||||||
|
// FlutterForegroundTask.removeTaskDataCallback(onReceiveForegroundServiceData);
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -186,15 +249,18 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||||
LogsProvider logs = context.read<LogsProvider>();
|
LogsProvider logs = context.read<LogsProvider>();
|
||||||
NotificationsProvider notifs = context.read<NotificationsProvider>();
|
NotificationsProvider notifs = context.read<NotificationsProvider>();
|
||||||
|
if (settingsProvider.updateInterval == 0) {
|
||||||
|
stopForegroundService();
|
||||||
|
} else {
|
||||||
|
startForegroundService(false);
|
||||||
|
}
|
||||||
if (settingsProvider.prefs == null) {
|
if (settingsProvider.prefs == null) {
|
||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings();
|
||||||
} else {
|
} else {
|
||||||
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
||||||
if (isFirstRun) {
|
if (isFirstRun) {
|
||||||
logs.add('This is the first ever run of Obtainium.');
|
logs.add('This is the first ever run of Obtainium.');
|
||||||
// If this is the first run, ask for notification permissions and add Obtainium to the Apps list
|
// If this is the first run, add Obtainium to the Apps list
|
||||||
Permission.notification.request();
|
|
||||||
if (!fdroid) {
|
if (!fdroid) {
|
||||||
getInstalledInfo(obtainiumId)
|
getInstalledInfo(obtainiumId)
|
||||||
.then((value) {
|
.then((value) {
|
||||||
@@ -236,7 +302,8 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
notifs.checkLaunchByNotif();
|
notifs.checkLaunchByNotif();
|
||||||
});
|
});
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return WithForegroundTask(
|
||||||
|
child: DynamicColorBuilder(
|
||||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||||
// Decide on a colour/brightness scheme based on OS and user settings
|
// Decide on a colour/brightness scheme based on OS and user settings
|
||||||
ColorScheme lightColorScheme;
|
ColorScheme lightColorScheme;
|
||||||
@@ -292,12 +359,14 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
),
|
),
|
||||||
home: Shortcuts(
|
home: Shortcuts(
|
||||||
shortcuts: <LogicalKeySet, Intent>{
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
LogicalKeySet(LogicalKeyboardKey.select):
|
||||||
|
const ActivateIntent(),
|
||||||
},
|
},
|
||||||
child: const HomePage(),
|
child: const HomePage(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
pubspec.lock
26
pubspec.lock
@@ -104,10 +104,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: battery_plus
|
name: battery_plus
|
||||||
sha256: a0409fe7d21905987eb1348ad57c634f913166f14f0c8936b73d3f5940fac551
|
sha256: fb794c34cee2e4ea31005fb17ff15e1d904951ec7f15eedead741021870ee834
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.1"
|
version: "6.2.2"
|
||||||
battery_plus_platform_interface:
|
battery_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -216,18 +216,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53"
|
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.4.0"
|
version: "11.5.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus_platform_interface
|
name: device_info_plus_platform_interface
|
||||||
sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2"
|
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.0.3"
|
||||||
dynamic_color:
|
dynamic_color:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -377,6 +377,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.1"
|
version: "0.7.1"
|
||||||
|
flutter_foreground_task:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_foreground_task
|
||||||
|
sha256: "9f1b25a81db95d7119d2c5cffc654048cbdd49d4056183e1beadc1a6a38f3e29"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.0"
|
||||||
flutter_keyboard_visibility:
|
flutter_keyboard_visibility:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -995,10 +1003,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
|
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.4.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1201,4 +1209,4 @@ packages:
|
|||||||
version: "6.3.0"
|
version: "6.3.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.1 <4.0.0"
|
dart: ">=3.8.1 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.29.0"
|
||||||
|
@@ -94,6 +94,7 @@ dependencies:
|
|||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
|
flutter_foreground_task: ^9.1.0
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: "ic_launcher"
|
android: "ic_launcher"
|
||||||
|
Reference in New Issue
Block a user