I want my user to set a time to receive a daily reminder from my app. In my ReminderActivity I create the PendingIntent and the Alarm Manager, and then in my Alarm Receiver class I create the notification inside onReceive(). I tried both the FLAG_CANCEL_CURRENT and FLAG_UPDATE_CURRENT flags when creating the pending intent but still when I am testing the app and changing the reminder time then sometimes the notification doesn't arrive at all, or it arrives only when the app is running in the background and the screen is on. I would greatly appreciate any thought or ideas.
ReminderActivity code:
private void setNotification() {
Calendar calendar = Calendar.getInstance();
Calendar now = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, chosenHour);
calendar.set(Calendar.MINUTE, chosenMinute);
calendar.set(Calendar.SECOND, 0);
//if user sets the alarm after their preferred time has already passed that day
if(now.after(calendar)) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
Intent intent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(ReminderActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager = (AlarmManager) getSystemService(Activity.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
Alarm Receiver code:
#Override
public void onReceive(Context context, Intent intent) {
Bitmap largeLogo = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ptwired_logo);
//create local notification
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, MainActivity.class);
//notificationIntent.putExtra("FromPTWired", true); //to track if user opens the app from the daily digest notification
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ptwired_logo)
.setLargeIcon(largeLogo)
.setContentTitle(context.getResources().getString(R.string.app_name))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setContentText(REMINDER_TEXT)
.setAutoCancel(true)
.setOngoing(false)
.build();
notificationManager.notify(1, notification);
}
}
Probably an issue of Doze mode, take a look in Android restriction:
Doze restrictions
The following restrictions apply to your apps while in Doze:
Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.
If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.
Related
I am creating a research app that should prompt the user 4 times a day to enter their mood - by sending a notification, which when clicked launches the correct Activity. I am able to schedule these notifications using AlarmManager, however only the last scheduled notification ever shows. So although I schedule them for 9AM, 2PM, 5PM, and 8PM, it only ever sends a notification at 8PM.
How can I get all of the scheduled notifications to show?
Here is my code for setting (one of) the alarms (from a notification manager class). Note that al alarms are set using the same instance of AlarmManager:
cal.setTimeInMillis(System.currentTimeMillis());
cal.set(Calendar.HOUR_OF_DAY, 9);
cal.set(Calendar.MINUTE, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), createPendingIntent(9, this));
Here is the createPendingIntent method (in the same notification manager class):
public static PendingIntent createPendingIntent(int hour, Context c){
Intent notificationIntent = new Intent(c, AlarmBroadcastReceiver.class);
notificationIntent.putExtra("time", hour);
PendingIntent pendingIntent = PendingIntent.getBroadcast(c, 0 , notificationIntent , PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
Here is the BroadcastReceiver for the alarm:
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationSender.createNotification(context);
}
}
And finally the createNotification method:
public static void createNotification(Context c){
Log.e("notif?", "creating");
Intent intent = new Intent(c, UpdateMoodActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
int notificationId = new Random().nextInt();
PendingIntent pendingIntent = PendingIntent.getActivity(c, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(c, "com.lizfltn.phdapp.notifChannelID")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("SoftMood")
.setContentText("Please record your mood")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(c);
notificationManager.notify(notificationId, builder.build());
}
Yes I know this isn't the best-practice way of doing things, or even the neatest, but unfortunately I need to get code working ahead of writing good code :P
I've tried various configurations of setting the alarm, e.g. using elapsed realtime instead of RTC, only setting the alarm, setting the exact alarm, etc, but there might be something fundamental I'm not understanding about how those work.
Any help appreciated!
Can you try with same id in pending intent and notify.?
Notification id in createNotification() method is random id.
int notificationId = new Random().nextInt();
and id used in createPendingIntent method is 0.
PendingIntent pendingIntent = PendingIntent.getBroadcast(c, 0 , notificationIntent , PendingIntent.FLAG_UPDATE_CURRENT);
Can you try with using same value for second parameter of getBroadcast?
This is the code for the alarm manager:
protected void alarmInit(){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 9);
calendar.set(Calendar.MINUTE, 25);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, SampleBootReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 234324243, alarmIntent, 0);
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
}
This is the Broadcast class:
public class SampleBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Toast.makeText(getApplicationContext(),"Hello",Toast.LENGTH_LONG).show();
intent = new Intent(context, NotificationService.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(
context)
// Set Icon
.setSmallIcon(R.drawable.icono)
// Set Ticker Message
.setTicker("message")
// Set Title
.setContentTitle("asdf")
// Set Text
.setContentText("message")
// Add an Action Button below Notification
// Set PendingIntent into Notification
.setContentIntent(pIntent)
// Dismiss Notification
.setAutoCancel(true);
// Create Notification Manager
NotificationManager notificationmanager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
// Build Notification with Notification Manager
notificationmanager.notify(0, builder.build());
}
}
}
}
But at the set time I do not receive any message (I don't see the Toast message with the word Hello).
Also, in Manifest XML I set inside aplication tag:
<receiver android:name=".Home$SampleBootReceiver"
android:enabled="false"
tools:ignore="Instantiatable">
The class SampleBootReceiver is a public class in Home class
Could you please help me with this? Thanks in advance
You are using setInexactRepeating so the OS will decide when to fire your alarm. The OS tries to group together alarms to save battery.
Beginning with API 19 (Build.VERSION_CODES.KITKAT) alarm delivery is
inexact: the OS will shift alarms in order to minimize wakeups and
battery use. There are new APIs to support applications which need
strict delivery guarantees; see setWindow(int, long, long,
android.app.PendingIntent) and setExact(int, long,
android.app.PendingIntent). Applications whose targetSdkVersion is
earlier than API 19 will continue to see the previous behavior in
which all alarms are delivered exactly when requested.
https://developer.android.com/reference/android/app/AlarmManager
I have to send 3 different notification(different content) at different time in a day.
Following the official documentation, https://developer.android.com/training/scheduling/alarms.html#type,
I was able to send notification at specified times, but having some trouble with changing the content of each notification. Following is the method to set notifications and is called in onCreate() method of MainActivity.
public void setNotification() {
Intent intent = new Intent(this, MyBroadcastReceiver.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
for (int id = 0; id < 3; id++) {
Calendar calendar = Calendar.getInstance();
// Defining different pendingIntent
switch (id){
case 0:
calendar.set(Calendar.HOUR_OF_DAY, 9);
break;
case 1:
calendar.set(Calendar.HOUR_OF_DAY, 14);
break;
case 2:
calendar.set(Calendar.HOUR_OF_DAY, 20);
break;
}
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, id, intent, 0);
// setRepeating() schedules an alarm
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
}
I am aware that I can use Intent.putExtra() features to send data, which can be used to identify the intent, but this data will be lost if app is killed.
I want this to work even if app is not running or app gets killed or device is booted. After some reading I found out that I can use a BOOT_COMPLETE listener to set alarms after device is booted. Because of setting notifications in onCreate() method, notification is appearing everytime I run the app, even though I tried to tackle this by using following code
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
Currently onReceive method looks like this
public void onReceive(Context context, Intent intent) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.app_icon)
.setContentTitle("Title")
.setContentText("Message");
Intent resultIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, mBuilder.build());
}
I need help in deciding where should I put setNotification() method so that it registers all alarms once and how to identify which intent is coming so that appropriate actions can be taken. One way to go about it is to define 3 different broadcastReceivers with intent filters and set intent URI while creating them. Is it the correct way or is there any other simple solution?
notification is appearing everytime I run the app,
Keep a check using SharedPreference and set only once if not done.
Also your code
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
should be somewhat like this
Calendar calendar = Calendar.getInstance();
// Get the current time in millis
long currentTime = calendar.getTimeInMillis();
// Set the reminder times
switch(id) {
..
..
}
//Get the reminder time in millis
long intendedTime = calendar.getTimeInMillis();
if (intendedTime < currentTime) {
// Set from next day and to repeat once a day.
// you might consider using calendar.add() for adding one day to the current day
calendar.add(Calendar.DAY_OF_MONTH, 1);
intendedTime = calendar.getTimeInMillis();
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
intendedTime, AlarmManager.INTERVAL_DAY, pendingIntent);
} else {
// Set for today and to repeat once a day.
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, intendedTime,
AlarmManager.INTERVAL_DAY, pendingIntent);
}
how to identify which intent is coming so that appropriate actions can
be taken
You need to set an action to Intent in order to receive data in BroadcastReceiver
Intent intent = new Intent(this, MyBroadcastReceiver.class);
intent.setAction("THIS_IS_MY_ACTION");
Case of BOOT_COMPLETE
Cancel all the alarm on Boot, and reset them. To cancel an alarm Call cancel() on AlarmManager with an same PendingIntent which you have used while setting an alarm using setRepeating().
Take care of context and INTENT_FLAGS while working with AlarmManager.
Use getApplicationContext() for context and Keep records of request_code as they are going to be used while cancelling an alarm, use PendingIntent.FLAG_UPDATE_CURRENT
This might help:-
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), request_code, intent, PendingIntent.FLAG_UPDATE_CURRENT)
Well I have tried enough to look for the answer to the question mentioned above, but my efforts have been futile so far.
I have created an alarm with the help of the alarmmanager class which will fire up the notification at regular intervals of time(probably about 5 days).
Below is the code for the implementation of the alarm happening inside onClick() of a button.
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION");
notificationIntent.addCategory("android.intent.category.DEFAULT");
PendingIntent broadcast = PendingIntent.getBroadcast(this, 100, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar cal = Calendar.getInstance();
if(fromDateEtxt.getText().toString().length()>0) {
cal.add(Calendar.HOUR_OF_DAY, 10);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), 5 * 24 * 60 * 60 * 1000, broadcast);
}
The code for the broadcast receiver.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent notificationIntent = new Intent(context, NotificationActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(NotificationActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.setContentTitle("Demo App Notification")
.setContentText("New Notification From Demo App..")
.setTicker("New Message Alert!")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}}
The onClick() method apart from starting of the alarm changes the activity also. Now the problem I am facing is I dont know really how to stop the repeating alarm. I want to stop the alarm on a particular date. Secondly, I was confused whether to use the alarm.cancel for cancelling the alarm or to use another alarm for cancellation of the previous alarm as shown here. Apart from this, I wanted to know if the alarm could be cancelled from another activity or does the point seems unnecessary and the limit to the date could be set beforehand?
am trying to schedule a notification with desired time and un the below code my desired time if after 10 seconds but i don't know why its showing up the notification instantly , am i doing anything wrong ? or missing anything please correct me if am wrong anywhere , and am using BlueStacks Emulator for testing (built version 4.4.2 Api 19)
notification = new NotificationCompat.Builder(this);
notification.setAutoCancel(true);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION");
notificationIntent.addCategory("android.intent.category.DEFAULT");
PendingIntent broadcast = PendingIntent.getBroadcast(this, 100, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
// why its showing up instantly insted of after 10 seconds
//alarmManager.setExact(AlarmManager.RTC_WAKEUP,20,broadcast);
Intent intent = new Intent(this , MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 , intent , PendingIntent.FLAG_UPDATE_CURRENT);
Intent switchIntent = new Intent(this, switchButtonListener.class);
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 0, switchIntent, 0);
notification.setSmallIcon(R.drawable.ok);
notification.setWhen(20);
notification.setTicker("you've got a meesage");
notification.setContentTitle("new message");
notification.setContentText("wanna take a ride?");
// notification.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(uniqueID, notification.build());
notification showing up instantly
When you call notificationManager.notify() in your last line, it displays the notification immediately.
I assume that you wanted to use an AlarmManager to display a notification and you don't quite understand what it does. An AlarmManager is used schedule an Intent to be broadcasted. An Intent can be used to perform a variety of task such as starting an Activity or Service. As far as i know it is not possible to use an Intent to display notification.
What you should be looking at is to use postDelayed() method in Handler class. For example:
handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
// Create your notification using NotificationCompat.Builder
// and call notificationManager.notify()
}
};
handler.postDelayed(r, /*time to delay for in ms*/);
Edit: If you really want to use AlarmManager and broadcast to display a notification, you will need to extend a BroadcastReceiver and have it listen for your PendingIntent broadcast. Next, you will schedule the PendingIntent to be broadcasted using AlarmManager. When the AlarmManager fire off the PendingIntent after 10 seconds, your BroadcastReceiver will receive the broadcast and call notificationManager.notify() to display the notification. This is quite a roundabout way of displaying notification.