Let me explain what I'm doing before I get into the problem.
Ultimately, I'm wanting to send different notifications on certain days of the week every week. I've been able to get it working with a single notification. The problem happens when I try to set up multiple notifications.
So in my MainController's OnCreate method I'm making a call to a function called setAlarm and passing variables:
setAlarm(this, 3, 14, 04, 1, 1);
where 'this' is the context, 3 is a day of the week, 14 is an hour, 04 is a minute, 1 is a second and the last 1 is what I call a notification ID. Here is the setAlarm function:
public void setAlarm(Context context2, int dayofweek, int hourofday, int dayminute, int daysecond, int notiID){
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("VALUE", notiID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context2, 0, alarmIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.DAY_OF_WEEK, dayofweek);
calendar.set(Calendar.HOUR_OF_DAY, hourofday);
calendar.set(Calendar.MINUTE, dayminute);
calendar.set(Calendar.SECOND, daysecond);
Date date = calendar.getTime();
//manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 0, pendingIntent);
manager.setExact(AlarmManager.RTC_WAKEUP, date.getTime(),pendingIntent);
}
What I'm doing in this function is setting up the alarm manager to trigger on a certain day of the week, at a certain time. I know once this is triggered it will call the AlarmReceiver class which I've set up. But I'm also passing an integer variable to the AlarmReceiver class through the alarmIntent intent I set up.
The alarm manager works, and triggers on the specified time. Here's the alarmReceiver class I've set up:
public class AlarmReceiver extends BroadcastReceiver {
private static AlarmReceiver instance = null;
#Override
public void onReceive(Context context, Intent intent) {
int value = intent.getIntExtra("VALUE", 0);
if (value == 1){
NotificationCompat.Builder noti = new NotificationCompat.Builder(context, "notify_001");
noti.setSmallIcon(R.drawable.clocknoon);
noti.setContentTitle("Bill Sez...");
noti.setContentText("First Notification Test!!!");
noti.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManager nm = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("notify_001", "Channel title", NotificationManager.IMPORTANCE_DEFAULT);
nm.createNotificationChannel(channel);
}
nm.notify(0, noti.build());
}
if (value == 2){
NotificationCompat.Builder noti = new NotificationCompat.Builder(context, "notify_002");
noti.setSmallIcon(R.drawable.clocknoon);
noti.setContentTitle("Bill Sez...");
noti.setContentText("This is the second notification test");
noti.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManager nm = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("notify_002", "Channel title", NotificationManager.IMPORTANCE_DEFAULT);
nm.createNotificationChannel(channel);
}
nm.notify(0, noti.build());
}
}
}
As you can see, I'm passing the int variable Value to the class, which works fine as long as I only call the setAlarm function one time. But I'm trying to get this working to set up multiple notifications. What I expect to happen is that I call the setAlarm function with the notification ID (notiID) and at the time specified, the alarm manager triggers and the extra variable value is passed to the alarmReceiver class along with the flow of execution. In the Alarm Receiver class I've got an if statement set up to test the variable 'value' to see if it's 1 or 2 and based on that, a notification will fire and appear on the phone.
What's happening is when I call setAlarm function twice, the first notification is never received. Even more strange, the second notification is received, but it's variable 'value' is 1, not 2 as I expect it to be.
for example:
So I call setAlarm and set the alarm to go off at 12:05:01 on Tuesday, and pass notiID of 1. Then I call setAlarm again and set the alarm to go off at 12:06:01 on Tuesday, and pass notiID of 2.
At 12:05:01, nothing happens. I should see the first notification, but I don't. At 12:06:01 I get a notification but instead of the second notification, I see the first one. Likewise if I put a breakpoint in the AlarmReceiver class and look at what 'value' is set to, it shows to be set to 1, not 2.
So I don't get what's happening. I'm hoping anyone experienced in making notifications with Android 8.0.0 and higher can help. Or even suggest a different way to send different notifications on different days.
Change the following line
PendingIntent pendingIntent = PendingIntent.getBroadcast(context2, 0, alarmIntent, 0);
to
PendingIntent pendingIntent = PendingIntent.getBroadcast(context2, notiID, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Related
I need to create multiple notifications that each have a certain day of the week to display at a certain time. E.g. Every Monday at 8pm and when the notification is clicked it should go to a activity in my application. I also have a settings activity where the user can choose which notifications they want to show or not.
I have looked at loads of examples and even android docs but haven't figured out a way to get this working.
Thanks in advance.
Exact times might be quite difficult due the the different restrictions on background processing by the system. But a satisfactory solution might be to use alarm manager.
https://developer.android.com/training/scheduling/alarms#examples-of-real-time-clock-alarms
Taken from the above docs
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
// setRepeating() lets you specify a precise custom interval--in this case,
// 24 hours.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 60 * 24, alarmIntent);
You'll have to create and register the AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver
{
public AlarmReceiver (){}
public void onReceive(Context context, Intent intent)
{
doSomething();
}
}
Register receiver somewhere in activity
private AlarmReceiver receiver = new AlarmReceiver();
this.registerReceiver(this.receiver, new IntentFilter());
I'm having some trouble with getting a daily notification appearing.
My application gets a time, a hour and minute that the user wishes to be reminded of something.
I then use AlarmManager to set up a alarm at this time repeating whilst using alarm receiver to create the notification.
I've been trying this for hours but can't figure out what I'm doing wrong.
I've looked at a bunch of other SO questions but none have helped me yet.
I've stored the user's hours and minute input into a date object get habitReminder.
My createNotifications() method:
private void createNotifications() {
Log.i("Reminder at",""+habitReminder.getHours()+":"+habitReminder.getMinutes());
//Create the alarms/notifications for the user
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("name", habitName);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Log.i("createNotifications", "Alarm manager is created.");
//Set the timing of the reminder
Calendar calendar = Calendar.getInstance();
Calendar now = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, habitReminder.getHours());
calendar.set(Calendar.MINUTE, habitReminder.getMinutes());
calendar.set(Calendar.SECOND,0);
//Check to make sure time is after the current date.
if(calendar.before(now)){
calendar.add(Calendar.DATE, 1);
}
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
Log.i("createNotifications", "Alarm has been set for " +habitReminder.getHours()+":"+habitReminder.getMinutes() +" daily.");
}
My alarm receiver class:
public class AlarmReceiver extends BroadcastReceiver {
private static int id =0;
#Override
public void onReceive(Context context, Intent intent) {
String name = intent.getStringExtra("name");
String title = name + " Reminder!";
String message = "Your reminder to keep up your habit!";
long when = System.currentTimeMillis();
Intent in = new Intent(context, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(context,0,in,PendingIntent.FLAG_CANCEL_CURRENT);
NotificationManager nM = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context)
.setContentIntent(contentIntent)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true)
.setWhen(when);
Notification notification = builder.build();
nM.notify(id,notification);
id++;
}
}
And my android manifest:
<receiver android:name="com.closedbracket.trackit.AlarmReceiver" android:enabled="true">
</receiver>
Any help would really be appreciated.
If you want a precise and reliable alarm, use setAlarmClock. It will drain more power from the battery but you are sure the alarm will ring precisely at the time set.
For more information, you can refer to Difference between setExact and setAlarmClock
I think you have the arguments to setRepeating wrong. 2nd argument is the time interval until first alarm fires (the jitter). 3rd is the interval, after the first occurrence at which subsequent alarms fire (the interval).
You are setting an alarm for AlarmManager.INTERVAL_DAY which has a value of 86400000. Your alarm will fire once a day.
Let's say a notification using AlarmManager was set at a specific date. What if I want to delete that notification with a button press before it has even appeared. The NotificationManager.cancel(id) method if I'm not mistaken will cancel a notification that is currently showing. What if I want to delete it before it has even appeared? Like how you remove an item in an arraylist or how you delete a row in a database.
Example I have a Personobject with id,name and everytime I add a Personto the database, a Notification will be set on a specified date.
Here is the set notification method to be called with a calendar instance of a specified date and a person object:
public void setNotification(Calendar calendar,Person person){
AlarmManager alertManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent intent = new Intent();
intent.setAction("android.media.action.DISPLAY_NOTIFICATION");
intent.addCategory("android.intent.category.DEFAULT");
intent.putExtra("myTag",person);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, person.getId(), intent, PendingIntent.FLAG_ONE_SHOT);
alertManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
My alarm receiver class (Yes I'm implementing DAO design pattern. I have a PersonDao and PersonDaoImpl with the latter being a singleton class):
public class AlarmReceiver extends BroadcastReceiver {
final static String GROUP_KEY = "GROUP_KEY";
#Override
public void onReceive(Context context, Intent intent) {
//retrieve info:
PersonDAO personDao = PersonDAOImpl.getInstance(context);
Person person = (Person)intent.getSerializableExtra("myTag");
String name = person.getName();
long[] vibrationPattern = {0, 300, 0, 300};
Intent notificationIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(ViewDetails.class);
stackBuilder.addNextIntent(notificationIntent);
int requestCode = person.getId();
PendingIntent pendingIntent = stackBuilder.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.setContentTitle("Deadline due today")
.setContentText("name: " + name)
.setTicker(name+"'s debt is due today")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pi)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(requestCode, notification);
}
Example the date set on the alarm reciever was September 12 2016 3:00pm. The current date is Septermber 10 2016. What I want to do is when I delete the person in the database, the notification will be deleted/canceled also therefore on September 12 2016 3:00pm there will no longer be an alarm notification.
If you want to cancel something that you scheduled via AlarmManager, call cancel() on AlarmManager, passing in an equivalent PendingIntent to the one that you used to schedule the work in the first place. Here, by "equivalent PendingIntent", I mean:
the same operation (e.g., getActivity() vs. getService() vs. getBroadcast())
the same request code (2nd parameter to those methods)
an equivalent Intent
By "equivalent Intent", I mean:
the same component
the same action
the same MIME type
the same categories
the same data (Uri)
for whatever of those properties you had set on the original Intent for the original PendingIntent.
The below code is from my MainActivity onCreate() method where I set an alarm to run a service ScheduleAlarm immediately and only once. sPrefs is the SharedPreference object that takes care of setting this alarm only once. The service ScheduleAlarm is an IntentService which is getting fired perfectly.
if(sPrefs.getBoolean("SettingAlarmForFirstTime",true)) {
//Creating alarm for showing notifications.
Calendar calendar = Calendar.getInstance();
//calendar.setTimeInMillis(System.currentTimeMillis());
//calendar.set(Calendar.HOUR_OF_DAY, 8);
Intent intent = new Intent(MainActivity.this, ScheduleAlarms.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);//Use AlarmManager.INTERVAL_DAY instead of int number here.
editor.putBoolean("SettingAlarmForFirstTime", false);
editor.commit();
}
Now this is my ScheduleAlarms service class. When this service is started onHandleIntent() method is called from where I am calling setWeeklyAlarms() method. Now my problem is from inside this method I want to set 7 alarms for a complete week based on timings that I will obtain from an API server call. Right now I am unable to even execute a single alarm perfectly. The alarm that i am setting is set to start after 3 secs delay which will call another service named NotificationService but the alarms gets fired immediately instead of waiting for 3 secs. Please analyze and tell me why this is happening.
public class ScheduleAlarms extends IntentService {
private boolean notificationsEnabled = true;
private String notificationTimings = "";
public ScheduleAlarms() {
super("ScheduleAlarms");
}
#Override
protected void onHandleIntent(Intent intent) {
setWeeklyAlarm();
}
private void setWeeklyAlarm() {
Intent i = new Intent(ScheduleAlarms.this, NotificationService.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(ScheduleAlarms.this, 10001, i, 0);
AlarmManager alarmManager = (AlarmManager) ScheduleAlarms.this.getSystemService(Context.ALARM_SERVICE);
/* Setting this alarmManager to fire up the alarm after 3 mins delay, but this alarm is getting fired immediately*/
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
}
}
Change the following line:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
to:
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, c.getTimeInMillis() + 3 * 60 * 1000, 3 * 60 * 1000, pendingIntent);
The alarm is fired up immediately, because
AlarmManager.ELAPSED_REALTIME_WAKEUP + 0, ...
So, the first time, it is fired up after 0 seconds. To change this, you need to add some time here.
AlarmManager.ELAPSED_REALTIME_WAKEUP + time
And, also it should be repeating, so you need to use setRepeating.
Note : 3 * 60 * 1000 == 3 minutes, not 3 seconds
I need to trigger a block of code after 20 minutes from the AlarmManager being set.
Can someone show me sample code on how to use an AlarmManager in ِAndroid?
I have been playing around with some code for a few days and it just won't work.
"Some sample code" is not that easy when it comes to AlarmManager.
Here is a snippet showing the setup of AlarmManager:
AlarmManager mgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, OnAlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), PERIOD, pi);
In this example, I am using setRepeating(). If you want a one-shot alarm, you would just use set(). Be sure to give the time for the alarm to start in the same time base as you use in the initial parameter to set(). In my example above, I am using AlarmManager.ELAPSED_REALTIME_WAKEUP, so my time base is SystemClock.elapsedRealtime().
Here is a larger sample project showing this technique.
There are some good examples in the android sample code
.\android-sdk\samples\android-10\ApiDemos\src\com\example\android\apis\app
The ones to check out are:
AlarmController.java
OneShotAlarm.java
First of, you need a receiver, something that can listen to your alarm when it is triggered. Add the following to your AndroidManifest.xml file
<receiver android:name=".MyAlarmReceiver" />
Then, create the following class
public class MyAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Alarm went off", Toast.LENGTH_SHORT).show();
}
}
Then, to trigger an alarm, use the following (for instance in your main activity):
AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, MyAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, 30);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(), pendingIntent);
.
Or, better yet, make a class that handles it all and use it like this
Bundle bundle = new Bundle();
// add extras here..
MyAlarm alarm = new MyAlarm(this, bundle, 30);
this way, you have it all in one place (don't forget to edit the AndroidManifest.xml)
public class MyAlarm extends BroadcastReceiver {
private final String REMINDER_BUNDLE = "MyReminderBundle";
// this constructor is called by the alarm manager.
public MyAlarm(){ }
// you can use this constructor to create the alarm.
// Just pass in the main activity as the context,
// any extras you'd like to get later when triggered
// and the timeout
public MyAlarm(Context context, Bundle extras, int timeoutInSeconds){
AlarmManager alarmMgr =
(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyAlarm.class);
intent.putExtra(REMINDER_BUNDLE, extras);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, timeoutInSeconds);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),
pendingIntent);
}
#Override
public void onReceive(Context context, Intent intent) {
// here you can get the extras you passed in when creating the alarm
//intent.getBundleExtra(REMINDER_BUNDLE));
Toast.makeText(context, "Alarm went off", Toast.LENGTH_SHORT).show();
}
}
What you need to do is first create the intent you need to schedule. Then obtain the pendingIntent of that intent. You can schedule activities, services and broadcasts. To schedule an activity e.g MyActivity:
Intent i = new Intent(getApplicationContext(), MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),3333,i,
PendingIntent.FLAG_CANCEL_CURRENT);
Give this pendingIntent to alarmManager:
//getting current time and add 5 seconds in it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);
Now MyActivity will be launched after 5 seconds of the application launch, no matter you stop your application or device went in sleep state (due to RTC_WAKEUP option).
You can read complete example code Scheduling activities, services and broadcasts #Android
I wanted to comment but <50 rep, so here goes. Friendly reminder that if you're running on 5.1 or above and you use an interval of less than a minute, this happens:
Suspiciously short interval 5000 millis; expanding to 60 seconds
See here.
Some sample code when you want to call a service from the Alarmmanager:
PendingIntent pi;
AlarmManager mgr;
mgr = (AlarmManager)ctx.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(DataCollectionActivity.this, HUJIDataCollectionService.class);
pi = PendingIntent.getService(DataCollectionActivity.this, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() , 1000, pi);
You dont have to ask userpermissions.
An AlarmManager is used to trigger some code at a specific time.
To start an Alarm Manager you need to first get the instance from the System. Then pass the PendingIntent which would get executed at a future time that you specify
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context, MyAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
int interval = 8000; //repeat interval
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
You need to be careful while using the Alarm Manager.
Normally, an alarm manager cannot repeat before a minute. Also in low power mode, the duration can increase to up to 15 minutes.