♿ Optimize notification background service
This commit is contained in:
		
							
								
								
									
										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:flutter/material.dart' hide Notification;
 | 
			
		||||
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:go_router/go_router.dart';
 | 
			
		||||
import 'package:protocol_handler/protocol_handler.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/firebase_options.dart';
 | 
			
		||||
import 'package:solian/models/notification.dart';
 | 
			
		||||
import 'package:solian/platform.dart';
 | 
			
		||||
import 'package:solian/providers/attachment_uploader.dart';
 | 
			
		||||
import 'package:solian/providers/daily_sign.dart';
 | 
			
		||||
@@ -70,82 +67,7 @@ Future<void> _initializeFirebase() async {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<void> _initializeBackgroundNotificationService() async {
 | 
			
		||||
  final prefs = await SharedPreferences.getInstance();
 | 
			
		||||
  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',
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
  autoConfigureBackgroundNotificationService();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<void> _initializePlatformComponents() async {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 | 
			
		||||
import 'package:get/get.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/unauthorized.dart';
 | 
			
		||||
import 'package:solian/providers/database/database.dart';
 | 
			
		||||
@@ -200,6 +201,7 @@ class AuthProvider extends GetConnect {
 | 
			
		||||
    Get.find<WebSocketProvider>().notificationUnread.value = 0;
 | 
			
		||||
 | 
			
		||||
    AppDatabase.removeDatabase();
 | 
			
		||||
    autoStopBackgroundNotificationService();
 | 
			
		||||
 | 
			
		||||
    storage.deleteAll();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:protocol_handler/protocol_handler.dart';
 | 
			
		||||
import 'package:solian/background.dart';
 | 
			
		||||
import 'package:solian/exts.dart';
 | 
			
		||||
import 'package:solian/providers/websocket.dart';
 | 
			
		||||
import 'package:solian/providers/auth.dart';
 | 
			
		||||
@@ -22,7 +23,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
 | 
			
		||||
  final _usernameController = TextEditingController();
 | 
			
		||||
  final _passwordController = TextEditingController();
 | 
			
		||||
 | 
			
		||||
  void requestResetPassword() async {
 | 
			
		||||
  void _requestResetPassword() async {
 | 
			
		||||
    final username = _usernameController.value.text;
 | 
			
		||||
    if (username.isEmpty) {
 | 
			
		||||
      context.showErrorDialog('signinResetPasswordHint'.tr);
 | 
			
		||||
@@ -52,7 +53,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
 | 
			
		||||
    context.showModalDialog('done'.tr, 'signinResetPasswordSent'.tr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void performAction() async {
 | 
			
		||||
  void _performAction() async {
 | 
			
		||||
    final AuthProvider auth = Get.find();
 | 
			
		||||
 | 
			
		||||
    final username = _usernameController.value.text;
 | 
			
		||||
@@ -100,6 +101,8 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Get.find<WebSocketProvider>().registerPushNotifications();
 | 
			
		||||
    autoConfigureBackgroundNotificationService();
 | 
			
		||||
    autoStartBackgroundNotificationService();
 | 
			
		||||
 | 
			
		||||
    Navigator.pop(context, true);
 | 
			
		||||
  }
 | 
			
		||||
@@ -121,7 +124,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
 | 
			
		||||
    final uri = url.replaceFirst('solink://', '');
 | 
			
		||||
    if (uri == 'auth?status=done') {
 | 
			
		||||
      closeInAppWebView();
 | 
			
		||||
      performAction();
 | 
			
		||||
      _performAction();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -175,19 +178,19 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
 | 
			
		||||
                ),
 | 
			
		||||
                onTapOutside: (_) =>
 | 
			
		||||
                    FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                onSubmitted: (_) => performAction(),
 | 
			
		||||
                onSubmitted: (_) => _performAction(),
 | 
			
		||||
              ),
 | 
			
		||||
              const Gap(12),
 | 
			
		||||
              Row(
 | 
			
		||||
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                children: [
 | 
			
		||||
                  TextButton(
 | 
			
		||||
                    onPressed: _isBusy ? null : () => requestResetPassword(),
 | 
			
		||||
                    onPressed: _isBusy ? null : () => _requestResetPassword(),
 | 
			
		||||
                    style: TextButton.styleFrom(foregroundColor: Colors.grey),
 | 
			
		||||
                    child: Text('forgotPassword'.tr),
 | 
			
		||||
                  ),
 | 
			
		||||
                  TextButton(
 | 
			
		||||
                    onPressed: _isBusy ? null : () => performAction(),
 | 
			
		||||
                    onPressed: _isBusy ? null : () => _performAction(),
 | 
			
		||||
                    child: Row(
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                      children: [
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user