setting multiple notifications at different times- android - android

I need to create multiple notifications at multiple times. The time when the notification is supposed to appear is fetched into the event_id,etc.time for notification is set in another class.
What happens with the code below is, for a notification set at, say, 10:00, all the notifications that are set after 10:00 also appear at the same time. Please help. Only the correct notification needs to appear. Not the future ones.
for(int j=0;j<event_id.size();j++)
{
if(Integer.parseInt(event_id.get(j).toString())>newEventID&&Long.parseLong(event_time.get(j).toString())>System.currentTimeMillis())
{
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);//alarm manager
NotificationManager notificationManager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification note=new Notification(R.drawable.friendi_main_logo, event_desc.get(j).toString(), System.currentTimeMillis());
Intent mainScreenIntent=new Intent(getApplicationContext(),MainScreenActivity.class);
mainScreenIntent.putExtra("UserID", user_id);
int uniqueCode=0;
uniqueCode= Integer.parseInt(event_id.get(j).toString());//unique code for each pending intent
//separate pending intent for each alarm.. one alarm manager can invoke only one PI
PendingIntent ListOfNotification=PendingIntent.getActivity(getApplicationContext(), uniqueCode,mainScreenIntent,0);
note.flags=Notification.FLAG_AUTO_CANCEL;
alarmManager.set(AlarmManager.RTC_WAKEUP, Long.valueOf(event_time.get(j).toString()), ListOfNotification);//invokes pending intent # the event_time
note.setLatestEventInfo(getApplicationContext(), "Event: "+event_title.get(j).toString(), event_group.get(j).toString()+": "+event_desc.get(j).toString(),ListOfNotification );
// Uri path=Uri.parse("android.resource://" + getPackageName() + "/alarm_sms.mp3");
// note.sound=path;
note.defaults=Notification.DEFAULT_ALL;
notificationManager.notify(EVENT_NOTIFY_ID, note);
EVENT_NOTIFY_ID++;
flag=true;
}
}

So.. what you do is..
if(Integer.parseInt(event_id.get(j).toString())>newEventID&&Long.parseLong(event_time.get(j).toString())>System.currentTimeMillis())
{
after this line..
see that the if loop is not entered again...
for example like this..
put
j=event_id.size();
this makes only one notification appear..

try using a boolean to check only the first one fires. make it true whenever you are ready to fire the 2nd one.
you are in a for loop, and you are calling notificationManager.notify(EVENT_NOTIFY_ID, note); every time, so all notifications would be fired.
boolean fire_only_one=true;
for(int j=0;j<event_id.size();j++)
{
if(Integer.parseInt(event_id.get(j).toString())>newEventID&&Long.parseLong(event_time.get(j).toString())>System.currentTimeMillis())
{
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);//alarm manager
NotificationManager notificationManager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification note=new Notification(R.drawable.friendi_main_logo, event_desc.get(j).toString(), System.currentTimeMillis());
Intent mainScreenIntent=new Intent(getApplicationContext(),MainScreenActivity.class);
mainScreenIntent.putExtra("UserID", user_id);
int uniqueCode=0;
uniqueCode= Integer.parseInt(event_id.get(j).toString());//unique code for each pending intent
//separate pending intent for each alarm.. one alarm manager can invoke only one PI
PendingIntent ListOfNotification=PendingIntent.getActivity(getApplicationContext(), uniqueCode,mainScreenIntent,0);
note.flags=Notification.FLAG_AUTO_CANCEL;
alarmManager.set(AlarmManager.RTC_WAKEUP, Long.valueOf(event_time.get(j).toString()), ListOfNotification);//invokes pending intent # the event_time
note.setLatestEventInfo(getApplicationContext(), "Event: "+event_title.get(j).toString(), event_group.get(j).toString()+": "+event_desc.get(j).toString(),ListOfNotification );
// Uri path=Uri.parse("android.resource://" + getPackageName() + "/alarm_sms.mp3");
// note.sound=path;
note.defaults=Notification.DEFAULT_ALL;
if(fire_only_one){
notificationManager.notify(EVENT_NOTIFY_ID, note);
fire_only_one=false;
}
EVENT_NOTIFY_ID++;
flag=true;
}
}
fire_only_one=true;
Also You can change the logic by setting only the most relevant notification here, and when the user opens an Activity by clicking the first notification, set the second notification using ALARM manager.

Related

Android - Trouble with service sending multiple local notifications

I've inherited a code base for an Android app and I'm facing a particularly though problem with local notifications.
The idea is to send a notification for each event which is scheduled in the future, considering also the reminder preference on how many minutes before the event the user wants to be notified.
Everything works just fine, except that after the notification is thrown for the first time, if the user opens the app before the event starts, the notification gets thrown another time. This happens every time the app is opened between (event start date - reminder) and event start date.
I've already gave a look at this and also this with no luck.
I've read that using a service may cause exactly this problem and some suggest to remove it but I think this is needed since the notification must be thrown also when the app is closed.
Currently the structure of the code is the following:
Edit - updated description of TabBarActivity
Inside TabBarActivity I have the method scheduleTravelNotification that schedules the AlarmManager.
This method is executed everytime there is a new event to be added on local database, or if an existing event have been updated.
The TabBarActivity runs this method inside the onCreate and onResume methods.
TabBarActivity is also the target of the notification - onclick event.
private static void scheduleTravelNotification(Context context, RouteItem routeItem) {
long currentTime = System.currentTimeMillis();
int alarmTimeBefore = routeItem.getAlarmTimeBefore();
long alarmTime = routeItem.getStartTime() - (alarmTimeBefore * 1000 * 60);
if(alarmTimeBefore < 0){
return;
}
if(alarmTime < currentTime){
return;
}
Intent actionOnClickIntent = new Intent(context, TravelNotificationReceiver.class);
PendingIntent travelServiceIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis(), actionOnClickIntent, PendingIntent.FLAG_ONE_SHOT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(alarmTime);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), travelServiceIntent);
Log.e("NEXT ALARM", "Time: " + String.valueOf(calendar.getTimeInMillis()));
}
This is TravelNotificationReceiver.java (should I use LocalBroadcastReceiver instead of BroadcastReceiver?)
public class TravelNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("RECEIVER", "received TravelNotification request");
Intent notificationIntent = new Intent(context, TravelNotificationService.class);
context.startService(notificationIntent);
}
}
TravelNotificationService.java extends NotificationService.java setting as type = "Travel", flags = 0, title = "something" and text = "something else".
public abstract class NotificationService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
sendNotification();
return super.onStartCommand(intent, flags, startId);
}
public abstract String setNotificationType();
public abstract int setNotificationFlags();
public abstract String setNotificationTitle();
public abstract String setNotificationText();
/**
* Executes all the logic to init the service, prepare and send the notification
*/
private void sendNotification() {
int flags = setNotificationFlags();
String type = setNotificationType();
NotificationHelper.logger(type, "Received request");
// Setup notification manager, intent and pending intent
NotificationManager manager = (NotificationManager) this.getApplicationContext().getSystemService(this.getApplicationContext().NOTIFICATION_SERVICE);
Intent intentAction = new Intent(this.getApplicationContext(), TabBarActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, intentAction, flags);
// Prepares notification
String title = setNotificationTitle();
String text = setNotificationText();
Notification notification = NotificationHelper.buildNotification(getApplicationContext(), title, text, pendingIntent);
// Effectively send the notification
manager.notify(101, notification);
NotificationHelper.logger(type, "Notified");
}
}
Edit - Here's the code for NotificationHelper.buildNotification
public static Notification buildNotification(Context context, String title, String text, PendingIntent pendingIntent) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setAutoCancel(true);
builder.setContentText(text);
builder.setContentTitle(title);
builder.setContentIntent(pendingIntent);
builder.setSmallIcon(R.mipmap.launcher);
builder.setCategory(Notification.CATEGORY_MESSAGE);
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
return builder.build();
}
Thank you for the answers!
Edit I've seen also this but has no accepted answers, while this post suggest something that I think it's already managed with if(alarmTime < currentTime){ return; } in scheduleTravelNotification.
This may not be your exact problem, but at a glance, you're sending the notification in onStartCommand() which can itself be run many times during the lifetime of the service -- for example, if you issue the service start command "blindly" in an onCreate of an activity, it will happen every time the activity is (re)created.
You have a few options for handling this.
One is to create a boolean flag as a property of the service, default to false, and check it before sending the notification. If it's false, send the notification and set it to true, and if it's already true you do not send a notification.
Another is to check and see if the service is already running, and if it is, don't send the service start command in the first place. This can be tedious to do everywhere, and violates DRY, so if you take this route you may want to create a static method in your service class which checks to see if the service is running and then starts it if not, and call that instead of explicitly starting the service.
Similar to user3137702 answer you could simple have a static boolean of APPISINFORGROUND which is checked everytime the send notification method is hit, and managed from your application/activities code.
As User said it is likely that your onStartCommand method is being called at odd times due to the app / service lifecycle.
Alternatively check your receiver is not being called somewhere else from your code.
It may be your NotificationHelper class which is causing an issue. Please share the code for this class.
One thought may be that your notification is not set to be auto cancelled, check if you include the setAutoCancel() method in your Notification Builder.
Notification notification = new Notification.Builder(this).setAutoCancel(true).build();
I've found a way to make it work, I'm posting this since it seems to be a problem of many people using the approach suggested in this and this articles. After months of testing I can say I'm pretty satisfied with the solution I've found.
The key is to avoid usage of Services and rely on AlarmScheduler and Receivers.
1) Register the receiver in your manifest by adding this line:
<receiver android:name="<your path to>.AlarmReceiver" />
2) In your activity or logic at some point you want to schedule a notification related to an object
private void scheduleNotification(MyObject myObject) {
// Cal object to fix notification time
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(myObject.getTime());
// Build intent and extras: pass id in case you need extra details in notification text
// AlarmReceiver.class will receive the pending intent at specified time and handle in proper way
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("OBJECT_ID", myObject.getId());
// Schedule alarm
// Get alarmManager system service
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(getBaseContext().ALARM_SERVICE);
// Build pending intent (will trigger the alarm) passing the object id (must be int), and use PendingIntent.FLAG_UPDATE_CURRENT to replace existing intents with same id
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), myObject.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Finally schedule the alarm
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
3) Define AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Find object details by using objectId form intent extras (I use Realm but it can be your SQL db)
MyObject myObject = RealmManager.MyObjectDealer.getObjectById(intent.getStringExtra("OBJECT_ID"), context);
// Prepare notification title and text
String title = myObject.getSubject();
String text = myObject.getFullContent();
// Prepare notification intent
// HomeActivity is the class that will be opened when user clicks on notification
Intent intentAction = new Intent(context, HomeActivity.class);
// Same procedure for pendingNotification as in method of step2
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(context, myObject.getId(), intentAction, PendingIntent.FLAG_UPDATE_CURRENT);
// Send notification (I have a static method in NotificationHelper)
NotificationHelper.createAndSendNotification(context, title, text, pendingNotificationIntent);
}
}
4) Define NotificationHelper
public class NotificationHelper {
public static void createAndSendNotification(Context context, String title, String text, PendingIntent pendingNotificationIntent) {
// Get notification system service
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Build notification defining each property like sound, icon and so on
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
notificationBuilder.setContentTitle(title);
notificationBuilder.setContentText(text);
notificationBuilder.setSmallIcon(R.drawable.ic_done);
notificationBuilder.setCategory(Notification.CATEGORY_MESSAGE);
notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setContentIntent(pendingNotificationIntent);
notificationBuilder.setDefaults(Notification.DEFAULT_SOUND);
notificationManager.notify(1001, notificationBuilder.build());
}
}
At this point it should work and schedule / trigger notification at the right time, and when notification is opened it will appear only once starting the activity declared in notification pending intent.
There is still a problem, AlarmManager have a "volatile" storage on user device, so if user reboots or switch off the phone you will lose all intents that you previously scheduled.
But fortunately there is also a solution for that:
5) Add at top of your manifest this uses permission
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
6) Right below the line added at step 1 register the boot receiver
<receiver android:name="<your path to>.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
7) Define the BootReceiver
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do something very similar to AlarmReceiver but this time (at least in my case) since you have no source of intents loop through collection of items to understand if you need to schedule an alarm or not
// The code is pretty similar to step 3 but repeated in a loop
}
}
At this point your app should be able to schedule / trigger notification and restores those reminders even if the phone is switched off or rebooted.
Hope this solution will help someone!

Android Service won't run from AlarmManager

I have a problem with running a service from Alarm manager.
I am building an app that notifies the owner on the namedays of his facebook friends. It all works nicely, but the notification won't show up.
I've set up an AlarmTask that creates the PendingIntent and sets the AlarmManager, like this:
public void run() {
// Request to start are service when the alarm date is upon us
Intent intent = new Intent(context, NotifyService.class);
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getService(context, ID, intent, 0);
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
}
The ID is specific for every nameday.
Now in my NotifyService, I have set up these:
#Override
public void onCreate() {
super.onCreate();
System.out.println("NOTIFICATION SERVICE onCreate()");
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
System.out.println("INTENT RECIEVED: " + intent + " " + flags + " " + startId);
// If this service was started by out AlarmTask intent then we want to show our notification
if(intent.getBooleanExtra(INTENT_NOTIFY, false)){
int ID = intent.getIntExtra("notifyID", -1);
showNotification(ID);
}
// We don't care if this service is stopped as we have already delivered our notification
return START_STICKY;
}
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
Is there a way to test if the AlarmManager really executes the PendingIntent?
Should I rather use IntentService? Why/how?
Thanks a lot.
I tried to change it to BroadcastReciever, looking like this:
public class NotificationBroadcastReciever extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("BROADCAST RECIEVED");
}
}
The AlarmTask bit is changed to this:
Intent intent = new Intent("NotificationBroadcast");
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), ID, intent, 0);
System.out.println("date for notification: " + date.get(Calendar.DAY_OF_MONTH) + "." + date.get(Calendar.MONTH) + "." + date.get(Calendar.YEAR));
System.out.println("epoch time in milils: " + date.getTimeInMillis());
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
and relevant manifest part looks like this:
<receiver
android:name="cz.cvut.kubispe2.jmeniny.NotificationBroadcastReciever"
android:exported="false">
<intent-filter>
<action android:name="NotificationBroadcast" />
</intent-filter>
</receiver>
I checked if the date that is to be set is equal to the epoch time and it is, but still, the onRecieve method is never called.
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
_WAKEUP alarms are only guaranteed to wake up the device if they route to a BroadcastReceiver, not a Service. So long as what you are doing is very short (1-2 milliseconds), you can safely do that work in onReceive() of a BroadcastReceiver. The work you are presently doing in your Service would qualify.
Beyond that, use adb shell dumpsys alarm to confirm that your alarm is scheduled for when you think it is.
Should I rather use IntentService?
It would certainly be a better option than a regular Service, which you are leaking in your current implementation. However, the _WAKEUP limitation still holds, which is why I wrote WakefulIntentService, to help bridge the gap. Again, though, with the current limited work you are doing, just using a BroadcastReceiver should suffice.
try using application context.
PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), ID, intent, 0);
And work with android logs. Then you will see if it's running in your console
Seems like I finally resolved it, I used the broadcast reciever, and found out where the error was - Calendar takes the month argument from 0 to 11, instead of 1-12, which I thought, since all the other arguments are dealt with normally. So I was just putting up a notification for the end of May, instead of today, when testing.
Anyway, thank you all for help, it was very appreciated.

How clear Android Notification PendingIntent

I got a Activity which creates an alarm.
The alarm calls a Broadcast Receiver. In on Receive i create a Notification with the extras from the Activity (ID, Title, Content). The Alarm triggers the Broadcast Receiver creates the notification well.
But when i re install the application or install a newer version and setup a new Alarm with a new title and content the receiver shows me the first create notification intent. i can create may Alarm triggers all works but they show always the first create Notification intents.
I use a internal application counter for creating a notification ID
public class CollectApplication extends Application {
private Integer reminderCount;
#Override
public void onCreate() {
reminderCount = 1;
super.onCreate();
}
public Integer getReminderCount() {
return reminderCount;
}
public void setReminderCount(Integer reminderCount) {
this.reminderCount = reminderCount;
}
}
Of course after re installing or updating the application the counter starts from 1. But i create a new intent with the same ID 1 so i override it right?
How to override it or remove the intent from the notification to create a new one with new extras to display?
Or Should i save the current ID in the shared preferences?
How to override it or remove the intent from the notification to create a new one with new extras to display?
Use FLAG_UPDATE_CURRENT when you create your PendingIntent for the new Notification.
Or Should i save the current ID in the shared preferences?
No, because you should only have one ID. You may wish to persist your reminder count, though.
->Just add PendingIntent.FLAG_UPDATE_CURRENT
Example:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

How to cancel repeated alarm set for every day

I'm working on calendar events .
When adding events to the calendar I'm creating an alert using alarm manager
this is working fine.
I need to cancel the alert for that particular event while deleting event.
I'm setting alarm like this.
AlarmManager amgr=(AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent=new Intent(AmdAddEvent.this, RepeatingAlarmReceiver.class);
intent.putExtra("time", mAlarmTime);
PendingIntent pendingIntent=PendingIntent.getBroadcast(AmdAddEvent.this,(int) mAlarmTime,intent,PendingIntent.FLAG_CANCEL_CURRENT);
amgr.setRepeating(AlarmManager.RTC_WAKEUP, mAlarmTime,AlarmManager.INTERVAL_DAY, pendingIntent);
how to cancel alert while deleting event.
Please help me regarding this
Thanks in Advance
amgr.cancel(Pending Intent); ,
you can use this to cancel the pending alarm event.
AlaramManager Cancel
If you are coding for deleting event in another class,then you need to create an intent again with the same id(in your case,it is (int) mAlarmTime)and then you can cancel the specific alarm intent.else you can just use cancel() of alarmManager oject. Enter this lines of code where you delete the alarm event:
try
{
Intent intent=new Intent(AmdAddEvent.this, RepeatingAlarmReceiver.class);
intent.putExtra("time", mAlarmTime);
PendingIntent pendingIntent=PendingIntent.getBroadcast(AmdAddEvent.this,(int) mAlarmTime,intent,PendingIntent.FLAG_CANCEL_CURRENT);
amgr.cancel(pendingIntent);
}
catch (Exception e) {
// TODO: handle exception
}
DO NOT USE getBroadcast(), method it will not work.
Use the following code to create an alarm to create an activity:
pendingIntent = PendingIntent.getActivity(RemindingService.this, day, intent, PendingIntent.FLAG_UPDATE_CURRENT |PendingIntent.FLAG_ONE_SHOT);
Use the following code to cancel the alarm anytime:
pendingIntent = PendingIntent.getActivity(RemindingService.this, id, intent, PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_ONE_SHOT);
if(pendingIntent != null) {
alarmMgr.cancel(pendingIntent);
pendingIntent.cancel();
}
This is will tested and it is working well.

Techniques to implement a notification service

I have a main activity where the user can enable/disable notifications, set the notification interval, and set the base time the notification interval will use. Notifications will typically trigger about 2 hours from each other. After a certain time, an accumulator will reach a maximum value and notifications will no longer be needed.
What is the standard way of implementing such a notification scheme? I tried using a handler inside of a service using postAtTime, but it seems that there are a lot of conditions that can cause it to never run. I looked at a timer inside of the service, but putting the phone in standby will stop any timers, plus it just seems like a bad idea.
The only other option I came across I have yet to explore, but it involves using an AlarmManager and a BroadcastReceiver. Should I just ditch the service and schedule a repeating alarm instead? I need to be able to disable all remaining alarms once my accumulator has reached max value.
Thanks for any input.
What if you start a service that spawns a thread like this:
thread t = new thread(new Runnable(){
public void Run(){
boolean notified = false;
while( !notified ){
if( notify_time - time > 1000 ){
Thread.sleep(999);
else if( notify_time - time <= 0 ){
// START NOTIFICATION ACTIVITY
notified = true;
}
}
}
}
t.start();
I have not done anything like this personally, so I am not sure what service can do to notify the user or start an activity, but it does have the full panoply of options available to an activity, so yeah.
Oh but it just occured to me you'll need to use a handler for that because of the multithreaded aspect here.
Since I will always have a finite number of notifications and I can calculate the elapsed time in advance, it seems the combination of AlarmManager and a BroadcastReceiver work pretty well. Here is how I implemented this:
I first created a BroadcastReceiver
public class NotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Get handle to system notification manager
NotificationManager mNM = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
//Get message from intent
Bundle bundle = intent.getExtras();
CharSequence text = bundle.getString("notification_message");
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.notification_icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(context, context.getText(R.string.app_name),text, contentIntent);
// Set Flags
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Send the notification.
mNM.notify(R.string.notification, notification);
}
}
I then created a class that used a AlarmManager to create/cancel alarms that send a message to the BroadcastReceiver
public class NotificationSender {
private AlarmManager mAlarmManager;
private Context mContext;
private Intent mIntent;
public NotificationSender(Context context){
this.mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.mIntent = new Intent(context, NotificationReceiver.class);
this.mContext = context;
}
public void setAlarm(Long etaMillis, int accumulator){
//Create intent to send to Receiver
this.mIntent.putExtra("notification_message","Message");
//Use accumulator as requestCode so we can cancel later
PendingIntent sender = PendingIntent.getBroadcast(this.mContext, accumulator, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Set Alarm
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, etaMillis, sender);
}
public void cancelAlarms(){
//requestCode (accumulator) will always be a multiple of 10 and less than 100
for (int x = 10; x <= 100; x += 10){
PendingIntent operation = PendingIntent.getBroadcast(this.mContext, x, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(operation);
}
}
public void createAlarms(PreferenceHelper prefs){
//Calculate time notifications are due and set an alarm for each one
//PreferenceHelper is a class to help pull values from shared preferences
Date currentTime = new Date();
for (int i = prefs.getNotificationInterval(); i <= 100; i += prefs.getNotificationInterval()) {
if (i > prefs.getAccumulator()) {
this.setAlarm(SystemClock.elapsedRealtime() + calculateETA(i, prefs).getTime() - currentTime.getTime(), i);
}
}
}
public void refreshAlarms(PreferenceHelper prefs){
this.cancelAlarms();
if (prefs.isNotificationsEnabled()) this.createAlarms(prefs);
}
}
The important part is to use the accumulator as the requestCode so we can cancel all of our alarms later.
Finally I used the NotificationSender class in my activity by calling refreshAlarms() in onCreate() and whenever the user modifies preferences that are relevant to scheduling notifications. Rebooting the phone will clear all alarms so the app must be restarted before notifications will begin. If the system happens to kills the process, the alarms will still trigger at the appropriate time.

Categories

Resources