android cancel multiple alarms with custom uri - java

I created some alarms in my service which service sometimes restarts. I would like to cancel these alarms when my service restarts.
I set multiple alarms with multiple action type and with custom URIs like this (I need custom URIs for multiple alarms for the same action):
for(..) {
String id = "id:"+i;
intent = new Intent(AlarmReceiver.MY_ALARM, Uri.parse(id), context, AlarmReceiver.class);
}
how could I cancel all of the alarms?
AlarmManager.cancel() need to know all Action and URI pair for filtering (maybe I'm wrong)
I tried using PendingIntent's requestCode for create multiple alarm for the same Action but after the first alarm fired AlarmReceiver didn't give me back the Intent's extras.
I think I have to store persistent details about what alarms I set before. :S

Yes, you need to persist the alarms that you are adding. And then cancel them one-by-one.

Related

How to create more than one foreground notification?

Below is a snippet of code in my service class.
If a user "joins a team" (operation = 0) then it create a notification with its designated specifications however if a user shares their location (operation = 1) then its supposed to create a separate foreground notification. Instead one just replaces the other.
I don't know why, they have different ID's just same channel. I've also tried separating their channel ID, still the same issue
int id = NOTIFICATION_LOCATION;
int icon = R.drawable.ic_gps_on;
String message = "Tap to disable location updates";
if (operation == 0) {
id = NOTIFICATION_RESPONDER;
icon = R.drawable.ic_responder_icon;
message = "Tap to leave the responding team";
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID_1)
.setSmallIcon(icon)
.setContentTitle("Location established")
.setContentText(message)
.setContentIntent(PendingIntent.getBroadcast(getApplicationContext(), 0, getBroadcastIntent(operation), PendingIntent.FLAG_UPDATE_CURRENT))
.setColor(ContextCompat.getColor(getApplicationContext(), R.color.primaryColor))
.setDefaults(Notification.DEFAULT_SOUND)
.setVisibility(VISIBILITY_PUBLIC)
.build();
startForeground(id, notification);
The notification you manipulate with startForeground() is meant to be the one "official" notification that corresponds to the foreground service; the one that Android insists you have up at all times the service is running.
It doesn't surprise me that, if you supply a different notification channel ID on a subsequent call to startForeground(), it erases and replaces the original notification. Otherwise, you might end up with multiple foreground notifications for a single service, and things could get confusing.
Instead, just use NotificationManager.notify() to manage any notifications that occur in excess of the original foreground service notification. Use distinct IDs for these extra notifications.
A good practice is to use a fixed ID for your foreground service notification. You can still change the Notification at will; it's just easier to remember which Notification is your "official" one, when you have a fixed ID.
You can also manipulate your "official foreground service notification" using notify(); you don't have to use startForeground(). A call to startForeground() is only needed once, at the beginning, to associate the service with a specific notification ID.

How do I process broadcasts that were sent while my activity was stopped?

My activity starts a service which runs a CountDownTimer. The timer sends broadcasts back to the activity as it counts down. The activity processes the broadcasts in the onReceive method of a BroadcastReceiver. All of this works fine.
My problem comes when the following events happen in this order:
App is stopped (via onPause())
Timer finishes
App is resumed (via onResume())
When the app is resumed the service is no longer sending broadcasts, so the activity does not know how much time is left on the timer or if it's finished. This prevents the activity from updating the UI.
I've tried a dozen ways of dealing with this, and read through many Stack Overflow questions and answers, but I've yet to find a solution. I would think that there's a way to pick up a broadcast that was sent while the activity was not active, but I've yet to find a way.
For the record, here is my relevant Activity and Service code:
activity.java
// Start service
timerIntent.putExtra("totalLength", totalLength);
this.startService(timerIntent);
// ...
// BroadcastReceiver
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null && inSession) {
session.setRemaining(intent.getExtras().getLong("millisUntilFinished"));
updateProgress();
}
}
};
// ...
// onResume
#Override
public void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter(TimerService.COUNTDOWN_TS));
}
service.java
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
long length = intent.getExtras().getLong("totalLength");
countDownTimer = new CountDownTimer(length, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timerServiceIntent.putExtra("millisUntilFinished", millisUntilFinished);
sendBroadcast(timerServiceIntent);
}
#Override
public void onFinish() {
}
};
countDownTimer.start();
return super.onStartCommand(intent, flags, startId);
}
What's the best way to process the broadcasts that the service sent while the activity was stopped?
Use the BroadcastReceiver to store the last request (SharedPreferences perhaps) it received and check it when the Activity starts.
Alternatively, instead of processing a countdown using broadcasts, just store the time that the countdown would end. The Activity can then handle the countdown all by itself as it knows when it should end. Using a service and broadcasts seem to be a little over-engineered for such a simple task.
Update:
From the way you have described your task, I see you needing to handle 2 scenarios. This is how I would likely do it.
Assuming that "XYZ" is the service\intent\whatever starting the countdown and "ABC" is the Activity displaying the progress. (ABC and XYZ could be the same activity if that is what you wanted)
Requirements:
When the countdown starts, I would make XYZ store the time that the countdown should end in SharedPreferences.
ABC is already running when the countdown starts. As Commonsware said, the Eventbus model is excellent for handling this scenario so long as XYZ and ABC are running in the same process. Just fire an event to read the preference value and count down to the specified time. If the user closes ABC and reopens it, Scenario 2 will kick in.
ABC is not running. Check in OnResume whether the countdown time has elapsed. If not, set up ABC to display the countdown again. If there is no countdown active, do something else.
If you also need to do something when the countdown has elapsed regardless of whether you have a UI active, then again Commonsware's suggestion of AlarmManager is perfect.
Let's pretend for a moment that using a Service with a CountDownTimer to track some passage of time for the purposes of updating an Activity actually is a good idea. It's not out of the question, assuming that the Service is actually doing something for real and this timing thing is some by-product.
An activity does not receive broadcasts while stopped, mostly for performance/battery reasons. Instead, the activity needs to pull in the current status when it starts, then use events (e.g., your current broadcasts) to be informed of changes in the data while it is started.
This would be simplified by using something like greenrobot's EventBus and their sticky events, as the activity would automatically get the last event when it subscribes to get events. Using greenrobot's EventBus for this purpose would also reduce the security and performance issues that you are introducing by your use of system broadcasts to talk between two Java classes in the same process.
Also, please stick with lifecycle pairs. onResume() is not the counterpart to onStop(). onStart() is the counterpart to onStop(); onResume() is the counterpart to onPause(). Initializing something in one pair (e.g., onResume()) and cleaning it up in the other pair (e.g., onStop()) runs the risk of double-initialization or double-cleanup errors.
What's the best way to process the broadcasts that the service sent
while the activity was stopped?
Using sticky broadcast intents from the service and then retrieving them from the activity would be a way to process the broadcasts that the service sent while the activity was stopped. I can only offer that as a possible solution rather than claiming it is the "best way".
http://developer.android.com/reference/android/content/Context.html#sendStickyBroadcast(android.content.Intent)
They have however, been deprecated since API level 21 due to security concerns.
Instead of using Normal broadcast you can use Ordered broadcast (sent with Context.sendOrderedBroadcast). For this along with defining a BroadcastReceiver in your activity you required to define BroadcastReceiver in your manifest with same intentfilter. Only change is while registering BroadcastReceiver in your activity you need to set priority to high, so that when your activity is running and activity's BroadcastReceiver is registered it gets called first, and inside onReceive of this BroadcastReceiver you can use abortBroadcast for getting the BroadcastReceiver called which is defined in your android manifest. Now when your activity is not running the BroadcastReceiver defined in your android manifest will get called. So this way you can have the status and if you wish you can display updates to user by notification even if your activity is not running.

How to cancel alarms after app was closed

I've programmed an app with multiple alarms using the AlarmManager. I also have a method which cancels all current/pending alarms. It works well, however if the user closes the app from recents (alarms are still active, as intended), my cancel-alarms-method doesn't work anymore (the app crashes). Is there any solution to this? How can I cancel my alarms after the user has closed the app?
This is what my alarms look like:
ArrayList<PendingIntent> intentArray = new ArrayList<PendingIntent>();
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
...
Intent intent = new Intent(this, AlertReceiver.class);
Long cal = new GregorianCalendar().getTimeInMillis() + sms_in_x_minutes * 60 * 1000;
PendingIntent i1 = PendingIntent.getBroadcast(this, intent_id++, intent, 0);
am.set(AlarmManager.RTC_WAKEUP, cal, i1);
intentArray.add(i1);
This is what my cancel-method looks like:
private void cancelAlarms(){
if(intentArray.size()>0){
for(int i=0; i<intentArray.size(); i++){
am.cancel(intentArray.get(i));
}
intentArray.clear();
}
}
My guess is that intentArray and am are empty after I close the app, and the PendingIntents are not saved. But I don't know how to work around that.
Do not keep an array of Pending intents. As you correctly diagnosed, your array is empty after the app is closed which causes a crash when you are trying to access them again.
Set up ONLY the earliest alarm. Save the ID of the alarm to temp storage (e.g SharedPreferences). (If it is easier, you can also use a constant value for the ID as you now only have one alarm to think about)
When your alarm fires, you can set up the next earliest alarm as the first task that is performed.
If you want to cancel your alarm and have an ID value saved in your SharedPreferences, use this to recreate the AlarmManager and cancel the alarm. If there is no ID value then no alarms are set and no cancellation is required.
I think you should add the closing alarm method to the ondestroy method of your app, because if an user closes your app without stopping them, your method will never be called causing the crash of the app.
hope it works :)
EDIT: try to have a look at service and similar. some months ago i did use them in an android app and i remember that even if i close the app from recents, this service still worked and for closing it i had to add a method in app or to stop the process.
If i got it this should be the method you were looking for, sorry if i'm not precise but months passed and i forgot the name of the service i used, i'm just sure it was a service :)

Android GCM: different way of handling push depending on whether the app is visible or not

I've got a couple of activities and an intent service which handles GCM incoming messages.
Right now for every push, I'm sending a Notification, and after the user clicks it, he is redirected to appropriate screen.
I would like to alter this behavior that if the app is visible (any activity is in the foreground), instead of the notification a dialog message is shown (with appropriate action).
Any idea how to implement it?
I have 2 ideas but none of them is perfect:
Keep track of every activity in the application, if the activity is visible, don't show notification, but sent an intent to the activity (not nice solution)
register/unregister the second broadcast receiver in each activity's onResume/onPause, "catch" the incoming GCM broadcast (I'm not sure if it is possible).
Any other solutions?
A possible solution (idea 1):
To detect whether your app is running back- or foreground, you can simply set a boolean in onPause/onResume:
#Override
protected void onResume() {
super.onResume();
runningOnBackground = false;
}
#Override
protected void onPause() {
super.onPause();
runningOnBackground = true;
}
When you start a new intent from an notification this method gets called: (if you are using singleTop), with the boolean you can determine what to do in the onNewIntent method.
#Override
protected void onNewIntent (Intent intent){
if(runningOnBackground){
//do this
}
else{
//do that
}
}
Hope it helps!
I didn't test it, but the docs say you can get the number of running activities per each task.
Try to find your application's task among currently running tasks:
ActivityManager acitivityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
// Get the top of running tasks, limit by 100
List<RunningTaskInfo> tasks = acitivityManager.getRunningTasks(100);
for (RunningTaskInfo taskInfo : tasks) {
if (YOUR_PACKAGE_NAME.equals(taskInfo.baseActivity.getPackageName())) {
if (taskInfo.numRunning > 0) {
// Show dialog
} else {
// Show notification
}
break;
}
}
Google added a note on getRunningTasks():
Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.
So use it at your own risk.
Also check if GCM broadcasts are ordered. If so, you can "override" your default BroadcastReceiver with the other ones in each Activity. Just play with the priority of IntentFilters. When the BroadcastReceiver with higher priority receives the message, it can abort it's further propagation. For your application this means that when some Activity is running, it registers the receiver which shows the dialog and aborts broadcast. If no activity is active, then your default receiver shows the notification.

How to set more than one alarm for calendar events in android?

I have already worked on calendar events and for each event the alarm should ring with notification. The problem i face is, If i set more than one event, the last set event is alone notifying the user with alarm. But all the other events doesn't ring alarm. Any Help is appreciated and thanks in advance...
If you create the alarm with the Single Intent instant you need to pass the different Request code into the getService() method.
Or if you elsewhere you can create multiple instant of the intent and with different request code you can set it
here snippet of code updated
AlarmManager alarms = (AlarmManager)context.getSystemService(
Context.ALARM_SERVICE);
Intent i = new Intent(MyReceiver.ACTION_ALARM); // "com.example.ALARM"
i.putExtra(MyReceiver.EXTRA_ID, id); // "com.example.ID", 2
PendingIntent p = PendingIntent.getBroadcast(context, requestcode, i, 0);
here the second parameter of getService was request code you need to set the different request for multiple alarm.

Categories