web-dev-qa-db-fra.com

Application de chat en utilisant Firebase: Recevez une notification lorsqu'un nouveau message est reçu - Android

Je développe une application de chat en utilisant la base de données en temps réel Firebase. J'ai pu envoyer et recevoir des messages correctement. Maintenant, je veux implémenter la notification chaque fois qu'un nouveau message est reçu. Pour cela, j'ai créé un Service qui écoute les modifications de la base de données à l'aide de ChildEventListener et crée une notification. Le problème est que je crée une notification dans la méthode onChildAdded et cette méthode se déclenche à la fois pour le nœud existant dans la base de données et le nouveau. Cela provoque la création de notifications plusieurs fois pour le même message chaque fois que l'utilisateur navigue dans les deux sens depuis l'application.

Voici comment je l'implémente:

chatMsgsRef.orderByChild(FirebaseDBKeys.LOCATION_LAST_UPDATED).addChildEventListener(new ChildEventListener() {

            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {

                ChatMessage message = dataSnapshot.getValue(ChatMessage.class);

                if (!message.getSenderId().equals(currentUserId)) {

                    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

                    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(NotificationsService.this)
                            .setSmallIcon(R.drawable.message)
                            .setContentTitle("New Message from " + message.getReceipientName())
                            .setContentText(message.getMessage())
                            .setOnlyAlertOnce(true)
                            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
                    mBuilder.setAutoCancel(true);
                    mBuilder.setLocalOnly(false);


                    mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());

                }
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

Comment puis-je implémenter des notifications dans la façon dont cela fonctionne dans d'autres applications de chat comme WhatsApp, etc. ??

16
Vaibhav Agarwal

La bonne façon de procéder consiste à envoyer un message de données Push au client avec la conversation lorsqu'une nouvelle conversation leur est envoyée.

Ce article de blog explique comment y parvenir. Fondamentalement, vous devez configurer un serveur pour écouter la référence Firebase du chat et envoyer des notifications Push lors de sa mise à jour. De cette façon, vos clients peuvent être dans l'application ou hors de l'application tout en bénéficiant du Push.

Si vous utilisez un service, il existe un certain nombre de problèmes potentiels.

Tout d'abord, vous devrez garder le téléphone éveillé. Cela déchargera la batterie.

Deuxièmement, Android peut tuer votre service d'arrière-plan à tout moment, donc votre application peut cesser de fonctionner soudainement.

Troisièmement, avec le mode Doze Android bloquera l'activité du réseau et empêchera votre application de fonctionner en arrière-plan.

12
Shmuel

Une meilleure réponse m'est venue:

Ce que vous voulez dans ce cas n'est pas nécessairement de connaître les nouveaux messages. C'est pour connaître les messages UNREAD.

Définissez un indicateur "lecture" dans l'objet ChatMessage (ou inversement, ayez une valeur quelque part qui donne l'horodatage ou l'ID du message lu le plus récent).

Maintenant, chaque fois que onChildAdded est déclenché, vérifiez si read == false. Si oui, affichez une notification indiquant que le message n'est pas lu (n'oubliez pas de mettre à jour la notification si elle existe, donc une seule notification sera affichée et elle affichera la plus récente - oh, et n'oubliez pas de supprimer la notification lorsque l'enfant est changé en lecture.)

Si l'utilisateur utilise votre application sur plusieurs appareils, il vérifiera correctement l'état de lecture. S'il lit le dernier message sur un téléphone, puis se rend sur la tablette, il ne montrera pas cette nouvelle notification de message.

Si vous le souhaitez, vous pouvez même utiliser cette fonctionnalité pour indiquer que le destinataire leur a lu votre message.

Comment savez-vous quand il est lu? Peut-être simplement lorsque vous l'ajoutez à l'écran. Peut-être vous assurez-vous qu'il est bien en vue (non défilé hors de l'écran) et qu'il est visible pendant plusieurs secondes.

5
Chad Schultz

Avez-vous essayé addValueEventListener?

https://www.firebase.com/docs/Android/guide/retrieving-data.html#section-start

// Get a reference to our posts
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");
// Attach an listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        System.out.println(snapshot.getValue());
    }
    @Override
    public void onCancelled(FirebaseError firebaseError) {
        System.out.println("The read failed: " + firebaseError.getMessage());
    }
});

"Cette méthode sera appelée chaque fois que de nouvelles données sont ajoutées à notre référence Firebase, et nous n'avons pas besoin d'écrire de code supplémentaire pour y arriver."

EDIT: "Il est déclenché une fois avec les données initiales et à chaque fois que les données changent." Je ne l'ai pas essayé, mais ma pensée serait de définir un indicateur lorsque la méthode se déclenche la première fois. Ensuite, chaque fois que la méthode et cet indicateur sont définis (en d'autres termes, la deuxième fois, la troisième fois, la quatrième fois, etc.), obtenez l'objet le plus récent de l'instantané.

3
Chad Schultz

Une combinaison de la réponse de @Chad Shultz et de votre code d'origine, avec quelques ajouts structurels à la base de données.

Vous utiliserez, comme vous le vouliez à l'origine, une requête

chatMsgsRef.orderByChild(...)

Cependant, modifiez la structure des données pour avoir la disposition suivante

>root
    > users
        > userID_001
            > chats
                > chat_001:true
        > userID_002
            > chats
                > chat_001:true
    > chats
        > chat_001
            > participants
                > userID_001:true
                > userID_002:true
            > messages
                > msg_001
                    > sender:"userID_001"
                    > text:"some message"
                    > time:"some time"
    > message_read_states
        > chat_001
            > msg_001
                > userID_001:true
                > userID_002:false

Ainsi, chaque fois qu'un message est envoyé, il est poussé vers le nœud "messages". Ensuite, le système obtient tous les "participants" et les pousse sous le nœud "message_read_states" pour ce chat/message, avec une valeur false pour tout le monde sauf l'expéditeur. Lorsque quelqu'un lit le message, il change leur valeur en vrai là-bas.

Le service doit maintenant déterminer pour quels messages avertir un utilisateur. Le Service placera un écouteur sur message_read_states/chat_X, et le classera par la valeur du champ enfant "userID_X" (selon qui est l'utilisateur actuel). Nous ferons en sorte que la requête ne renvoie que les ID de message, pour chaque conversation, pour lesquels il y a une valeur "userID_X: false" en dessous

Ainsi, dans cet exemple, pour "userID_002", la requête renverra "msg_001", mais pour "userID_001", elle ne renverra rien, car la valeur requise est introuvable pour cette clé (il était l'expéditeur).

La requête sera construite comme suit, à l'intérieur de onStartCommand de votre service:

    //TODO: Get the ID of the chat the user is taking part in
    String chatID = "chat_001";

    // Check for new messages
    FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
    if (currentUser != null){
        String UID = currentUser.getUid();
        DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
        Query query = rootRef.child("message_read_states").child(chatID).orderByChild(UID).equalTo(false);
        query.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                String messageID = dataSnapshot.getKey();
                //TODO: Handle Notification here, using the messageID
                // A datasnapshot received here will be a new message that the user has not read
                // If you want to display data about the message or chat,
                // Use the chatID and/or messageID and declare a new 
                // SingleValueEventListener here, and add it to the chat/message DatabaseReference.
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                String messageID = dataSnapshot.getKey();
                //TODO: Remove the notification
                // If the user reads the message in the app, before checking the notification
                // then the notification is no longer relevant, remove it here.
                // In onChildAdded you could use the messageID(s) to keep track of the notifications
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
    }

Maintenant, bien sûr, il est très probable qu'un utilisateur soit impliqué dans plusieurs chats. Vous devrez parcourir toutes les conversations associées à l'utilisateur et implémenter une requête pour chaque conversation.

Source: Je travaille actuellement sur une application Firebase avec un système de chat

2
gorrox23