Project

General

Profile

Push Notifications

Ildar Zaripov
Added 8 months ago

I try to set up Push Notifications, but I have some difficulties with it.
I have the following server configuration

push (class: tigase.push.PushNotificationsComponent) {
    'fcm-xmpp-api' () {
        'pool-size' = 5
        'sender-id' = '***'
        'server-key' = '***'
    }
}

'sess-man' (class: tigase.server.xmppsession.SessionManager) {
    amp () {
        message () {}
        msgoffline () {}
    }
    message (active: false) {}
    'message-archive-xep-0136' (active: false) {}
    msgoffline (active: false) {}
    'urn:xmpp:csi:0' (class: tigase.xmpp.impl.ClientStateIndication) {
        logic (class: tigase.xmpp.impl.MobileV2) {}
    }

    'urn:xmpp:push:0' () {}

    ...
}

Server starts without errors.

And I used 2 methods from Tigase messenger source code:


   AdHocCommansModule adHocCommandModule = new AdHocCommansModule();
   jaxmpp.getModulesManager().register(adHocCommandModule);

   PushNotificationModule pushNotificationModule = new PushNotificationModule();
   jaxmpp.getModulesManager().register(pushNotificationModule);



   public static final JID PUSH_SERVICE_JID = JID.jidInstance("push.tigase.org");


   private void registerInPushService() throws JaxmppException {
        final AdHocCommansModule ah = jaXMPPConnection.getJaxmpp().getModule(AdHocCommansModule.class);

        String deviceToken = FirebaseInstanceId.getInstance().getToken();

        final JabberDataElement fields = new JabberDataElement(XDataType.submit);
        fields.addListSingleField("provider", "fcm-xmpp-api");
        fields.addTextSingleField("device-token", deviceToken);

        ah.execute(PUSH_SERVICE_JID, "register-device", null, fields, new AdHocCommansModule.AdHocCommansAsyncCallback() {
            @Override
            protected void onResponseReceived(String sessionid, String node, State status, JabberDataElement data) throws JaxmppException {
                TextSingleField nodeField = data.getField("node");
                String pushServiceNode = nodeField.getFieldValue();
                enablePushService(pushServiceNode);
            }

            @Override
            public void onError(Stanza responseStanza, XMPPException.ErrorCondition error) throws JaxmppException {
                System.out.println("AdHocCommansAsyncCallback onError "+responseStanza.getErrorMessage());
            }

            @Override
            public void onTimeout() throws JaxmppException {
                System.out.println("AdHocCommansAsyncCallback onTimeout()");
            }
        });
    }



    private void enablePushService(String pushServiceNode) throws JaxmppException {
        final PushNotificationModule pm = jaXMPPConnection.getJaxmpp().getModule(PushNotificationModule.class);
        if (!pm.isSupportedByServer()) {
            System.out.println("Notification Push is not supported by server.");
            return;
        }

        pm.enable(PUSH_SERVICE_JID, pushServiceNode, new AsyncCallback() {
            @Override
            public void onError(Stanza stanza, XMPPException.ErrorCondition errorCondition) throws JaxmppException {
                System.out.println("Cannot enable Push Service: " + errorCondition);
            }

            @Override
            public void onSuccess(Stanza stanza) throws JaxmppException {
                System.out.println("Push Service is enabled");
            }

            @Override
            public void onTimeout() throws JaxmppException {
                System.out.println("Cannot enable Push Service: timeout");
            }
        });
    }

I can't understand, what JID should I use instead of JID.jidInstance("push.tigase.org")?


Replies (4)

Added by Andrzej Wójcik IoT 1 CloudTigaseTeam 8 months ago

You need to use JID of your push component. In your config you enabled is named as push, so if your domain is example.org it will be available as push.example.org.

Added by Ildar Zaripov 8 months ago

Thank you, yes, you were right - using (in my case) push.localhost allowed to enable Push service.
Also, I checked tig_push_devices table in AWS RDS and there is a valid table row user_jid => device_id(fcm token)
But I still have problems - push notifications are not delivered to the device when app is in background.

I use android.arch.lifecycle.LifecycleObserver to detect whether app is in background or foreground. And this observer works correctly. Here is example:

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;

public class AppLifecycleListener implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onMoveToForeground() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onMoveToBackground() {

    }
}

The app logic is the following:

  1. Start Firebase services
  2. Start JaXMPP service
  3. When JaXMPP connection is established, enable JaXMPP Push module (by calling method above registerInPushService() )
  4. If app moves to background, activate CSI
  5. If app moves to foreground, deactivate CSI

def clientStateIndicationModule = jaxmpp.getModulesManager().getModule(ClientStateIndicationModule.class);
def presenceModule = jaxmpp.getModulesManager().getModule(PresenceModule.class);

public class AppLifecycleListener implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onMoveToForeground() {
        try {
            presenceModule.setPresence(Presence.Show.online, "", 50);
        } catch (JaxmppException e) {
            e.printStackTrace();
        }

        clientStateIndicationModule.active();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onMoveToBackground() {
        try {
            presenceModule.setPresence(Presence.Show.away, "", 50);
        } catch (JaxmppException e) {
            e.printStackTrace();
        }

        clientStateIndicationModule.inactive();
    }
}

After calling clientStateIndicationModule.inactive() I expect the server to start storing messages in DB and sending them to Push component, but I continue receiving messages in JaXMPP service, since this service is "unclosable" (onStartCommand() returns "START_STICKY").
Is it right logic? In what case app will receive Push notifications?
The only purpose of calling registerInPushService() method is to bind/update user jid and user firebase token?

Added by Andrzej Wójcik IoT 1 CloudTigaseTeam 8 months ago

Yes, you are right about all the things which you mentioned, except:

After calling clientStateIndicationModule.inactive() I expect the server to start storing messages in DB and sending them to Push component, but I continue receiving messages in JaXMPP service, since this service is "unclosable" (onStartCommand() returns "START_STICKY").

If you call that, you are just informing XMPP server that app is in the background and it should stop sending presence updates, while you still will get messages - this is how it should work.
If you would like to send messages to the offline storage then send Presence.Show.offline instead of sending Presence.Show.away but I do not get why you would prefer to store them in DB if XMPP connection is active?

As for Push Notifications, is any of user connections has presence which is not offline and has priority higher than -1 (if I'm correct), you will get message delivered on this connection. In the other case, you will get Push Notification - ie. when client drops connection ie. due to poor network or due to the fact that it was killed by the OS.

Added by Ildar Zaripov 8 months ago

If you would like to send messages to the offline storage then send Presence.Show.offline instead of sending Presence.Show.away but I do not get why you would prefer to store them in DB if XMPP connection is active?

Because of lack of knowledge :)
I set up the first version of Push notifications in my app, but now I see there are a lot of work/improvements should be done before "getting to the finish line".

Thanks for your help!

    (1-4/4)