I'm developing an Android app and I wish to use Push notifications to perform a custom task without displaying a notification to the user.
Specifically, the app is in alpha stage and I wish to be able to send a remote command to a specific device to restart a background service running in the background.
I know that you can do that with GCM. How can this be done using Parse?
You need to subclass ParsePushBroadcastReceiver and override onPushReceive() method in it. Add these lines to your manifest
<receiver android:name=".yourPushReceiverSubclass" android:exported=false>
<intent-filter>
<action android:name="com.parse.push.intent.RECEIVE" />
<action android:name="com.parse.push.intent.OPEN" />
<action android:name="com.parse.push.intent.DELETE" />
</intent-filter>
</receiver>
Just remove call to super in the onPushReceive method and do whatever you want. If you going to send push from pars.com in JSON format, you can obtain this message using just like that:
JSONObject data = new JSONObject(intent.getExtras().getString("com.parse.Data"));
Related
I am sure there is an answer out there, but I can't seem to find it. Most examples are using the legacy HTTP FireBase call (and 2 years ago), but I want to use the new V1 HTTP API. I can get it to work when the App is running in the foreground (who hasn't), but not in background or when the app is not running. Complicating things is we are also supporting IOS, so the same message must work for both.
A few other items to be very clear about what I am trying to do:
If App is in foreground the FireBase Service is working fine and I get the notification and get the data. I will show that code a bit later
If the App is not running, when I click the Notification in the system tray I want to be able to get the Data from the notification and process it. Not Working.
If the App is running in the background, when I click the Notification in the system tray I want to be able to get the Data from the notification and process it. Not Working.
I suspect it have something wrong with my Setup in the AndroidManifest.xml and/or in the actual message, but I have tried a ton of variations with no success.
First, the message I am posting from FireBase to the device currently:
{
"message":{
"token":"mynastylongdevicetoken...",
"data":{
"State":"Whatever",
},
"notification":{
"title":"New Info",
"body":"A new info has been dispatched to you"
},
"android": {
"notification": {
"click_action": ".StartActivity"
}
}
}
}
Note about the above message - I have tried various combinations of the "click_action" such as "StartActivity" ".StartActivity" ".SplashActivity" "SplashActivity".
This is based on the latest documentation from FireBase:
https://firebase.google.com/docs/cloud-messaging/migrate-v1
and
https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#AndroidNotification
Now my AndroidManifest.xml. This is bit more complicated, since I have some deeplinks implemented and I would prefer to reuse the same Activity for that, but if I need to create a new one, so be it. Currently I have two main entry point Activities. One for the launcher called SplashActivity which is used to throw up a splash image and one for deep linking called StartActivity. Here is the XML for both:
<activity
android:name="com.mycompany.SplashActivity"
android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation"
android:label="#string/app_name"
android:launchMode="singleTask"
android:theme="#style/Theme.Splash" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.mycompany.StartActivity"
android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation"
android:label="StartActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="stateHidden" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="#string/launch_protocol" android:host="deeplink1"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink2"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink3"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink4"/>
</intent-filter>
</activity>
Now the source code. I have two main "chunks" of code. The first is your standard FireBase Message Servicing code which when the App is in foreground works:
public class MyCompanyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
String test = remoteMessage.getData().get("State");
}
}
and
public class MyCompanyInstanceIDService extends FirebaseInstanceIdService {
public void sendNotification ( ) {
String token = FirebaseInstanceId.getInstance().getToken();
if (!Utilities.stringIsBlank(token)) {
// Register with our server
}
}
}
And the AndroidManifest.xml reference:
<service android:name="com.mycompany.MyCompanyMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="com.mycompany.MyCompanyInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
The second chunk I have put into the OnCreate of the two forementioned Activities to try to pull the data from the Intent extra in the OnCreate of the Activities. I have not see any data come in thru that mechanism that a lot of the other StackOverflows have suggested it should (they were generally over a year old and may not apply to V1 of the API?). Here is the example of it:
protected void onCreate(Bundle savedInstanceState)
{
if (getIntent().getExtras() != null) {
String test = getIntent().getExtras().getString("State", "");
}
}
I have looked at the myriad of other StackoverFlows on this subject without success. I can list them if you want - maybe I have missed something there? The one with the most hits (224,000) did not help.
Thanks in advance!
UPDATE
Thanks to Bob for the help. Just wanted to mark the changes so others will not be lost in strange way Android is handling this (IOS does not seem to have such problems, lucky them).
First the AndroidManifest.xml as shown in the answer, now has a new intent filter:
<activity
android:name="com.mycompany.SplashActivity"
android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation"
android:label="#string/app_name"
android:launchMode="singleTask"
android:theme="#style/Theme.Splash" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.mycompany.NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="com.mycompany.StartActivity"
android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation"
android:label="StartActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="stateHidden" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="#string/launch_protocol" android:host="deeplink1"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink2"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink3"/>
<data android:scheme="#string/launch_protocol" android:host="deeplink4"/>
</intent-filter>
</activity>
The code was pretty much right in the onCreate(). Just need to check for the Intent for extras if the app is not running or in the background. When in foreground the public void onMessageReceived(RemoteMessage remoteMessage) will be called and only if a Notification is in the message. If you are data only or, like me, Notification with extra data, you will have to implement both the onCreate() Intent and onMessageReceived() in the Firebase service above.
The new message is as follows:
{
"message":{
"token":"mynastylongdevicetoken...",
"data":{
"State":"Whatever",
},
"notification":{
"title":"New Info",
"body":"A new info has been dispatched to you"
},
"android": {
"notification": {
"click_action": "com.mycompany.NOTIFICATION_CLICK"
}
}
}
}
Finally, I was never able to get the notification to be picked up by selecting the App from the tool bar (when it had the indication of the notification) if the App was not already running. Not sure if that is the desired behavior or if I just messed up somewhere.
Finally, I wanted to pull up the currently running Activity if the App was already running foreground or background, or start the app if it was not. To do that I had to add the following to the onCreate() of the Activity targeted to receive the notification:
protected void onCreate(Bundle savedInstanceState)
{
if (getIntent().getExtras() != null) {
String test = getIntent().getExtras().getString("State", "");
}
// If this activity is the root activity of the task, the app is not running
if (isTaskRoot()) {
setContentView(R.layout.activity_splash);
}
else
finish();
}
Basically it checks to see if the SplashActivity is the root, meaning that it is the only activity (new start) or not (app already running). If so it would just finish and pull the App forward.
What I am surprised by is how hard this turned out to be using a Google product (Firebase) with a Google product (Android), where as with IOS (I am told) this was all easy and built it.
The string you specify in the message for click_action must match the action name in the intent filter defined in your app manifest for the target activity.
If you want SplashActivity to receive the data values in the message, change click_action in the message to be something like com.mycompany.USER_CLICK and add another intent filter under SplashActivity with DEFAULT category and matching action name:
<intent-filter>
<action android:name="com.mycompany.USER_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Note that the action name can be any valid string but should be prefixed with your package name to ensure uniqueness. Although many related posts on SO use the activity name, that is not necessary.
Also, if you want SplashActivity to handle both user clicks and dynamic links, you will need to check the action in the received intent and branch to appropriate processing.
I used to use com.google.android.gms.wearable.BIND_LISTENER and WearableListenerService to communicate between my Mobile and Wear device.
After the intent com.google.android.gms.wearable.BIND_LISTENER getting deprecated I am supposed to use:
<service android:name=".MyListenerService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/prefix" />
</intent-filter>
</service>
But how do I combine it with my WearableListenerService? where will the broadcast received?
Like you said the BIND_LISTENER is deprecated now, so according to this thread, the alternative for this is by using a fine-grained intent filter mechanism that allows developers to specify exactly what events they are interested in.
There are intent filters for DATA_CHANGED, MESSAGE_RECEIVED, CHANNEL_EVENT, and CAPABILITY_CHANGED. You can specify multiple elements, and if any of them match, it will call your service and filter out anything else. If you do not include an element, all events will be filtered out and your service will never be called, so make sure to include at least one.
Check also the Live listener part and Best practice to know more information about this issue, including the use of WearableListenerService.
migrating from Parse I got stuck by not being able to override PushBots notification creation code like I used to do with getNotification function in ParsePushBroadcastReceiver. My project needs multiline and separate push notifications, but PushBots default behavior is to show single line and group notifications by application.
I tried canceling notification in the onReceive function of BroadcastReceiver (PBConstants.EVENT_MSG_RECEIVE event), so I could create & display my own push, but I don't know notification ID and CancelAll() didn't work (also would be a bad idea).
Pushbots.sharedInstance() does have a setNotificationBuilder function, which accepts PBGenerate object, but I have no idea how to construct it properly and if this actually would help my case.
Couldn't find any documentation & get a response from support yet, so asking here if maybe someone knows a solution or a way to workaround this issue.
Here's how receivers are defined in AndroidManifest.xml
<receiver
android:name="com.pushbots.google.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.my.app" />
</intent-filter>
</receiver>
<receiver android:name="com.my.app.MyPushReceiver" />
<service android:name="com.pushbots.push.GCMIntentService" />
Thank you in advance!
I create simple "ParseStarterProject" as default of parse.com project. It works. I send notification from one device to other device successfully via setchannel method.
But the problem is I do not use receiver class(I do not know how can I use that) so when a notification comes I need to get its message ? it is possible? or if I use set data can I get its data ?
I use this code to send message:
ParsePush push = new ParsePush();
String yourMessage = "Selam from LG G2";//I want to get this message from other device?
push.setChannel("device2");
push.setMessage(yourMessage);
//push.setData("exampledata"); if I use this can I get this data from other device?
push.sendInBackground();
my manifest file:
<receiver android:name="com.parse.ParseBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver android:name="com.parse.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<!--
IMPORTANT: Change "com.parse.starter" to match your app's package name.
-->
<category android:name="com.parse.starter" />
</intent-filter>
</receiver>
thanks in advance
Create a class that extends BroadcastReceiver, here we call it MyCustomReceiver. Declare the usage of this receiver in your manifest:
<receiver android:name="com.example.MyCustomReceiver" android:exported="false">
<intent-filter>
<action android:name="com.example.UPDATE_STATUS" />
</intent-filter>
</receiver>
I'm not sure what you mean by you have problems with "MyCustomReceiver class on the main activity", in your comment to #kingspeech's answer.
In the worst case, you can create your own Receiver which extends the ParseBroadcastReceiver (and reference the extended class in the manifest). Then it should work by default, but you'll be able to hook into onReceive(Context, Intent)
As Parse tutorial directs, you ca use JSONObject to set data and you can put whatever you want.
Then when you receive Push notification you can have a access to read the previously prepared JSONObject. Please look at the Android tutorial https://parse.com/docs/push_guide#options/Android.
Hope this helps,
Regards.
I am studying RemoteService example in Android's APISample. In the
manifest file, it declares the service like this:
My question is how can I specify the service to be 'auto-start', i.e.
it gets start whenever the phone start?
<service android:name=".app.RemoteService" android:process=":remote" >
<intent-filter>
<!-- These are the interfaces supported by the service, which
you can bind to. -->
<action
android:name="com.example.android.apis.app.IRemoteService" />
<action
android:name="com.example.android.apis.app.ISecondary" />
<!-- This is an action code you can use to select the service
without explicitly supplying the implementation class. -->
<action android:name="com.example.android.apis.app.REMOTE_SERVICE" />
</intent-filter>
</service>
First, you do not want to do that.
Second, you cannot do that directly. You will need to set up a BroadcastReceiver to watch for the BOOT_COMPLETED broadcast Intent, and have that receiver start the service.