mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-22 09:09:41 +02: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:flutter/services.dart';
|
||||||
import 'package:obtainium/pages/home.dart';
|
import 'package:obtainium/pages/home.dart';
|
||||||
import 'package:obtainium/services/apps_provider.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/settings_provider.dart';
|
||||||
import 'package:obtainium/services/source_service.dart';
|
import 'package:obtainium/services/source_service.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
@@ -16,44 +17,23 @@ const String currentReleaseTag =
|
|||||||
void bgTaskCallback() {
|
void bgTaskCallback() {
|
||||||
Workmanager().executeTask((task, taskName) async {
|
Workmanager().executeTask((task, taskName) async {
|
||||||
var appsProvider = AppsProvider(bg: true);
|
var appsProvider = AppsProvider(bg: true);
|
||||||
await appsProvider.notify(
|
var notificationsProvider = NotificationsProvider();
|
||||||
4,
|
await notificationsProvider.notify(checkingUpdatesNotification);
|
||||||
'Checking for Updates',
|
|
||||||
'',
|
|
||||||
'BG_UPDATE_CHECK',
|
|
||||||
'Checking for Updates',
|
|
||||||
'Transient notification that appears when checking for updates',
|
|
||||||
important: false);
|
|
||||||
try {
|
try {
|
||||||
await appsProvider.loadApps();
|
await appsProvider.loadApps();
|
||||||
List<App> updates = await appsProvider.checkUpdates();
|
List<App> updates = await appsProvider.checkUpdates();
|
||||||
if (updates.isNotEmpty) {
|
if (updates.isNotEmpty) {
|
||||||
String message = updates.length == 1
|
notificationsProvider.notify(UpdateNotification(updates),
|
||||||
? '${updates[0].name} has an update.'
|
cancelExisting: true);
|
||||||
: '${(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');
|
|
||||||
}
|
}
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await appsProvider.downloaderNotifications.cancel(5);
|
notificationsProvider.notify(
|
||||||
await appsProvider.notify(
|
ErrorCheckingUpdatesNotification(e.toString()),
|
||||||
5,
|
cancelExisting: true);
|
||||||
'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);
|
|
||||||
return Future.value(false);
|
return Future.value(false);
|
||||||
} finally {
|
} finally {
|
||||||
await appsProvider.downloaderNotifications.cancel(4);
|
await notificationsProvider.cancel(checkingUpdatesNotification.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,7 +50,8 @@ void main() async {
|
|||||||
runApp(MultiProvider(
|
runApp(MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (context) => AppsProvider()),
|
ChangeNotifierProvider(create: (context) => AppsProvider()),
|
||||||
ChangeNotifierProvider(create: (context) => SettingsProvider())
|
ChangeNotifierProvider(create: (context) => SettingsProvider()),
|
||||||
|
Provider(create: (context) => NotificationsProvider())
|
||||||
],
|
],
|
||||||
child: const MyApp(),
|
child: const MyApp(),
|
||||||
));
|
));
|
||||||
|
@@ -6,9 +6,10 @@ import 'dart:io';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
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:path_provider/path_provider.dart';
|
||||||
import 'package:flutter_fgbg/flutter_fgbg.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:obtainium/services/source_service.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
||||||
@@ -28,16 +29,11 @@ class AppsProvider with ChangeNotifier {
|
|||||||
bool loadingApps = false;
|
bool loadingApps = false;
|
||||||
bool gettingUpdates = 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)
|
// Variables to keep track of the app foreground status (installs can't run in the background)
|
||||||
bool isForeground = true;
|
bool isForeground = true;
|
||||||
StreamSubscription<FGBGType>? foregroundSubscription;
|
StreamSubscription<FGBGType>? foregroundSubscription;
|
||||||
|
|
||||||
AppsProvider({bool bg = false}) {
|
AppsProvider({bool bg = false}) {
|
||||||
initializeNotifs();
|
|
||||||
// Subscribe to changes in the app foreground status
|
// Subscribe to changes in the app foreground status
|
||||||
foregroundSubscription = FGBGEvents.stream.listen((event) async {
|
foregroundSubscription = FGBGEvents.stream.listen((event) async {
|
||||||
isForeground = event == FGBGType.foreground;
|
isForeground = event == FGBGType.foreground;
|
||||||
@@ -46,30 +42,10 @@ class AppsProvider with ChangeNotifier {
|
|||||||
loadApps();
|
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)
|
// Given a App (assumed valid), initiate an APK download (will trigger install callback when complete)
|
||||||
Future<void> downloadAndInstallLatestApp(
|
Future<void> downloadAndInstallLatestApp(
|
||||||
String appId, BuildContext context) async {
|
String appId, BuildContext context) async {
|
||||||
|
var notificationsProvider = context.read<NotificationsProvider>();
|
||||||
if (apps[appId] == null) {
|
if (apps[appId] == null) {
|
||||||
throw 'App not found';
|
throw 'App not found';
|
||||||
}
|
}
|
||||||
@@ -132,14 +108,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isForeground) {
|
if (!isForeground) {
|
||||||
await downloaderNotifications.cancel(1);
|
await notificationsProvider.notify(completeInstallationNotification,
|
||||||
await notify(
|
cancelExisting: true);
|
||||||
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');
|
|
||||||
while (await FGBGEvents.stream.first != FGBGType.foreground) {
|
while (await FGBGEvents.stream.first != FGBGType.foreground) {
|
||||||
// We need to wait for the App to come to the foreground to install it
|
// 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:
|
// 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