diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index da67d66da..2fd225a0c 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -25,8 +25,8 @@ jobs: - name: Check license compliance run: dart run license_checker check-licenses -c licenses.yaml --problematic - run: flutter analyze - - name: Add Firebase Messaging - run: ./scripts/add-firebase-messaging.sh + - name: Apply google services patch + run: ./scripts/enable-google-services.sh - run: flutter analyze - run: flutter test diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 167602f73..d76dbc2b9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -114,13 +114,6 @@ - - - - - - diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt deleted file mode 100644 index 0db8bdcae..000000000 --- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt +++ /dev/null @@ -1,28 +0,0 @@ -/*package chat.fluffy.fluffychat - -import com.famedly.fcm_shared_isolate.FcmSharedIsolateService - -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint -import android.content.Context - -class FcmPushService : FcmSharedIsolateService() { - override fun getEngine(): FlutterEngine { - return provideEngine(getApplicationContext()) - } - - companion object { - fun provideEngine(context: Context): FlutterEngine { - var engine = MainActivity.engine - if (engine == null) { - engine = MainActivity.provideEngine(context) - engine.getLocalizationPlugin().sendLocalesToFlutter( - context.getResources().getConfiguration()) - engine.getDartExecutor().executeDartEntrypoint( - DartEntrypoint.createDefault()) - } - return engine - } - } -} -*/ \ No newline at end of file diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt index 747183cd3..da84a642c 100644 --- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt +++ b/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt @@ -6,12 +6,10 @@ import io.flutter.embedding.engine.FlutterEngine import android.content.Context class MainActivity : FlutterActivity() { - override fun attachBaseContext(base: Context) { super.attachBaseContext(base) } - override fun provideFlutterEngine(context: Context): FlutterEngine? { return provideEngine(this) } diff --git a/firebase.json b/firebase.json new file mode 100644 index 000000000..093a4e3c2 --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"fluffychat-ef3e8","appId":"1:865731724731:android:ec427b3b1dcd4a1e64309e","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"fluffychat-ef3e8","configurations":{"android":"1:865731724731:android:ec427b3b1dcd4a1e64309e","ios":"1:865731724731:ios:79fd983ce46cb40c64309e","macos":"1:865731724731:ios:6fb777cf513cdb6264309e","web":"1:865731724731:web:d367990bc625c24864309e","windows":"1:865731724731:web:06933086ac59630464309e"}}}}}} \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 4c88a1141..d33509226 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '13.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/lib/main.dart b/lib/main.dart index f3be9dc52..3ea49877a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,7 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:fluffychat/utils/push_helper.dart'; + import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; @@ -22,6 +26,8 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); await vod.init(wasmPath: './assets/assets/vodozemac/'); + await Firebase.initializeApp(); + FirebaseMessaging.onBackgroundMessage(pushHelperBackground); Logs().nativeColors = !PlatformInfos.isIOS; final store = await SharedPreferences.getInstance(); diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index ba9596046..1a716d591 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -39,7 +39,7 @@ import '../config/setting_keys.dart'; import '../widgets/matrix.dart'; import 'platform_infos.dart'; -//import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; class NoTokenException implements Exception { String get cause => 'Cannot get firebase token'; @@ -64,7 +64,7 @@ class BackgroundPush { final pendingTests = >{}; - //final firebase = FcmSharedIsolate(); + final firebase = FirebaseMessaging.instance; DateTime? lastReceivedPush; @@ -80,17 +80,7 @@ class BackgroundPush { onDidReceiveNotificationResponse: goToRoom, ); Logs().v('Flutter Local Notifications initialized'); - //firebase.setListeners( - // onMessage: (message) => pushHelper( - // PushNotification.fromJson( - // Map.from(message['data'] ?? message), - // ), - // client: client, - // l10n: l10n, - // activeRoomId: matrix?.activeRoomId, - // flutterLocalNotificationsPlugin: _flutterLocalNotificationsPlugin, - // ), - //); + FirebaseMessaging.onMessage.listen(_onFirebaseMessage); if (Platform.isAndroid) { await UnifiedPush.initialize( onNewEndpoint: _newUpEndpoint, @@ -148,7 +138,7 @@ class BackgroundPush { bool useDeviceSpecificAppId = false, }) async { if (PlatformInfos.isIOS) { - //await firebase.requestPermission(); + await firebase.requestPermission(); } if (PlatformInfos.isAndroid) { _flutterLocalNotificationsPlugin @@ -172,7 +162,7 @@ class BackgroundPush { if (deviceAppId.length > 64) { deviceAppId = deviceAppId.substring(0, 64); } - if (!useDeviceSpecificAppId && PlatformInfos.isAndroid) { + if (!useDeviceSpecificAppId) { appId += '.data_message'; } final thisAppId = useDeviceSpecificAppId ? deviceAppId : appId; @@ -308,7 +298,7 @@ class BackgroundPush { Logs().v('Setup firebase'); if (_fcmToken?.isEmpty ?? true) { try { - //_fcmToken = await firebase.getToken(); + _fcmToken = await firebase.getToken(); if (_fcmToken == null) throw ('PushToken is null'); } catch (e, s) { Logs().w('[Push] cannot get token', e, e is String ? null : s); @@ -385,8 +375,9 @@ class BackgroundPush { Logs().i('[Push] UnifiedPush using endpoint $endpoint'); final oldTokens = {}; try { - //final fcmToken = await firebase.getToken(); - //oldTokens.add(fcmToken); + String? fcmToken; + fcmToken = await firebase.getToken(); + oldTokens.add(fcmToken); } catch (_) {} await setupPusher( gatewayUrl: endpoint, @@ -413,11 +404,19 @@ class BackgroundPush { } } - Future _onUpMessage(PushMessage pushMessage, String i) async { - final message = pushMessage.content; + // ignore: unused_element + Future _onFirebaseMessage(message) => pushHelper( + PushNotification.fromJson(message.data), + client: client, + l10n: l10n, + activeRoomId: matrix?.activeRoomId, + flutterLocalNotificationsPlugin: _flutterLocalNotificationsPlugin, + ); + + Future _onUpMessage(PushMessage message, String i) async { upAction = true; final data = Map.from( - json.decode(utf8.decode(message))['notification'], + json.decode(utf8.decode(message.content))['notification'], ); // UP may strip the devices list data['devices'] ??= []; diff --git a/lib/utils/init_with_restore.dart b/lib/utils/init_with_restore.dart index 523b22f9e..dc7a6dcad 100644 --- a/lib/utils/init_with_restore.dart +++ b/lib/utils/init_with_restore.dart @@ -61,7 +61,10 @@ extension InitWithRestoreExtension on Client { ); } - Future initWithRestore({void Function()? onMigration}) async { + Future initWithRestore({ + bool isBackgroundClient = false, + void Function()? onMigration, + }) async { final storageKey = '${AppConfig.applicationName}_session_backup_$clientName'; final storage = PlatformInfos.isMobile || PlatformInfos.isLinux @@ -69,6 +72,7 @@ extension InitWithRestoreExtension on Client { : null; try { + if (isBackgroundClient) await abortSync(); await init( onInitStateChanged: (state) { if (state == InitState.migratingDatabase) onMigration?.call(); diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 39f6bd90f..9dd8d4793 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:ui'; +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -19,13 +20,29 @@ import 'package:fluffychat/utils/platform_infos.dart'; const notificationAvatarDimension = 128; +@pragma('vm:entry-point') +Future pushHelperBackground(message) async { + await Firebase.initializeApp(); + return pushHelper(PushNotification.fromJson(message.data)); +} + Future pushHelper( PushNotification notification, { Client? client, L10n? l10n, String? activeRoomId, - required FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin, + FlutterLocalNotificationsPlugin? flutterLocalNotificationsPlugin, }) async { + if (flutterLocalNotificationsPlugin == null) { + flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + await flutterLocalNotificationsPlugin.initialize( + const InitializationSettings( + android: AndroidInitializationSettings('notifications_icon'), + iOS: DarwinInitializationSettings(), + ), + ); + } + try { await _tryPushHelper( notification, @@ -43,7 +60,7 @@ Future pushHelper( l10n.newMessageInFluffyChat, l10n.openAppToReadMessages, NotificationDetails( - iOS: const DarwinNotificationDetails(), + iOS: const DarwinNotificationDetails(sound: 'notification.caf'), android: AndroidNotificationDetails( AppConfig.pushNotificationsChannelId, l10n.incomingMessages, @@ -278,7 +295,9 @@ Future _tryPushHelper( priority: Priority.max, groupKey: event.room.spaceParents.firstOrNull?.roomId ?? 'rooms', ); - const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); + const iOSPlatformChannelSpecifics = DarwinNotificationDetails( + sound: "notification.caf", + ); final platformChannelSpecifics = NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index dd7fa9ed5..e92e84cb6 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,8 @@ import dynamic_color import emoji_picker_flutter import file_picker import file_selector_macos +import firebase_core +import firebase_messaging import flutter_local_notifications import flutter_new_badger import flutter_secure_storage_macos @@ -41,6 +43,8 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterNewBadgerPlugin.register(with: registry.registrar(forPlugin: "FlutterNewBadgerPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 41d360c6f..8de2f3534 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 3AF44CC3D61A8332019AFCDE /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5AEFA815523969D4ADFBE7E8 /* GoogleService-Info.plist */; }; 9CAF203E1D098383F2EDFFCB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3C62FEBAA272B5A33AFFC95 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -69,6 +70,7 @@ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 35E6B919318905352ECC7D69 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 5AEFA815523969D4ADFBE7E8 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; 5CDC3DD55F4AC23D2067B292 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; @@ -110,6 +112,7 @@ 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, F9F203356080D460FB6D4567 /* Pods */, + 5AEFA815523969D4ADFBE7E8 /* GoogleService-Info.plist */, ); sourceTree = ""; }; @@ -255,6 +258,7 @@ files = ( 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + 3AF44CC3D61A8332019AFCDE /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/Runner/GoogleService-Info.plist b/macos/Runner/GoogleService-Info.plist new file mode 100644 index 000000000..9fe83e59a --- /dev/null +++ b/macos/Runner/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyA8ZUBcuny0HjPwF2Q2fvDyQTC5dG2VHlE + GCM_SENDER_ID + 865731724731 + PLIST_VERSION + 1 + BUNDLE_ID + im.fluffychat.fluffychat + PROJECT_ID + fluffychat-ef3e8 + STORAGE_BUCKET + fluffychat-ef3e8.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:865731724731:ios:6fb777cf513cdb6264309e + + \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 3e400e7ca..f52d724bb 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: bb84ee51e527053dd8e25ecc9f97a6abfdc19130fb4d883e4e8585e23e7e6dd8 + url: "https://pub.dev" + source: hosted + version: "1.3.60" analyzer: dependency: transitive description: @@ -473,6 +481,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+4" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "6b343e6f7b72a4f32d7ce8df8c9a28d8f54b4ac20d7c6500f3e8b3969afca457" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "5d28b14dd32282fb7ce2b22b897362453755b6b8541d491127dc72b755bb7b16" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "10272b553a49c13a6cedfd00121047157521f82a5d3f2a1706b9dd28342cc482" + url: "https://pub.dev" + source: hosted + version: "16.0.0" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: b846a305feb3f74ee3f0aace447f65a4696bc6550bc828ecf5a84a1b77473d16 + url: "https://pub.dev" + source: hosted + version: "4.7.0" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "28714749880f7242c5fb3b1ee6c66b41f61453f02ae348b43c82957df80b87ae" + url: "https://pub.dev" + source: hosted + version: "4.0.0" fixnum: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e1a288092..f34c9fb44 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,8 @@ dependencies: emojis: ^0.9.9 file_picker: ^10.3.1 file_selector: ^1.0.3 + firebase_core: ^4.0.0 + firebase_messaging: ^16.0.0 flutter: sdk: flutter flutter_foreground_task: ^9.1.0 diff --git a/scripts/add-firebase-messaging.sh b/scripts/add-firebase-messaging.sh index af7ce0259..99d7e5c60 100755 --- a/scripts/add-firebase-messaging.sh +++ b/scripts/add-firebase-messaging.sh @@ -1,12 +1,15 @@ #!/usr/bin/env bash -flutter pub add fcm_shared_isolate +flutter pub add firebase_core +flutter pub add firebase_messaging flutter pub get if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's,//,,g' lib/main.dart sed -i '' 's,//,,g' lib/utils/background_push.dart sed -i '' -e 's,^/\*,,' -e 's,\*/$,,' android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt else + sed -i 's,//,,g' lib/main.dart sed -i 's,//,,g' lib/utils/background_push.dart sed -i -e 's,^/\*,,' -e 's,\*/$,,' android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt fi diff --git a/scripts/release-ios-testflight.sh b/scripts/release-ios-testflight.sh index 14e6ea08d..0755625ae 100755 --- a/scripts/release-ios-testflight.sh +++ b/scripts/release-ios-testflight.sh @@ -1,5 +1,6 @@ #!/bin/sh -ve -flutter pub add fcm_shared_isolate:0.1.0 +flutter pub add firebase_core +flutter pub add firebase_messaging sed -i '' 's,//,,g' lib/utils/background_push.dart flutter clean flutter pub get diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d083bd166..dcd539388 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("EmojiPickerFlutterPluginCApi")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); FlutterWebRTCPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 3276ee890..8ff3cca2c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color emoji_picker_flutter file_selector_windows + firebase_core flutter_secure_storage_windows flutter_webrtc geolocator_windows