# mattermost_push_plugin An Android-first Flutter plugin for receiving Mattermost push notifications, displaying system notifications, handling tap events, processing inline replies, delivering ACKs, and securely storing device tokens, authentication tokens, and signing keys. --- ## Support Matrix | Platform | Support Status | Native Features Implemented | |---|---|---| | **Android** | **Full (Production Ready)** | Kotlin, Native FCM Service, Room DB, JJWT Verification, OkHttp ACK Client, Notification Builder, Inline Reply Receiver, Dismiss Receiver | | **iOS** | No-op Scaffold (Stub) | Future roadmap milestone | | **macOS** | No-op Scaffold (Stub) | Future roadmap milestone | --- ## Core Responsibilities This plugin isolates the Mattermost push notification domain logic from the host application. ```mermaid graph TD A[Mattermost Server] -->|Secure JWT Push| B(Plugin Native FCM Service) B -->|1. Signature Verification JJWT| C{Valid?} C -->|Yes| D[2. Store & Build Notification] C -->|No| E[Drop Push] D -->|3. OkHttp ACK API| A D -->|4. Display System Notification| F(Android Status Bar) F -->|Tap Notification| G[5. Method Channel Opened Event] F -->|Inline Reply| H[6. Broadcast Reply Receiver] H -->|Deliver via OkHttp| A G -->|Flutter Stream| I(Host App Navigation) ``` --- ## Public Dart API Reference Get the singleton instance via `MattermostPushPlugin.instance`. ### 1. Initialization Initialize the plugin channel subscriptions, listen to token refreshes, and request notification permissions: ```dart import 'package:mattermost_push_plugin/mattermost_push_plugin.dart'; await MattermostPushPlugin.instance.initialize(); ``` ### 2. Streams & Callbacks Set up listeners and navigation callbacks inside your app initialization flow: ```dart // 1. Listen to raw native notification events (Message, Clear, Session, etc.) MattermostPushPlugin.instance.onNotification.listen((data) { print("Raw push received: $data"); }); // 2. Listen to user interaction (notification tap) events MattermostPushPlugin.instance.onNotificationOpened.listen((event) { print("Notification clicked! Channel: ${event.channelId}, Root: ${event.rootId}"); }); // 3. Register navigation handlers (automatically triggered by onNotificationOpened) MattermostPushPlugin.instance.onNavigateToChannel = (String serverUrl, String channelId) { // Navigate the app to the specific channel }; MattermostPushPlugin.instance.onNavigateToThread = (String serverUrl, String rootId) { // Navigate the app to the specific CRT thread }; // 4. Triggered when the device token is registered/refreshed MattermostPushPlugin.instance.onDeviceTokenReady = (String deviceToken) { // e.g. "android_rn-v2:YOUR_FCM_TOKEN" // Send this token to the Mattermost server for token registration }; ``` ### 3. Credential & Signing Key Storing Securely write server-specific secrets to native persistent storage. These are required by the native service to decrypt and verify the authenticity of push messages. ```dart // Save authenticating token for HTTP ACK requests await MattermostPushPlugin.instance.setAuthToken( "https://mattermost.example.com", "YOUR_USER_AUTH_TOKEN", identifier: "user_session_id", // optional ); // Save public signing key for verification await MattermostPushPlugin.instance.setSigningKey( "https://mattermost.example.com", "YOUR_SERVER_PUBLIC_SIGNING_KEY", ); // Clear token on logout await MattermostPushPlugin.instance.clearAuthToken("https://mattermost.example.com"); // Fetch the formatted device token String? token = await MattermostPushPlugin.instance.getDeviceToken(); ``` --- ## Host App Integration & Responsibilities The host application (`apps/mobile`) acts as the consumer of the plugin and is responsible for: 1. **Firebase Configuration**: Include the `google-services.json` in `android/app/` and initialize Firebase in the Dart main block. 2. **Authentication Flow**: Call `setAuthToken` and `setSigningKey` on successful Mattermost login, and `clearAuthToken` on logout. 3. **App Navigation**: Implement `onNavigateToChannel` and `onNavigateToThread` callbacks to route users inside the Flutter UI. 4. **FCM Token Registration**: Send the formatted token received in `onDeviceTokenReady` to the Mattermost server. ### Firebase Messaging Manifest Collision Guard This plugin implements a custom Android FCM service (`MattermostFirebaseMessagingService`) that consumes incoming FCM payloads. To prevent manifest merge collisions with the standard Flutter `firebase_messaging` library, the plugin's `AndroidManifest.xml` disables the generic Flutter Firebase service via `tools:node="remove"`. Ensure that the host app does not redeclare duplicate FCM services unless explicitly configured to do so. --- ## Test Harness Strategy This repository keeps the plugin and its independent consumer test app together: ```text mattermost-push-plugin/ lib/ # Public Dart API and Flutter-facing behavior android/ # Android native plugin implementation test/ # Dart unit tests for the plugin contract example/ # Thin Flutter app used as the integration test harness test/ # Widget tests for the harness app integration_test/ # Device/emulator integration tests ``` The `example/` app is the official place to validate plugin integration without coupling this package to any consuming product app. Consumer apps should only test their own wiring, such as calling initialization, storing credentials, and connecting navigation callbacks. Recommended verification layers: 1. `flutter test` from the repository root for Dart API behavior, event parsing, callback routing, and token formatting. 2. `flutter test` from `example/` for the test harness app. 3. Android unit tests from `example/android` for native payload, storage, signing, and ACK behavior. 4. `flutter test integration_test` from `example/` on a device or emulator for plugin registration, method/event channels, notification-open routing, and manifest integration. 5. Manual smoke tests for true Firebase FCM delivery and real Mattermost server ACK flows, because those depend on external infrastructure. Recommended local commands: ```sh flutter analyze flutter test (cd example && flutter analyze && flutter test) (cd example/android && ./gradlew testDebugUnitTest) ``` Android Gradle tests require a configured Android SDK via `ANDROID_HOME` or `example/android/local.properties`. --- ## Verification & Manual Smoke Testing Checklist Since full end-to-end FCM flows cannot be automated in headless test runners, verify functionality manually using the following checklist: - [ ] **FCM Payload Receipt**: Verify that sending an FCM notification payload targeting the host application successfully wakes up `MattermostFirebaseMessagingService`. - [ ] **Signature Verification**: Ensure that a valid push payload signed by the Mattermost server's private key compiles and passes the native JJWT verification using the stored signing key. - [ ] **Notification Display**: Verify the system notification builds with the correct title, message, avatar (if supplied), and CRT threading hierarchy. - [ ] **ACK Delivery**: Verify the plugin sends a HTTP `POST` receipt delivery ACK back to the Mattermost server and logs a `200 OK` or `201 Created` response. - [ ] **Notification Tap & Route**: Tap the system notification and verify the host app launches/resumes, triggers the `onNotificationOpened` stream, and executes the designated navigation callback. - [ ] **Inline Reply Action**: Click "Reply" directly on the system notification. Type a reply, press send, and verify the native broadcast receiver intercepts the text, writes it to the local Room database, and sends it via HTTP to the Mattermost API. - [ ] **Notification Dismiss (Clear)**: Swipe away or dismiss a notification and verify the `NotificationDismissService` is triggered, clearing the corresponding notification records natively. - [ ] **Token Storage & Generation**: Ensure the generated token has the correct `android_rn-v2:` prefix and is saved securely in the device's native database/shared preferences.