I have been fighting with this for quite a while now, I am using alarm manager to schedule an alarm, I have a receiver declared in my manifest file. The alarm and the receiver work as intended while the app is running or in the background, I am unable to get my alarms to fire after the app has been closed by the user. I'm essentially just trying to have local notifications in my application. None of the other "answers" here have been much help. Is it possible to have a local notification fire when the app has been closed?
public static void writtenGoalsNotification(Context context) {
final int _id = 15;
pref = context.getSharedPreferences("userpref", 0);
Notification notification = getNotification("Don't forget to write your daily goals!", context, "none", "Goals");
Intent notificationIntent = new Intent(context, NotificationReceiver.class);
notificationIntent.putExtra(NotificationReceiver.NOTIFICATION_ID, "written goals notification");
notificationIntent.putExtra(NotificationReceiver.NOTIFICATION, notification);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, _id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Integer hour = pref.getInt(NotificationKeys.Goals.ReminderTime.hour, 10);
Integer minute = pref.getInt(NotificationKeys.Goals.ReminderTime.minute, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.MILLISECOND, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1000 * 60 * 60 * 24, pendingIntent);
}
Here is my receiver,
public class NotificationReceiver extends WakefulBroadcastReceiver {
public static String NOTIFICATION_ID = "notification-id";
public static String NOTIFICATION = "notification";
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
notificationManager.notify(id, notification);
startWakefulService(context, intent);
WakeLocker.acquire(context);
WakeLocker.release();
}
}
This is in my manifest,
<receiver
android:name=".Controllers.NotificationReceiver"
</receiver>
Broadcast receiver is deprecated in API level 26.
The main reason you do not get alram received is because the process of App you are using is finished and cleared if there is low memory in device. Instead you should use service so that it has higher priority.
So for this I suggest you to use BroadcastReceiver class instead of WakeupBroadcastReceiver because android docs says this-
(It is generally not safe to start a service from the receipt of a broadcast, because you don't have any guarantees that your app is in the foreground at this point and thus allowed to do so.)
Thus your process of setting alram may be destroyed. So use BroadcastReceiver.
In manifest do this
<receiver
android:name=".NotificationReceiver"
</receiver>
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 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.
I have an app that sends a notification with AlarmManager every day at an exact time.
But it has a bug. The notification is sent whenever the app is open.
How can I get to the notification is send once a day? Thank you
MAINACTIVITY
public class MainActivity extends Activity {
private PendingIntent pendingIntent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Calendar calendar = Calendar.getInstance();
int d = Integer.valueOf(1440);
calendar.setTimeInMillis(System.currentTimeMillis());
Intent i = new Intent(MainActivity.this, Receiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, i, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * d, pendingIntent);
}
}
RECEIVER
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
Intent i = new Intent(context, MainActivity.class);
Notification notification = new Notification(R.drawable.ic_launcher,"This is a test message!", System.currentTimeMillis());
long[] vibrate = {100, 100, 200, 300};
notification.vibrate = vibrate;
notification.defaults = Notification.DEFAULT_ALL;
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(context, 0, i,PendingIntent.FLAG_UPDATE_CURRENT);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(context, "AlarmManagerDemo", "This is a test message!", pendingNotificationIntent);
notificationManager.notify(0, notification);
}
}
Code to call CallThisClass.class every int d mins
Intent myIntent = new Intent(getActivity(), CallThisClass.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
int d = Integer.valueOf(duration);
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 2);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * d, pendingIntent);
Code to stop calling CallThisClass.class
Intent myIntent = new Intent(getActivity(), CallThisClass.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 0, myIntent, 0);
alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
CallThisClass.class
public class CallThisClass extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
Log.i("Received", "Broadcast Received !");
// Do your stuff
}
}
Dont forget Receiver inyour AndroidManifest.xml
<receiver android:name=".CallThisClass" />
And Permission
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
You can start calling CallThisClass.class whenever you want. Eg. on a click of a button, or when your main activity start. Just make sure to Stop Calling the class when you dont want it working. Eg You want when the user first installs your application you want every 24 hours there should be something updated (notification or something), rit? in your prefrences store a variable that changes itself only if it is loaded for the first time. Check for that variable every time user enters application. if it is first time Start the Calling code. (now we dont want to call the stop code yet because we cant to keep on calling that class even if the application is closed!) close the application and it will work. And you can have a button in you setting or something for testing that when clicked will run code for not calling the class. It will stop the calling calling class every d mins i.e 24 hours in your case (also you can change the prefrnce to default value on clik of this button so that next time you start activity or click activate or something again that code to start calling that class starts). I hope it helps.
Also, i would suggest a read here for Best practices
I've got an application that needs to show a notification of certain events in a day, depending on the event.
I know how to do it, if I need a service to stay running in the background, or it will run in the background getting these events on certain days.
private void Notificar(String descricaoEvento){
NotificationManager notifier = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.ic_launcher, "Novo evento no The Point Club", System.currentTimeMillis());
notification.flags |= Notification.DEFAULT_SOUND;
PendingIntent pIntent = PendingIntent.getActivity(this, 0, null, 0);
notification.setLatestEventInfo(this, "Title", descricaoEvento.toString(), pIntent);
notifier.notify(0x007, notification);
}
using this method to call when the right date!
Thanks for listening, I'll try to explain what I'm doing right: I'm making an application for a club party, and the application will show the next event of the celebrations when I open the application, but what I wish is that a notification is created in android notification bar, on which there is a party, serving as a reminder.
I use this code when I open the activity to list events:
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
int anoAtual= c.get(Calendar.YEAR);
long time = c.getTimeInMillis();
Intent it = new Intent("EXECUTAR_ALARME")
p = PendingIntent.getBroadcast(Eventos.this, 0, it, 0);
AlarmManager alarme = (AlarmManager) getSystemService(ALARM_SERVICE);
alarme.set(AlarmManager.RTC_WAKEUP, time, p);
the line
Intent it = new Intent("EXECUTAR_ALARME")
will call the class:
public class Alarm extends BroadcastReceiver{ #Override
public void onReceive(Context contexto, Intent intent) {
NotificationManager notifier =(NotificationManager) contexto.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.ic_launcher, "New event in Club", System.currentTimeMillis());
notification.flags |= Notification.DEFAULT_SOUND;
//Intent i = new Intent(this, Eventos.class);
PendingIntent pIntent = PendingIntent.getActivity(contexto, 0, null, 0);
notification.setLatestEventInfo(contexto, "Club", "Teste", pIntent);
notifier.notify(0x007, notification);
}
}
Now I want to know two things:
1 - How do I leave it running in the background without being open with the application.
2 This code only creates only an alarm, and I need to create multiple.
Thanks for the help.
Use AlarmManager to run a PendingIntent at your specific time. At that time, the intent that it stored in the PendingIntent should likely be a service if you're planning on creating a Notification to display to the user. If you could form this as a question, I may be able to help more, but it's unclear what you're trying to accomplish, and what you've managed to accomplish.
Additional Details
In your code, it looks like you're scheduling the alarm for right now, instead of at some time in the future. I'd suggest the following to create the alarm:
Calendar c = Calendar.getInstance();
// don't do this:
// c.setTimeInMillis(System.currentTimeMillis()); // This just sets the alarm time to "now"
// set the calendar to 8:00 pm:
c.set(Calendar.HOUR_OF_DAY, 20);
c.set(Calendar.MINUTE, 0);
long alarmTime = c.getTimeInMillis();
Intent it = new Intent(ServiceClass.getClass());
// Even though the docs say that the request code doesn't matter making it unique tells the alarmManager that it's a different PendingIntent
PendingIntent pendingIntent = PendingIntent.getService(this, 0, it, 0);
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmMgr.set(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent); // set the first alarm for 8:00
c.set(Calendar.MINUTE, 30);
Intent it2 = new Intnt(ServiceClass.getClass());
PendingIntent pi2 = PendingIntent.getService(this, 0, it2, 0);
alarmMgr.set(AlarmManager.RTC_WAKEUP, alarmTime, pi2); // set the second alarm for 8:30
the line
Intent it = new Intent(ServiceClass.getClass());
will start the Class ServiceClass:
class ServiceClass extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO TRAVIS Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) { // onStartCommand is run when the service is started by the AlarmManager
// build and set the notification here
NotificationManager notifMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// build notification
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle("New mail from " + sender.toString())
.setContentText(subject)
.setSmallIcon(R.drawable.new_mail)
.setLargeIcon(aBitmap)
.build();
int ID = 0;
notifMgr.notify(ID++, notification); // use a unique id every time if you want multiple notifications (sounds like what you want, but not a best practice)
notification = new Notification.Builder(getApplicationContext())
.setContentTitle("New event from " + sender.toString())
.setContentText(subject)
.setSmallIcon(R.drawable.new_mail)
.setLargeIcon(aBitmap)
.build();
notifMgr.notify(ID++, notification); // add the second notification to the notification bar
return super.onStartCommand(intent, flags, startId);
}
}
Answers to your questions:
1 - By setting the alarm for sometime in the future, you're asking the OS to start your PendingIntent (in your case, send a Broadcast; in my exmaple above, a Service) at the time specified (that's why you need to set the time with the calendar to sometime in the future!)
2 - Creating multiple alarms is a matter of calling the alarm manager with different PendingIntent s scheduled at the appropriate time (for when you want them to fire). The same is true for Notification s, create multiple ones (with unique ids).