♿ Optimize notification background service
This commit is contained in:
parent
a487924300
commit
306ce9e2b4
109
lib/background.dart
Normal file
109
lib/background.dart
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart' hide Notification;
|
||||||
|
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:solian/models/notification.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/providers/websocket.dart';
|
||||||
|
|
||||||
|
FlutterBackgroundService? bgNotificationService;
|
||||||
|
|
||||||
|
void autoConfigureBackgroundNotificationService() async {
|
||||||
|
if (bgNotificationService != null) return;
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.getBool('service_background_notification') != true) return;
|
||||||
|
|
||||||
|
bgNotificationService = FlutterBackgroundService();
|
||||||
|
|
||||||
|
await bgNotificationService!.configure(
|
||||||
|
androidConfiguration: AndroidConfiguration(
|
||||||
|
onStart: onBackgroundNotificationServiceStart,
|
||||||
|
autoStart: true,
|
||||||
|
autoStartOnBoot: true,
|
||||||
|
isForegroundMode: false,
|
||||||
|
),
|
||||||
|
// This feature won't be able to use on iOS
|
||||||
|
// We got APNs support covered
|
||||||
|
iosConfiguration: IosConfiguration(
|
||||||
|
autoStart: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void autoStartBackgroundNotificationService() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.getBool('service_background_notification') != true) return;
|
||||||
|
if (bgNotificationService == null) return;
|
||||||
|
bgNotificationService!.startService();
|
||||||
|
}
|
||||||
|
|
||||||
|
void autoStopBackgroundNotificationService() async {
|
||||||
|
if (bgNotificationService == null) return;
|
||||||
|
if (await bgNotificationService!.isRunning()) {
|
||||||
|
bgNotificationService?.invoke('stopService');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void onBackgroundNotificationServiceStart(ServiceInstance service) async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
DartPluginRegistrant.ensureInitialized();
|
||||||
|
|
||||||
|
Get.put(AuthProvider());
|
||||||
|
Get.put(WebSocketProvider());
|
||||||
|
|
||||||
|
service.on('stopService').listen((event) {
|
||||||
|
service.stopSelf();
|
||||||
|
});
|
||||||
|
|
||||||
|
final auth = Get.find<AuthProvider>();
|
||||||
|
await auth.refreshAuthorizeStatus();
|
||||||
|
await auth.ensureCredentials();
|
||||||
|
if (!auth.isAuthorized.value) {
|
||||||
|
debugPrint(
|
||||||
|
'Background notification do nothing due to user didn\'t sign in.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notificationChannelId = 'solian_notification_service';
|
||||||
|
|
||||||
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
|
final ws = Get.find<WebSocketProvider>();
|
||||||
|
await ws.connect();
|
||||||
|
debugPrint('Background notification has been started');
|
||||||
|
ws.stream.stream.listen(
|
||||||
|
(event) {
|
||||||
|
debugPrint(
|
||||||
|
'Background notification service incoming message: ${event.method} ${event.message}',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (event.method == 'notifications.new' && event.payload != null) {
|
||||||
|
final data = Notification.fromJson(event.payload!);
|
||||||
|
debugPrint(
|
||||||
|
'Background notification service got a notification id=${data.id}',
|
||||||
|
);
|
||||||
|
flutterLocalNotificationsPlugin.show(
|
||||||
|
data.id,
|
||||||
|
data.title,
|
||||||
|
[data.subtitle, data.body].where((x) => x != null).join('\n'),
|
||||||
|
const NotificationDetails(
|
||||||
|
android: AndroidNotificationDetails(
|
||||||
|
notificationChannelId,
|
||||||
|
'Solian Notification Service',
|
||||||
|
channelDescription: 'Notifications that sent via Solar Network',
|
||||||
|
importance: Importance.high,
|
||||||
|
icon: 'mipmap/ic_launcher',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -4,16 +4,13 @@ import 'package:firebase_core/firebase_core.dart';
|
|||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/material.dart' hide Notification;
|
import 'package:flutter/material.dart' hide Notification;
|
||||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:protocol_handler/protocol_handler.dart';
|
import 'package:protocol_handler/protocol_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:solian/background.dart';
|
||||||
import 'package:solian/bootstrapper.dart';
|
import 'package:solian/bootstrapper.dart';
|
||||||
import 'package:solian/firebase_options.dart';
|
import 'package:solian/firebase_options.dart';
|
||||||
import 'package:solian/models/notification.dart';
|
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/attachment_uploader.dart';
|
import 'package:solian/providers/attachment_uploader.dart';
|
||||||
import 'package:solian/providers/daily_sign.dart';
|
import 'package:solian/providers/daily_sign.dart';
|
||||||
@ -70,82 +67,7 @@ Future<void> _initializeFirebase() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializeBackgroundNotificationService() async {
|
Future<void> _initializeBackgroundNotificationService() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
autoConfigureBackgroundNotificationService();
|
||||||
if (prefs.getBool('service_background_notification') != true) return;
|
|
||||||
|
|
||||||
final service = FlutterBackgroundService();
|
|
||||||
|
|
||||||
await service.configure(
|
|
||||||
androidConfiguration: AndroidConfiguration(
|
|
||||||
onStart: onBackgroundNotificationServiceStart,
|
|
||||||
autoStart: true,
|
|
||||||
autoStartOnBoot: true,
|
|
||||||
isForegroundMode: false,
|
|
||||||
),
|
|
||||||
// This feature won't be able to use on iOS
|
|
||||||
// We got APNs support covered
|
|
||||||
iosConfiguration: IosConfiguration(
|
|
||||||
autoStart: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await service.startService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
|
||||||
void onBackgroundNotificationServiceStart(ServiceInstance service) async {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
DartPluginRegistrant.ensureInitialized();
|
|
||||||
|
|
||||||
Get.put(AuthProvider());
|
|
||||||
Get.put(WebSocketProvider());
|
|
||||||
|
|
||||||
final auth = Get.find<AuthProvider>();
|
|
||||||
await auth.refreshAuthorizeStatus();
|
|
||||||
await auth.ensureCredentials();
|
|
||||||
if (!auth.isAuthorized.value) {
|
|
||||||
debugPrint(
|
|
||||||
'Background notification do nothing due to user didn\'t sign in.',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const notificationChannelId = 'solian_notification_service';
|
|
||||||
|
|
||||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
|
||||||
FlutterLocalNotificationsPlugin();
|
|
||||||
|
|
||||||
final ws = Get.find<WebSocketProvider>();
|
|
||||||
await ws.connect();
|
|
||||||
debugPrint('Background notification has been started');
|
|
||||||
ws.stream.stream.listen(
|
|
||||||
(event) {
|
|
||||||
debugPrint(
|
|
||||||
'Background notification service incoming message: ${event.method} ${event.message}',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.method == 'notifications.new' && event.payload != null) {
|
|
||||||
final data = Notification.fromJson(event.payload!);
|
|
||||||
debugPrint(
|
|
||||||
'Background notification service got a notification id=${data.id}',
|
|
||||||
);
|
|
||||||
flutterLocalNotificationsPlugin.show(
|
|
||||||
data.id,
|
|
||||||
data.title,
|
|
||||||
[data.subtitle, data.body].where((x) => x != null).join('\n'),
|
|
||||||
const NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails(
|
|
||||||
notificationChannelId,
|
|
||||||
'Solian Notification Service',
|
|
||||||
channelDescription: 'Notifications that sent via Solar Network',
|
|
||||||
importance: Importance.high,
|
|
||||||
icon: 'mipmap/ic_launcher',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializePlatformComponents() async {
|
Future<void> _initializePlatformComponents() async {
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get/get_connect/http/src/request/request.dart';
|
import 'package:get/get_connect/http/src/request/request.dart';
|
||||||
|
import 'package:solian/background.dart';
|
||||||
import 'package:solian/exceptions/request.dart';
|
import 'package:solian/exceptions/request.dart';
|
||||||
import 'package:solian/exceptions/unauthorized.dart';
|
import 'package:solian/exceptions/unauthorized.dart';
|
||||||
import 'package:solian/providers/database/database.dart';
|
import 'package:solian/providers/database/database.dart';
|
||||||
@ -200,6 +201,7 @@ class AuthProvider extends GetConnect {
|
|||||||
Get.find<WebSocketProvider>().notificationUnread.value = 0;
|
Get.find<WebSocketProvider>().notificationUnread.value = 0;
|
||||||
|
|
||||||
AppDatabase.removeDatabase();
|
AppDatabase.removeDatabase();
|
||||||
|
autoStopBackgroundNotificationService();
|
||||||
|
|
||||||
storage.deleteAll();
|
storage.deleteAll();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:protocol_handler/protocol_handler.dart';
|
import 'package:protocol_handler/protocol_handler.dart';
|
||||||
|
import 'package:solian/background.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/providers/websocket.dart';
|
import 'package:solian/providers/websocket.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
@ -22,7 +23,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
final _usernameController = TextEditingController();
|
final _usernameController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
|
|
||||||
void requestResetPassword() async {
|
void _requestResetPassword() async {
|
||||||
final username = _usernameController.value.text;
|
final username = _usernameController.value.text;
|
||||||
if (username.isEmpty) {
|
if (username.isEmpty) {
|
||||||
context.showErrorDialog('signinResetPasswordHint'.tr);
|
context.showErrorDialog('signinResetPasswordHint'.tr);
|
||||||
@ -52,7 +53,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
context.showModalDialog('done'.tr, 'signinResetPasswordSent'.tr);
|
context.showModalDialog('done'.tr, 'signinResetPasswordSent'.tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void performAction() async {
|
void _performAction() async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
|
|
||||||
final username = _usernameController.value.text;
|
final username = _usernameController.value.text;
|
||||||
@ -100,6 +101,8 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Get.find<WebSocketProvider>().registerPushNotifications();
|
Get.find<WebSocketProvider>().registerPushNotifications();
|
||||||
|
autoConfigureBackgroundNotificationService();
|
||||||
|
autoStartBackgroundNotificationService();
|
||||||
|
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
}
|
}
|
||||||
@ -121,7 +124,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
final uri = url.replaceFirst('solink://', '');
|
final uri = url.replaceFirst('solink://', '');
|
||||||
if (uri == 'auth?status=done') {
|
if (uri == 'auth?status=done') {
|
||||||
closeInAppWebView();
|
closeInAppWebView();
|
||||||
performAction();
|
_performAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,19 +178,19 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
onSubmitted: (_) => performAction(),
|
onSubmitted: (_) => _performAction(),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: _isBusy ? null : () => requestResetPassword(),
|
onPressed: _isBusy ? null : () => _requestResetPassword(),
|
||||||
style: TextButton.styleFrom(foregroundColor: Colors.grey),
|
style: TextButton.styleFrom(foregroundColor: Colors.grey),
|
||||||
child: Text('forgotPassword'.tr),
|
child: Text('forgotPassword'.tr),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: _isBusy ? null : () => performAction(),
|
onPressed: _isBusy ? null : () => _performAction(),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
Loading…
Reference in New Issue
Block a user