From 012e6fb28a6c5c7cb555a46e9d4a4011c7fda88b Mon Sep 17 00:00:00 2001 From: Abhimanyu-dev Date: Tue, 29 Oct 2024 18:38:09 +0530 Subject: [PATCH] Added notifications --- android/app/build.gradle | 9 +- android/app/google-services.json | 29 +++ android/app/src/main/AndroidManifest.xml | 4 +- .../attendance}/MainActivity.kt | 2 +- android/build.gradle | 4 + android/settings.gradle | 3 + firebase.json | 1 + lib/config/firebase.dart | 70 ++++++ lib/config/firebase_options.dart | 62 +++++ lib/config/localStorage.dart | 30 +++ lib/config/notification.dart | 61 +++++ lib/config/storage.dart | 8 + lib/main.dart | 10 + pubspec.lock | 222 ++++++++++++++++-- pubspec.yaml | 5 + 15 files changed, 496 insertions(+), 24 deletions(-) create mode 100644 android/app/google-services.json rename android/app/src/main/kotlin/com/{example/attendance_app => pclub/attendance}/MainActivity.kt (72%) create mode 100644 firebase.json create mode 100644 lib/config/firebase.dart create mode 100644 lib/config/firebase_options.dart create mode 100644 lib/config/localStorage.dart create mode 100644 lib/config/notification.dart create mode 100644 lib/config/storage.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index ae1b5ff..4fa83f6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -3,6 +3,7 @@ plugins { id "kotlin-android" // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" + id 'com.google.gms.google-services' } def localProperties = new Properties() @@ -24,7 +25,7 @@ if (flutterVersionName == null) { } android { - namespace = "com.example.attendance_app" + namespace = "com.pclub.attendance" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -35,7 +36,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.attendance_app" + applicationId = "com.pclub.attendance" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = flutter.minSdkVersion @@ -56,3 +57,7 @@ android { flutter { source = "../.." } +dependencies { + implementation platform('com.google.firebase:firebase-bom:33.5.1') + implementation 'com.google.firebase:firebase-analytics' +} \ No newline at end of file diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..453e3c7 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "788037853247", + "project_id": "attendance-338eb", + "storage_bucket": "attendance-338eb.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:788037853247:android:c1e473148d4ba293e7988a", + "android_client_info": { + "package_name": "com.pclub.attendance" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyATmGPGCnhEzncmC9lYhixCTn0jvXrhykA" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 38be97b..af85163 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + initialize() async { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform + ); + } + + static void listenNotification() { + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { + }); + } + + static Future checkForInitialMessage() async { + RemoteMessage? initialMessage = + await FirebaseMessaging.instance.getInitialMessage(); + } + + static Future _getNotificationsPermission() async { + var status = await Permission.notification.status; + if (!status.isGranted) { + final result = await Permission.notification.request(); + return result; + } else { + return status; + } + } + + static Future registerNotification() async { + PermissionStatus status=await _getNotificationsPermission(); + if(kDebugMode){ + print(status); + } + var messaging = FirebaseMessaging.instance; + print("FCM TOKEN: ${await messaging.getToken()}"); + NotificationSettings settings = await messaging.requestPermission( + alert: true, + announcement: false, + badge: true, + carPlay: false, + criticalAlert: false, + provisional: false, + sound: true, + ); + if (settings.authorizationStatus == AuthorizationStatus.authorized) { + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + LocalStorage.addNotification(message.notification!.toMap()); + NotificationConfig.showNotification(message); + }); + } + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + } + @pragma('vm:entry-point') + static Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + Firebase.initializeApp(); + await Storage.initialize(); + if(Hive.box("localStorage").isOpen){ + LocalStorage.addNotification(message.notification!.toMap()); + } + } +} diff --git a/lib/config/firebase_options.dart b/lib/config/firebase_options.dart new file mode 100644 index 0000000..b11d91b --- /dev/null +++ b/lib/config/firebase_options.dart @@ -0,0 +1,62 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for ios - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyATmGPGCnhEzncmC9lYhixCTn0jvXrhykA', + appId: '1:788037853247:android:c1e473148d4ba293e7988a', + messagingSenderId: '788037853247', + projectId: 'attendance-338eb', + storageBucket: 'attendance-338eb.appspot.com', + ); +} diff --git a/lib/config/localStorage.dart b/lib/config/localStorage.dart new file mode 100644 index 0000000..716e872 --- /dev/null +++ b/lib/config/localStorage.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; +import 'package:hive/hive.dart'; + +class LocalStorage { + static final _localStorage = Hive.box('localStorage'); + static addNotification(notification){ + var rawData=_localStorage.get("notifications"); + if(rawData!=null){ + List previousNotifications= jsonDecode(rawData); + previousNotifications.add(notification); + _localStorage.put("notifications",jsonEncode(previousNotifications)); + } + else{ + List emptyNotifications=[]; + emptyNotifications.add(notification); + _localStorage.put("notifications",jsonEncode(emptyNotifications)); + } + } + + static getNotifications(){ + var rawData=_localStorage.get("notifications"); + if(rawData!=null){ + return jsonDecode(rawData); + } + else{ + return []; + } + } + +} \ No newline at end of file diff --git a/lib/config/notification.dart b/lib/config/notification.dart new file mode 100644 index 0000000..6a457fe --- /dev/null +++ b/lib/config/notification.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:http/http.dart' as http; +class NotificationConfig { + static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + static const AndroidNotificationChannel channel = AndroidNotificationChannel( + 'pclub-notification', + 'pclub-ui-notification', + description: 'This channel is used for important notifications.', + importance: Importance.max, + ); + static Future initialize(context) async { + var androidInitialize = + const AndroidInitializationSettings('mipmap/ic_noti'); + var initializationsSettings = + InitializationSettings(android: androidInitialize); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + await flutterLocalNotificationsPlugin.initialize(initializationsSettings); + } + + static Future showNotification(RemoteMessage message) async { + late AndroidNotificationDetails androidPlatformChannelSpecifics; + if(message.notification!.android!.imageUrl!=null && message.notification!.android!.imageUrl!=""){ + final http.Response response = await http.get(Uri.parse(message.notification!.android!.imageUrl!)); + BigPictureStyleInformation bigPictureStyleInformation = + BigPictureStyleInformation( + ByteArrayAndroidBitmap.fromBase64String(base64Encode(response.bodyBytes)), + largeIcon: ByteArrayAndroidBitmap.fromBase64String(base64Encode(response.bodyBytes)), + ); + androidPlatformChannelSpecifics = AndroidNotificationDetails( + channel.id, + channel.name, + playSound: true, + icon: "mipmap/ic_noti", + importance: Importance.max, + priority: Priority.max, + styleInformation: bigPictureStyleInformation, + largeIcon: ByteArrayAndroidBitmap.fromBase64String( + base64Encode(response.bodyBytes)) + ); + }else{ + androidPlatformChannelSpecifics = AndroidNotificationDetails( + channel.id, + channel.name, + playSound: true, + icon: "mipmap/ic_noti", + importance: Importance.max, + priority: Priority.max + ); + } + + var not = NotificationDetails(android: androidPlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show(0,message.notification!.title , message.notification!.body, not, + payload: jsonEncode(message.data)); + } +} diff --git a/lib/config/storage.dart b/lib/config/storage.dart new file mode 100644 index 0000000..2fad01f --- /dev/null +++ b/lib/config/storage.dart @@ -0,0 +1,8 @@ +import 'package:hive_flutter/hive_flutter.dart'; + +class Storage { + static Future initialize() async { + await Hive.initFlutter(); + await Hive.openBox('localStorage'); + } +} diff --git a/lib/main.dart b/lib/main.dart index e3f9676..9ad6714 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'package:attendance_app/config/firebase.dart'; +import 'package:attendance_app/config/storage.dart'; import 'package:attendance_app/screens/user_dashboard.dart'; import 'package:attendance_app/screens/admin_dashboard.dart'; import 'package:attendance_app/screens/user_events.dart'; @@ -12,8 +14,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:async'; +import 'package:permission_handler/permission_handler.dart'; + Future main() async { fillData(); + WidgetsFlutterBinding.ensureInitialized(); + await FirebaseConfig.initialize(); + FirebaseConfig.listenNotification(); + FirebaseConfig.registerNotification(); + FirebaseConfig.checkForInitialMessage(); + Storage.initialize(); runApp(const MyApp()); // Use MyApp directly } diff --git a/pubspec.lock b/pubspec.lock index fed9b97..4f5f10c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "67.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2" + url: "https://pub.dev" + source: hosted + version: "1.3.44" analyzer: dependency: transitive description: @@ -153,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" fake_async: dependency: transitive description: @@ -209,6 +225,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96" + url: "https://pub.dev" + source: hosted + version: "3.6.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 + url: "https://pub.dev" + source: hosted + version: "5.3.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 + url: "https://pub.dev" + source: hosted + version: "2.18.1" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: eb6e28a3a35deda61fe8634967c84215efc19133ba58d8e0fc6c9a2af2cba05e + url: "https://pub.dev" + source: hosted + version: "15.1.3" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: b316c4ee10d93d32c033644207afc282d9b2b4372f3cf9c6022f3558b3873d2d + url: "https://pub.dev" + source: hosted + version: "4.5.46" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: d7f0147a1a9fe4313168e20154a01fd5cf332898de1527d3930ff77b8c7f5387 + url: "https://pub.dev" + source: hosted + version: "3.9.2" fixnum: dependency: transitive description: @@ -238,6 +302,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35" + url: "https://pub.dev" + source: hosted + version: "17.2.4" + 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_map: dependency: "direct main" description: @@ -254,6 +342,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.22" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_shaders: dependency: transitive description: @@ -352,6 +488,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.1" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.dev" + source: hosted + version: "1.1.0" http: dependency: "direct main" description: @@ -460,10 +612,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.6.7" jwt_decoder: dependency: "direct main" description: @@ -484,18 +636,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -556,18 +708,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mgrs_dart: dependency: transitive description: @@ -704,6 +856,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" platform: dependency: transitive description: @@ -873,26 +1033,34 @@ packages: dependency: "direct dev" description: name: test - sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.2" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.4" + timezone: + dependency: transitive + description: + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + url: "https://pub.dev" + source: hosted + version: "0.9.4" typed_data: dependency: transitive description: @@ -929,10 +1097,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" watcher: dependency: transitive description: @@ -965,6 +1133,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: e1d0cc62e65dc2561f5071fcbccecf58ff20c344f8f3dc7d4922df372a11df1f + url: "https://pub.dev" + source: hosted + version: "5.7.1" wkt_parser: dependency: transitive description: @@ -981,6 +1157,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" yaml: dependency: transitive description: @@ -990,5 +1174,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.3 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.22.2" diff --git a/pubspec.yaml b/pubspec.yaml index a6513c5..a00fa87 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,11 @@ dependencies: jwt_decoder: ^2.0.1 http: ^1.2.2 flutter_secure_storage: ^9.2.2 + firebase_core: ^3.6.0 + firebase_messaging: ^15.1.3 + hive: ^2.2.3 + flutter_local_notifications: ^17.2.4 + hive_flutter: ^1.1.0 dev_dependencies: flutter_test: