Hypertrack empêche la notification FCM dans Flutter

Nov 12 2020

Lorsque j'utilise hypertrack_plugin avec firebase_messaging dans Flutter, je ne peux recevoir aucun message sous Android. Cependant, cela fonctionne très bien sous iOS.

Version de hypertrack_plugin: 0.1.3 Version de firebase_messaging: 7.0.3

Réponses

bikram Nov 12 2020 at 13:11

METTRE À JOUR

Ce problème a été corrigé dans la mise à jour du plugin qui est dans la version hypertrack_plugin : 0.1.4

Problème

Vous êtes confronté à ce problème car plusieurs classes de service s'étendent de FirebaseMessagingService. Pour cette raison, les messages sont reçus dans une classe avec une priorité élevée et pas dans l'autre.

Solution

Méthode 1

Ajoutez ce qui suit à votre AndroidManifest.xmlfichier

<service android:name="io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService">
      <intent-filter android:priority="100">
            <action android:name="com.google.firebase.MESSAGING_EVENT"/>
      </intent-filter>
</service>

Comment ça fonctionne?

La priorité définie FlutterFirebaseMessagingServicedans son fichier manifeste est zéro (par défaut) mais HyperTrackMessagingServicedans son fichier manifeste est déclarée avec une priorité de 5 (version 4.8.0 maintenant). La solution ci-dessus remplace simplement la priorité et permet aux messages entrants d'arriver au FlutterFirebaseMessagingServicelieu de HyperTrackMessagingService.

Limitation:

Bien que HyperTrack fonctionne correctement, HyperTrack utilise FCM pour la communication périphérique-serveur pour l'optimisation qui ne fonctionnera pas sans FCM. Cependant, vous ne le remarquerez peut-être pas.

Méthode 2

Transférer le message entrant dans HyperTrackMessagingServicepour FlutterFirebaseMessagingServicedans le plug - in en hypertrack_pluginutilisant la réflexion.

Pas:

  1. Vous devrez télécharger le code source de hypertrack_plugin et l'utiliser comme dépendance locale. Comment ?
  2. Créer une nouvelle classe dans la bibliothèque sdk-flutter/android/src/main/kotlin/com/hypertrack/sdk/flutter/MyFirebaseMessagingService.java
package com.hypertrack.sdk.flutter;

import android.annotation.SuppressLint;
import android.util.Log;

import com.google.firebase.messaging.RemoteMessage;
import com.hypertrack.sdk.HyperTrackMessagingService;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@SuppressLint("LongLogTag")
public class MyFirebaseMessagingService extends HyperTrackMessagingService {

    private static final String TAG = "MyFirebaseMessagingService";

    private Class<?> serviceClass;
    private Object serviceObject;

    public MyFirebaseMessagingService() {
        try {
            serviceClass = Class.forName("io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService");
            serviceObject = serviceClass.newInstance();
            injectContext();

            Log.d(TAG, "io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService is found");
        } catch (Throwable t) {
            Log.w(TAG, "Can't find the class io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService", t);
        }
    }

    @Override
    public void onNewToken(final String s) {
        super.onNewToken(s);
        injectToken(s);
    }

    @Override
    public void onMessageReceived(final RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        injectMessage(remoteMessage);
    }

    public void injectToken(String newToken) {
        if (serviceClass != null) {
            try {
                Method sendTokenRefresh = serviceClass.getMethod("onNewToken", String.class);
                sendTokenRefresh.invoke(serviceObject, newToken);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                Log.w(TAG, "Can't inject token due to error ", e);
            }
        }
    }

    public void injectMessage(RemoteMessage remoteMessage) {
        if (serviceClass != null) {
            try {
                Method sendTokenRefresh = serviceClass.getMethod("onMessageReceived", RemoteMessage.class);
                sendTokenRefresh.invoke(serviceObject, remoteMessage);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                Log.w(TAG, "Can't inject message due to error ", e);
            }
        }
    }

    private void injectContext() {
        if (serviceObject != null) {
            if (setField(serviceObject, "mBase", this)) {
                Log.d(TAG, "context is set to io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService");
            }
        }
    }

    private boolean setField(Object targetObject, String fieldName, Object fieldValue) {
        Field field;
        try {
            field = targetObject.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            field = null;
        }
        Class<?> superClass = targetObject.getClass().getSuperclass();
        while (field == null && superClass != null) {
            try {
                field = superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                superClass = superClass.getSuperclass();
            }
        }
        if (field == null) {
            return false;
        }
        field.setAccessible(true);
        try {
            field.set(targetObject, fieldValue);
            return true;
        } catch (IllegalAccessException e) {
            return false;
        }
    }
}
  1. Modifier le fichier manifeste sdk-flutter/android/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.hypertrack.sdk.flutter">

    <application>
        <service
            android:name=".MyFirebaseMessagingService"
            android:exported="false" >
            <intent-filter android:priority="100" >
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
    </application>
</manifest>

Comment ça fonctionne?

MyFirebaseMessagingServiceétendu de HyperTrackMessagingService, est déclaré dans le AndroidManifest.xmlfichier avec une priorité plus élevée (nettement supérieure à HyperTrackMessagingService). Cela permettra aux messages d'arriver directement à la nouvelle classe. Cela supprimera également la limitation de la méthode 1. Maintenant, nous transmettons également le message à l' FlutterFirebaseMessagingServiceutilisation de la réflexion.

Limitation:

Aucune limitation, mais vous devez mettre à jour manuellement le hypertrack_pluginlorsque la bibliothèque hôte est mise à jour. Il est bon d'avoir cette mise à jour dans le plugin lui-même qui n'est pas présent maintenant (13 novembre)

Méthode 3

Maintenant, nous n'allons toucher aucun code dans la bibliothèque mais écrire notre propre code. Nous proposerons une solution de manière sûre. Vous n'avez pas à ajouter en hypertrack_plugintant que dépendances locales.

  1. Créer une classe <project_root>/android/app/src/main/<your_package_name>/MyFirebaseMessagingService.java
package com.example.myapp;

import android.annotation.SuppressLint;
import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@SuppressLint("LongLogTag")
public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMessagingService";

    // put all the firebase messaging service classes used in your project here
    private String[] fcmClasses = new String[]{
            "io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService",
            "com.hypertrack.sdk.HyperTrackMessagingService"};

    @Override
    public void onNewToken(@NotNull String token) {
        Log.d(TAG, "onNewToken()");
        super.onNewToken(token);
        injectToken(token);
    }

    @Override
    public void onMessageReceived(@NotNull RemoteMessage remoteMessage) {
        Log.d(TAG, "onMessageReceived()");
        super.onMessageReceived(remoteMessage);
        injectMessage(remoteMessage);
    }

    public void injectToken(String newToken) {
        Log.d(TAG, "injectToken()");

        for (String fcmClass : fcmClasses) {
            try {
                Class<?> serviceClass = Class.forName(fcmClass);
                Object serviceObject = serviceClass.newInstance();
                injectContext(serviceClass, serviceObject);

                Method sendTokenRefresh = serviceClass.getMethod("onNewToken", String.class);
                sendTokenRefresh.invoke(serviceObject, newToken);

            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | InstantiationException e) {
                Log.w(TAG, "Can't inject token due to error ", e);
            }
        }
    }

    public void injectMessage(RemoteMessage remoteMessage) {
        Log.d(TAG, "injectMessage()");

        for (String fcmClass : fcmClasses) {
            try {
                Class<?> serviceClass = Class.forName(fcmClass);
                Object serviceObject = serviceClass.newInstance();
                injectContext(serviceClass, serviceObject);

                Method sendTokenRefresh = serviceClass.getMethod("onMessageReceived", RemoteMessage.class);
                sendTokenRefresh.invoke(serviceObject, remoteMessage);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | InstantiationException e) {
                Log.w(TAG, "Can't inject token due to error ", e);
            }
        }
    }

    private void injectContext(Class<?> serviceClass, Object serviceObject) {
        Log.d(TAG, "injectContext()");
        if (serviceClass != null) {
            if (setField(serviceObject, "mBase", this)) {
                Log.d(TAG, "context is set to " + serviceClass.getName());
            }
        }
    }

    private boolean setField(Object targetObject, String fieldName, Object fieldValue) {
        Log.d(TAG, "setField()");

        Field field;
        try {
            field = targetObject.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            field = null;
        }
        Class<?> superClass = targetObject.getClass().getSuperclass();
        while (field == null && superClass != null) {
            try {
                field = superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                superClass = superClass.getSuperclass();
            }
        }
        if (field == null) {
            return false;
        }
        field.setAccessible(true);
        try {
            field.set(targetObject, fieldValue);
            return true;
        } catch (IllegalAccessException e) {
            return false;
        }
    }
}
  1. Déclarez-le dans <project_root>/couriers/android/app/src/main/AndroidManifest.xml

Important: définissez une priorité supérieure à 5 (une déclarée dans HyperTrack SDK)

     <service
            android:name=".MyFirebaseMessagingService"
            android:exported="false">
            <intent-filter android:priority="100">
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

Comment ça marche?

Nous avons créé MyFirebaseMessagingServiceexplicitement et étendu à partir de FirebaseMessagingService. Nous avons également défini une priorité aussi élevée que 100 pour nous assurer que nous recevons les messages entrants uniquement ici et non dans aucun autre service de messagerie Firebase tel que HyperTrack. Nous avons maintenant la flexibilité de transmettre ce message partout où nous en avons besoin, en utilisant la réflexion.

Limitation:

Aucun