I want to display notification every morning at 9 AM from my app.
So I am using Notification Manager, Alarm Manager, BroadcastReciever and Service to make that possible.
But I have a problem, because the notification shows randomly. When I first start the app and set the time, it works OK, but later the app fires and shows notification at random time.
How I can solve that?
Here is my code:
MainActivity
#Override
protected void onStart() {
super.onStart();
setAlarm();
}
public void setAlarm(){
Calendar calendar = Calendar.getInstance();
Calendar now = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 43);
calendar.set(Calendar.SECOND, 0);
if(calendar.getTime().after(now.getTime())) {
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmIntent = new Intent(MainActivity.this, HoroscopeNotification.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent); }
}
HoroscopNotification (BroadcastReciever)
public class HoroscopeNotification extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
showNotification(context);
}
private void showNotification(Context context) {
Intent service1 = new Intent(context, AlarmService.class);
context.startService(service1);
}
}
AlarmService
public class AlarmService extends Service {
private static final int NOTIFICATION_ID = 1;
private NotificationManager notificationManager;
private PendingIntent pendingIntent;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
Context context = this.getApplicationContext();
notificationManager = (NotificationManager)context.getSystemService(context.NOTIFICATION_SERVICE);
Intent mIntent = new Intent(this, MainActivity.class);
pendingIntent = PendingIntent.getActivity(context, 0, mIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("Horoskop");
builder.setContentText("Pročitajte današnji horoskop");
builder.setSmallIcon(R.drawable.ic_bik);
builder.setAutoCancel(true);
builder.setContentIntent(pendingIntent);
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
You'll notice in the Android SDK Reference material for the AlarmManager.setRepeating() states:
Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.
You need to use AlarmManager.set() on pre-APIv19 and AlarmManager.setExact() on APIv19+. When your PendingIntent is fired and you receive your Broadcast in your BroadcastReceiver.onReceive() you can set another exact alarm for the next day.
Alarm Manager Example
I think you should follow above link. From my point of view, your design pattern (setting alarm in Activity class is not a good approach). Instead (like showed in the answer above) you should set your alarm from a service. Also the code for notification goes in BroadcastReceiver class, method OnReceive (In the example it is commented "Put here YOUR code").
Good luck
Related
I tried to use the setRepeating of AlarmManager, and then I read that the method isn't working anymore in API 26+, so the another solution was to schedule each alarm when the previous is finish. How can I do that?
MainActivity.java
public class MainActivity extends AppCompatActivity {
calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour.getText().toString()));
calendar.set(Calendar.MINUTE, Integer.parseInt(minute.getText().toString()));
calendar.set(Calendar.SECOND, 00);
Intent intent = new Intent(getApplicationContext(), Notification_receiver.class);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
Notification_reciever.java
public class Notification_receiver extends BroadcastReceiver {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
Intent repeating_intent = new Intent(context, MainActivity.class);
repeating_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, time, repeating_intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setChannelId(CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_notifications_active_24)
.setContentTitle("Notification Title")
.setContentText("Notification Text")
.setAutoCancel(true);
notificationManager.notify(time, builder.build());
}
How can i create another alarm manager when the previous is finish?
You will need to schedule your next alarm, at the end of BroadcastReceiver
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
...
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextAlarmTimeInMillis(), pendingIntent);
Please note that, due to different battery saving mechanism implemented by different OEMs, AlarmManager is not guarantee to work all the time. Please refer to https://issuetracker.google.com/issues/122098785
In short, AlarmManager is the current best available mechanism, for timed notification to work, even without Internet connection. But, it is not gurantee to work all the time.
Here is example to make alarm every 2 minutes (tested on API 30)
move code to set alarm from MainActivity in own class
public class AlarmExecutor {
public static void makeAlarm(Context context, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MINUTE, minute);
Intent intent = new Intent(context, Notification_receiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
Log.d("Testing", "make alarm at " + calendar.getTime().toString());
}
}
now you can add in
Notification_receiver.java after notificationManager.notify(time, builder.build());
MainActivity
following line
// make alarm in 2 minutes
AlarmExecutor.makeAlarm(context, 2);
that will make an alarm every 2 minutes.
And don't forget to register the receiver in Manifest
<application >
<receiver android:name=".yourReceiver" />
</application>
I want set the alarm at the specified time and implement the notification at the specified time(in a day) by using BroadcastReceiver and AlaramManager.
It alarmed specified time. After when I run the app, it again alarm even though it's not the time I set.
In other words, when I set the alarm 9:21 pm and I run the app after 9:21 pm, the notification is generated.
I just want to make the alarm notification at the specified time I set and app doesn't run.
Also, when I run the app, it doesn't alarm.
How can I fix it?
This is my BroadcastReceiver Code -
public class BroadcastD extends BroadcastReceiver{
Context context;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
showNotification();
}
public void showNotification() {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent pendingIntent = PendingIntent
.getActivity(context, 0, new Intent(context, MainActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setTicker("Ticket")
.setContentTitle("Title")
.setContentText("Context")
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
notificationManager.notify(1, builder.build());
}
}
This is my MainActivity code :
public class MainActivity extends AppCompatActivity {
AlarmManager am ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
am = (AlarmManager) getSystemService(ALARM_SERVICE);
AlarmHATT alarmHATT = new AlarmHATT(getApplicationContext());
alarmHATT.Alarm();
}
public class AlarmHATT {
private Context context;
public AlarmHATT(Context context) {
this.context = context;
}
public void Alarm() {
Intent intent = new Intent( context.getApplicationContext(), BroadcastD.class);
PendingIntent sender = PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
calendar.get(Calendar.DATE), 21, 21, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 24*60*60*1000, sender);
}
}
To avoid having the alarm fire immediately, configure your start time with the Calendar like this:
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 9);
calendar.set(Calendar.MINUTE, 30);
calendar.set(Calendar.SECOND, 0);
And then if the time you are setting has already passed today, you need to set the calendar to the following day with:
calendar.add(Calendar.DATE, 1);
That will avoid having your alarm fire immediately because it was set to a time that already passed earlier in the day.
Then continue to use calendar.getTimeInMillis() as your starting time.
Try below code to set alarm on a specified time.
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
I want to set an alarm at 9am that will execute only once in a day and with
this alarm i want to execute one BroadcastReciver and through that BroadcastReciver
i want to execute Service.
I am Using the following code but in this code following are problems
1)Alarm is not execute at exact 9am.
2)When it execute it execute more than one times
Please help me to resolve this problems.Any help will be appreciable.
===========================================================
The value of breakfastflag is boolean which i am taking from this activity when user press on the click to customize b
if(breakfastflag){
Intent myIntent = new Intent(SplashScreenActivity.this, MyBreakfastReciver.class);
System.out.println("getting Breakfast Reminder");
pendingIntent = PendingIntent.getBroadcast(SplashScreenActivity.this, 0, myIntent,0);
// Set the alarm to start at approximately 9:00 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 9);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
BreakfastReciever.java
==============================
public class MyBreakfastReciver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Intent service1 = new Intent(context, MyBreakfastAlarmService.class);
context.startService(service1);
}
}
MyBreakfastAlarmService.java
===================================
public class MyBreakfastAlarmService extends Service
{
private NotificationManager mManager;
#Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
DatabaseHandler db=new DatabaseHandler(getApplicationContext());
HashMap<String,String> user = new HashMap<String,String>();
String abc="Breakfast";
user= db.getUserCalDetails();
String userid=user.get("userid");
final Calendar c = Calendar.getInstance();
int mYear = c.get(Calendar.YEAR);
int mMonth = c.get(Calendar.MONTH);
int mDay = c.get(Calendar.DAY_OF_MONTH);
String day="";
String month="";
mMonth=mMonth+1;
if(mMonth<=9){
month="0"+mMonth;
}
else{
month=""+month;
}
if(mDay<=9){
day="0"+mDay;
}
else{
day=""+mDay;
}
String year=mYear+"";
String finalDate=year+"-"+month+"-"+day;
int ab=db.getMealDettailsForRemider(userid,abc ,finalDate);
if(ab==0)
{
showNotification(this);
}
}
#SuppressLint("NewApi") private void showNotification(Context context) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.capture)
.setAutoCancel(true)
.setContentTitle("DietGuru")
.setContentText("You haven't logged your Breakfast for today.")
.setSubText("Would you like to do it now?")
;
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(context, SplashScreenActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(SplashScreenActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setDefaults(Notification.DEFAULT_ALL);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
}
#Override
public void onDestroy()
{
super.onDestroy();
}
}
You should use setRepeating() instead of setInexactRepeating().
EDIT:
I have noticed two more mistakes in your code:
1. You have called mBuilder.setContentIntent(resultPendingIntent) twice. You should call it only once. I think this is why the alarm may be coming more than once.
2. You have written month=""+month;. It should be month=""+mMonth;
Try this. This should work.
EDIT 2:
According to the docs:
as of API 19, all repeating alarms are inexact. If your application needs
precise delivery times then it must use one-time exact alarms, rescheduling
each time as described above. Legacy applications whose targetSdkVersion is
earlier than API 19 will continue to have all of their alarms, including
repeating alarms, treated as exact.
To get the exact time, the only way is to use setExact() which needs to be set each time the alarm rings, because it doesn't repeat.
this is my code for menage a single notification:
myActivity.java
public class myActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
cal = Calendar.getInstance();
// it is set to 10.30
cal.set(Calendar.HOUR, 10);
cal.set(Calendar.MINUTE, 30);
cal.set(Calendar.SECOND, 0);
long start = cal.getTimeInMillis();
if(cal.before(Calendar.getInstance())) {
start += AlarmManager.INTERVAL_FIFTEEN_MINUTES;
}
Intent mainIntent = new Intent(this, myReceiver.class);
pIntent = PendingIntent.getBroadcast(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager myAlarm = (AlarmManager)getSystemService(ALARM_SERVICE);
myAlarm.setRepeating(AlarmManager.RTC_WAKEUP, start, AlarmManager.INTERVAL_FIFTEEN_MINUTES, pIntent);
}
}
myReceiver.java
public class myReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent i) {
Intent myService1 = new Intent(c, myAlarmService.class);
c.startService(myService1);
}
}
myAlarmService.java
public class myAlarmService extends Service {
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#SuppressWarnings("deprecation")
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
displayNotification();
}
#Override
public void onDestroy() {
super.onDestroy();
}
public void displayNotification() {
Intent mainIntent = new Intent(this, myActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_noti)
.setTicker(getString(R.string.notifmsg))
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.notifmsg));
nm.notify(0, builder.build());
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
...
...
...
<service android:name=".myAlarmService" android:enabled="true" />
<receiver android:name=".myReceiver"/>
IF the time has NOT past yet everything works perfectly. The notification appears when it must appear.
BUT if the time HAS past (let's assume it is 10.31 AM) the notification fires every time... when I close and re-open the app, when I click on the notification... it has a really strange behavior.
I can't figure out what's wrong in it. Can you help me please (and explain why, if you find a solution), thanks in advance :)
Place display notification inside an if statement , such that compare the current time with the notification set time and if the current time is before the set time then display notification, else do nothing.
int temp = calTemp.getTime().compareTo(calendar.getTime());
if(temp > 0){
}else{
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(),
pendingIntent1);
}
here calTemp gives current time and calender gives the time i want to fire the alarm. So according to above code if the time has already past then the notification will not fire for sure .
Hi I've had the same problem and found a solution in this SO post, basically the idea is to rely on AlarmManager, Receiver but avoid usage of Service.
Since you are using the Service just to build and display the notification you may find useful my approach.
Let me know.
I need to create reminder application based on the date and time from the database (Eg:31-08-2011 10:30,05-09-2011 14:40,etc.. )even if the app is not running.. Database will contain many dates with times. If the time reaches i need to display the notificaiton.How can I do that. Please provide any samples or suggestions
You should use AlarmManager for this.
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);
where the PERIOD is your time to something that should be executed in OnAlarmReceiver.
And then, just implement method in
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification();
notification.tickerText = "10 Minutes past";
nm.notify(0, notification);
}
And also see here,
http://developer.android.com/reference/android/app/AlarmManager.html
Edit: A minor code issue fixed!
Put this code wherever you need..
new CountDownTimer(diff, 1) //Constructor for CountDownTimer class (milliseconds,difference);
{
public void onTick(long millisUntilFinished)
{
// DO NOTHING
}
public void onFinish() {
sendSMS(phoneNo,message);
}
}.start();