mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-04 07:13:28 +01:00 
			
		
		
		
	Separated notification service
This commit is contained in:
		@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:obtainium/pages/home.dart';
 | 
			
		||||
import 'package:obtainium/services/apps_provider.dart';
 | 
			
		||||
import 'package:obtainium/services/notifications_provider.dart';
 | 
			
		||||
import 'package:obtainium/services/settings_provider.dart';
 | 
			
		||||
import 'package:obtainium/services/source_service.dart';
 | 
			
		||||
import 'package:permission_handler/permission_handler.dart';
 | 
			
		||||
@@ -16,44 +17,23 @@ const String currentReleaseTag =
 | 
			
		||||
void bgTaskCallback() {
 | 
			
		||||
  Workmanager().executeTask((task, taskName) async {
 | 
			
		||||
    var appsProvider = AppsProvider(bg: true);
 | 
			
		||||
    await appsProvider.notify(
 | 
			
		||||
        4,
 | 
			
		||||
        'Checking for Updates',
 | 
			
		||||
        '',
 | 
			
		||||
        'BG_UPDATE_CHECK',
 | 
			
		||||
        'Checking for Updates',
 | 
			
		||||
        'Transient notification that appears when checking for updates',
 | 
			
		||||
        important: false);
 | 
			
		||||
    var notificationsProvider = NotificationsProvider();
 | 
			
		||||
    await notificationsProvider.notify(checkingUpdatesNotification);
 | 
			
		||||
    try {
 | 
			
		||||
      await appsProvider.loadApps();
 | 
			
		||||
      List<App> updates = await appsProvider.checkUpdates();
 | 
			
		||||
      if (updates.isNotEmpty) {
 | 
			
		||||
        String message = updates.length == 1
 | 
			
		||||
            ? '${updates[0].name} has an update.'
 | 
			
		||||
            : '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} have updates.';
 | 
			
		||||
        await appsProvider.downloaderNotifications.cancel(2);
 | 
			
		||||
        await appsProvider.notify(
 | 
			
		||||
            2,
 | 
			
		||||
            'Updates Available',
 | 
			
		||||
            message,
 | 
			
		||||
            'UPDATES_AVAILABLE',
 | 
			
		||||
            'Updates Available',
 | 
			
		||||
            'Notifies the user that updates are available for one or more Apps tracked by Obtainium');
 | 
			
		||||
        notificationsProvider.notify(UpdateNotification(updates),
 | 
			
		||||
            cancelExisting: true);
 | 
			
		||||
      }
 | 
			
		||||
      return Future.value(true);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      await appsProvider.downloaderNotifications.cancel(5);
 | 
			
		||||
      await appsProvider.notify(
 | 
			
		||||
          5,
 | 
			
		||||
          'Error Checking for Updates',
 | 
			
		||||
          e.toString(),
 | 
			
		||||
          'BG_UPDATE_CHECK_ERROR',
 | 
			
		||||
          'Error Checking for Updates',
 | 
			
		||||
          'A notification that shows when background update checking fails',
 | 
			
		||||
          important: false);
 | 
			
		||||
      notificationsProvider.notify(
 | 
			
		||||
          ErrorCheckingUpdatesNotification(e.toString()),
 | 
			
		||||
          cancelExisting: true);
 | 
			
		||||
      return Future.value(false);
 | 
			
		||||
    } finally {
 | 
			
		||||
      await appsProvider.downloaderNotifications.cancel(4);
 | 
			
		||||
      await notificationsProvider.cancel(checkingUpdatesNotification.id);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
@@ -70,7 +50,8 @@ void main() async {
 | 
			
		||||
  runApp(MultiProvider(
 | 
			
		||||
    providers: [
 | 
			
		||||
      ChangeNotifierProvider(create: (context) => AppsProvider()),
 | 
			
		||||
      ChangeNotifierProvider(create: (context) => SettingsProvider())
 | 
			
		||||
      ChangeNotifierProvider(create: (context) => SettingsProvider()),
 | 
			
		||||
      Provider(create: (context) => NotificationsProvider())
 | 
			
		||||
    ],
 | 
			
		||||
    child: const MyApp(),
 | 
			
		||||
  ));
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,10 @@ import 'dart:io';
 | 
			
		||||
import 'dart:ui';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:obtainium/services/notifications_provider.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
import 'package:flutter_fgbg/flutter_fgbg.dart';
 | 
			
		||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
 | 
			
		||||
import 'package:obtainium/services/source_service.dart';
 | 
			
		||||
import 'package:http/http.dart';
 | 
			
		||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
 | 
			
		||||
@@ -28,16 +29,11 @@ class AppsProvider with ChangeNotifier {
 | 
			
		||||
  bool loadingApps = false;
 | 
			
		||||
  bool gettingUpdates = false;
 | 
			
		||||
 | 
			
		||||
  // Notifications plugin for downloads
 | 
			
		||||
  FlutterLocalNotificationsPlugin downloaderNotifications =
 | 
			
		||||
      FlutterLocalNotificationsPlugin();
 | 
			
		||||
 | 
			
		||||
  // Variables to keep track of the app foreground status (installs can't run in the background)
 | 
			
		||||
  bool isForeground = true;
 | 
			
		||||
  StreamSubscription<FGBGType>? foregroundSubscription;
 | 
			
		||||
 | 
			
		||||
  AppsProvider({bool bg = false}) {
 | 
			
		||||
    initializeNotifs();
 | 
			
		||||
    // Subscribe to changes in the app foreground status
 | 
			
		||||
    foregroundSubscription = FGBGEvents.stream.listen((event) async {
 | 
			
		||||
      isForeground = event == FGBGType.foreground;
 | 
			
		||||
@@ -46,30 +42,10 @@ class AppsProvider with ChangeNotifier {
 | 
			
		||||
    loadApps();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> initializeNotifs() async {
 | 
			
		||||
    // Initialize the notifications service
 | 
			
		||||
    await downloaderNotifications.initialize(const InitializationSettings(
 | 
			
		||||
        android: AndroidInitializationSettings('ic_notification')));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> notify(int id, String title, String message, String channelCode,
 | 
			
		||||
      String channelName, String channelDescription,
 | 
			
		||||
      {bool important = true}) {
 | 
			
		||||
    return downloaderNotifications.show(
 | 
			
		||||
        id,
 | 
			
		||||
        title,
 | 
			
		||||
        message,
 | 
			
		||||
        NotificationDetails(
 | 
			
		||||
            android: AndroidNotificationDetails(channelCode, channelName,
 | 
			
		||||
                channelDescription: channelDescription,
 | 
			
		||||
                importance: important ? Importance.max : Importance.min,
 | 
			
		||||
                priority: important ? Priority.max : Priority.min,
 | 
			
		||||
                groupKey: 'dev.imranr.obtainium.$channelCode')));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Given a App (assumed valid), initiate an APK download (will trigger install callback when complete)
 | 
			
		||||
  Future<void> downloadAndInstallLatestApp(
 | 
			
		||||
      String appId, BuildContext context) async {
 | 
			
		||||
    var notificationsProvider = context.read<NotificationsProvider>();
 | 
			
		||||
    if (apps[appId] == null) {
 | 
			
		||||
      throw 'App not found';
 | 
			
		||||
    }
 | 
			
		||||
@@ -132,14 +108,8 @@ class AppsProvider with ChangeNotifier {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!isForeground) {
 | 
			
		||||
      await downloaderNotifications.cancel(1);
 | 
			
		||||
      await notify(
 | 
			
		||||
          1,
 | 
			
		||||
          'Complete App Installation',
 | 
			
		||||
          'Obtainium must be open to install Apps',
 | 
			
		||||
          'COMPLETE_INSTALL',
 | 
			
		||||
          'Complete App Installation',
 | 
			
		||||
          'Asks the user to return to Obtanium to finish installing an App');
 | 
			
		||||
      await notificationsProvider.notify(completeInstallationNotification,
 | 
			
		||||
          cancelExisting: true);
 | 
			
		||||
      while (await FGBGEvents.stream.first != FGBGType.foreground) {
 | 
			
		||||
        // We need to wait for the App to come to the foreground to install it
 | 
			
		||||
        // Can't try to call install plugin in a background isolate (may not have worked anyways) because of:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								lib/services/notifications_provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								lib/services/notifications_provider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
 | 
			
		||||
import 'package:obtainium/services/source_service.dart';
 | 
			
		||||
 | 
			
		||||
class ObtainiumNotification {
 | 
			
		||||
  late int id;
 | 
			
		||||
  late String title;
 | 
			
		||||
  late String message;
 | 
			
		||||
  late String channelCode;
 | 
			
		||||
  late String channelName;
 | 
			
		||||
  late String channelDescription;
 | 
			
		||||
  Importance importance;
 | 
			
		||||
 | 
			
		||||
  ObtainiumNotification(this.id, this.title, this.message, this.channelCode,
 | 
			
		||||
      this.channelName, this.channelDescription, this.importance);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class UpdateNotification extends ObtainiumNotification {
 | 
			
		||||
  UpdateNotification(List<App> updates)
 | 
			
		||||
      : super(
 | 
			
		||||
            2,
 | 
			
		||||
            'Updates Available',
 | 
			
		||||
            '',
 | 
			
		||||
            'UPDATES_AVAILABLE',
 | 
			
		||||
            'Updates Available',
 | 
			
		||||
            'Notifies the user that updates are available for one or more Apps tracked by Obtainium',
 | 
			
		||||
            Importance.max) {
 | 
			
		||||
    message = updates.length == 1
 | 
			
		||||
        ? '${updates[0].name} has an update.'
 | 
			
		||||
        : '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} have updates.';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
 | 
			
		||||
  ErrorCheckingUpdatesNotification(String error)
 | 
			
		||||
      : super(
 | 
			
		||||
            5,
 | 
			
		||||
            'Error Checking for Updates',
 | 
			
		||||
            error,
 | 
			
		||||
            'BG_UPDATE_CHECK_ERROR',
 | 
			
		||||
            'Error Checking for Updates',
 | 
			
		||||
            'A notification that shows when background update checking fails',
 | 
			
		||||
            Importance.high);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
final completeInstallationNotification = ObtainiumNotification(
 | 
			
		||||
    1,
 | 
			
		||||
    'Complete App Installation',
 | 
			
		||||
    'Obtainium must be open to install Apps',
 | 
			
		||||
    'COMPLETE_INSTALL',
 | 
			
		||||
    'Complete App Installation',
 | 
			
		||||
    'Asks the user to return to Obtanium to finish installing an App',
 | 
			
		||||
    Importance.max);
 | 
			
		||||
 | 
			
		||||
final checkingUpdatesNotification = ObtainiumNotification(
 | 
			
		||||
    4,
 | 
			
		||||
    'Checking for Updates',
 | 
			
		||||
    '',
 | 
			
		||||
    'BG_UPDATE_CHECK',
 | 
			
		||||
    'Checking for Updates',
 | 
			
		||||
    'Transient notification that appears when checking for updates',
 | 
			
		||||
    Importance.min);
 | 
			
		||||
 | 
			
		||||
class NotificationsProvider {
 | 
			
		||||
  FlutterLocalNotificationsPlugin notifications =
 | 
			
		||||
      FlutterLocalNotificationsPlugin();
 | 
			
		||||
 | 
			
		||||
  bool isInitialized = false;
 | 
			
		||||
 | 
			
		||||
  Map<Importance, Priority> importanceToPriority = {
 | 
			
		||||
    Importance.defaultImportance: Priority.defaultPriority,
 | 
			
		||||
    Importance.high: Priority.high,
 | 
			
		||||
    Importance.low: Priority.low,
 | 
			
		||||
    Importance.max: Priority.max,
 | 
			
		||||
    Importance.min: Priority.min,
 | 
			
		||||
    Importance.none: Priority.min,
 | 
			
		||||
    Importance.unspecified: Priority.defaultPriority
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Future<void> initialize() async {
 | 
			
		||||
    isInitialized = await notifications.initialize(const InitializationSettings(
 | 
			
		||||
            android: AndroidInitializationSettings('ic_notification'))) ??
 | 
			
		||||
        false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> cancel(int id) async {
 | 
			
		||||
    if (!isInitialized) {
 | 
			
		||||
      await initialize();
 | 
			
		||||
    }
 | 
			
		||||
    await notifications.cancel(id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> notifyRaw(
 | 
			
		||||
      int id,
 | 
			
		||||
      String title,
 | 
			
		||||
      String message,
 | 
			
		||||
      String channelCode,
 | 
			
		||||
      String channelName,
 | 
			
		||||
      String channelDescription,
 | 
			
		||||
      Importance importance,
 | 
			
		||||
      {bool cancelExisting = false}) async {
 | 
			
		||||
    if (cancelExisting) {
 | 
			
		||||
      await cancel(id);
 | 
			
		||||
    }
 | 
			
		||||
    if (!isInitialized) {
 | 
			
		||||
      await initialize();
 | 
			
		||||
    }
 | 
			
		||||
    await notifications.show(
 | 
			
		||||
        id,
 | 
			
		||||
        title,
 | 
			
		||||
        message,
 | 
			
		||||
        NotificationDetails(
 | 
			
		||||
            android: AndroidNotificationDetails(channelCode, channelName,
 | 
			
		||||
                channelDescription: channelDescription,
 | 
			
		||||
                importance: importance,
 | 
			
		||||
                priority: importanceToPriority[importance]!,
 | 
			
		||||
                groupKey: 'dev.imranr.obtainium.$channelCode')));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> notify(ObtainiumNotification notif,
 | 
			
		||||
          {bool cancelExisting = false}) =>
 | 
			
		||||
      notifyRaw(notif.id, notif.title, notif.message, notif.channelCode,
 | 
			
		||||
          notif.channelName, notif.channelDescription, notif.importance,
 | 
			
		||||
          cancelExisting: cancelExisting);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user