So the question is quite self-explanatory. I want to know if the notification clicked was grouped or was a single notification. Based on which, an Activity will be launched.
For example, if the notification was grouped, I would like to launch the "All messages" Activity, and if not, it'll simply launch the Chat Activity.
This is what my current code looks like:
public class NotificationOpenedHandler implements OneSignal.NotificationOpenedHandler {
private Application application;
String message;
public NotificationOpenedHandler(Application application) {
this.application = application;
}
#Override
public void notificationOpened(OSNotificationOpenResult result) {
// Get custom data from notification
JSONObject data = result.notification.payload.additionalData;
message = data.optString("message");
startApp(message);
}
private void startApp(String text) {
Intent intent;
SharedPreferences sharedPreferences = application.getSharedPreferences("appdata", Context.MODE_PRIVATE);
if (sharedPreferences.getInt("pending_notifications", 1) > 1) {
intent = new Intent(application, DemoActivity.class);
} else {
intent = new Intent(application, ReadLetterActivity.class);
}
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt("pending_notifications", 0);
editor.apply();
intent.putExtra("message", text);
startActivity(intent);
}
}
As you can see, I tried playing with SharedPreferences, but it was not up to the mark.
Wrapping up, I wanna know if there's a way (native or provided by OneSignal) to know if the notification was grouped or not. Thanks.
Nevermind. Found the way out. This works flawlessly to distinguish between single and grouped notifications, even if a notification from a group is opened separately.
#Override
public void notificationOpened(OSNotificationOpenResult result) {
if (result.notification.groupedNotifications == null) {
// if the clicked notification was single/clicked separately from a group
Toast.makeText(getApplicationContext(), "Single notification", Toast.LENGTH_LONG).show();
} else {
// if the clicked notification was grouped
Toast.makeText(getApplicationContext(), "Grouped notification quantity: " + String.valueOf(result.notification.groupedNotifications.size()), Toast.LENGTH_LONG).show();
}
}
Related
I am trying to use FCM to send UpStream Message, so I followed the tutorial on google and it works.
As shown in the code below in MainActivity, I send Upstream message when the button is clicked, then in MyAndroidFirebaseMsgService I should see a Log message as shown
below in MyAndroidFirebaseMsgService.
But what happen is, the Log messages in MyAndroidFirebaseMsgService in onMessageSent in do not get displayed even I kept pressing the button several times.
the Log message in MyAndroidFirebaseMsgService in onMessageSent can be displayed only if sent a downstream messagefrom FCM to the App, in this case, both the Logs in
in MyAndroidFirebaseMsgService will be displayed.
Please let me know why the Log message in onMessageSent is not getting displayed once there is an UpStream message sent?and how to fix it.
Mainactivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnSendUpstreamMsg = (Button) findViewById(R.id.btn_send_upstream_message);
mBtnSendUpstreamMsg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseMessaging fm = FirebaseMessaging.getInstance();
fm.send(new RemoteMessage.Builder("673xxxxx" + "#gcm.googleapis.com")
.setMessageId("2")
.addData("my_message", "Hello World")
.addData("my_action","SAY_HELLO")
.build());
}
});
}
MyAndroidFirebaseMsgService:
public class MyAndroidFirebaseMsgService extends FirebaseMessagingService {
private final static String TAG = MyAndroidFirebaseMsgService.class.getSimpleName();
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
Log.d(TAG, "onMessageSent: upstream message");
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived: downstream message");
//Log data to Log Cat
Log.d(TAG, "onMessageReceived->From: " + remoteMessage.getFrom());
Log.d(TAG, "onMessageReceived->Notification Message Body: " + remoteMessage.getNotification().getBody());
//create notification
createNotification(remoteMessage.getNotification().getBody());
}
private void createNotification( String messageBody) {
Intent intent = new Intent( this , ResultActivity.class );
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultIntent = PendingIntent.getActivity( this , 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri notificationSoundURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder( this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Android Tutorial Point FCM Tutorial")
.setContentText(messageBody)
.setAutoCancel( true )
.setSound(notificationSoundURI)
.setContentIntent(resultIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, mNotificationBuilder.build());
}
}
Yes, is possible to send a Firebase messaging push notification and receive it in all app life cycles using onMessageReceived.
But is necessary to change the default Firebase behaviour, intercepting the intent request before everything else.
** IMPORTANT NOTE **
This was a pretty stupid idea from Firebase by remove the developers processment capability when the FCM message arives with the notification message format, but not for data message.
This created a bunch of "workarounds" in many solutions, which made the analythics and everything else being messed up.
If I had designed this solution, I would always call the onMessageReceived method with a completion handle. Let the developer decide what to do (free tip for you, Firebase).
Use onMessageReceived is the correct way to do. This method is the only one who brings RemoteMessage object, that have every information what you need. It was designed for it. You are on correct path.
** HOW TO DO **
In your Firebase Class MyAndroidFirebaseMsgService, which extends FirebaseMessagingService, override the public method handleIntent to intercep the intent request before Firebase catch it.
#Override
public void handleIntent(Intent intent){
if(intent.hasExtra("google.message_id")){
intent = handleFirebaseIntent(intent);
}
super.handleIntent(intent);
}
After, transform the notification message package into an data message, removing all "gcm.notification.%" and "gcm.n.%" extras from intent, and translating "gcm.notification.title", "gcm.notification.body" and "gcm.notification.image" elements into what you need:
// Thank you Google, for that brilliant idea to treat notification message and notification data
// differently on Android, depending of what app life cycle is. Because of that, all the developers
// are doing "workarounds", using data to send push notifications, and that's not what you planned for.
// Let the developers decide what to do on their apps and ALWAYS deliver the notification
// to "onMessageReceived" method. Its simple, is freedom and its what the creative ones need.
private Intent handleFirebaseIntent(Intent intent){
//printIntentExtras(intent);
String FCM_TITLE_KEY = "gcm.notification.title";
String FCM_BODY_KEY = "gcm.notification.body";
String FCM_IMAGE_KEY = "gcm.notification.image";
String title = intent.getStringExtra(FCM_TITLE_KEY);
String body = intent.getStringExtra(FCM_BODY_KEY);
String image = intent.getStringExtra(FCM_IMAGE_KEY);
// Remove the key extras that identifies an Notification type message
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
if (key.startsWith("gcm.notification.") || key.startsWith("gcm.n."))
{
intent.removeExtra(key);
}
}
}
Boolean isTitleEmpty = StringUtils.isNullOrEmpty(title);
Boolean isBodyEmpty = StringUtils.isNullOrEmpty(body);
Boolean isImageEmpty = StringUtils.isNullOrEmpty(image);
// Notification title and body has prevalence over Data title and body
if(
!isTitleEmpty || !isBodyEmpty || !isImageEmpty
){
// This is my personalized translation method, designed for my solution.
// Probably you gonna need to do it by your own
String contentData = intent.getStringExtra(Definitions.PUSH_NOTIFICATION_CONTENT);
Map<String, Object> content;
if(StringUtils.isNullOrEmpty(contentData)){
content = new HashMap<String, Object>();
content.put(Definitions.NOTIFICATION_ID, new Random().nextInt(65536) - 32768);
content.put(Definitions.NOTIFICATION_CHANNEL_KEY, "basic_channel" );
} else {
content = JsonUtils.fromJson(new TypeToken<Map<String, Object>>(){}.getType(),contentData);
}
if(!isTitleEmpty) content.put(Definitions.NOTIFICATION_TITLE, title);
if(!isBodyEmpty) content.put(Definitions.NOTIFICATION_BODY, body);
if(!isImageEmpty){
content.put(Definitions.NOTIFICATION_BIG_PICTURE, image);
content.put(Definitions.NOTIFICATION_LAYOUT, NotificationLayout.BigPicture.toString());
}
contentData = JsonUtils.toJson(content);
intent.putExtra(Definitions.PUSH_NOTIFICATION_CONTENT, contentData);
}
//printIntentExtras(intent);
return intent;
}
private void printIntentExtras(Intent intent){
Bundle bundle;
if ((bundle = intent.getExtras()) != null) {
for (String key : bundle.keySet()) {
System.out.println(key + " : " + (bundle.get(key) != null ? bundle.get(key) : "NULL"));
}
}
}
You can check my entire solution here.
I am making a Chat App (just for fun). I am using Pushy API for sending message between two users. Following the tutorial available in pushy.me site, the Push Messages are received in a Broadcast Receiver. Well, this part is working fine, but now I am making a notification system like Whats App, that launches a notification bar when user is not in the chat.
My idea is the following: if the chat fragment is visible, just update the Fragment using LocalBroadcastManager sendBroadcast method, else start a Notification.
I'am making this with sucess with the following code:
if (!Utility.isAppInBg(context)) {
Intent chatPushNotification = new Intent(Constants.CHAT_PUSH_NOTIFICATION);
chatPushNotification.putExtra("chat", obj.toString());
LocalBroadcastManager.getInstance(context).sendBroadcast(chatPushNotification);
} else {
if (title != null && msg != null) {
NotificationUtil.notify(context, NOTIFICATION_ID, notifyIntent,
URLDecoder.decode(title, "UTF-8"), URLDecoder.decode(msg, "UTF-8"));
}
}
The problem is that the method isAppInBg uses ActivityManager with getRunningAppProcesses() method, which is discouraged. There is a way to replace this method by another that checks if a Fragment is visible (remember that this check is made in a Broadcast Receiver)? If not, there is a better approach?
This approach is working fine for me .
public class MyActivity extends Activity {
static boolean active = false;
#Override
public void onStart() {
super.onStart();
active = true;
}
#Override
public void onStop() {
super.onStop();
active = false;
}
#Override
public void onPause() {
super.onPause();
active = false;
}
#Override
public void onResume() {
super.onResume();
active = true;
}
and in receiver
if(!MyActivity.active){
//alert notification
}
else{
//send broadcast
}
I have been using parse for about 2 months for android and am very happy with it. I have built most of the app, just need to get push notifications finalized. I read the parse push guide and am able to receive and customize them.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
ParseObject.registerSubclass(ParseFeedBack.class);
ParseObject.registerSubclass(ParseReportIssue.class);
ParseObject.registerSubclass(ParseOrder.class);
ParseObject.registerSubclass(ParseAddress.class);
Parse.enableLocalDatastore(getApplicationContext());
Parse.initialize(this, "ID", "ID");
ParseInstallation.getCurrentInstallation().saveInBackground();
}}
So the questions is
Do I leave ParseInstallation.getCurrentInstallation() in the application class itself? Or shift it out to the main activity and register over there since I also want to identify the user associated with it, hence ParseInstallation.put("userPointer", ParseUser.getCurrentUser) will need to be added on my main activity.
Does this function have to be called everytime the user opens the app or only the first time? In which case I will place the code in the registration page so its called only once.
What happens If user A uses user B's phone to login. In that case this code ParseInstallation.put("userPointer", ParseUser.getCurrentUser) will associate installation on B with user A as he had logged in last. So If i send a targeted push notification to user A, it will be received on his as well as User B phone.
In ParsePushBroadcastReceiver, I am overriding onPushRecieve, and building my notification over there. Im not placing any pending intent with the notification but instead overriding onPushOpen(). Is this recommended or should I just add a pending intent in the builder itself and leave onPushOpen() empty.
Google metioned that on October 15th. I noticed in manifest parse still uses C2DM. Have they released any blog about the changes they will implement?
.
public class MyPushReceiver extends ParsePushBroadcastReceiver {
final Integer PRIORITY_MAX=2;
NotificationCompat.Builder builder;
String message;
public MyPushReceiver() {
super();
}
#Override
protected void onPushReceive(Context context, Intent intent) {
super.onPushReceive(context, intent);
builder=new NotificationCompat.Builder(context);
try {
JSONObject jsonObject=new JSONObject(intent.getExtras().getString("com.parse.Data"));
message=jsonObject.getString("alert");
builder.setContentText(message);
builder.setContentTitle(jsonObject.getString("title"));
builder.setPriority(PRIORITY_MAX);
int mNotificationId = 001;
NotificationManager mNotifyMgr =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyMgr.notify(mNotificationId, builder.build());
Log.d("PUSH ", jsonObject.toString());
Log.d("s", intent.toString());
}catch (JSONException e){
e.printStackTrace();
}
}
#Override
protected void onPushOpen(Context context, Intent intent) {
super.onPushOpen(context, intent);
Toast.makeText(context, "Opening push!", Toast.LENGTH_LONG).show();
Intent i=new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
I am using Parse to do push notifications and the issue I am running into is that while my application is running (either in the foreground or background) the phone's operating system does not show the push notification in the notification bar. What changes to my implementation do I need to make to see the push display on the notification bar?
My extended Application class has the following in onCreate()
// initialize Parse SDK
Parse.initialize(this, Constants.APPLICATION_ID_DEBUG, Constants.CLIENT_KEY_DEBUG);
ParsePush.subscribeInBackground(Constants.CHANNEL, new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Logger.i(TAG, "successfully subscribed to broadcast channel");
} else {
Logger.e(TAG, "failed to subscribe for push: " + e);
}
}
});
ParseInstallation.getCurrentInstallation().saveInBackground();
I have a sign in system for my app, so I am using the ID of the logged in user as the Channel to subscribe users to. So in the first Activity of my app I call the following code snippet in onCreate().
private void registerNotifications() {
List<String> arryChannel = new ArrayList<String>();
arryChannel.add(session.id);
ParseInstallation parseInstallation = ParseInstallation.getCurrentInstallation();
parseInstallation.put("channels", arryChannel);
parseInstallation.saveEventually();
}
I also have a custom receiver that is working. Each time a push is sent out, it is being received by the onPushReceive method, however, I want the push to display in the notification bar.
public class ParsePushReceiver extends ParsePushBroadcastReceiver {
private static final String TAG = ParsePushReceiver.class.getSimpleName();
#Override
public void onPushOpen(Context context, Intent intent) {
Log.i(TAG, "onPushOpen");
}
#Override
protected void onPushReceive(Context context, Intent intent) {
Log.i(TAG, "onPushReceive");
}
}
Thanks in advance!
Just remove the onPushReceive method and the default behaviour will remain (show the notification in the status bar.
You are getting this behaviour because if the application is running the Parse Push notification will call the method onPushReceive that does nothing.
I have figured this out. Although the answer provided by Sandra will make a push notification appear on the notification bar, it is not connected to Parse.
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
This causes issues, because if you click on that notification the receiver you create extending ParsePushBroadcastReceiver will not register onPushOpen. My implementation for everything was correct, I only needed to add
super.onPushReceive(context, intent);
That will make the notification appear on the notification bar and also register clicks.
So make sure to make your receiver looks like this (at minimum)
public class ParsePushReceiver extends ParsePushBroadcastReceiver {
private static final String TAG = ParsePushReceiver.class.getSimpleName();
#Override
public void onPushOpen(Context context, Intent intent) {
Log.i(TAG, "onPushOpen");
}
#Override
protected void onPushReceive(Context context, Intent intent) {
Log.i(TAG, "onPushReceive");
**super.onPushReceive(context, intent);**
}
}
So I am developing a social game, where you have a certain ammount of time to do a task. Since it is social, you can chat with the other player.
Everytime you receive a chat message you will also get a notification and upon clicking on it you are redirected to the chat screen with him. Right now this is working good.
What I don't want is, that the user will receive a chat notification while he actually is playing on the PlayActivity.
It would be also good, if already shown notification could be ignored or suspended to a later time while he is on the PlayActivity.
Is this accomplishable?
A different approach could be to cancel all already shown notifications and put the BroadcastReceiver to sleep and revive all notifications and the BroadcastReceiver after the PlayActivity is done?
Not possible either?
Could the app at least ask before leaving the activity? that way i could warn him and if he still leaves, the game could be valued against him.
What I do right now is just a workaround, I make the PlayActivity full screen and check if the focus has changed e.g. if he is dragging down the status bar / notification area. But this is just a hack, something I would really like to do away with it.
As you see, I am not really sure what the right approach here could be. What would a pro Android software developer do in my case?
Thanks in advance!
Right now I am handling notifications by extending a BroadcastReceiver. Code is appended at the end, if you need other parts of my code, let me know!
public class PushBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
try
{
JSONObject json =
new JSONObject(
intent.getExtras()
.getString("KEY"));
notify(context,intent,json);
}
catch (JSONException e)
{
L.debug(App.TAG, "JSONException: " + e.getMessage());
}
}
private void notify(Context ctx, Intent i, JSONObject dataObject) throws JSONException
{
NotificationManager nm = (NotificationManager)
ctx.getSystemService(Context.NOTIFICATION_SERVICE);
boolean createNotification = false;
PendingIntent pi = null;
int gameId = 0;
// chat
if (dataObject.getString("KEY_CHAT").equals("VALUE_CHAT")) {
Intent intent = new Intent(ctx, ChatActivity.class);
intent.putExtra("opponentUsername", dataObject.getString(PARSE_JSON_OPPONENT_USERNAME_KEY));
intent.putExtra("gameId", dataObject.getString(PARSE_JSON_GAME_ID_KEY));
pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
gameId = Integer.parseInt(dataObject.getString(PARSE_JSON_GAME_ID_KEY));
createNotification = true;
// game
} else if (dataObject.getString("KEY_GAME").equals("VALUE_GAME")) {
Intent intent = new Intent(ctx, SS6RunningGameActivity.class);
intent.putExtra("gameId", dataObject.getString(PARSE_JSON_GAME_ID_KEY));
gameId = Integer.parseInt(dataObject.getString(PARSE_JSON_GAME_ID_KEY));
pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
createNotification = true;
}
if (createNotification) {
Uri sound = Uri.parse("android.resource://"
+ ctx.getPackageName() + "/" + R.raw.push_notif);
int icon = R.drawable.icon_notification_android;
String tickerText =
dataObject.getString("TEXT");
Notification mNotification = new NotificationCompat.Builder(ctx)
.setContentTitle(ctx.getResources().getString(R.string.app_name))
.setContentText(tickerText)
.setSmallIcon(icon)
.setContentIntent(pi)
.setSound(sound)
.setDefaults(Notification.DEFAULT_VIBRATE)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(tickerText))
.build();
nm.notify(gameId, mNotification);
}
}
There can be many approaches to this.
One of the simplest would be the following:
Use SharedPreferences. Add a boolean value to indicate that PlayActivity is currently running.
In onCreate(Bundle) of PlayActivity, do the following:
// Initialization
SharedPreferences preferences = getSharedPreferences("MY_PREF_FILE_NAME", 0);
Editor edit = preferences.edit();
// Here, "PLAY_ACTIVITY_IS_RUNNING" is the `key` and `true` is the value
// We are saying that `PlayActivity` is running
edit.putBoolean("PLAY_ACTIVITY_IS_RUNNING", true);
edit.commit();
Now, in onPause() of PlayActivity, set this boolean to false - indicating that we are about to leave PlayActivity:
// Initialization
SharedPreferences preferences = getSharedPreferences("MY_PREF_FILE_NAME", 0);
Editor edit = preferences.edit();
// Here, "PLAY_ACTIVITY_IS_RUNNING" is the `key` and `false` is the value
// We are saying that `PlayActivity` is not running anymore
edit.putBoolean("PLAY_ACTIVITY_IS_RUNNING", false);
edit.commit();
Rest is quite simple.
In PushBroadcastReceiver, open SharedPreferences and check for the value assigned to key PLAY_ACTIVITY_IS_RUNNING. If this value is false, continue posting the notifications. Else, collect them for later.
public class PushBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences preferences = context.getSharedPreferences(
"MY_PREF_FILE_NAME", 0);
// Retrieve the value stored for key "PLAY_ACTIVITY_IS_RUNNING"
// The second argument `false` is the default value
// in case the key does not exist - this is logically sound
boolean playActivityIsRunning = preferences.getBoolean(
"PLAY_ACTIVITY_IS_RUNNING", false)
// PlayActivity is running
if (playActivityIsRunning) {
// store JSON objects somewhere and deal with them later
} else {
try {
JSONObject json = new JSONObject(intent.getExtras().getString("KEY"));
notify(context,intent,json);
} catch (JSONException e) {
L.debug(App.TAG, "JSONException: " + e.getMessage());
}
}
}
private void notify(Context ctx, Intent i, JSONObject dataObject) throws JSONException {
....
....
}
}
You will need to find a way to post the pending notifications. One solution for this is to send a separate broadcast to another BroadcastReceiver in onPause() of PlayActivity. But this could be problematic because onPause() is called even when the user is changing orientation. Perhaps this BroadcastReceiver can start working after a 1 second delay? This would be sufficient time for the activity to be recreated - and the value of PLAY_ACTIVITY_IS_RUNNING reset (since onCreate(Bundle) of PlayActivity will be called again).
So, the flow would be:
onPause is called
set the value to false in SharedPreferences
send the broadcast to deal with pending notifications
place the code of BroadcastReceiver inside a Runnable. Post this Runnable with a 1 second delay using a Handler.
inside the BroadcastReciever => check value of PLAY_ACTIVITY_IS_RUNNING in SharedPreferences <= this will be done after 1 second. If the user only changed screen orientation, the value of PLAY_ACTIVITY_IS_RUNNING would be true. Otherwise, if the user is navigated away from PlayActivity, the value would be false.