From 23f9be2fd563a4e09e07bb55e86e52890ff0d75b Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Sat, 14 May 2022 12:06:07 +0200 Subject: [PATCH] feat: Use Firebase Cloud Messaging over UP This switches to the FOSS implementation of the google libraries so we have no more Blobs there. --- .gitlab-ci.yml | 1 - android/app/build.gradle | 7 +- android/app/src/main/AndroidManifest.xml | 8 +- .../fluffy/fluffychat/EmbeddedDistributor.kt | 14 ++ .../chat/fluffy/fluffychat/FcmPushService.kt | 36 ---- android/build.gradle | 1 - lib/utils/background_push.dart | 96 ++++------ pubspec.yaml | 1 - scripts/build-ios.sh | 1 - scripts/code_analyze.sh | 1 - scripts/enable-android-google-services.patch | 168 ------------------ scripts/prepare-android-release.sh | 1 - scripts/release-ios-testflight.sh | 1 - 13 files changed, 60 insertions(+), 276 deletions(-) create mode 100644 android/app/src/main/kotlin/chat/fluffy/fluffychat/EmbeddedDistributor.kt delete mode 100644 android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt delete mode 100644 scripts/enable-android-google-services.patch diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46abae8e..a0a4686a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -62,7 +62,6 @@ integration_test: # properly set the homeserver IP for the test - sed -i "s/10.0.2.2/$(drill docker | grep -m 1 -P "\d+\.\d+\.\d+.\d+" | awk -F ' ' '{print $NF}')/g" integration_test/users.dart - curl docker:8008/_matrix/static/ 2> /dev/null | grep "It works! Synapse is running" - - git apply ./scripts/enable-android-google-services.patch - flutter pub get - flutter test integration_test tags: diff --git a/android/app/build.gradle b/android/app/build.gradle index fba903e3..707a0394 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -78,8 +78,9 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - //implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698 + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' implementation 'androidx.multidex:multidex:2.0.1' + implementation('com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1') } - -//apply plugin: 'com.google.gms.google-services' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ff394d9d..99394376 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -122,7 +122,13 @@ - + + + + + + + ? router; - String? _fcmToken; void Function(String errorMsg, {Uri? link})? onFcmError; L10n? l10n; Store? _store; @@ -65,8 +65,6 @@ class BackgroundPush { final pendingTests = >{}; - final dynamic firebase = null; //FcmSharedIsolate(); - DateTime? lastReceivedPush; bool upAction = false; @@ -77,17 +75,6 @@ class BackgroundPush { onRoomSync ??= client.onSync.stream .where((s) => s.hasRoomUpdate) .listen((s) => _onClearingPush(getFromServer: false)); - firebase?.setListeners( - onMessage: (message) => pushHelper( - PushNotification.fromJson( - Map.from(message['data'] ?? message)), - client: client, - l10n: l10n, - activeRoomId: router?.currentState?.pathParameters['roomid'], - onSelectNotification: goToRoom, - ), - onNewToken: _newFcmToken, - ); if (Platform.isAndroid) { UnifiedPush.initialize( onNewEndpoint: _newUpEndpoint, @@ -123,20 +110,12 @@ class BackgroundPush { StreamSubscription? onLogin; StreamSubscription? onRoomSync; - void _newFcmToken(String token) { - _fcmToken = token; - setupPush(); - } - Future setupPusher({ String? gatewayUrl, String? token, Set? oldTokens, bool useDeviceSpecificAppId = false, }) async { - if (PlatformInfos.isIOS) { - await firebase?.requestPermission(); - } final clientName = PlatformInfos.clientName; oldTokens ??= {}; final pushers = await (client.getPushers().catchError((e) { @@ -229,11 +208,10 @@ class BackgroundPush { if (upAction) { return; } - if (!PlatformInfos.isIOS && - (await UnifiedPush.getDistributors()).isNotEmpty) { + if ((await UnifiedPush.getDistributors()).isNotEmpty) { await setupUp(); } else { - await setupFirebase(); + _noFcmWarning(); } // ignore: unawaited_futures @@ -273,24 +251,6 @@ class BackgroundPush { } } - Future setupFirebase() async { - Logs().v('Setup firebase'); - if (_fcmToken?.isEmpty ?? true) { - try { - _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); - await _noFcmWarning(); - return; - } - } - await setupPusher( - gatewayUrl: AppConfig.pushNotificationsGatewayUrl, - token: _fcmToken, - ); - } - Future goToRoom(String? roomId) async { try { Logs().v('[Push] Attempting to go to room $roomId...'); @@ -321,8 +281,20 @@ class BackgroundPush { await _upUnregistered(i); return; } + var endpoint = 'https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify'; + + if (newEndpoint.startsWith(AppConfig.pushNotificationsGatewayUrl)) { + await setupPusher( + gatewayUrl: AppConfig.pushNotificationsGatewayUrl, + token: Uri.parse(newEndpoint).queryParameters['token'], + ); + await store.setItem(SettingKeys.unifiedPushEndpoint, newEndpoint); + await store.setItemBool(SettingKeys.unifiedPushRegistered, true); + return; + } + try { final url = Uri.parse(newEndpoint) .replace( @@ -344,16 +316,11 @@ class BackgroundPush { '[Push] No self-hosted unified push gateway present: ' + newEndpoint); } Logs().i('[Push] UnifiedPush using endpoint ' + endpoint); - final oldTokens = {}; - try { - final fcmToken = await firebase?.getToken(); - oldTokens.add(fcmToken); - } catch (_) {} + await setupPusher( gatewayUrl: endpoint, token: newEndpoint, - oldTokens: oldTokens, - useDeviceSpecificAppId: true, + useDeviceSpecificAppId: false, ); await store.setItem(SettingKeys.unifiedPushEndpoint, newEndpoint); await store.setItemBool(SettingKeys.unifiedPushRegistered, true); @@ -374,17 +341,24 @@ class BackgroundPush { } Future _onUpMessage(Uint8List message, String i) async { - upAction = true; - final data = Map.from( - json.decode(utf8.decode(message))['notification']); - // UP may strip the devices list - data['devices'] ??= []; - await pushHelper( - PushNotification.fromJson(data), - client: client, - l10n: l10n, - activeRoomId: router?.currentState?.pathParameters['roomid'], - ); + try { + Logs().d('Received Push Notification over UP'); + final jsonMessage = json.decode(utf8.decode(message)); + upAction = true; + final data = + Map.from(jsonMessage['notification'] ?? jsonMessage); + // UP may strip the devices list + data['devices'] ??= []; + await pushHelper( + PushNotification.fromJson(data), + client: client, + l10n: l10n, + activeRoomId: router?.currentState?.pathParameters['roomid'], + ); + } catch (e, s) { + Logs().wtf('Push Helper has crashed!', e, s); + SentryController.captureException(e, s); + } } /// Workaround for the problem that local notification IDs must be int but we diff --git a/pubspec.yaml b/pubspec.yaml index b0e4daef..3bf780aa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,7 +25,6 @@ dependencies: email_validator: ^2.0.1 emoji_picker_flutter: ^1.1.2 encrypt: ^5.0.1 - #fcm_shared_isolate: ^0.1.0 file_picker_cross: ^4.6.0 flutter: sdk: flutter diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index 7fd791b5..9ea2c8c9 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash -git apply ./scripts/enable-android-google-services.patch FLUFFYCHAT_ORIG_GROUP="im.fluffychat" FLUFFYCHAT_ORIG_TEAM="4NXF6Z997G" #FLUFFYCHAT_NEW_GROUP="com.example.fluffychat" diff --git a/scripts/code_analyze.sh b/scripts/code_analyze.sh index c0249a7c..e3602763 100755 --- a/scripts/code_analyze.sh +++ b/scripts/code_analyze.sh @@ -2,7 +2,6 @@ flutter pub get flutter pub run import_sorter:main --no-comments --exit-if-changed flutter format lib/ test/ --set-exit-if-changed -git apply ./scripts/enable-android-google-services.patch flutter pub get flutter analyze flutter pub run dart_code_metrics:metrics lib -r gitlab > code-quality-report.json || true \ No newline at end of file diff --git a/scripts/enable-android-google-services.patch b/scripts/enable-android-google-services.patch deleted file mode 100644 index a190cbc3..00000000 --- a/scripts/enable-android-google-services.patch +++ /dev/null @@ -1,168 +0,0 @@ -diff --git a/android/app/build.gradle b/android/app/build.gradle -index 001fbd72..339b35af 100644 ---- a/android/app/build.gradle -+++ b/android/app/build.gradle -@@ -44,7 +44,7 @@ android { - - defaultConfig { - applicationId "chat.fluffy.fluffychat" -- minSdkVersion 16 -+ minSdkVersion 19 - targetSdkVersion 30 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName -@@ -68,6 +68,10 @@ android { - } - release { - signingConfig signingConfigs.release -+ minifyEnabled false -+ shrinkResources false -+ -+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - } -@@ -78,8 +82,11 @@ flutter { - - dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -- //implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698 -+ implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698 -+ testImplementation 'junit:junit:4.12' -+ androidTestImplementation 'androidx.test:runner:1.1.1' -+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - implementation 'androidx.multidex:multidex:2.0.1' - } - --//apply plugin: 'com.google.gms.google-services' -+apply plugin: 'com.google.gms.google-services' -diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro -new file mode 100644 -index 00000000..40570865 ---- /dev/null -+++ b/android/app/proguard-rules.pro -@@ -0,0 +1,41 @@ -+-optimizationpasses 5 -+## Flutter wrapper -+-keep class io.flutter.app.** { *; } -+-keep class io.flutter.plugin.** { *; } -+-keep class io.flutter.util.** { *; } -+-keep class io.flutter.view.** { *; } -+-keep class io.flutter.** { *; } -+-keep class io.flutter.plugins.** { *; } -+-dontwarn io.flutter.embedding.** -+ -+##---------------Begin: proguard configuration for Gson (Needed for flutter_local_notifications) ---------- -+# Gson uses generic type information stored in a class file when working with fields. Proguard -+# removes such information by default, so configure it to keep all of it. -+-keepattributes Signature -+ -+# For using GSON @Expose annotation -+-keepattributes *Annotation* -+ -+# Gson specific classes -+-dontwarn sun.misc.** -+ -+# Application classes that will be serialized/deserialized over Gson -+-keep class com.google.gson.examples.android.model.** { ; } -+ -+# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, -+# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) -+-keep class * extends com.google.gson.TypeAdapter -+-keep class * implements com.google.gson.TypeAdapterFactory -+-keep class * implements com.google.gson.JsonSerializer -+-keep class * implements com.google.gson.JsonDeserializer -+ -+# Prevent R8 from leaving Data object members always null -+-keepclassmembers,allowobfuscation class * { -+ @com.google.gson.annotations.SerializedName ; -+} -+ -+# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. -+-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken -+-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -+ -+##---------------End: proguard configuration for Gson (Needed for flutter_local_notifications) ---------- -\ No newline at end of file -diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt -index d9930f55..510e9845 100644 ---- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt -+++ b/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt -@@ -1,4 +1,4 @@ --/*package chat.fluffy.fluffychat -+package chat.fluffy.fluffychat - - import com.famedly.fcm_shared_isolate.FcmSharedIsolateService - -@@ -33,4 +33,3 @@ class FcmPushService : FcmSharedIsolateService() { - } - } - } --*/ -\ 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 1afc4606..894d1571 100644 ---- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt -+++ b/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt -@@ -7,13 +7,11 @@ import android.content.Context - import androidx.multidex.MultiDex - - class MainActivity : FlutterActivity() { -- - override fun attachBaseContext(base: Context) { - super.attachBaseContext(base) - MultiDex.install(this) - } - -- - override fun provideFlutterEngine(context: Context): FlutterEngine? { - return provideEngine(this) - } -diff --git a/android/build.gradle b/android/build.gradle -index 85aa8647..3b7e09e7 100644 ---- a/android/build.gradle -+++ b/android/build.gradle -@@ -8,7 +8,7 @@ buildscript { - dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" -- //classpath 'com.google.gms:google-services:4.3.8' -+ classpath 'com.google.gms:google-services:4.3.8' - } - } - -diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart -index cd79b0ab..c2db0f1e 100644 ---- a/lib/utils/background_push.dart -+++ b/lib/utils/background_push.dart -@@ -39,7 +39,7 @@ import '../config/setting_keys.dart'; - import 'famedlysdk_store.dart'; - import 'platform_infos.dart'; - --//import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; -+import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; - - class NoTokenException implements Exception { - String get cause => 'Cannot get firebase token'; -@@ -65,7 +65,7 @@ class BackgroundPush { - - final pendingTests = >{}; - -- final dynamic firebase = null; //FcmSharedIsolate(); -+ final dynamic firebase = FcmSharedIsolate(); - - DateTime? lastReceivedPush; - -diff --git a/pubspec.yaml b/pubspec.yaml -index 6999d0b8..b2c9144f 100644 ---- a/pubspec.yaml -+++ b/pubspec.yaml -@@ -25,7 +25,7 @@ dependencies: - email_validator: ^2.0.1 - emoji_picker_flutter: ^1.1.2 - encrypt: ^5.0.1 -- #fcm_shared_isolate: ^0.1.0 -+ fcm_shared_isolate: ^0.1.0 - file_picker_cross: ^4.6.0 - flutter: - sdk: flutter diff --git a/scripts/prepare-android-release.sh b/scripts/prepare-android-release.sh index 8204f7ca..a2a11dcb 100755 --- a/scripts/prepare-android-release.sh +++ b/scripts/prepare-android-release.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash -git apply ./scripts/enable-android-google-services.patch cd android echo $FDROID_KEY | base64 --decode --ignore-garbage > key.jks echo "storePassword=${FDROID_KEY_PASS}" >> key.properties diff --git a/scripts/release-ios-testflight.sh b/scripts/release-ios-testflight.sh index f536702c..fc241f5c 100755 --- a/scripts/release-ios-testflight.sh +++ b/scripts/release-ios-testflight.sh @@ -1,5 +1,4 @@ #!/bin/sh -ve -git apply ./scripts/enable-android-google-services.patch flutter clean flutter pub get cd ios