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
Related
I searched the web for alot of time and I don't understand why my custom broadcast
isn't working.
<receiver
android:name=".myservice.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.intent.action.BATTERY_CHANGED"/>
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
</intent-filter>
</receiver>
I don't it not recieve when I reconnet and disconnect the charger.
I did this for making thing simpale
public class MyReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context,"Battery", Toast.LENGTH_SHORT).show();
Log.i("Recive", "Yes");
}
}
From docs:
ACTION_BATTERY_CHANGED
Broadcast Action: This is a sticky broadcast containing the charging state, level, and other information about the battery. See BatteryManager for documentation on the contents of the Intent.
You cannot receive this through components declared in manifests, only by explicitly registering for it with Context.registerReceiver(). See ACTION_BATTERY_LOW, ACTION_BATTERY_OKAY, ACTION_POWER_CONNECTED, and ACTION_POWER_DISCONNECTED for distinct battery-related broadcasts that are sent and can be received through manifest receivers
So, you cannot use this BroadcastReceiver decalred in Manifest, only registering explicitly from your context.
Also, your power connection BroadcastReceiver seems correct. Try to separate it into another BroadcastReceiver, maybe action ACTION_BATTERY_CHANGED is interfering with other actions.
This is my declared BroadcastReceiver which I use and it's working in my app.
<receiver android:name=".PowerConnectionBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
PowerConnectionBroadcastReceiver
public class PowerConnectionBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "PowerRcvr";
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_POWER_CONNECTED)) {
Log.d(TAG, "Device is charging");
} else if (action.equals(Intent.ACTION_POWER_DISCONNECTED)) {
Log.d(TAG, "Device is NOT charging");
} else {
Log.d(TAG, "Unable to check if device is charging or not");
}
}
}
NOTE: This code is working on Android 8 with targetSdkVersion 25 or lower.
In targetSdkVersion 26 or higher most of BroadcastReceivers doesn't work through Manifest due to background limitations. Here are documentation (thanks to Pawel) about that. So your IntentFilters wont work. To keep it working you can download your targetSdkVersion to 25 or lower.
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!
So I have a boot receiver that is supposed to call an intent service but the receiver isn't registering at all.
Manifest file -
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name=".ClockReciever">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
ClockReceiver.java
public class ClockReceiver extends BroadcastReceiver {
private final String TAG = "ClockReciever";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"onRecive");
context.startService(new Intent(context, RefreshIntentService.class));
}
}
I think this is correct, but according to my logcat the ClockReciever is never called and the program crashes with a "Unable to instantiate receiver" error.
Any suggestions? Thank you
Here you have a typo
<receiver android:name=".ClockReciever">
Should be ClockReceiver, i.e. same as your class.
Cheers!
I have problems to register a BroadcastReceiver.
It works when I register it with registerReceiver :
registerReceiver(new NotifReceiver(), new IntentFilter(NotifReceiver.POST_NOTIF));
But not with AndroidManifest.xml :
<receiver
android:name=".receiver.NotifReceiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name=".receiver.NotifReceiver.POST_NOTIF" />
</intent-filter>
</receiver>
NotifReceiver.java :
public class NotifReceiver extends BroadcastReceiver {
public static final String POST_NOTIF = "POST_NOTIF";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(POST_NOTIF)) {
Log.d("debug", "action received");
}
}
}
According to Google Guidelines about the action tag.
It's best to use the package name as a prefix to ensure uniqueness.
Then your should declare your action as:
public static final String POST_NOTIF = "com.my.package.POST_NOTIF";
And reference in your Manifest as:
<intent-filter>
<action android:name="com.my.package.POST_NOTIF" />
</intent-filter>
Note that android:name just need to match the value of the POST_NOTIF static string.
But in order to avoid conflict with other app, you should put an unique value for the string you use as an action. That's why Google recommend to prefix it with your package.
Try changing below two lines in your Manifest.xml:
1. android:name=".receiver.NotifReceiver"
2. <action android:name=".receiver.NotifReceiver.POST_NOTIF"/>
to
1. android:name="*full.pkg.name*.receiver.NotifReceiver"
2. <action android:name="*full.pkg.name*.receiver.NotifReceiver.POST_NOTIF"/>
replace <action android:name=".receiver.NotifReceiver.POST_NOTIF" />
with this <action android:name="POST_NOTIF" />
I have created a BroadcastReceiver to detect SDCard mount and unmount
event, however, I am not able to receive any events at all:
here's the AndroidManifest.xml:
<receiver android:enabled="true" android:label="SDCardMountReceiver" android:exported="true" android:name="xxx.broadcasts.SDCardBroadcastReceiver">
<intent-filter>
<action android:name="android.content.Intent.ACTION_MEDIA_MOUNTED"></action>
<!-- or <action android:name="android.content.Intent.ACTION_MEDIA_UNMOUNTED" />--></intent-filter>
</receiver>
And the SDCardMountReceiver class:
public class SDCardBroadcastReceiver extends BroadcastReceiver {
public SDCardBroadcastReceiver() {
super();
System.err.println("constructor");
}
public void onReceive(Context context, Intent intent) {
Log.d("SDCardBroadCastReceiver", "receive " + intent.getAction());
System.err.println("jonathan receive " + intent.getAction());
}
}
You also need to set the data scheme to "file".
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<data android:scheme="file" />
</intent-filter>
Reference: android-developers thread
The Intent javadoc specifies a different action:name value.
Use "android.intent.action.MEDIA_MOUNTED" instead of "android.content.Intent.ACTION_MEDIA_MOUNTED"
If you register a broadcast receiver programmatically, you must also set the scheme to "file".
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addDataScheme("file");
mContext.registerReceiver(mExternalStorageReceiver, filter);