Added foreground service option + other updates

This commit is contained in:
Imran Remtulla
2025-07-02 20:09:20 -04:00
parent 09d729261c
commit 918091ba54
36 changed files with 343 additions and 94 deletions

View File

@@ -9,7 +9,6 @@ import 'package:obtainium/providers/native_provider.dart';
import 'package:obtainium/providers/notifications_provider.dart';
import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
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';
@@ -19,6 +18,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('en'), 'English'),
@@ -51,6 +51,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('ko'), '한국어'),
MapEntry(Locale('ca'), 'Català'),
MapEntry(Locale('ar'), 'العربية'),
MapEntry(Locale('ml'), 'മലയാളം'),
];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';
@@ -98,6 +99,34 @@ void backgroundFetchHeadlessTask(HeadlessTask task) async {
BackgroundFetch.finish(taskId);
}
@pragma('vm:entry-point')
void startCallback() {
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
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 {
WidgetsFlutterBinding.ensureInitialized();
try {
@@ -119,6 +148,7 @@ void main() async {
}
final np = NotificationsProvider();
await np.initialize();
FlutterForegroundTask.initCommunicationPort();
runApp(
MultiProvider(
providers: [
@@ -153,6 +183,80 @@ class _ObtainiumState extends State<Obtainium> {
void initState() {
super.initState();
initPlatformState();
WidgetsBinding.instance.addPostFrameCallback((_) {
requestNonOptionalPermissions();
initForegroundService();
});
}
Future<void> requestNonOptionalPermissions() async {
final NotificationPermission notificationPermission =
await FlutterForegroundTask.checkNotificationPermission();
if (notificationPermission != NotificationPermission.granted) {
await FlutterForegroundTask.requestNotificationPermission();
}
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
}
}
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,
),
);
}
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();
}
Future<void> initPlatformState() async {
@@ -186,15 +290,25 @@ class _ObtainiumState extends State<Obtainium> {
AppsProvider appsProvider = context.read<AppsProvider>();
LogsProvider logs = context.read<LogsProvider>();
NotificationsProvider notifs = context.read<NotificationsProvider>();
if (settingsProvider.updateInterval == 0) {
stopForegroundService();
BackgroundFetch.stop();
} else {
if (settingsProvider.useFGService) {
BackgroundFetch.stop();
startForegroundService(false);
} else {
stopForegroundService();
BackgroundFetch.start();
}
}
if (settingsProvider.prefs == null) {
settingsProvider.initializeSettings();
} else {
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
if (isFirstRun) {
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
Permission.notification.request();
// If this is the first run, add Obtainium to the Apps list
if (!fdroid) {
getInstalledInfo(obtainiumId)
.then((value) {
@@ -236,68 +350,71 @@ class _ObtainiumState extends State<Obtainium> {
notifs.checkLaunchByNotif();
});
return DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
// Decide on a colour/brightness scheme based on OS and user settings
ColorScheme lightColorScheme;
ColorScheme darkColorScheme;
if (lightDynamic != null &&
darkDynamic != null &&
settingsProvider.useMaterialYou) {
lightColorScheme = lightDynamic.harmonized();
darkColorScheme = darkDynamic.harmonized();
} else {
lightColorScheme = ColorScheme.fromSeed(
seedColor: settingsProvider.themeColor,
return WithForegroundTask(
child: DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
// Decide on a colour/brightness scheme based on OS and user settings
ColorScheme lightColorScheme;
ColorScheme darkColorScheme;
if (lightDynamic != null &&
darkDynamic != null &&
settingsProvider.useMaterialYou) {
lightColorScheme = lightDynamic.harmonized();
darkColorScheme = darkDynamic.harmonized();
} else {
lightColorScheme = ColorScheme.fromSeed(
seedColor: settingsProvider.themeColor,
);
darkColorScheme = ColorScheme.fromSeed(
seedColor: settingsProvider.themeColor,
brightness: Brightness.dark,
);
}
// set the background and surface colors to pure black in the amoled theme
if (settingsProvider.useBlackTheme) {
darkColorScheme = darkColorScheme
.copyWith(surface: Colors.black)
.harmonized();
}
if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont();
return MaterialApp(
title: 'Obtainium',
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
navigatorKey: globalNavigatorKey,
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.dark
? darkColorScheme
: lightColorScheme,
fontFamily: settingsProvider.useSystemFont
? 'SystemFont'
: 'Montserrat',
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.light
? lightColorScheme
: darkColorScheme,
fontFamily: settingsProvider.useSystemFont
? 'SystemFont'
: 'Montserrat',
),
home: Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select):
const ActivateIntent(),
},
child: const HomePage(),
),
);
darkColorScheme = ColorScheme.fromSeed(
seedColor: settingsProvider.themeColor,
brightness: Brightness.dark,
);
}
// set the background and surface colors to pure black in the amoled theme
if (settingsProvider.useBlackTheme) {
darkColorScheme = darkColorScheme
.copyWith(surface: Colors.black)
.harmonized();
}
if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont();
return MaterialApp(
title: 'Obtainium',
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
navigatorKey: globalNavigatorKey,
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.dark
? darkColorScheme
: lightColorScheme,
fontFamily: settingsProvider.useSystemFont
? 'SystemFont'
: 'Montserrat',
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.light
? lightColorScheme
: darkColorScheme,
fontFamily: settingsProvider.useSystemFont
? 'SystemFont'
: 'Montserrat',
),
home: Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
},
child: const HomePage(),
),
);
},
},
),
);
}
}