I need my android app to work with two push services, GCM and parse.
My problem is that I can't find a way for register correctly to Parse, get parse notification and GCM notification too. I can reach all these things individually, but never together.
My current implementation looks this way:
<!-- GCM BradcastReceiver & Service -->
<service android:name=".GcmIntentService"
android:enabled="true"/>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter
android:priority="100"> <!-- higher priority to GCM messages -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.twentyLines.GCM_NOTIFICATION_ACTION" />
<category android:name="com.twentyLines.app" />
</intent-filter>
</receiver>
This is the broadcastReceiver for GCM, and the one below is the other receiver for parse:
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter android:priority="0">
<!--<action android:name="com.google.android.c2dm.intent.RECEIVE" />-->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.twentyLines.app" />
</intent-filter>
</receiver>
<!-- Can remove this if application is already registered for GCM -->
<receiver android:name="com.parse.ParseBroadcastReceiver" android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
</intent-filter>
</receiver>
I've tried to add a custom broadcast receiver to handle just parse notification, so I can avoid it to handle GCM too. I've done it like this:
<receiver android:name="com.twentyLines.app.ParseBroadcastReceiver" android:exported="false" >
<intent-filter>
<action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
</intent-filter>
</receiver>
This is the implementation of my BroadcastReceiver for GCM, that avoid parse notification to be displayed.
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean isFromParse = intent.getExtras().get("action").equals("com.twentyLines.PARSE_NOTIFICATION_ACTION");
if (isFromParse)
return;
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_CANCELED);
}
}
And this is the implementation for my PARSE custom BroadcastReceiver that SHOULD avoid GCM to be showed.
The parse custom receiver is never invoked, I think because the com.parse.GcmBroadcastReceiver handle the notification itself instead of pass them.
The result is that, when I send a notification from my server to GCM receiver, this is retrieved from both, and double notification is showed.
(double is already good, every time I uninstall and reinstall my app Parse register another user.. the "UniqueId" I send to parse every time is not so UNIQUE for them).
What have I tried?
I'm really getting crazy, I've tried about everything.
- I've read all related questions, and no one is good for me;
- I've tried to remove the parse receiver, and this cause a parse exception (so doesn't register to parse)
- I've tried to set the intent to RESULT_CANCELED, but in some way parse get it before GCM, so isn't working when I use GCM.
- I've changed about all using cowboy-coding, and it still not work...
Any help will be really welcome. Thank you guys!
EDIT - ADD WORKING MANIFEST
<!-- GCM BradcastReceiver & Service -->
<service android:name=".GcmIntentService"
android:enabled="true"/>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter
android:priority="2"> <!-- higher priority to GCM messages -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.twentyLines.GCM_NOTIFICATION_ACTION" />
<category android:name="com.twentyLines.app" />
</intent-filter>
</receiver>
<!-- Parse service broacastReceiver and receiver. -->
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter android:priority="1">
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.twentyLines.app" />
</intent-filter>
</receiver>
<!-- Can remove this if application is already registered for GCM -->
<receiver android:name="com.parse.ParseBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
</intent-filter>
</receiver>
<receiver android:name="com.twentyLines.app.ParseBroadcastReceiver" android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
</intent-filter>
</receiver>
EDIT 2: HOW I REGISTER PARSE AND GCM
I register Parse in my application class:
Parse.initialize(this, PARSE_APP_KEY_VALUE, PARSE_CLIENT_KEY_VALUE);
PushService.setDefaultPushCallback(getApplicationContext(), MainActivity.class);
final ParseInstallation installation = ParseInstallation.getCurrentInstallation();
final String androidId = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
installation.put("UniqueId", androidId);
installation.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
Log.d("Parse installation saved in background", "If operation successfull, 'e' have to be NULL e= " + e);
}
});
}
}, 5000
);
And I get gcm registration_id in my MainActivity:
// Check if there is a saved registration_id in shared_prefs,
// or if app version has changed
private String getSavedRegistrationId() {
final SharedPreferences gcmShared = getGCMSharedPrefss();
String registrationId = gcmShared.getString(Constant.GCM_REGISTRATION_ID_KEY, "");
if (registrationId.isEmpty()) {
Log.i("GCM", "Registration not found.");
return "";
}
int registeredVersion = gcmShared.getInt(Constant.APP_VERSION_KEY, Integer.MIN_VALUE);
int currentVersion = getAppVersion(this);
if (registeredVersion != currentVersion) {
clearSavedGCMRegistrationId();
Log.i("GCM", "App version changed.");
return "";
}
return registrationId;
}
// If there isn't, request one
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg;
try {
if (_gcm == null) {
_gcm = GoogleCloudMessaging.getInstance(MainActivity.this);
}
String regid = _gcm.register(Constant.GCM_SENDER_ID);
msg = "Device registered, registration ID=" + regid;
sendRegistrationIdToBackend(regid);
storeRegistrationId(regid); // Save reg_id to shared
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the startupLoggedUser to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Log.d("GCM registration", msg);
}
}.execute(null, null, null);
}
We have two broadcast receivers here to listen c2dm intent
.GcmBroadcastReceiver : lets call this as GCM receiver... Parse Push will never come to this receiver.
com.parse.GcmBroadcastReceiver : lets call this as Parse receiver
As you have given higher priority to GCM receiver, broadcast will come to GCM receiver first and then to Parse receiver. This does not guarantee that Broadcast will not go to Parse receiver. You need to add abortBroadcast(); as last line in onReceive method to make sure that Parse receiver is not triggered when we have GCM receiver working.
As per Parse Push notification guidelines, Push is received with a specific intent action. Data is sent to broadcast receiver registered with that action.
In your case, if push is received with action "com.twentyLines.PARSE_NOTIFICATION_ACTION", you can have a custom broadcast receiver to listen to this action. In that broadcast receiver you can fetch data by below code,
try {
String action = intent.getAction();
String channel = intent.getExtras().getString("com.parse.Channel");
JSONObject json = new JSONObject(intent.getExtras().getString("com.parse.Data"));
Log.d(TAG, "got action " + action + " on channel " + channel + " with:");
Iterator itr = json.keys();
while (itr.hasNext()) {
String key = (String) itr.next();
Log.d(TAG, "..." + key + " => " + json.getString(key));
}
} catch (JSONException e) {
Log.d(TAG, "JSONException: " + e.getMessage());
}
When there is GCM push, this custom receiver will never get broadcast event as the C2DM broadcast is being aborted in GCM receiver (".GcmBroadcastReceiver")
Hope This Helps!
Related
I've managed to get this working in the past, but its suddenly stopped working and I don't know what to check in order to get it working again. I am trying to get push notifications working using Firebase Cloud Messaging through Localytics. I am testing using the Localytics Rapid Push Notification Test module. The device pairs ok but when I send the notification nothing happens on the device.
1) I've regenerated my google-services.json file and pasted into the root of my folder.
2) I'm pretty sure my Android Manifest is fine:
<meta-data android:name="LOCALYTICS_APP_KEY" android:value="{HIDDEN FROM STACK OVERFLOW}" />
<activity android:name="com.localytics.android.PushTrackingActivity" android:exported="true" />
<receiver android:name="com.localytics.android.ReferralReceiver" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" 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="{HIDDEN FROM STACK OVERFLOW}" />
</intent-filter>
</receiver>
3) My implementation of FirebaseInstanceIdService gets called ok as I can debug it:
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
const string TAG = "MyFirebaseIIDService";
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
Localytics.PushRegistrationId = refreshedToken;
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{
// Add custom implementation, as needed.
}
}
4) My implementation of FirebaseMessagingService never ever gets called!
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
const string _tag = "MyFirebase";
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(_tag, "Remote Message Received");
}
}
5) In my MainActivity I am calling Localytics.RegisterPush(); within the OnCreate() override.
Can someone please help? Thanks in advance!
I've finished the tutorial and able to debug and send broadcasts manually
But I can't intercept the referrer data with another custom receiver. It simply doesn't trigger on incoming broadcast com.android.vending.INSTALL_REFERRER.
If I send broadcast to /com.google.android.gms.analytics.CampaignTrackingReceiver custom receiver not see it. Otherways, sending to exactly co.primesignals.android.primesignalsapp.campaign.LaunchReceiver works fine. But google sends that broadcast to own receiver...
How to implement custom receiver properly to intercept campaign referrer?
Manifest:
...
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name="com.google.android.gms.analytics.AnalyticsReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.analytics.ANALYTICS_DISPATCH" />
</intent-filter>
</receiver>
<service android:name="com.google.android.gms.analytics.AnalyticsService"
android:enabled="true"
android:exported="false"/>
<!--Used for Google Play Store Campaign Measurement-->
<service android:name="com.google.android.gms.analytics.CampaignTrackingService"
android:enabled="true"
android:exported="false"/>
<receiver
android:name="com.google.android.gms.analytics.CampaignTrackingReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<receiver android:name=".campaign.LaunchReceiver" //custom receiver
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
LaunchReceiver:
public class LaunchReceiver extends BroadcastReceiver {
public static final String INSTALL_REFERRER_ACTION = "com.android.vending.INSTALL_REFERRER";
public static final String REFERRER = "referrer";
public static final String TAG = "GAv4 app ->";
#Override
public void onReceive(Context context, Intent intent) {
android.util.Log.d(TAG, "LaunchReceiver.onReceive");
final String action = intent.getAction();
if (!TextUtils.isEmpty(action) && INSTALL_REFERRER_ACTION.equals(action)) {
android.util.Log.d(TAG, String.format("LaunchReceiver.onReceive action=%s", action));
String referrer = intent.getStringExtra(REFERRER);
if (!TextUtils.isEmpty(referrer)) {
android.util.Log.d(TAG, String.format("LaunchReceiver.onReceive referrer=%s", referrer));
SPManager.add(context, REFERRER, referrer);
CampaignData.parseAndSaveData(context, referrer);
}
}
new CampaignTrackingReceiver().onReceive(context, intent);
}
}
ADB shell commands I used to pass broadcasts:
am broadcast -a com.android.vending.INSTALL_REFERRER -n "[package]/com.google.android.gms.analytics.CampaignTrackingReceiver" --es referrer "[referrer value]" -> this is not handled by custom receiver
am broadcast -a com.android.vending.INSTALL_REFERRER -n "[package]/co.primesignals.android.primesignalsapp.campaign.LaunchReceiver" --es referrer "[referrer value]" -> this works perfect but google wont send this broadcast
Make exported="true" for AnalyticsReceiver and CampaignTrakingReceiver. For more refer https://developers.google.com/analytics/devguides/collection/android/v4/campaigns
I'm stuck while working with BroadCastReceiver in which it is invoking only when device is powered on or restarted. I am connecting the USB device using OTG cable. Android system showing USB inserted icon every time but my app not receiving any event.
Let me know what wrong I'm doing.
I am having an application which only has a BroadcastReceiver as below.
public class MountReceiver extends BroadcastReceiver {
private static final String TAG = MountReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
String actionName = intent.getAction();
Toast.makeText(context.getApplicationContext()
, "on Receive", Toast.LENGTH_LONG).show();
extractAllDataFromIntent(intent, context);
boolean isMounted;
if (actionName.equals("android.intent.action.MEDIA_MOUNTED")) {
isMounted = true;
} else {
// actionName.equals("android.intent.action.MEDIA_UNMOUNTED"
isMounted = false;
}
}
private void extractAllDataFromIntent(Intent intent, Context context) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Set<String> keys = bundle.keySet();
Iterator<String> it = keys.iterator();
Log.e(TAG, "Dumping Intent start");
StringBuilder msg = new StringBuilder();
msg.append(TAG + " Seprate app");
while (it.hasNext()) {
String key = it.next();
Log.e(TAG, "[" + key + "=" + bundle.get(key) + "]");
msg.append("[" + key + "=" + bundle.get(key) + "]");
}
Log.e(TAG, "Dumping Intent end");
Toast.makeText(context.getApplicationContext()
, msg, Toast.LENGTH_LONG).show();
//Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
}
}
I have manifest entry for this receiver as follows.
<receiver
android:name="com.example.manmohan.mountreceiverdemo.MountReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<action android:name="android.intent.action.MEDIA_UNMOUNTED" />
<action android:name="android.intent.action.MEDIA_REMOVED" />
<action android:name="android.intent.action.MEDIA_EJECT" />
<action android:name="android.intent.action.MEDIA_BAD_REMOVAL" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
<action android:name="android.hardware.usb.action.USB_STATE" />
<data android:scheme="file" />
</intent-filter>
</receiver>
A weird case I see is that receiver is receiving WiFi change events when added WiFi state change callbacks, but still no USB events are getting received.
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
I believe you want to know the external storage is mounted or not at any given point of time:
Is there a way to tell if the sdcard is mounted vs not installed at all?
You have clubbed all the actions together for one receiver. Try keeping separate receiver for each action, and check whether you can see the difference.
I believe you have added a permission in your manifest:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
My app server is sending out messages to GCM successfully, however, in the client side, the messages are not being received, even though it has the token and it has been subscribed to different topics.
For listening to the incoming messages, I am using an extension of GcmListenerService. Since this is a service, it starts from the mainactivity. Bellow is the relevant part of the manifest:
<!-- [START gcm_permission] -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- [END gcm_permission] -->
<!-- [START gcm_receiver] -->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="my.app" />
</intent-filter>
</receiver>
<!-- [END gcm_receiver] -->
<!-- [START gcm_listener] -->
<service
android:name="my.app.MyGcmListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<!-- [END gcm_listener] -->
<!-- [START instanceId_listener] -->
<service
android:name="my.app.MyInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
<!-- [END instanceId_listener] -->
<service
android:name="my.app.RegisterIntentService"
android:exported="false">
</service>
The GCM listener is below:
public class MyGcmListenerService extends GcmListenerService {
private static final String TAG = "MyGcmListenerService";
// [START receive_message]
#Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("message");
Log.d(TAG, "From: " + from);
Log.d(TAG, "Message: " + message);
if (from.startsWith("/topics/")) {
// message received from some topic.
//TOPICS = {"News","Updates","GeneralInfo"};
if (from.split("/")[2].equals("News")) {
....
As it was mentioned before, the GCM listener is being started from the mainactivity as a regular service.
//starting the listener service for messages
intentGCMListen = new Intent(this,MyGcmListenerService.class);
startService(intentGCMListen);
In the example (GCM quickstart) that I followed, which is basically the core of my deployment for GCM in this part, there is no such a start for the GCM listener service. I assumed that it should be started because it is a service.
Any ideas why the messages are not being downloaded from the GCM server?
UPDATE
The bash script that I am using for sending the messages from the server to GCM is:
#!/bin/bash
api_key=AIadasdasdsa
message=$1
topic=/topics/GeneralInfo
curl --header "Authorization: key=$api_key" --header Content-Type:"application/json" https://gcm-http.googleapis.com/gcm/send -d "{\"to\":\"$topic\",\"data\":{\"message\":\"$message\"}}"
I think you are missing the BroadcastReceiver in your code that serves the purpose of receiving the Broadcast that has been send by the GCM server for your app clients to process. You need to check for the following lines in the code:
/**
* Receiving push messages
* */
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
// Waking up mobile if it is sleeping
WakeLocker.acquire(getApplicationContext());
/**
* Take appropriate action on this message
* depending upon your app requirement
* For now i am just displaying it on the screen
* */
// Showing received message
lblMessage.append(newMessage + "\n");
Toast.makeText(getApplicationContext(), "New Message: " + newMessage, Toast.LENGTH_LONG).show();
// Releasing wake lock
WakeLocker.release();
}
};
For more code description take a look at this tutorial.
I have a written a program which intercepts incoming call using BroadcastReceiver. When I start my app, it starts working. Now the problem is when I restart my android phone, this BroadcastReceiver doesn't work. So I am assuming that I need to make a service for this. But I don't know when to start service and where to start BroadcastReceiver.
BroadcastReceiver code -
public class CallInterceptor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Service started", Toast.LENGTH_LONG).show();
Bundle extras = intent.getExtras();
if (extras != null) {
String state = extras.getString(TelephonyManager.EXTRA_STATE);
Toast.makeText(context, "Phone is " + state, Toast.LENGTH_LONG).show();
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String phoneNumber = extras
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Toast.makeText(context, "Call from " + phoneNumber, Toast.LENGTH_LONG).show();
//sendSMS(phoneNumber);
//toggle ringer mode
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
toggleMode(am);
}
}
}
AndroidManifest file -
<receiver android:name="com.nagarro.service.CallInterceptor" >
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" >
<action android:name="android.intent.action.BOOT_COMPLETED" />
</action>
</intent-filter>
</receiver>
I don't think <action android:name="android.intent.action.BOOT_COMPLETED" /> is working.
Also, is it possible to do this without service? Please suggest approach which will help me to intercept call evertime (even after reboot).
You've improperly closed your first action tag. Your receiver section should look like:
<receiver android:name="com.nagarro.service.CallInterceptor" >
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Also, you are not using a Service. You're using a Broadcast Receiver.
See this page for the basics Application Fundamentals.