google/gcm

GcmListenerService.onMessageReceived not called

void99 opened this issue · 25 comments

Maybe not a proper place to post (not a demo issue), but could not find answer elsewhere: when sending both "data" and "notification" keys as payload notification is displayed but onMessageReceived method is not called. The method is properly called when "notification" key is removed from payload. What could be the cause?

Indeed, this seems to be the case. I've tested with a payload as follows:

send_queue.append({'to': REGISTRATION_ID,
                   'message_id': random_id(),
                   "notification" : {
                      "body" : "Hello from Server! What's going?",
                      "title" : "Hello from Server!",
                      "icon" : "@drawable/ic_school_white_48dp",
                      "sound": "default",
                      "color": "#03A9F4"
                    },
                   'data': { 'message': "Hello" } })

The notification is displayed by the system, but the onMessageReceived(String from, Bundle data) method of the GcmListenerService implemention doesn't get called

In AndroidManifest.xml MyGcmListenerService is defined as follows:

 <!-- [START gcm_listener] -->
        <service
            android:name="com.company.myapp.service.MyGcmListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <!-- [END gcm_listener] -->

The server reference payload docs (https://developers.google.com/cloud-messaging/server#payload) says:
The app server can send a message including both notifications and data payloads. In such cases, GCM handles displaying the notification payload and the client app handles the data payload.

The "the client app handles the data payload" part doesn't work.
If I remove the notification key from the payload the method on the receiver is called.

@void99

I think I found what's "the problem"

From https://developers.google.com/cloud-messaging/server#notifications_and_data_messages
"GCM will display the notification part on the client app’s behalf. When optional data is provided, it is sent to the client app once user clicks on the notification and opens the client app.
[...] On Android, data payload can be retrieved in the Intent used to launch your activity.
"

So, the data is passed in the intent used to launch the activity, once the user clicks on the notification.
This means you need to do the following:

  • Add a click_action to the notification key you send from the server:
    e.g.
send_queue.append({'to': REGISTRATION_ID,
                   'message_id': random_id(),
                   "notification" : {
                      "body" : "Hello from Server! What is going on? Seems to work!!!",
                      "title" : "Hello from Server!",
                      "icon" : "@drawable/ic_school_white_48dp",
                      "sound": "default",
                      "color": "#03A9F4",
                      "click_action": "OPEN_MAIN_ACTIVITY"
                    },
                   'data': { 'message': "Hello" } })

See the reference for notification payload at: https://developers.google.com/cloud-messaging/server-ref#notification-payload-support

  • In AndroidManifest.xml add an intent filter on the activity you want to be opened once the user clicks on the notification, with the same action name you used on the "click_action" key on the server side
    e.g.
         <activity
            android:name=".ui.MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="OPEN_MAIN_ACTIVITY" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
  • Get the data from the intent on your onCreate() method or on onNewIntent() if you've set the launchMode to singleTop for the activity you want to launch when the notification is clicked.
    e.g.
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();

        if (intent.hasExtra(Constants.KEY_MESSAGE_TXT)) {
            String message = intent.getStringExtra(Constants.KEY_MESSAGE_TXT);
            Log.d(TAG, message);
        }
}

I've tested this and can confirm that it works. (using XMPP connection)

Thanks for Your explanation. But imo the implementation is kind of awkward. If the user dismisses the notification, the data will never be passed to application. Or am i missing something?

Yes, that's the downside of letting the system handle the notification.

So what if I want the iOS app to use the notification payload, and the android app to use the data payload for displaying the notification? Seems like I am SoL.

Also, the notification icon is very limiting. On Lollipop, you cannot use a complex icon because it is automatically forced into a single color (makes the icon all white in my case. This doesn't seem right.

I'm currently experiencing a consistent InternalServerError when trying to push a payload with the notification tag. I need this for my iOS app to display a banner notification when the app is in the background.

+1 having similar issues as well.

@void99 Why is this closed? This problem pretty much makes it impossible to route iOS and Android traffic together through GCM if you want any processing to occur.

@joelSimpson and @StephenStrickland Make sure both body & title are set in the notification field. iOS doesn't use the title tag but it's required in order to send and may be the reason for this issue you're both having.

EDIT: Unfortunately, I'm now seeing the InternalServerError again. The above suggestion doesn't fix the problem.

Having the same problem as @ZakTaccardi trying to use Lollipop and support the same iOS notification body. This seems like it was overlooked.

hi, i have problems with the Constants.KEY_MESSAGE_TXT variable, it doesnt import

@pato89xd You have to define that constant yourself. The above code is just an example, you have to adapt it for your own needs.

@mnemonicflow so there is no Constants class?

@pato89xd You have to create it yourself.

@mnemonicflow sorry about that i'm new on android, I'm implementing this, thanks

Is this being fixed? This bug makes it impossible to use GCM to both iOS and Android users.

We are having the exact same issue.

It would suck to have to manage two different sets of GCM registration files to manage Android and iOS.

Any news on the matter?

The current implementation doesn't allow to send a message that is simultaneously a display notification for ios and a silent notification for android.

I understand the issue and I appreciate your feedback.
We might be able to cover it in a future update, but I cannot promise an ETA on this.

Hi I am new to android ,I have added custom additional data so that when the user clicks the notification, it opens the data I entered. However, it does not open. Anyone have a solution or an idea of the error I'm receiving?

I have "an idea": show your source code.

On Thu, Feb 18, 2016 at 10:45 AM, jackdevco notifications@github.com
wrote:

Hi I am new to android ,I have added custom additional data so that when
the user clicks the notification, it opens the data I entered. However, it
does not open. Anyone have a solution or an idea of the error I'm receiving?


Reply to this email directly or view it on GitHub
#63 (comment).

Hi

Thank you for the feedback. Please see code below.

Appreciate the assistance.


MainActivity.java

package com.app.bradv_000.pushk;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.pushwoosh.BasePushMessageReceiver;
import com.pushwoosh.BaseRegistrationReceiver;
import com.pushwoosh.PushManager;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity
{
private static final String bTAG = "example";

boolean broadcastPush = true;

JSONAdapter jsonAdapter;

private ListView list;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //here we register receivers for push notifications
    registerReceivers();

    final PushManager pushManager = PushManager.getInstance(this);

    //Now we start the push manager, this will count app open for

Pushwoosh stats as well
try
{
pushManager.onStartup(this);
}
catch (Exception e)
{
e.printStackTrace();
}

    //now we register for push notification
    pushManager.registerForPushNotifications();

    //then check launch notification
    String launchNotification = pushManager.getLaunchNotification();
    if(launchNotification != null)
    {
        Log.d(bTAG, "Launch notification received" + launchNotification);
    }
    else
    {
        Log.d(bTAG, "No notification received");
    }

    //Clear application icon badge number
    pushManager.setBadgeNumber(0);
}

/**
 * Called when the activity receives a new intent.
 */
public void onNewIntent(Intent intent)
{
    super.onNewIntent(intent);
    //have to check if we've got new intent as a part of push notification
    try
    {
        checkMessage(intent);
    } catch (JSONException e)
    {
        e.printStackTrace();
    }

}

//Registration receiver
BroadcastReceiver mBroadcastReceiver = new BaseRegistrationReceiver()
{
    @Override
    public void onRegisterActionReceive(Context context, Intent intent)
    {
        try
        {
            checkMessage(intent);
        } catch (JSONException e)
        {
            e.printStackTrace();
        }
    }
};

//Push message receiver
private BroadcastReceiver mReceiver = new BasePushMessageReceiver()
{
    @Override
    protected void onMessageReceive(Intent intent)
    {
        //JSON_DATA_KEY contains JSON payload of push notification.
        try
        {
            doOnMessageReceive(intent.getExtras().getString(JSON_DATA_KEY));
        } catch (JSONException e)
        {
            e.printStackTrace();
        }
    }
};

//Registration of the receivers
public void registerReceivers()
{
    IntentFilter intentFilter = new IntentFilter(getPackageName()
  • ".action.PUSH_MESSAGE_RECEIVE");

    if (broadcastPush)
        registerReceiver(mReceiver, intentFilter, getPackageName()
    

    +".permission.C2D_MESSAGE", null);

    registerReceiver(mBroadcastReceiver, new
    

    IntentFilter(getPackageName() + "." +
    PushManager.REGISTER_BROAD_CAST_ACTION));

    }

    public void unregisterReceivers()
    {
    //Unregister receivers on pause
    try
    {
    unregisterReceiver(mReceiver);
    }
    catch (Exception e)
    {
    // pass.
    }

    try
    {
        unregisterReceiver(mBroadcastReceiver);
    }
    catch (Exception e)
    {
        //pass through
    }
    

    }

    @OverRide
    public void onPause()
    {
    super.onPause();

    //Unregister receivers on pause
    unregisterReceivers();
    

    }

    @OverRide
    public void onResume()
    {
    super.onResume();

    //Re-register receivers on resume
    registerReceivers();
    

    }

    /**

    • Will check PushWoosh extras in this intent, and fire actual method
      *
    • @param intent activity intent
      */
      private void checkMessage(Intent intent) throws JSONException
      {
      if (null != intent)
      {
      if (intent.hasExtra(PushManager.PUSH_RECEIVE_EVENT))
      {

doOnMessageReceive(intent.getExtras().getString(PushManager.PUSH_RECEIVE_EVENT));
}
else if (intent.hasExtra(PushManager.REGISTER_EVENT))
{

doOnRegistered(intent.getExtras().getString(PushManager.REGISTER_EVENT));
}
else if (intent.hasExtra(PushManager.UNREGISTER_EVENT))
{

doOnUnregistered(intent.getExtras().getString(PushManager.UNREGISTER_EVENT));
}
else if (intent.hasExtra(PushManager.REGISTER_ERROR_EVENT))
{

doOnRegisteredError(intent.getExtras().getString(PushManager.REGISTER_ERROR_EVENT));
}
else if (intent.hasExtra(PushManager.UNREGISTER_ERROR_EVENT))
{

doOnUnregisteredError(intent.getExtras().getString(PushManager.UNREGISTER_ERROR_EVENT));
}

        resetIntentValues();
    }
}

public void doOnRegistered(String registrationId)
{
    Log.d(bTAG, "registered: " + registrationId);
}

public void doOnRegisteredError(String errorId)
{
    Log.d(bTAG, "registration error: " + errorId);
}

public void doOnUnregistered(String registrationId)
{
    Log.d(bTAG, "unregistered: " + registrationId);
}

public void doOnUnregisteredError(String errorId)
{
    Log.d(bTAG, "deregistration error: " + errorId);
}

public void doOnMessageReceive(String message) throws JSONException
{
    ArrayList<String> user = new ArrayList<String>();

    Log.d(bTAG, "received push: " + message);
    JSONObject json  = new JSONObject("message");

    JSONArray array = json.getJSONArray("employees");

    for (int i=0; i<array.length(); i++)
    {
        JSONObject o = array.getJSONObject(i);
        String firstName = json.getString("firstName");
        user.add(firstName);
        System.out.println(o);
    }

    ArrayAdapter<String> arrayAdapter = new

ArrayAdapter(this, android.R.layout.simple_list_item_1, user);

    //Set the adapter as the adapter of choice for our list
    list.setAdapter(arrayAdapter);


    try
    {
        json = new JSONObject(message);
    }
    catch (JSONException e)
    {
        e.printStackTrace();
    }

    try
    {
        JSONObject userdataObject = json.getJSONObject("userdata");

        array = userdataObject.getJSONArray("employees");
    }
    catch (JSONException e)
    {
        e.printStackTrace();
    }


    for (int i=0; i<array.length();i++)
    {
        JSONObject o = null;
        try
        {
            o = array.getJSONObject(i);
        }
        catch (JSONException e)
        {
            e.printStackTrace();
        }
        try
        {
            Log.i("Testing this ...", o.getString("firstName"));
        }
        catch (JSONException e)
        {
            e.printStackTrace();
        }
        try
        {
            Log.i("Testing this ...", o.getString("lastName"));
        }
        catch (JSONException e)
        {
            e.printStackTrace();
        }
    }

}

/**
 * Will check main Activity intent and if it contains any

PushWoosh data, will clear it
*/
private void resetIntentValues()
{
Intent mainAppIntent = getIntent();

    if (mainAppIntent.hasExtra(PushManager.PUSH_RECEIVE_EVENT))
    {
        mainAppIntent.removeExtra(PushManager.PUSH_RECEIVE_EVENT);
    }
    else if (mainAppIntent.hasExtra(PushManager.REGISTER_EVENT))
    {
        mainAppIntent.removeExtra(PushManager.REGISTER_EVENT);
    }
    else if (mainAppIntent.hasExtra(PushManager.UNREGISTER_EVENT))
    {
        mainAppIntent.removeExtra(PushManager.UNREGISTER_EVENT);
    }
    else if (mainAppIntent.hasExtra(PushManager.REGISTER_ERROR_EVENT))
    {
        mainAppIntent.removeExtra(PushManager.REGISTER_ERROR_EVENT);
    }
    else if (mainAppIntent.hasExtra(PushManager.UNREGISTER_ERROR_EVENT))
    {
        mainAppIntent.removeExtra(PushManager.UNREGISTER_ERROR_EVENT);
    }

    setIntent(mainAppIntent);
}

}


UserNameAdapter.java (My custom Adapter)

package com.app.bradv_000.pushk;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**

  • Created by bradv_000 on 2016/02/16.
    */
    class JSONAdapter extends BaseAdapter
    {
    private final Activity activity;
    private final JSONArray jsonArray;

    public JSONAdapter (Activity activity, JSONArray jsonArray)
    {
    assert activity != null;
    assert jsonArray != null;

    this.jsonArray = jsonArray;
    this.activity = activity;
    

    }

    @OverRide public int getCount() {
    if(null==jsonArray)
    return 0;
    else
    return jsonArray.length();
    }

    @OverRide public JSONObject getItem(int position) {
    if(null==jsonArray) return null;
    else
    return jsonArray.optJSONObject(position);
    }

    @OverRide public long getItemId(int position) {
    JSONObject jsonObject = getItem(position);

    return jsonObject.optLong("id");
    

    }

    @OverRide public View getView(int position, View convertView,
    ViewGroup parent)
    {

    if (convertView == null)
        convertView =
    

    activity.getLayoutInflater().inflate(R.layout.user_name_list, null);

    TextView text_list = (TextView)convertView.findViewById(R.id.text_list);
    
    JSONObject json_data = getItem(position);
    
    if(null!=json_data )
    {
        try
        {
            text_list.setText(json_data.getString("firstName"));
        }
        catch (JSONException e) {
            e.printStackTrace();
        }
    }
    
    return convertView;
    

    }

}


activity_main.xml

<ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/list"/>

username_list.xml

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/text_list">

</TextView>

On Thu, Feb 18, 2016 at 1:46 PM, Alexander Farber notifications@github.com
wrote:

I have "an idea": show your source code.

On Thu, Feb 18, 2016 at 10:45 AM, jackdevco notifications@github.com
wrote:

Hi I am new to android ,I have added custom additional data so that when
the user clicks the notification, it opens the data I entered. However,
it
does not open. Anyone have a solution or an idea of the error I'm
receiving?


Reply to this email directly or view it on GitHub
#63 (comment).


Reply to this email directly or view it on GitHub
#63 (comment).

In additional, when I have only data field (without notification) and "content_available": true, then GcmListenerService.onMessageReceived is not called. "content_available": true is needed for iOS, but it breaks data payload on Android. I removed "content_available": true on App Server for messages, which go to Android, and it is worked for me.

@KirillMakarov the issue you mentioned is being investigated in #178

i'm only using the data payload in my android app , because i want the application to handle creating notifications , not GCM , problem is that when app. is in background the "onMessageReceived " method is never triggered , i don't want to use both notification and data payloads and pass data when the user clicks the notification to my activity this seems too awkward , appreciate any suggestions

Can you post json which your app server post to gcm server for data notification?

yayah commented

Yes
On 14 Jun 2016 09:32, "Kirill Makarov" notifications@github.com wrote:

Can you post json which your app server post to gcm server for data
notification?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#63 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AGZ2dJjJhedl4rnBvv9VzN4rqYto_Prcks5qLnUYgaJpZM4FBGQU
.