TF-1202: Configure FCM

This commit is contained in:
ManhNTX
2022-11-17 13:07:26 +07:00
committed by Dat H. Pham
parent 8276f5b041
commit f94cfe7ee8
34 changed files with 718 additions and 25 deletions
+13 -4
View File
@@ -22,6 +22,7 @@ if (flutterVersionName == null) {
} }
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
@@ -32,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 32 compileSdkVersion 33
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
@@ -40,8 +41,8 @@ android {
defaultConfig { defaultConfig {
applicationId "com.linagora.android.teammail" applicationId "com.linagora.android.teammail"
minSdkVersion 16 minSdkVersion 19
targetSdkVersion 31 targetSdkVersion 33
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
// The number of method references in a .dex file cannot exceed 64K // The number of method references in a .dex file cannot exceed 64K
@@ -51,6 +52,12 @@ android {
] ]
} }
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// Use key information file // Use key information file
signingConfigs { signingConfigs {
release { release {
@@ -74,7 +81,9 @@ flutter {
} }
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
implementation 'androidx.work:work-runtime-ktx:2.7.0' implementation 'androidx.work:work-runtime-ktx:2.7.0'
implementation 'com.android.support:multidex:1.0.3'
implementation 'androidx.window:window:1.0.0'
} }
+3 -4
View File
@@ -32,12 +32,15 @@
</queries> </queries>
<application <application
android:hardwareAccelerated="true"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
@@ -56,10 +59,6 @@
screen fades out. A splash screen is useful to avoid any visual screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of gap between the end of Android's launch screen and the painting of
Flutter's first frame. --> Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
+2 -1
View File
@@ -6,8 +6,9 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.google.gms:google-services:4.3.10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.android.tools.build:gradle:4.2.2'
} }
} }
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+4 -2
View File
@@ -1,19 +1,19 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:tmail_ui_user/features/caching/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/account_cache_client.dart';
import 'package:tmail_ui_user/features/caching/email_cache_client.dart'; import 'package:tmail_ui_user/features/caching/email_cache_client.dart';
import 'package:tmail_ui_user/features/caching/firebase_config_cache_client.dart';
import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart';
import 'package:tmail_ui_user/features/caching/recent_search_cache_client.dart'; import 'package:tmail_ui_user/features/caching/recent_search_cache_client.dart';
import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; import 'package:tmail_ui_user/features/caching/state_cache_client.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
class CachingManager { class CachingManager {
final MailboxCacheClient _mailboxCacheClient; final MailboxCacheClient _mailboxCacheClient;
final StateCacheClient _stateCacheClient; final StateCacheClient _stateCacheClient;
final EmailCacheClient _emailCacheClient; final EmailCacheClient _emailCacheClient;
final RecentSearchCacheClient _recentSearchCacheClient; final RecentSearchCacheClient _recentSearchCacheClient;
final AccountCacheClient _accountCacheClient; final AccountCacheClient _accountCacheClient;
final FirebaseCacheClient _firebaseCacheClient;
CachingManager( CachingManager(
this._mailboxCacheClient, this._mailboxCacheClient,
@@ -21,6 +21,7 @@ class CachingManager {
this._emailCacheClient, this._emailCacheClient,
this._recentSearchCacheClient, this._recentSearchCacheClient,
this._accountCacheClient, this._accountCacheClient,
this._firebaseCacheClient,
); );
Future<void> clearAll() async { Future<void> clearAll() async {
@@ -39,6 +40,7 @@ class CachingManager {
_emailCacheClient.deleteBox(), _emailCacheClient.deleteBox(),
_recentSearchCacheClient.deleteBox(), _recentSearchCacheClient.deleteBox(),
_accountCacheClient.deleteBox(), _accountCacheClient.deleteBox(),
_firebaseCacheClient.deleteBox(),
]); ]);
} }
} }
@@ -0,0 +1,8 @@
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
import 'package:tmail_ui_user/features/push_notification/data/model/firebase_cache.dart';
class FirebaseCacheClient extends HiveCacheClient<FirebaseCache> {
@override
String get tableName => 'FirebaseCache';
}
@@ -13,4 +13,5 @@ class CachingConstants {
static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11; static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11;
static const int RECENT_LOGIN_URL_HIVE_CACHE_IDENTITY = 12; static const int RECENT_LOGIN_URL_HIVE_CACHE_IDENTITY = 12;
static const int RECENT_LOGIN_USERNAME_HIVE_CACHE_IDENTITY = 13; static const int RECENT_LOGIN_USERNAME_HIVE_CACHE_IDENTITY = 13;
static const int FIREBASE_CONFIG_CACHE_IDENTITY = 14;
} }
@@ -124,7 +124,6 @@ class MailboxDashBoardController extends ReloadableController {
final viewStateMarkAsReadMailbox = Rx<Either<Failure, Success>>(Right(UIState.idle)); final viewStateMarkAsReadMailbox = Rx<Either<Failure, Success>>(Right(UIState.idle));
final vacationResponse = Rxn<VacationResponse>(); final vacationResponse = Rxn<VacationResponse>();
final routerParameters = Rxn<Map<String, String?>>(); final routerParameters = Rxn<Map<String, String?>>();
Session? sessionCurrent; Session? sessionCurrent;
Map<Role, MailboxId> mapDefaultMailboxIdByRole = {}; Map<Role, MailboxId> mapDefaultMailboxIdByRole = {};
Map<MailboxId, PresentationMailbox> mapMailboxById = {}; Map<MailboxId, PresentationMailbox> mapMailboxById = {};
@@ -0,0 +1,9 @@
import 'package:model/firebase/firebase_dto.dart';
abstract class FirebaseDatasource {
Future<FirebaseDto> getCurrentFirebase();
Future<void> setCurrentFirebase(FirebaseDto newCurrentFirebase);
Future<void> deleteCurrentFirebase(String token);
}
@@ -0,0 +1,39 @@
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/push_notification/data/datasource/firebase_datasource.dart';
import 'package:tmail_ui_user/features/push_notification/data/local/firebase_cache_manager.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
class HiveFirebaseDatasourceImpl extends FirebaseDatasource {
final FirebaseCacheManager _firebaseCacheManager;
final ExceptionThrower _exceptionThrower;
HiveFirebaseDatasourceImpl(this._firebaseCacheManager, this._exceptionThrower);
@override
Future<FirebaseDto> getCurrentFirebase() {
return Future.sync(() async {
return await _firebaseCacheManager.getFirebase();
}).catchError((error) {
_exceptionThrower.throwException(error);
});
}
@override
Future<void> setCurrentFirebase(FirebaseDto newCurrentFirebase) {
return Future.sync(() async {
return await _firebaseCacheManager.setFirebase(newCurrentFirebase);
}).catchError((error) {
_exceptionThrower.throwException(error);
});
}
@override
Future<void> deleteCurrentFirebase(String token) {
return Future.sync(() async {
return await _firebaseCacheManager.deleteFirebase(token);
}).catchError((error) {
_exceptionThrower.throwException(error);
});
}
}
@@ -0,0 +1,10 @@
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/push_notification/data/model/firebase_cache.dart';
extension FirebaseCacheExtension on FirebaseCache {
FirebaseDto toFirebaseDto() {
return FirebaseDto(token);
}
}
@@ -0,0 +1,8 @@
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/push_notification/data/model/firebase_cache.dart';
extension FirebaseExtensions on FirebaseDto {
FirebaseCache toCache() {
return FirebaseCache(token);
}
}
@@ -0,0 +1,37 @@
import 'package:core/utils/app_logger.dart';
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/caching/firebase_config_cache_client.dart';
import 'package:tmail_ui_user/features/push_notification/data/extensions/firebase_extensions.dart';
import 'package:tmail_ui_user/features/push_notification/data/model/firebase_cache.dart';
import 'package:tmail_ui_user/features/push_notification/data/extensions/firebase_cache_extensions.dart';
import 'package:tmail_ui_user/features/push_notification/domain/exceptions/firebase_exception.dart';
class FirebaseCacheManager {
final FirebaseCacheClient _firebaseCacheClient;
FirebaseCacheManager(this._firebaseCacheClient);
Future<FirebaseDto> getFirebase() async {
try {
final firebase = await _firebaseCacheClient.getItem(FirebaseCache.keyCacheValue);
if(firebase != null ) {
return firebase.toFirebaseDto();
} else {
throw NotFoundStoredFirebaseException();
}
} catch (e) {
logError('FirebaseCacheManager::getFirebase(): $e');
throw NotFoundStoredFirebaseException();
}
}
Future<void> setFirebase(FirebaseDto firebaseDto) {
log('FirebaseCacheManager::setFirebase(): $_firebaseCacheClient');
return _firebaseCacheClient.insertItem(firebaseDto.token, firebaseDto.toCache());
}
Future<void> deleteFirebase(String token) {
log('FirebaseCacheManager::deleteSelectedFirebase(): $token');
return _firebaseCacheClient.deleteItem(token);
}
}
@@ -0,0 +1,19 @@
import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
part 'firebase_cache.g.dart';
@HiveType(typeId: CachingConstants.FIREBASE_CONFIG_CACHE_IDENTITY)
class FirebaseCache extends HiveObject with EquatableMixin {
static const String keyCacheValue = 'firebaseCache';
@HiveField(0)
final String token;
FirebaseCache(this.token);
@override
List<Object?> get props => [token];
}
@@ -0,0 +1,27 @@
import 'package:core/utils/app_logger.dart';
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/push_notification/data/datasource/firebase_datasource.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/firebase_repository.dart';
class FirebaseRepositoryImpl extends FirebaseRepository {
final FirebaseDatasource _firebaseDatasource;
FirebaseRepositoryImpl(this._firebaseDatasource);
@override
Future<FirebaseDto> getCurrentFirebase() {
return _firebaseDatasource.getCurrentFirebase();
}
@override
Future<void> setCurrentFirebase(FirebaseDto newCurrentFirebase) {
log('FirebaseRepositoryImpl::setCurrentFirebase(): $newCurrentFirebase');
return _firebaseDatasource.setCurrentFirebase(newCurrentFirebase);
}
@override
Future<void> deleteCurrentFirebase(String token) {
return _firebaseDatasource.deleteCurrentFirebase(token);
}
}
@@ -0,0 +1,2 @@
class NotFoundStoredFirebaseException implements Exception {}
@@ -0,0 +1,9 @@
import 'package:model/firebase/firebase_dto.dart';
abstract class FirebaseRepository {
Future<FirebaseDto> getCurrentFirebase();
Future<void> setCurrentFirebase(FirebaseDto newCurrentFirebase);
Future<void> deleteCurrentFirebase(String token);
}
@@ -0,0 +1,19 @@
import 'package:core/core.dart';
class DeleteFirebaseSuccess extends UIState {
DeleteFirebaseSuccess();
@override
List<Object> get props => [];
}
class DeleteFirebaseFailure extends FeatureFailure {
final dynamic exception;
DeleteFirebaseFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,21 @@
import 'package:core/core.dart';
import 'package:model/firebase/firebase_dto.dart';
class GetFirebaseSuccess extends UIState {
final FirebaseDto firebaseDto;
GetFirebaseSuccess(this.firebaseDto);
@override
List<Object> get props => [firebaseDto];
}
class GetFirebaseFailure extends FeatureFailure {
final dynamic exception;
GetFirebaseFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,19 @@
import 'package:core/core.dart';
class SaveFirebaseSuccess extends UIState {
SaveFirebaseSuccess();
@override
List<Object> get props => [];
}
class SaveFirebaseFailure extends FeatureFailure {
final dynamic exception;
SaveFirebaseFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,20 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/firebase_repository.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/delete_firebase_state.dart';
class DeleteFirebaseCacheInteractor {
final FirebaseRepository _firebaseRepository;
DeleteFirebaseCacheInteractor(this._firebaseRepository);
Future<Either<Failure, Success>> execute(String token) async {
try {
await _firebaseRepository.deleteCurrentFirebase(token);
return Right<Failure, Success>(DeleteFirebaseSuccess());
} catch (e) {
logError('DeleteFirebaseCacheInteractor::execute(): $e');
return Left<Failure, Success>(DeleteFirebaseFailure(e));
}
}
}
@@ -0,0 +1,25 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/firebase_repository.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_firebase_state.dart';
class GetFirebaseCacheInteractor {
final FirebaseRepository _firebaseRepository;
GetFirebaseCacheInteractor(this._firebaseRepository);
Future<Either<Failure, Success>> execute(
OIDCConfiguration config,
String refreshToken) async {
try {
final firebase = await _firebaseRepository.getCurrentFirebase();
return Right<Failure, Success>(GetFirebaseSuccess(firebase));
} catch (e) {
logError('GetFirebaseCacheInteractor::execute(): $e');
return Left<Failure, Success>(GetFirebaseFailure(e));
}
}
}
@@ -0,0 +1,21 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/firebase_repository.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/save_firebase_state.dart';
class SaveFirebaseCacheInteractor {
final FirebaseRepository _firebaseRepository;
SaveFirebaseCacheInteractor(this._firebaseRepository);
Future<Either<Failure, Success>> execute(FirebaseDto firebaseDto) async {
try {
await _firebaseRepository.setCurrentFirebase(firebaseDto);
return Right<Failure, Success>(SaveFirebaseSuccess());
} catch (e) {
logError('SaveFirebaseCacheInteractor::execute(): $e');
return Left<Failure, Success>(SaveFirebaseFailure(e));
}
}
}
@@ -0,0 +1,46 @@
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/push_notification/data/datasource/firebase_datasource.dart';
import 'package:tmail_ui_user/features/push_notification/data/datasource_impl/hive_account_datasource_impl.dart';
import 'package:tmail_ui_user/features/push_notification/data/local/firebase_cache_manager.dart';
import 'package:tmail_ui_user/features/push_notification/data/repository/firebase_repository_impl.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/firebase_repository.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/save_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
class FireBaseBindings extends BaseBindings {
@override
void bindingsController() {}
@override
void bindingsDataSource() {
Get.put<FirebaseDatasource>(Get.find<HiveFirebaseDatasourceImpl>());
}
@override
void bindingsDataSourceImpl() {
Get.put(HiveFirebaseDatasourceImpl(
Get.find<FirebaseCacheManager>(),
Get.find<CacheExceptionThrower>(),
));
}
@override
void bindingsInteractor() {
Get.put(DeleteFirebaseCacheInteractor(Get.find<FirebaseRepositoryImpl>()));
Get.put(SaveFirebaseCacheInteractor(Get.find<FirebaseRepositoryImpl>()));
Get.put(GetFirebaseCacheInteractor(Get.find<FirebaseRepositoryImpl>()));
}
@override
void bindingsRepository() {
Get.put<FirebaseRepository>(Get.find<FirebaseRepositoryImpl>());
}
@override
void bindingsRepositoryImpl() {
Get.put(FirebaseRepositoryImpl(Get.find<FirebaseDatasource>()));
}
}
@@ -0,0 +1,155 @@
import 'dart:async';
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import 'package:model/firebase/firebase_dto.dart';
import 'package:rxdart/rxdart.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/save_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/firebase_options.dart';
import 'notification_strings.dart';
final StreamController<NotificationResponse?> selectNotificationStream =
StreamController<NotificationResponse?>.broadcast();
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await NotificationService.displayPushNotification(message);
}
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
//handle action
}
void onDidReceiveNotificationResponse(
NotificationResponse details,
) {
selectNotificationStream.add(details);
}
class NotificationService {
factory NotificationService() => _instance;
NotificationService._internal();
static final NotificationService _instance = NotificationService._internal();
static get _firebaseMessaging => FirebaseMessaging.instance;
static get _flutterLocalNotificationsPlugin =>
FlutterLocalNotificationsPlugin();
static final BehaviorSubject<NotificationResponse?>
_notificationActionStream =
BehaviorSubject<NotificationResponse?>.seeded(null);
static Stream<NotificationResponse?> get notificationActionStream =>
_notificationActionStream.stream;
static Stream<String> get onTokenRefresh => _firebaseMessaging.onTokenRefresh;
static bool _isFlutterLocalNotificationsInitialized = false;
static Future<void> initializeNotificationService(
Function(NotificationResponse)? onDidReceiveNotificationResponse,
) async {
if (_isFlutterLocalNotificationsInitialized) return;
final _saveFirebaseCacheInteractor = Get.find<SaveFirebaseCacheInteractor>();
final token = await _firebaseMessaging.getToken();
_saveFirebaseCacheInteractor.execute(FirebaseDto(token));
await _initFirebaseMessaging();
await _flutterLocalNotificationsPlugin.initialize(
InitializationSettings(
android: androidInitializationSettings,
iOS: iosInitializationSettings,
),
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
await _requestPermissions();
await _createNotificationChannels();
_listenNotificationActionStream();
_isFlutterLocalNotificationsInitialized = true;
}
static Future<void> displayPushNotification(
RemoteMessage notification,
) async {
try {
await _flutterLocalNotificationsPlugin.show(
createUniqueId,
channelName,
notification.data.toString(),
pushNotificationDetails,
);
} catch (e) {
debugPrint(e.toString());
}
}
static Future<void> cancelAllNotifications() async {
await _flutterLocalNotificationsPlugin.cancelAll();
}
static Future<void> _initFirebaseMessaging() async {
FirebaseMessaging.onMessage.listen(_handleIncomingForegroundNotification);
FirebaseMessaging.onMessageOpenedApp.listen(_handleOpenedAppNotification);
}
static Future<void> _handleIncomingForegroundNotification(
RemoteMessage remoteMessage,
) async {
await displayPushNotification(remoteMessage);
}
static void _handleOpenedAppNotification(RemoteMessage remoteMessage) {}
static Future<void> _requestPermissions() async {
if (Platform.isAndroid) {
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestPermission();
} else {
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
}
static Future<void> _listenNotificationActionStream() async {
selectNotificationStream.stream
.listen((event) => event)
.onData((data) async {
_notificationActionStream.add(data);
switch (data?.notificationResponseType) {
case NotificationResponseType.selectedNotification:
break;
case NotificationResponseType.selectedNotificationAction:
break;
default:
}
});
}
static Future<void> _createNotificationChannels() async {
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(androidChannel);
}
static Future<void> deleteToken() async => _firebaseMessaging.deleteToken();
}
int get createUniqueId =>
DateTime.now().millisecondsSinceEpoch.remainder(100000);
@@ -0,0 +1,54 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
const channelId = 'team_mail_channel_id';
const channelKey = 'team_mail_channel_key';
const channelName = 'Team Mail notifications';
const channelDescription = 'Team Mail notifications';
final iosInitializationSettings = DarwinInitializationSettings(
notificationCategories: [
DarwinNotificationCategory(
channelId,
actions: <DarwinNotificationAction>[
DarwinNotificationAction.plain(
channelKey,
channelName,
options: {
DarwinNotificationActionOption.foreground,
},
),
],
),
],
onDidReceiveLocalNotification:
(int id, String? title, String? body, String? payload) {},
);
const androidInitializationSettings =
AndroidInitializationSettings('background');
const androidChannel = AndroidNotificationChannel(
channelId,
channelName,
description: channelDescription,
importance: Importance.max,
enableLights: true,
);
const pushNotificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
channelId,
channelName,
channelDescription: channelDescription,
visibility: NotificationVisibility.public,
importance: Importance.max,
ledOnMs: 100,
ledOffMs: 1000,
category: AndroidNotificationCategory.message,
),
iOS: DarwinNotificationDetails(
presentSound: true,
presentAlert: true,
presentBadge: true,
),
);
@@ -3,6 +3,7 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/app_toast.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/firebase/firebase_dto.dart';
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart';
import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart';
@@ -11,6 +12,8 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oi
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/save_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/notification_service.dart';
import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
@@ -26,6 +29,7 @@ class SessionController extends ReloadableController {
final CachingManager _cachingManager; final CachingManager _cachingManager;
final DeleteAuthorityOidcInteractor _deleteAuthorityOidcInteractor; final DeleteAuthorityOidcInteractor _deleteAuthorityOidcInteractor;
final AuthorizationInterceptors _authorizationInterceptors; final AuthorizationInterceptors _authorizationInterceptors;
final SaveFirebaseCacheInteractor _saveFirebaseCacheInteractor;
final AppToast _appToast; final AppToast _appToast;
final DynamicUrlInterceptors _dynamicUrlInterceptors; final DynamicUrlInterceptors _dynamicUrlInterceptors;
@@ -40,13 +44,19 @@ class SessionController extends ReloadableController {
this._authorizationInterceptors, this._authorizationInterceptors,
this._appToast, this._appToast,
this._dynamicUrlInterceptors, this._dynamicUrlInterceptors,
this._saveFirebaseCacheInteractor,
) : super(logoutOidcInteractor, ) : super(logoutOidcInteractor,
deleteAuthorityOidcInteractor, deleteAuthorityOidcInteractor,
getAuthenticatedAccountInteractor); getAuthenticatedAccountInteractor);
@override @override
void onReady() { Future<void> onReady() async {
super.onReady(); super.onReady();
NotificationService.initializeNotificationService(
onDidReceiveNotificationResponse);
NotificationService.onTokenRefresh.listen((token) {
_saveFirebaseCacheInteractor.execute(FirebaseDto(token));
});
final arguments = Get.arguments; final arguments = Get.arguments;
if (arguments != null && arguments is String) { if (arguments != null && arguments is String) {
_getSession(); _getSession();
@@ -24,6 +24,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_a
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/save_firebase_cache_interactor.dart';
import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart';
import 'package:tmail_ui_user/features/session/presentation/session_controller.dart'; import 'package:tmail_ui_user/features/session/presentation/session_controller.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
@@ -44,6 +45,7 @@ class SessionPageBindings extends BaseBindings {
Get.find<AuthorizationInterceptors>(), Get.find<AuthorizationInterceptors>(),
Get.find<AppToast>(), Get.find<AppToast>(),
Get.find<DynamicUrlInterceptors>(), Get.find<DynamicUrlInterceptors>(),
Get.find<SaveFirebaseCacheInteractor>(),
)); ));
} }
+11
View File
@@ -1,10 +1,14 @@
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/notification_service.dart';
import 'package:tmail_ui_user/firebase_options.dart';
import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart';
@@ -22,6 +26,13 @@ void main() async {
statusBarColor: Colors.black, statusBarColor: Colors.black,
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,
)); ));
try {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
} on FirebaseException catch (e) {
debugPrint(e.toString());
}
await MainBindings().dependencies(); await MainBindings().dependencies();
await HiveCacheConfig().setUp(); await HiveCacheConfig().setUp();
await HiveCacheConfig.initializeEncryptionKey(); await HiveCacheConfig.initializeEncryptionKey();
@@ -6,6 +6,7 @@ import 'package:tmail_ui_user/features/caching/authentication_info_cache_client.
import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart';
import 'package:tmail_ui_user/features/caching/email_cache_client.dart'; import 'package:tmail_ui_user/features/caching/email_cache_client.dart';
import 'package:tmail_ui_user/features/caching/encryption_key_cache_client.dart'; import 'package:tmail_ui_user/features/caching/encryption_key_cache_client.dart';
import 'package:tmail_ui_user/features/caching/firebase_config_cache_client.dart';
import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart';
import 'package:tmail_ui_user/features/caching/recent_login_url_cache_client.dart'; import 'package:tmail_ui_user/features/caching/recent_login_url_cache_client.dart';
import 'package:tmail_ui_user/features/caching/recent_login_username_cache_client.dart'; import 'package:tmail_ui_user/features/caching/recent_login_username_cache_client.dart';
@@ -22,6 +23,7 @@ import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
import 'package:tmail_ui_user/features/push_notification/data/local/firebase_cache_manager.dart';
import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
@@ -55,12 +57,15 @@ class LocalBindings extends Bindings {
Get.put(RecentLoginUrlCacheManager((Get.find<RecentLoginUrlCacheClient>()))); Get.put(RecentLoginUrlCacheManager((Get.find<RecentLoginUrlCacheClient>())));
Get.put(RecentLoginUsernameCacheClient()); Get.put(RecentLoginUsernameCacheClient());
Get.put(RecentLoginUsernameCacheManager(Get.find<RecentLoginUsernameCacheClient>())); Get.put(RecentLoginUsernameCacheManager(Get.find<RecentLoginUsernameCacheClient>()));
Get.put(FirebaseCacheClient());
Get.put(FirebaseCacheManager(Get.find<FirebaseCacheClient>()));
Get.put(CachingManager( Get.put(CachingManager(
Get.find<MailboxCacheClient>(), Get.find<MailboxCacheClient>(),
Get.find<StateCacheClient>(), Get.find<StateCacheClient>(),
Get.find<EmailCacheClient>(), Get.find<EmailCacheClient>(),
Get.find<RecentSearchCacheClient>(), Get.find<RecentSearchCacheClient>(),
Get.find<AccountCacheClient>(), Get.find<AccountCacheClient>(),
Get.find<FirebaseCacheClient>(),
)); ));
} }
+2
View File
@@ -1,4 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/firebase_bindings.dart';
import 'package:tmail_ui_user/main/bindings/core/core_bindings.dart'; import 'package:tmail_ui_user/main/bindings/core/core_bindings.dart';
import 'package:tmail_ui_user/main/bindings/credential/credential_bindings.dart'; import 'package:tmail_ui_user/main/bindings/credential/credential_bindings.dart';
import 'package:tmail_ui_user/main/bindings/local/local_bindings.dart'; import 'package:tmail_ui_user/main/bindings/local/local_bindings.dart';
@@ -17,5 +18,6 @@ class MainBindings extends Bindings {
CredentialBindings().dependencies(); CredentialBindings().dependencies();
NetWorkConnectionBindings().dependencies(); NetWorkConnectionBindings().dependencies();
SessionBindings().dependencies(); SessionBindings().dependencies();
FireBaseBindings().dependencies();
} }
} }
+10
View File
@@ -0,0 +1,10 @@
import 'package:equatable/equatable.dart';
class FirebaseDto with EquatableMixin {
final String token;
FirebaseDto(this.token);
@override
List<Object?> get props => [token];
}
+99 -8
View File
@@ -8,6 +8,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "47.0.0" version: "47.0.0"
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.8"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@@ -141,6 +148,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
cloud_firestore_platform_interface:
dependency: transitive
description:
name: cloud_firestore_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "5.8.5"
cloud_firestore_web:
dependency: transitive
description:
name: cloud_firestore_web
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@@ -364,9 +385,9 @@ packages:
dependency: transitive dependency: transitive
description: description:
path: "." path: "."
ref: email_supported ref: "feature/update_inapp_webview"
resolved-ref: "241d7ba620137d6534cc9bbb5fd10e182b93b94a" resolved-ref: a805acd03aae48997b4e9625412a02aff975747e
url: "https://github.com/linagora/enough_html_editor.git" url: "https://github.com/ManhNTX/enough_html_editor.git"
source: git source: git
version: "0.0.5" version: "0.0.5"
enough_platform_widgets: enough_platform_widgets:
@@ -432,6 +453,48 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.5.2"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
version: "14.0.4"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.6"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.6"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@@ -524,7 +587,7 @@ packages:
name: flutter_inappwebview name: flutter_inappwebview
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.4.3+8" version: "5.7.1"
flutter_keyboard_visibility: flutter_keyboard_visibility:
dependency: transitive dependency: transitive
description: description:
@@ -560,6 +623,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "12.0.3+1"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -998,14 +1082,14 @@ packages:
name: permission_handler name: permission_handler
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "9.2.0" version: "10.2.0"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "9.0.2+1" version: "10.2.0"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
@@ -1110,8 +1194,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: master ref: demo
resolved-ref: d6c954206ac885a6f269f8be06dfd914a6393e89 resolved-ref: "70cea1832b0e93bce515d4641f96cd1b12366725"
url: "https://github.com/linagora/rich-text-composer.git" url: "https://github.com/linagora/rich-text-composer.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@@ -1323,6 +1407,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.2" version: "3.2.2"
timezone:
dependency: transitive
description:
name: timezone
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0"
timing: timing:
dependency: transitive dependency: transitive
description: description:
+5 -2
View File
@@ -48,7 +48,7 @@ dependencies:
rich_text_composer: rich_text_composer:
git: git:
url: https://github.com/linagora/rich-text-composer.git url: https://github.com/linagora/rich-text-composer.git
ref: master ref: demo
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
@@ -117,7 +117,7 @@ dependencies:
device_info_plus: 4.0.2 device_info_plus: 4.0.2
# permission_handler # permission_handler
permission_handler: 9.2.0 permission_handler: 10.2.0
# share # share
share: 2.0.4 share: 2.0.4
@@ -196,6 +196,9 @@ dependencies:
ref: master ref: master
url_launcher: 6.1.5 url_launcher: 6.1.5
firebase_core: ^2.2.0
firebase_messaging: 14.0.4
flutter_local_notifications: ^12.0.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: