How to send Order broadcast receiver using pending intent android - android

Hi i have function like when user hit on notification i have to check my application is in foreground if it so, just close the notification. Otherwise need to open up the application.
I have use concept of ordered broadcast to achieve but i am stuck to call ordered broadcast receiver from pending intent.

To send an ordered broadcast using a PendingIntent, use one of the send() methods, for example this one, that takes a PendingIntent.OnFinished argument. This capability is not explicitly documented and only the description of the parametersto PendingIntent.OnFinished gives some hint that ordered broadcasts are supported.
Here is example for sending an ordered broadcast:
Intent i = new Intent("com.my.package.TEST_ACTION");
PendingIntent.OnFinished listener = new PendingIntent.OnFinished() {
#Override
public void onSendFinished(PendingIntent pendingIntent, Intent intent,
int resultCode, String resultData, Bundle resultExtras) {
Log.i("TEST", String.format("onSendFinished(): result=%d action=%s",
resultCode, intent.getAction()));
}
};
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
int initResult = -1;
try {
pi.send(initResult, listener, null);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
I confirmed that this produces an ordered broadcast by defining a number of receivers with this general form, registered in the manifest with different priorities:
public class ReceiverA extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("AAAA", String.format("result=%d ordered=%b", getResultCode(), isOrderedBroadcast()));
setResultCode(1111);
}
}
The logcat output confirmed that the receivers were invoked in the expected order, that isOrderedBroadcast() is true for each, and the result code set by setResultCode() is passed to the next receiver, and finally to the PendingIntent.OnFinished callback.

Related

Issue Moving from IntentService to JobIntentService for Android O

I am using Intent Service to monitor Geofence transition. For that I am using following call from a Sticky Service.
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent()
)
and the Pending Intent calls Transition service (an IntentService) like below.
private PendingIntent getGeofencePendingIntent() {
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the
//same pending intent back when calling addgeoFences()
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
This worked fine Pre Oreo. However, I had to convert my sticky service to a JobScheduler and I need to convert GeofenceTransitionsIntentService which is an intentService to JobIntentService.
Having said that I am not sure how to return create a PendingIntent for JobIntentService, because I need to call enqueueWork for JobIntentService.
Any suggestions/pointer would be appreciated.
Problem
I had the same issue when migrating from IntentService to JobIntentService on Android Oreo+ devices.
All the guides and snippets I've found are incomplete, they leave out the breaking change this migration has on the use of PendingIntent.getServce.
In particular, this migration breaks any Alarms scheduled to start a service with the AlarmManager and any Actions added to a Notification that start a service.
Solution
Replace PendingIntent.getService with PendingIntent.getBroadcast that starts a BroastcastReceiver.
This receiver then starts the JobIntentService using enqueueWork.
This can be repetitive and error prone when migrating multiple services.
To make this easier and service agnostic, I created a generic StartJobIntentServiceReceiver that takes a job ID and an Intent meant for a JobIntentService.
When the receiver is started, it will start the originally intended JobIntentService with a job ID and actually forwards the Intent's original contents through to the service behind the scenes.
/**
* A receiver that acts as a pass-through for enqueueing work to a {#link android.support.v4.app.JobIntentService}.
*/
public class StartJobIntentServiceReceiver extends BroadcastReceiver {
public static final String EXTRA_SERVICE_CLASS = "com.sg57.tesladashboard.extra_service_class";
public static final String EXTRA_JOB_ID = "com.sg57.tesladashboard.extra_job_id";
/**
* #param intent an Intent meant for a {#link android.support.v4.app.JobIntentService}
* #return a new Intent intended for use by this receiver based off the passed intent
*/
public static Intent getIntent(Context context, Intent intent, int job_id) {
ComponentName component = intent.getComponent();
if (component == null)
throw new RuntimeException("Missing intent component");
Intent new_intent = new Intent(intent)
.putExtra(EXTRA_SERVICE_CLASS, component.getClassName())
.putExtra(EXTRA_JOB_ID, job_id);
new_intent.setClass(context, StartJobIntentServiceReceiver.class);
return new_intent;
}
#Override
public void onReceive(Context context, Intent intent) {
try {
if (intent.getExtras() == null)
throw new Exception("No extras found");
// change intent's class to its intended service's class
String service_class_name = intent.getStringExtra(EXTRA_SERVICE_CLASS);
if (service_class_name == null)
throw new Exception("No service class found in extras");
Class service_class = Class.forName(service_class_name);
if (!JobIntentService.class.isAssignableFrom(service_class))
throw new Exception("Service class found is not a JobIntentService: " + service_class.getName());
intent.setClass(context, service_class);
// get job id
if (!intent.getExtras().containsKey(EXTRA_JOB_ID))
throw new Exception("No job ID found in extras");
int job_id = intent.getIntExtra(EXTRA_JOB_ID, 0);
// start the service
JobIntentService.enqueueWork(context, service_class, job_id, intent);
} catch (Exception e) {
System.err.println("Error starting service from receiver: " + e.getMessage());
}
}
}
You will need to replace package names with your own, and register this BroadcastReceiver per usual in your AndroidManifest.xml:
<receiver android:name=".path.to.receiver.here.StartJobIntentServiceReceiver"/>
You are now safe to use Context.sendBroadcast or PendingIntent.getBroadcast anywhere, simply wrap the Intent you want delivered to your JobIntentService in the receiver's static method, StartJobIntentServiceReceiver.getIntent.
Examples
You can start the receiver, and by extension your JobIntentService, immediately by doing this:
Context.sendBroadcast(StartJobIntentServiceReceiver.getIntent(context, intent, job_id));
Anywhere you aren't starting the service immediately you must use a PendingIntent, such as when scheduling Alarms with AlarmManager or adding Actions to Notifications:
PendingIntent.getBroadcast(context.getApplicationContext(),
request_code,
StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
PendingIntent.FLAG_UPDATE_CURRENT);
As #andrei_zaitcev suggested, I implemented my custom BroadCastReceiver and call enqueueWork() of the Service, which works perfectly.

Broadcasting custom intent from one service to another

I have two services. I need to broadcast custom intent with data from one service. and in second service I need to receive it. I have tried following:
In first service:
public String ACTION_CHANGE_TIME_FORMAT = "com.example.agile.mywatchface.changetimeformat";
Intent timeFormatIntent = new Intent();
timeFormatIntent.putExtra("format", 12);
timeFormatIntent.setAction(ACTION_CHANGE_TIME_FORMAT);
LocalBroadcastManager.getInstance(this).sendBroadcast(timeFormatIntent);
In Second Service:
public String ACTION_CHANGE_TIME_FORMAT = "com.example.agile.mywatchface.changetimeformat";
public class TimeFormatReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Log.d(MyPreferences.LOGCAT_TAG, "Time format Broadcast received: ");
format = intent.getExtras().getInt("format", 0);
updateTime();
}
}
and I have registered and unregistered receivers properly in second service:
IntentFilter timeFormatIntentFilter = new IntentFilter();
timeFormatIntentFilter.addAction(ACTION_CHANGE_TIME_FORMAT);
MyWatchfaceService.this.registerReceiver(timeFormatReceiver, timeFormatIntentFilter);
Is there anything wrong here? I can't get data(format).
Edit: onRecieve() is not calling.
In Second Service:
Change format = intent.getExtras().getInt("format", 0); to format = intent.getIntExtra("format", 0);
The intent.getExtras() will return the Bundle which put by Intent.putExtras(Bundle bundle), but you didn't do it.
1_ Check that your onReceive() method is called. If not, your Receiver still does not registered.
2_ if (1) is OK, try int format = intent.getIntExtra("format", 0);
P/S: I dont know where do you use the "format" variable, consider use it as local variable and pass it like a parameter.
int format = intent.getIntExtra("format", 0);
updateTime(format)

how to use delete intent to perform some action on clear notification?

I want to reset a variable of my service when user clears my notification: that's all!
Looking around I see that everyone suggest to add a delete intent on my notification, but intent is used to start an activity, a service o whatever while I just need a thing like this:
void onClearPressed(){
aVariable = 0;
}
how to obtain this result?
Notifications are not managed by your app and all things like showing notifications and clearing them are actually happening in another process. You can't make another app directly execute a piece of code just because of security reasons.
The only possibility in your case is to provide a PendingIntent which just wraps around a regular Intent and will be started on behalf of your app when notification is cleared.
You need to use PendingIntent for sending broadcast or starting a service and then doing what you want in the broadcast receiver or in the service. What exactly to use depends on from which application component you are showing notifications.
In case of broadcast receiver you can just create an anonymous inner class for broadcast receiver and register it dynamically before showing notification. It will look something like that:
public class NotificationHelper {
private static final String NOTIFICATION_DELETED_ACTION = "NOTIFICATION_DELETED";
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
aVariable = 0; // Do what you want here
unregisterReceiver(this);
}
};
public void showNotification(Context ctx, String text) {
Intent intent = new Intent(NOTIFICATION_DELETED_ACTION);
PendingIntent pendintIntent = PendingIntent.getBroadcast(ctx, 0, intent, 0);
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext).
setContentText(text).
setDeleteIntent(pendintIntent).
build();
NotificationManager.notify(0, n);
}
}
Andrei is correct.
If you want multiple messages back such as:
you want to know if the message was clicked
you attached an action with an icon that you want to catch
AND you want to know if the message was canceled
you must register each of those response filters:
public void showNotification(Context ctx, String text) ()
{
/… create intents and pending intents same format as Andrie did../
/… you could also set up the style of your message box etc. …/
//need to register each response filter
registerReceiver(receiver, new IntentFilter(CLICK_ACTION));
registerReceiver(receiver, new IntentFilter(USER_RESPONSE_ACTION));
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext)
.setContentText(text)
.setContentIntent(pendingIntent) //Click action
.setDeleteIntent(pendingCancelIntent) //Cancel/Deleted action
.addAction(R.drawable.icon, "Title", pendingActionIntent) //Response action
.build();
NotificationManager.notify(0, n);
}
Then you can catch the different responses with if, else statements (as Andrei did), or with a switch statement.
Note: I make this response primarily because I could not find this anywhere, and I had to figure it out on my own. (perhaps I will remember it better for that :-) Have Fun!

Telling if the user or the OS initiated Android C2DM registration

In my Activity when the user says they want to enable notifications I call the following method:
private Intent buildRegistrationIntent(boolean register) {
String intentName = "com.google.android.c2dm.intent." + (register ? "REGISTER" : "UNREGISTER");
Intent intent = new Intent(intentName);
intent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // boilerplate
intent.putExtra("sender", "xxxxx#gmail.com");
intent.putExtra("TEST", "test extras");
return intent;
}
In my C2DM BroadcastReceiver I have this:
#Override
public void onReceive(Context context, Intent intent) {
log.fine(Boolean.toString(intent.getExtras().containsKey("TEST")));
log.fine(Boolean.toString(intent.getExtras().containsKey("registration_id")));
if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
handleRegistration(context, intent);
} else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
handleMessage(context, intent);
}
}
The problem is, the "TEST" extra never gets sent to the C2DM BroadcastReceiver. The output from the below lines is "False" then "True" i.e. it has the registration_id, but not TEST.
Is there any way to pass some user defined data to the BroadcastReceiver or does the C2DM infrastructure eat the original intent and create a new one with just the registration_id?
Why you send TEST via Intent?
You need send push notification messages not via intents.
Please look at onHandleIntent(Intent intent) method in C2DMBaseReceiver.java class. You can override method onMessage() in your C2DMReceiver.java class extended C2DMBaseReceiver.java
It looks like this is impossible. Whatever receives the REGISTER intent doesn't pass it on to onReceive.

Android-Broadcast Receiver

I am new to android. I what to know the difference between Intent and BroadcastReceiver. I am more confused with BroadcastReceiver than Intent.
Please help me out. Simple code will be helpful.
Ok, I will explain it with an example.
Let's suppose I want to create an app to check subway status from it's webpage. I also want a system notification if the subway is not working ok.
I will have:
An Activity to show results.
A Service to check if the subway is working and show a notification if it's not working.
A Broadcast Receiver called Alarm Receiver to call the service every 15 minutes.
Let me show you some code:
/* AlarmReceiver.java */
public class AlarmReceiver extends BroadcastReceiver {
public static final String ACTION_REFRESH_SUBWAY_ALARM =
"com.x.ACTION_REFRESH_SUBWAY_ALARM";
#Override
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, StatusService.class);
context.startService(startIntent);
}
}
Explanation:
As you can see you can set an alarm. and when the alarm is received we use an intent to start the service. Basically the intent is a msg which can have actions, an serialized stuff.
public class StatusService extends Service {
#Override
public void onCreate() {
super.onCreate();
mAlarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intentToFire = new Intent(AlarmReceiver.ACTION_REFRESH_ALARM);
mAlarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
}
#Override
public void onStart(Intent intent, int arg1) {
super.onStart(intent, arg1);
Log.d(TAG, "SERVICE STARTED");
setAlarm();
Log.d(TAG, "Performing update!");
new SubwayAsyncTask().execute();
stopSelf();
}
private void setAlarm() {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
mAlarms.setInexactRepeating(alarmType, SystemClock.elapsedRealtime() + timeToRefresh(),
AlarmManager.INTERVAL_HALF_DAY, mAlarmIntent);
}
}
Explanation:
The service starts and:
Set the alarm for the next call. (Check the intent it's used. Just a msg)
Calls an AsyncTask which takes care of updating an notifying the Activity
It doesn't make sense to paste the AsyncTask but when it finished it calls:
private void sendSubwayUpdates(LinkedList<Subway> subways) {
Intent intent = new Intent(NEW_SUBWAYS_STATUS);
intent.putExtra("subways", subways);
sendBroadcast(intent);
}
This creates a new Intent with a certain NEW_SUBWAYS_STATUS action, put inside the intent the subways and sendBroadcast. If someone is interested in getting that info, it will have a receiver.
I hope I made myself clear.
PS: Some days ago someone explained broadcast and intents in a very cool way.
Someone wants to share his beer, so he sends a broadcast
with an intent having action:"FREE_BEER" and with an extra: "A glass of beer".
The API states:
A BroadcastReceiver is a base class for code that will receive intents sent by sendBroadcast().
An intent is an abstract description of an operation to be performed.
So, a BroadcastReceiver is just an Activity that responds to Intents. You can send your own broadcasts or even the Android Device can send these system wide broadcasts including things like the battery is low, or the device just booted-up.

Categories

Resources