From cda41ecf7429be98595517bd98fc215d0e3713a7 Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Thu, 23 May 2024 17:09:10 +0300 Subject: [PATCH] Implemented firebase and onesignal --- android/app/build.gradle | 8 ++- android/app/google-services.json | 29 ++++++++ android/app/src/main/AndroidManifest.xml | 3 + .../com/example/syncrow_app/MainActivity.kt | 2 +- android/settings.gradle | 4 ++ firebase.json | 1 + ios/Runner.xcodeproj/project.pbxproj | 23 +++++++ ios/Runner/GoogleService-Info.plist | 30 +++++++++ lib/features/app_layout/bloc/home_cubit.dart | 66 +++++++++++++++++++ .../ceiling_sensor_interface.dart | 53 ++++++++++----- .../text_widgets/body_large.dart | 32 ++++----- .../text_widgets/body_small.dart | 3 + .../text_widgets/custom_text_widget.dart | 3 + lib/firebase_options.dart | 53 +++++++-------- lib/main.dart | 8 ++- pubspec.lock | 56 ++++++++++++++++ pubspec.yaml | 2 + 17 files changed, 312 insertions(+), 64 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 firebase.json create mode 100644 ios/Runner/GoogleService-Info.plist diff --git a/android/app/build.gradle b/android/app/build.gradle index c817c87..4a14eae 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,5 +1,9 @@ plugins { id "com.android.application" + // START: FlutterFire Configuration + id 'com.google.gms.google-services' + id 'com.google.firebase.crashlytics' + // END: FlutterFire Configuration id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" } @@ -23,7 +27,7 @@ if (flutterVersionName == null) { } android { - namespace "com.example.syncrow_app" + namespace "com.example.syncrow_application" compileSdkVersion 34 ndkVersion flutter.ndkVersion @@ -42,7 +46,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.syncrow_app" + applicationId "com.example.syncrow_application" // 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. minSdkVersion 21 diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..9fcd360 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "427332280600", + "project_id": "test2-8a3d2", + "storage_bucket": "test2-8a3d2.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:427332280600:android:bb6047adeeb80fb00c7e6d", + "android_client_info": { + "package_name": "com.example.syncrow_application" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0" + } + ], + "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 bac98fa..fdf5596 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,7 @@ + + + + + + + API_KEY + AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw + GCM_SENDER_ID + 427332280600 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.syncrowApp + PROJECT_ID + test2-8a3d2 + STORAGE_BUCKET + test2-8a3d2.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:427332280600:ios:373a65cce55a3af40c7e6d + + \ No newline at end of file diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart index 4760f88..1574ded 100644 --- a/lib/features/app_layout/bloc/home_cubit.dart +++ b/lib/features/app_layout/bloc/home_cubit.dart @@ -1,8 +1,14 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:onesignal_flutter/onesignal_flutter.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdown.dart'; +import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; @@ -23,6 +29,7 @@ part 'home_state.dart'; class HomeCubit extends Cubit { HomeCubit._() : super(HomeInitial()) { + checkIfNotificationPermissionGranted(); if (selectedSpace == null) { fetchUnitsByUserId().then((value) { if (selectedSpace != null) { @@ -52,6 +59,9 @@ class HomeCubit extends Cubit { selectedSpace = null; selectedRoom = null; pageIndex = 0; + OneSignal.User.pushSubscription.removeObserver((stateChanges) => oneSignalSubscriptionObserver); + OneSignal.Notifications.removePermissionObserver((permission) => oneSignalPermissionObserver); + OneSignal.Notifications.removeClickListener((event) => oneSignalClickListenerObserver); return super.close(); } @@ -69,11 +79,67 @@ class HomeCubit extends Cubit { var duration = const Duration(milliseconds: 300); + void oneSignalPermissionObserver; + void oneSignalSubscriptionObserver; + void oneSignalClickListenerObserver; + // selectSpace(SpaceModel space) async { // selectedSpace = space; // emit(SpaceSelected(space)); // } + checkIfNotificationPermissionGranted() async { + try { + OneSignal.initialize('762350c9-1e5d-4d95-a648-16d4dc8a25e1'); + + //Show native push notification dialog + if (Platform.isIOS) { + await OneSignal.Notifications.permissionNative(); + } else { + await OneSignal.Notifications.requestPermission(true); + } + + if (await Permission.notification.isGranted == false) { + return; + } + + var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + if (userUuid.isNotEmpty) { + await OneSignal.login(userUuid); + } + //Enable push notifications + await OneSignal.User.pushSubscription.optIn(); + + //this function will be called once a user is subscribed + oneSignalSubscriptionObserver = OneSignal.User.pushSubscription.addObserver((state) async { + if (state.current.optedIn) { + await _sendSubscriptionId(); + } + }); + + // Send the player id when a user allows notifications + oneSignalPermissionObserver = OneSignal.Notifications.addPermissionObserver((state) async { + await _sendSubscriptionId(); + }); + + //check if the player id is sent, if not send it again + await _sendSubscriptionId(); + + oneSignalClickListenerObserver = OneSignal.Notifications.addClickListener((event) async { + //Once the user clicks on the notification + }); + } catch (err) { + debugPrint("******* Error"); + debugPrint(err.toString()); + rethrow; + } + } + + _sendSubscriptionId() async { + String? subscriptionId = OneSignal.User.pushSubscription.id ?? ''; + //TODO send the subscription id to BE + } + changeSelectedSpace(SpaceModel space) { selectedSpace = space; emitSafe(SpaceSelected(space)); diff --git a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart index 0f147e0..201ea4a 100644 --- a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart @@ -1,5 +1,7 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_bloc.dart'; @@ -157,19 +159,22 @@ class CeilingSensorInterface extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ DefaultContainer( - padding: - const EdgeInsets.symmetric(vertical: 20, horizontal: 15), + padding: const EdgeInsets.symmetric( + vertical: 20, + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Column( mainAxisSize: MainAxisSize.min, children: [ - const BodySmall(text: 'Sports Para'), - BodyLarge( - text: '0', - style: context.bodyLarge.copyWith( - fontWeight: FontsManager.bold, + const Flexible(child: BodySmall(text: 'Sports Para')), + Flexible( + child: BodyLarge( + text: '0', + style: context.bodyLarge.copyWith( + fontWeight: FontsManager.bold, + ), ), ), ], @@ -185,11 +190,18 @@ class CeilingSensorInterface extends StatelessWidget { Column( mainAxisSize: MainAxisSize.min, children: [ - const BodySmall(text: 'Detection Range'), - BodyLarge( - text: '0.0M', - style: context.bodyLarge.copyWith( - fontWeight: FontsManager.bold, + const Flexible( + child: BodySmall( + text: 'Detection Range', + textOverflow: TextOverflow.ellipsis, + )), + Flexible( + child: BodyLarge( + text: '0.0M', + textOverflow: TextOverflow.ellipsis, + style: context.bodyLarge.copyWith( + fontWeight: FontsManager.bold, + ), ), ), ], @@ -205,11 +217,18 @@ class CeilingSensorInterface extends StatelessWidget { Column( mainAxisSize: MainAxisSize.min, children: [ - const BodySmall(text: 'Movement'), - BodyLarge( - text: 'none', - style: context.bodyLarge.copyWith( - fontWeight: FontsManager.bold, + const Flexible( + child: BodySmall( + text: 'Movement', + textOverflow: TextOverflow.ellipsis, + )), + Flexible( + child: BodyLarge( + text: 'none', + textOverflow: TextOverflow.ellipsis, + style: context.bodyLarge.copyWith( + fontWeight: FontsManager.bold, + ), ), ), ], diff --git a/lib/features/shared_widgets/text_widgets/body_large.dart b/lib/features/shared_widgets/text_widgets/body_large.dart index 2e759ad..5cc0819 100644 --- a/lib/features/shared_widgets/text_widgets/body_large.dart +++ b/lib/features/shared_widgets/text_widgets/body_large.dart @@ -4,16 +4,16 @@ import 'package:syncrow_app/utils/context_extension.dart'; import 'custom_text_widget.dart'; class BodyLarge extends StatelessWidget { - const BodyLarge({ - required this.text, - super.key, - this.textAlign, - this.style, - this.height, - this.fontWeight, - this.fontColor, - this.fontSize, - }); + const BodyLarge( + {required this.text, + super.key, + this.textAlign, + this.style, + this.height, + this.fontWeight, + this.fontColor, + this.fontSize, + this.textOverflow}); final String text; final TextAlign? textAlign; @@ -28,16 +28,18 @@ class BodyLarge extends StatelessWidget { final double? fontSize; + final TextOverflow? textOverflow; + @override Widget build(BuildContext context) => CustomText( text, textAlign: textAlign, style: style ?? context.bodyLarge.copyWith( - height: height, - fontWeight: fontWeight, - color: fontColor, - fontSize: fontSize, - ), + height: height, + fontWeight: fontWeight, + color: fontColor, + fontSize: fontSize, + overflow: textOverflow), ); } diff --git a/lib/features/shared_widgets/text_widgets/body_small.dart b/lib/features/shared_widgets/text_widgets/body_small.dart index 5973781..ec04027 100644 --- a/lib/features/shared_widgets/text_widgets/body_small.dart +++ b/lib/features/shared_widgets/text_widgets/body_small.dart @@ -11,6 +11,7 @@ class BodySmall extends StatelessWidget { this.fontSize, this.fontWeight, this.textAlign, + this.textOverflow, }); final String text; @@ -22,6 +23,7 @@ class BodySmall extends StatelessWidget { final FontWeight? fontWeight; final TextAlign? textAlign; + final TextOverflow? textOverflow; @override Widget build(BuildContext context) => CustomText( @@ -31,5 +33,6 @@ class BodySmall extends StatelessWidget { fontSize: fontSize, fontWeight: fontWeight, textAlign: textAlign, + textOverflow: textOverflow, ); } diff --git a/lib/features/shared_widgets/text_widgets/custom_text_widget.dart b/lib/features/shared_widgets/text_widgets/custom_text_widget.dart index 90d1faf..42b755d 100644 --- a/lib/features/shared_widgets/text_widgets/custom_text_widget.dart +++ b/lib/features/shared_widgets/text_widgets/custom_text_widget.dart @@ -9,6 +9,7 @@ class CustomText extends StatelessWidget { this.minLines, this.maxLines, this.textDirection, + this.textOverflow, this.fontSize, this.fontColor, this.fontWeight}); @@ -20,6 +21,7 @@ class CustomText extends StatelessWidget { final int? minLines; final int? maxLines; final TextDirection? textDirection; + final TextOverflow? textOverflow; final double? fontSize; final Color? fontColor; @@ -36,6 +38,7 @@ class CustomText extends StatelessWidget { textAlign: textAlign, // onTap: onTap, // minLines: minLines, + overflow: textOverflow, maxLines: maxLines, textDirection: textDirection, ); diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 72e03b6..6a97585 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -1,5 +1,5 @@ // File generated by FlutterFire CLI. -// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +// ignore_for_file: type=lint import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform; @@ -17,10 +17,7 @@ import 'package:flutter/foundation.dart' 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.', - ); + return web; } switch (defaultTargetPlatform) { case TargetPlatform.android: @@ -50,30 +47,30 @@ class DefaultFirebaseOptions { } static const FirebaseOptions android = FirebaseOptions( - apiKey: '', - //'AIzaSyDXEMzUFunmu9RX9bbzwKldLDgxhPXyCyg', - appId: '', - //'1:922241269489:android:2b51e077ee59a8da5d7350', - messagingSenderId: '', - //'922241269489', - projectId: '', - // 'realkeyper-v3', - storageBucket: '', // 'realkeyper-v3.appspot.com', + apiKey: 'AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0', + appId: '1:427332280600:android:bb6047adeeb80fb00c7e6d', + messagingSenderId: '427332280600', + projectId: 'test2-8a3d2', + storageBucket: 'test2-8a3d2.appspot.com', ); static const FirebaseOptions ios = FirebaseOptions( - apiKey: '', - //'AIzaSyD2r7wkhuYKTRH-FM-6PVNfzqE865ASYys', - appId: '', - //'1:922241269489:ios:9a43e191466515c65d7350', - messagingSenderId: '', - //'922241269489', - projectId: '', - //'realkeyper-v3', - storageBucket: '', - //'realkeyper-v3.appspot.com', - iosClientId: '', - //'922241269489-01hod7jlea19gg983v0b1nfmp7ce6p0v.apps.googleusercontent.com', - iosBundleId: '', //'com.realkeyper.investorapp2', + apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw', + appId: '1:427332280600:ios:373a65cce55a3af40c7e6d', + messagingSenderId: '427332280600', + projectId: 'test2-8a3d2', + storageBucket: 'test2-8a3d2.appspot.com', + iosBundleId: 'com.example.syncrowApp', ); -} + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0', + appId: '1:427332280600:web:ad50516a87a35a1a0c7e6d', + messagingSenderId: '427332280600', + projectId: 'test2-8a3d2', + authDomain: 'test2-8a3d2.firebaseapp.com', + storageBucket: 'test2-8a3d2.appspot.com', + measurementId: 'G-Z1RTTTV5H9', + ); + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 189df41..786eeda 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,8 @@ import 'dart:async'; - +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/firebase_options.dart'; import 'package:syncrow_app/services/locator.dart'; import 'package:syncrow_app/utils/bloc_observer.dart'; import 'package:syncrow_app/utils/helpers/localization_helpers.dart'; @@ -32,6 +33,11 @@ void main() { //to initialize the locator initialSetup(); + await Firebase.initializeApp( + name: 'test2', + options: DefaultFirebaseOptions.currentPlatform, + ); + //final SharedPreferences prefs = await SharedPreferences.getInstance(); //to save the locale in the shared preferences diff --git a/pubspec.lock b/pubspec.lock index a594ef4..f341d5e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -477,6 +477,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + onesignal_flutter: + dependency: "direct main" + description: + name: onesignal_flutter + sha256: f3940387d6c7033a9c341aa0548f24d98217fce9182f9ad80bf2554b9dd3dc1a + url: "https://pub.dev" + source: hosted + version: "5.2.0" path: dependency: transitive description: @@ -541,6 +549,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5" + url: "https://pub.dev" + source: hosted + version: "12.0.6" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662 + url: "https://pub.dev" + source: hosted + version: "9.4.4" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e7a704f..b5c29bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,8 @@ dependencies: smooth_page_indicator: ^1.1.0 html: ^0.15.4 equatable: ^2.0.5 + onesignal_flutter: ^5.2.0 + permission_handler: ^11.3.1 dev_dependencies: flutter_test: