Goal
Mobile app with Parse's back-end
User creates message to all/admins/specific users
The "recipients" get notification that they have new message
When the notification is clicked, details about the message are displayed
Current approach
I create a custom push in the afterSave cloud code method. The call looks like this:
Parse.Push.send({
where : query,
data : {
action : "com.acme.CUSTOM_ACTION_GOES_HERE",
content : messageContent
}
}).then(...
(The query is defined to get specific Parse.Installation objects.)
In Android app, I have a BroadcastReceiver registered like this
<receiver
android:name=".MyBroadcastReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.acme.CUSTOM_ACTION_GOES_HERE" >
</action>
</intent-filter>
</receiver>
In the onReceive method of the broadcastreceiver, I create custom notification where I also define the pending intent starting an activity when the notification is clicked:
Intent contentIntent = new Intent(context, DisplayDetailsActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(DisplayDetailsActivity.class);
stackBuilder.addNextIntent(contentIntent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
Notification noti = new Notification.Builder(context)
.setContentTitle("New message")
.setContentText(content)
.setSmallIcon(R.drawable.ic_stat_notify_message)
.setNumber(notificationsCount)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
noti.defaults |= Notification.DEFAULT_SOUND;
NotificationManager mgr = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
mgr.notify(555, noti);
Problem
Without calling (e.g. in application's onCreate method)
PushService.setDefaultPushCallback(getApplicationContext(), SomeActivity.class);
... the pushes are not delivered to the device
When using that call, the activity SomeActivity is started when the notification is clicked instead of the activity DisplayDetailsActivity set in the intent in the broadcast receiver.
When I have just one type of custom notification, it is possible to call the setDefaultPushCallback with the DisplayDetailsActivity.class as second parameter.
However, I plan to have multiple different custom notifications, and clicking each of them should start different activity. In such case the workaround mentioned above is not viable option any more.
Any ideas how to solve this?
After working on this for a few hours I stumbled upon this post, I started from #pareshgoel workaround, the basic idea is to remove the need to register an Activity or subscribe to a Channel if it is not required and also lower the server requests for the Installation class.
I took the Parse-1.5.1.jar, from Parse.com and using JD-GUI I looked in PushService.setDefaultPushCallback(Context context, Class<? extends Activity> cls).
What it does is
if cls is null remove subscription;
create subscription for new cls;
call PushService.startServiceIfRequired
The function PushService.startServiceIfRequired starts parse push service if GCM is not supported on the device or sends a registration request to GCM, waits for the registration_id, and upon arrival updates the installation device token.
My workaround is to use : PushService.startServiceIfRequired - which doesn't seem to be documented, you can use if after the initialize or if you have users after login/signup, as it is using a background task it can be called on the main thread.
Another advantage is that using PushService.startServiceIfRequired you have 2 server requests to save/update the Installation class, first one sets push type to GCM and the second sets pushtype to GCM, sets device token to registration_id. PushService.setDefaultPushCallback introduces an initial server request.
I faced the same issue, and have a workaround.
First of all setDefaultPushCallback needs to be called to have Parse push notification working. This is most likely a bug n Parse. I am on Parse Android 1.5.1 .
The workaround is to not set the "alert" param in the parse push notification send code.
If this is not set, then Parse will not display the alert on its own, and your custom broadcast receiver could handle the push.
Push notification in Parse need some more bug fixing before it could be reliably used.
Related
about event for click on notification:
I am searching since yesterday about that , what I found/understood: in FirebaseMessagingService will receive notification data , after will fire local notification, so need to add event in that local notification, I tried to add that many times with many ways but nothing worked …
after I tried to deleted notification files (firebase notification files, and local notification files) but still can receive notification. do you know how to know if the user clicked on the notification ?
To receive messages, use a service that extends FirebaseMessagingService. Your service should override the onMessageReceived and onDeletedMessages callbacks. It should handle any message within 20 seconds of receipt (10 seconds on Android Marshmallow). The time window may be shorter depending on OS delays incurred ahead of calling onMessageReceived. After that time, various OS behaviors such as Android O's background execution limits may interfere with your ability to complete your work.
For further info. you can visit the official website:
Link: https://firebase.google.com/docs/cloud-messaging/android/receive
Hope you'll get your answer here.
Step 1:
// Create an Intent for the activity you want to start
Intent intent = new Intent(this, MainActivity.class);
Step 2:
// Create the PendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(this, Calendar.getInstance().get(Calendar.MILLISECOND), intent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
Step3:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(NOTIFICATION_ID, builder.build());
Whenever a user clicks on notification MainActivity will be opened.
Here is details implementation of Android Notification Sample https://github.com/android/user-interface-samples/tree/master/Notifications
I'm sending notifications from my Flask server using PyFCM, and I'm testing it on a single Android device. The test is like this: I am signed in as user A, and I make a comment on a post of user B which should display a push notification once B signs in. Here is how I send the notification from my server:
registration_id="<device_registration_id>"
message_body = "A has commented on your post."
data_message = {"sender": current_user.id}
result = push_service.notify_single_device(
registration_id=registration_id,
message_body=message_body,
data_message=data_message
)
And this is how I receive the message in the Android's Firebase messaging service:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT):
String senderId = remoteMessage.getData().get("sender");
if (senderId != currentUser.id) {
NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder(this, "default_channel")
.setSmallIcon(R.drawable.android_icon)
.setContentTitle("New Comment")
.setContentText(remoteMessage.getNotification().getBody())
.setAutoCancel(true)
.setSound(soundURI)
.setContentIntent(resultIntent);
NoticationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, mNotificationBuilder.build());
}
}
As you can see, I have this condition: senderId != currentUser.id before actually composing the notification. It's because I'm using one device to send and receive the notification so there's only one registation_id/token for both users A and B. If I remove that condition, user A will receive the notification right after commenting on B's post. I want to ensure that B is the one who receives the notification. However, after logging out as A and logging in as B, I couldn't see any push notification.
I think onMessageReceived is only being triggered once. Say, I'm trying to send a notification from my server to all users, and I receive it while logged in as A. I clear the notification tray, log out, and log in as B, but I don't see another instance of the notification.
This is working as intended. From your post, I assume that you are not specifically deleting the registration token i.e. users of the same device re-use the same token. So the flow now looks like this:
User A signs in, so deviceToken = A.
You send a message from your server to deviceToken.
deviceToken receives the message, but you do not display it.
User A signs out, User B signs in, now deviceToken = B. Note: Same deviceToken, just different user.
In step 4, you're still expecting a message to arrive, but technically, it already did when User A was still signed in. onMessageReceived won't trigger again since it already received the message as expected.
In order to test your desired behavior, you would need two devices. I'm also actually doing this to an app I made (checking the senderId with the currentUser id), so I think it should work as well for you.
Also, the usual and suggested flow with FCM when signing out is you have to invalidate the token -- see my answer here for more details.
As of now, I am using Firebase Cloud Messaging as my app's notification system. From my understanding, even the largest companies use GCM, which is now being replaced with FCM. I am stuck, however, with the limitations imposed by sending things as a "notification" pay load.
Firebase recieves data messages in OnMessageRecieved, and I am able to get my exact desired behavior through using this. However, for the past three months I noticed that my app's notifications were simply not working because data payloads aren't received when the app is in the background/ inactive. This is very frustrating as the only workaround I have found to actually send notifications that alert users is through sending notification payloads.
I don't know how to get my desired behavior with the notification payload as I have been with data payload, but I realize that having notifications display perfectly when the app is in the foreground is pointless if they don't display at all when the app is in the background.
What I'm wondering, and what I'd really appreciate guidance on, is if somebody could recommend a way to display notifications with my desired style rather than the default Firebase notification style. Just to elaborate, I currently have a fully functional system of allowing users to respond to messages through the notification and without opening the app, but only through the data payload.
I know there is a way to somehow send notifications in a customized fashion (adding intents, buttons, etc) when the app is in the background or not running because this is done by Facebook, WhatsApp, and many other apps on the market. I am aware of something called AlarmManagers, but unsure of how I could use them to get my desired behavior, or if this is what people use anyways.
Thank you, and please let me know if there is a way to customize the payload when the app is in the background or inactive, through onMessagesRecieved or any other methods. As of now, the only other solution would be to somehow send the message's contents to a service, but I've faced issues where services could not display notifications in the background without crashing the app and without being very hacky.
The only way I came up with is to send the data payload only, which should be delivered to the onMessageReceived callback despite the app's status (foreground/background).
Then you put all required fields to the data payload and build a notification from it. Here the map "data" is from remoteMessage.getData()
private void sendNotification(Map<String, String> data) {
String body = Optional.ofNullable(data.get("body")).orElse("");
if (body.isEmpty()) {
return;
}
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Uri sound = getSoundUri(Optional.ofNullable(data.get("sound")).orElse(""));
String defaultChannel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? NotificationChannel.DEFAULT_CHANNEL_ID
: DEFAULT_CHANNEL_ID;
String channel = Optional.ofNullable(data.get("android_channel_id")).orElse(defaultChannel);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channel)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(body)
.setAutoCancel(true)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setContentIntent(resultPendingIntent);
if (sound != null)
notificationBuilder.setSound(sound);
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (notificationManager != null)
notificationManager.notify(0, notificationBuilder.build());
}
I have a requirement (android) where my app should run its main activity automatically when a push notification is received without the user clicking on notification in system tray. I am having a map where it shows current location, but in the push, i will receive a location, and i need my map in the main activity to move the camera to the currently received location on receiving the push, and alert the user with a custom sound. All this should happen without the user clicking on anything. Pls help. Thanks in advance.
Yes, it's possible. Parse.com documentation says:
You can also specify an Intent to be fired in the background when the push notification is
received. This will allow your app to perform custom handling for the notification, and
can be used whether or not you have chosen to display a system tray message. To implement
custom notification handling, set the Action entry in your push notification data
dictionary to the Intent action which you want to fire. Android guidelines suggest that
you prefix the action with your package name to avoid namespace collisions with other
running apps.
So you send push notifications in this way:
JSONObject data = new JSONObject("{\"action\": \"com.example.UPDATE_STATUS\""}));
ParsePush push = new ParsePush();
push.setData(data);
push.sendPushInBackground();
Then in your AndroidManifest.xml register a broadcast receiver that will be called whenever a push notification is received with an action parameter of com.example.UPDATE_STATUS:
<receiver android:name="com.example.MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="com.example.UPDATE_STATUS" />
</intent-filter>
</receiver>
In your broadcast receiver you can start a new activity:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startActivity(new Intent(context, MyActivity.class));
}
}
warning to #makovkastar, major changes since version V1.8. for instance: no more com.example.UPDATE_STATUS.
push notifications guide is more clear now: https://www.parse.com/tutorials/android-push-notifications
this is the ParsePushBroadcastReceiver subclass: https://gist.github.com/panchicore/97d5ad25842258576109
this answer have a good tutorial to send/receive local broadcasts: https://stackoverflow.com/a/8875292/155293
Basically onPushReceive will be called when a push is received in the device, in this method use LocalBroadcastManager to make something in the app, for instance, add a new message to a chatroom.
I'm implementing GCM in my app and keeping a hash of notifications to keep track of what is in the notification shade (I have to change intents based on if the user is in or out of app).
I set the deleteIntent PendingIntent for all my notifications. All this does is remove the Notification from my local hash so it won't be updated anymore. The intent is fired fine if I clear all or swipe to delete a notification. However, I also set my notifications to auto cancel. Clicking on a notification does not trigger the deleteIntent for my notification.
My question is, is there any way to be notified when my Notifications are auto-cancelled?
This bug has been reported, but it doesn't look like it has been investigated at all. To work around this here's what I did:
Turn off auto cancel
Use broadcast for both content and delete intents with different actions
Broadcast receiver checks action
Content action: Do both click and delete operations, and cancel notification manually
Delete action: Do delete operation only
For example:
Send Notification
Notification.Builder builder = new Notification.Builder(context)
// Set other properties (not auto-cancel)
.setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(NOTIFICATION_CLICKED_ACTION), 0))
.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(NOTIFICATION_DELETED_ACTION), 0));
notificationManager.notify(NOTIFICATION_ID, builder.build());
Receive Broadcast
if (intent.getAction().equals(NOTIFICATION_CLICKED_ACTION)) {
startActivity(new Intent(context, MyActivity.class));
notificationManager.cancel(NOTIFICATION_ID);
}
// Do deletion behaviour here (for both click and delete actions)
This is the correct behaviour od the DeleteIntent as described here in the Android SDK documentation:
Supply a PendingIntent to send when the notification is cleared
explicitly by the user.
The DeleteIntent will only get called when the notification is explicitly cleared by the user by swiping it away or by using the "clear all" function of the notification menu. Tapping on the notification will ONLY trigger the ContentIntent EVEN IF the AutoCancel is set to True.
Documentation says here and here, that clicking on notification with FLAG_AUTO_CANCEL cancels it automatically. This behavior means also that regular contentIntent (if set) will fire along with automatic cancellation, because it is fired in response for user's click action.
Use contentIntent field along with deleteIntent to detect cancellation performed by explicit user tap.