diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 1ba4651..3e7a40e 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,69 +1,73 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - didvan - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - FirebaseAppDelegateProxyEnabled - - LSRequiresIPhoneOS - - NSCameraUsageDescription - We need to access to the user gallery to add user profile photo - NSMicrophoneUsageDescription - Some message to describe why you need this permission - NSPhotoLibraryUsageDescription - We need to access to the user gallery to add user profile photo - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - audio - fetch - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - - UIViewControllerBasedStatusBarAppearance - - UNNotificationServiceExtension - - $(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension - - NSAppleMusicUsageDescription - This app requires access to Apple Music to [explain specific reason]. - NSMicrophoneUsageDescription - ... explain why the app uses the microphone here ... - NSAppTransportSecurity - NSAllowsArbitraryLoads - + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + didvan + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + FirebaseAppDelegateProxyEnabled + + LSRequiresIPhoneOS + + NSCameraUsageDescription + We need to access to the user gallery to add user profile photo + NSMicrophoneUsageDescription + Some message to describe why you need this permission + NSPhotoLibraryUsageDescription + We need to access to the user gallery to add user profile photo + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + audio + fetch + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + + UIViewControllerBasedStatusBarAppearance + + UNNotificationServiceExtension + + $(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension + + NSAppleMusicUsageDescription + This app requires access to Apple Music to [explain specific reason]. + NSMicrophoneUsageDescription + ... explain why the app uses the microphone here ... + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + BGTaskSchedulerPermittedIdentifiers + + dev.flutter.background.refresh + - - + \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 011224a..3ffdb74 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,6 +15,7 @@ import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/route_generator.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/app_home_widget/home_widget_repository.dart'; +import 'package:didvan/services/back_services.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/notification/firebase_api.dart'; import 'package:didvan/services/notification/notification_service.dart'; @@ -32,24 +33,23 @@ import 'package:provider/provider.dart'; final GlobalKey navigatorKey = GlobalKey(); -@pragma('vm:entry-point') -Future _initPushNotification(RemoteMessage message) async { - if (!kIsWeb) { - await NotificationService.initializeNotification(); - } - await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - // if (kDebugMode) { - print("background: ${NotificationData.fromJson(message.data).toJson()}"); - // } - try { - NotificationService.showFirebaseNotification(message); - NotificationService.startListeningNotificationEvents(); - } catch (e) { - // if (kDebugMode) { - print(e); - // } - } -} +// @pragma('vm:entry-point') +// Future _initPushNotification(RemoteMessage message) async { +// if (!kIsWeb) { +// await NotificationService.startListeningNotificationEvents(); +// } +// await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); +// // if (kDebugMode) { +// print("background: ${NotificationData.fromJson(message.data).toJson()}"); +// // } +// try { +// NotificationService.showFirebaseNotification(message); +// } catch (e) { +// // if (kDebugMode) { +// print(e); +// // } +// } +// } @pragma('vm:entry-point') Future _backgroundCallbackHomeWidget(Uri? uri) async { @@ -65,7 +65,6 @@ Future _backgroundCallbackHomeWidget(Uri? uri) async { void main() async { WidgetsFlutterBinding.ensureInitialized(); - try { if (!kIsWeb) { HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget); @@ -73,7 +72,7 @@ void main() async { await NotificationService.initializeNotification(); } - FirebaseMessaging.onBackgroundMessage(_initPushNotification); + // FirebaseMessaging.onBackgroundMessage(_initPushNotification); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); await FirebaseApi().initNotification(); diff --git a/lib/providers/user.dart b/lib/providers/user.dart index 08d329f..77fd775 100644 --- a/lib/providers/user.dart +++ b/lib/providers/user.dart @@ -65,9 +65,7 @@ class UserProvider extends CoreProvier { value: service.result['user']['end'], ); - FirebaseApi() - .initNotification() - .then((value) => _registerFirebaseToken()); + await _registerFirebaseToken(); return true; } catch (e) { diff --git a/lib/services/back_services.dart b/lib/services/back_services.dart new file mode 100644 index 0000000..6a6b552 --- /dev/null +++ b/lib/services/back_services.dart @@ -0,0 +1,57 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:didvan/firebase_options.dart'; +import 'package:didvan/models/notification_data.dart'; +import 'package:didvan/services/notification/notification_service.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:get/get.dart'; + +Future initializeService() async { + final service = FlutterBackgroundService(); + await service.configure( + iosConfiguration: IosConfiguration( + autoStart: true, + onForeground: onStart, + onBackground: onIosBackground), + androidConfiguration: AndroidConfiguration( + onStart: onStart, + isForegroundMode: true, + autoStart: true, + autoStartOnBoot: true)); +} + +@pragma('vm:entry-point') +Future onIosBackground(ServiceInstance service) async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + return true; +} + +@pragma('vm:entry-point') +void onStart(ServiceInstance service) { + DartPluginRegistrant.ensureInitialized(); + if (service is AndroidServiceInstance) { + service.on('setAsForeground').listen((event) { + service.setAsForegroundService(); + }); + service.on('setAsBackground').listen((event) { + service.setAsBackgroundService(); + }); + } + service.on('stopService').listen((event) { + service.stopSelf(); + }); + Timer.periodic(const Duration(seconds: 1), (timer) async { + if (service is AndroidServiceInstance) { + if (await service.isForegroundService()) {} + } + + NotificationService.startListeningNotificationEvents(); + print('background service running'); + service.invoke('update'); + }); +} diff --git a/lib/services/notification/firebase_api.dart b/lib/services/notification/firebase_api.dart index dc2448a..7eaeb6d 100644 --- a/lib/services/notification/firebase_api.dart +++ b/lib/services/notification/firebase_api.dart @@ -7,6 +7,7 @@ import 'package:didvan/services/notification/notification_service.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; class FirebaseApi { @@ -33,11 +34,34 @@ class FirebaseApi { sound: true, ); - FirebaseMessaging.instance - .getInitialMessage() - .asStream() - .listen((event) async {}); - FirebaseMessaging.onMessageOpenedApp.listen((event) {}); + final initMsg = await FirebaseMessaging.instance.getInitialMessage(); + + if (initMsg != null) { + try { + NotificationMessage data = NotificationMessage.fromJson(initMsg.data); + HomeWidgetRepository.data = data; + print("data: ${HomeWidgetRepository.data}"); + await HomeWidgetRepository.decideWhereToGoNotif(); + await StorageService.delete( + key: 'notification${AppInitializer.createNotificationId(data)}'); + } catch (e) { + e.printError(); + } + } + + FirebaseMessaging.onMessageOpenedApp.listen((initMsg) async { + try { + NotificationMessage data = NotificationMessage.fromJson(initMsg.data); + HomeWidgetRepository.data = data; + print("data: ${HomeWidgetRepository.data}"); + await HomeWidgetRepository.decideWhereToGoNotif(); + await StorageService.delete( + key: 'notification${AppInitializer.createNotificationId(data)}'); + } catch (e) { + e.printError(); + } + }); + FirebaseMessaging.onMessage.listen((event) => handleMessage(event)); } @@ -46,6 +70,13 @@ class FirebaseApi { //do ever you want with message // if (kDebugMode) { print("forground: ${NotificationData.fromJson(message.data).toJson()}"); + const platform = MethodChannel('com.didvan.didvanapp/notification'); + + await platform.invokeMethod('showNotification', { + 'title': message.notification!.title, + 'message': message.notification!.body + }); + // } try { await NotificationService.showFirebaseNotification(message); diff --git a/lib/services/notification/notification_service.dart b/lib/services/notification/notification_service.dart index 46ceb8b..410c552 100644 --- a/lib/services/notification/notification_service.dart +++ b/lib/services/notification/notification_service.dart @@ -5,6 +5,7 @@ import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; class NotificationService { @@ -163,3 +164,47 @@ class NotificationService { } } } + +class NotificationHelper { + static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + + static Future initialized() async { + const AndroidInitializationSettings initializationSettingsAndroid = + AndroidInitializationSettings('@mipmap/ic_launcher'); + final initializationSettingsIos = DarwinInitializationSettings( + requestAlertPermission: true, + requestBadgePermission: true, + requestSoundPermission: true, + onDidReceiveLocalNotification: (id, title, body, payload) async {}, + ); + + await flutterLocalNotificationsPlugin.initialize( + InitializationSettings( + android: initializationSettingsAndroid, + iOS: initializationSettingsIos), + onDidReceiveNotificationResponse: (details) { + print("onDidReceiveNotificationResponse: ${details}"); + }, onDidReceiveBackgroundNotificationResponse: localBackgroundHandler); + } + + static void displayNotification(RemoteMessage message) async { + try { + final id = DateTime.now().millisecondsSinceEpoch ~/ 1000; + const notifDetails = NotificationDetails( + android: AndroidNotificationDetails( + 'push_notificatiion', 'push_notificatiion_channel', + importance: Importance.max, priority: Priority.high)); + + await flutterLocalNotificationsPlugin.show( + id, message.data['title'], message.data['body'], notifDetails); + } on Exception catch (e) { + e.printError(); + } + } +} + +@pragma('vm:entry-point') +Future localBackgroundHandler(NotificationResponse data) async { + print("onDidReceiveBackgroundNotificationResponse: ${data}"); +} diff --git a/lib/views/webview/web_view.dart b/lib/views/webview/web_view.dart index 3a5ca60..962eea6 100644 --- a/lib/views/webview/web_view.dart +++ b/lib/views/webview/web_view.dart @@ -22,7 +22,6 @@ class _WebViewState extends State { void initState() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..setBackgroundColor(Theme.of(context).colorScheme.background) ..setNavigationDelegate( NavigationDelegate( onProgress: (int progress) { diff --git a/lib/views/widgets/didvan/page_view.dart b/lib/views/widgets/didvan/page_view.dart index b3bcb25..1ef8447 100644 --- a/lib/views/widgets/didvan/page_view.dart +++ b/lib/views/widgets/didvan/page_view.dart @@ -311,7 +311,7 @@ class _DidvanPageViewState extends State { ), ); } else { - AppInitializer.openWebLink(content, href, + AppInitializer.openWebLink(context, href, mode: LaunchMode.inAppWebView); } }, diff --git a/pubspec.lock b/pubspec.lock index d578c26..a282de3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -358,6 +358,38 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_background_service: + dependency: "direct main" + description: + name: flutter_background_service + sha256: d32f078ec57647c9cfd6e1a8da9297f7d8f021d4dcc204a35aaad2cdbfe255f0 + url: "https://pub.dev" + source: hosted + version: "5.0.10" + flutter_background_service_android: + dependency: transitive + description: + name: flutter_background_service_android + sha256: "39da42dddf877beeef82bc2583130d8bedb4d0765e99ca9e7b4a32e8c6abd239" + url: "https://pub.dev" + source: hosted + version: "6.2.7" + flutter_background_service_ios: + dependency: transitive + description: + name: flutter_background_service_ios + sha256: "6037ffd45c4d019dab0975c7feb1d31012dd697e25edc05505a4a9b0c7dc9fba" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + flutter_background_service_platform_interface: + dependency: transitive + description: + name: flutter_background_service_platform_interface + sha256: ca74aa95789a8304f4d3f57f07ba404faa86bed6e415f83e8edea6ad8b904a41 + url: "https://pub.dev" + source: hosted + version: "5.1.2" flutter_cache_manager: dependency: "direct main" description: @@ -382,6 +414,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: c500d5d9e7e553f06b61877ca6b9c8b92c570a4c8db371038702e8ce57f8a50f + url: "https://pub.dev" + source: hosted + version: "17.2.2" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af + url: "https://pub.dev" + source: hosted + version: "4.0.1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66" + url: "https://pub.dev" + source: hosted + version: "7.2.0" flutter_localizations: dependency: "direct main" description: flutter @@ -1098,6 +1154,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + timezone: + dependency: transitive + description: + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + url: "https://pub.dev" + source: hosted + version: "0.9.4" toggle_switch: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 238d458..dde99d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -92,6 +92,8 @@ dependencies: mime: ^1.0.2 path: any flutter_cache_manager: any + flutter_local_notifications: ^17.2.2 + flutter_background_service: ^5.0.10 # url_launcher: ^6.3.0 dev_dependencies: diff --git a/web/index.html b/web/index.html index 5761fa8..bf5f328 100644 --- a/web/index.html +++ b/web/index.html @@ -28,12 +28,16 @@ - + Didvan