setRepeating() function is not workingproperly (AlarmManger Android) - java

I was using setRepeating() function to schedule events in my Application. According to Android documentation setRepeating() is not exact from API level 19+.In the app, when it is time to alarm (every day at 23:59) broadcast receiver is called. However, this broadcast receiver is being called too early(ex: sometimes it is being called right after I set the alarm although there are more than 10 hours until 23:00)
Here is my code:
set Alarm function:
public void setEmaResetAlarm(Context context) {
alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManagerMorning = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
intentResetNight = new Intent(context, EMAAlarmRcvr.class);
intentResetNight.putExtra("ema_reset_night", true); //time to reset EMA to 0
intentResetMorning = new Intent(context, EMAAlarmRcvr.class);
intentResetMorning.putExtra("ema_reset_morning", true); //time to reset EMA to 0
intentRemove = new Intent(context, EmaDismissReceiver.class);
intentRemove.putExtra("ema_pop_up_remove", true); // time to remove EMA pop up
pendingIntentResetNight = PendingIntent.getBroadcast(context, 10, intentResetNight, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntentResetMorning = PendingIntent.getBroadcast(context, 8, intentResetMorning, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntentRemove = PendingIntent.getBroadcast(context, 11, intentRemove, PendingIntent.FLAG_UPDATE_CURRENT);
if (alarmManager == null || alarmManagerMorning == null)
return;
emaTimeSubscription = context.getSharedPreferences("SubscriptionCheck", Context.MODE_PRIVATE);
int hour1 = Integer.parseInt(emaTimeSubscription.getString("hours1", null));
int hour2 = Integer.parseInt(emaTimeSubscription.getString("hours2", null));
int hour3 = Integer.parseInt(emaTimeSubscription.getString("hours3", null));
Calendar firingCallReset = Calendar.getInstance();
firingCallReset.set(Calendar.HOUR_OF_DAY, 23); // at 11:59pm
firingCallReset.set(Calendar.MINUTE, 59); // Particular minute
firingCallReset.set(Calendar.SECOND, 0); // particular second
firingCallReset.set(Calendar.MILLISECOND, 0); // particular second
//alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, firingCallReset.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentResetNight);
Calendar firingCalResetMorning = Calendar.getInstance();
firingCalResetMorning.set(Calendar.HOUR_OF_DAY, 9); // at 9:30am
firingCalResetMorning.set(Calendar.MINUTE, 30); // Particular minute
firingCalResetMorning.set(Calendar.SECOND, 0); // particular second
firingCalResetMorning.set(Calendar.MILLISECOND, 0); // particular second
//alarmManagerMorning.setRepeating(AlarmManager.RTC_WAKEUP, firingCalResetMorning.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentResetMorning);
//remove EMA pop up
Calendar firingCalRemove11 = Calendar.getInstance();
firingCalRemove11.set(Calendar.HOUR_OF_DAY, hour1 + 1); // at 11:01am
firingCalRemove11.set(Calendar.MINUTE, 1); // Particular minute
firingCalRemove11.set(Calendar.SECOND, 0); // particular second
firingCalRemove11.set(Calendar.MILLISECOND, 0); // particular second
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, firingCalRemove11.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentRemove);
Calendar firingCalRemove15 = Calendar.getInstance();
firingCalRemove15.set(Calendar.HOUR_OF_DAY, hour2 + 1); // at 3:01pm
firingCalRemove15.set(Calendar.MINUTE, 1); // Particular minute
firingCalRemove15.set(Calendar.SECOND, 0); // particular second
firingCalRemove15.set(Calendar.MILLISECOND, 0); // particular second
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, firingCalRemove15.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentRemove);
Calendar firingCalRemove19 = Calendar.getInstance();
firingCalRemove19.set(Calendar.HOUR_OF_DAY, hour3 + 1); // at 7:01pm
firingCalRemove19.set(Calendar.MINUTE, 1); // Particular minute
firingCalRemove19.set(Calendar.SECOND, 0); // particular second
firingCalRemove19.set(Calendar.MILLISECOND, 0); // particular second
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, firingCalRemove19.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentRemove);
}
BroadcastReceiver to be called:
public class EMAAlarmRcvr extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences loginPrefs = context.getSharedPreferences("UserLogin", MODE_PRIVATE);
SharedPreferences configPrefs = context.getSharedPreferences("Rewards", MODE_PRIVATE);
//For resetting ema prefr (11:59 PM ) and (9:30 AM )
if (intent.getBooleanExtra("ema_reset_night", false) || intent.getBooleanExtra("ema_reset_morning", false)) {
SharedPreferences.Editor ema_editor = configPrefs.edit();
SharedPreferences.Editor hrm_editor = configPrefs.edit();
Log.e("TAG", "onReceive: TaskManager" );
hrm_editor.putBoolean("hrm_completed", false);
hrm_editor.apply();
ema_editor.putBoolean("ema1_answered", false);
ema_editor.putBoolean("ema2_answered", false);
ema_editor.putBoolean("ema3_answered", false);
ema_editor.putInt("ema_answered_count", 0);
ema_editor.apply();
SharedPreferences.Editor loginEditor = loginPrefs.edit();
loginEditor.putBoolean("ema_btn_make_visible", false);
loginEditor.apply();
}
}
}

Before setting a new alarm you should cancel any previous alarms as:
alarmManager.cancel(pendingIntent);
Also, use same pending intent that you use to create a new alarm to cancel old set one because is used as unique identifier, so:
// cancel old alarm
alarmManager.cancel(pendingIntentRemove);
// then create new one
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, firingCalRemove11.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntentRemove);
You get called at random times because is created a new alarm

Related

Edit shared preferences using repeating alarm

I am trying to change one key of the shared preferences file at the end of the day. So here is what I managed to find on the internet:
boolean alarmUp = (PendingIntent.getBroadcast(this, 0,
new Intent(this, AlarmResetFoodAdded.class),
PendingIntent.FLAG_NO_CREATE) != null);
if (!alarmUp) {
setAlarm();
}
Function setAlarm():
private void setAlarm() {
alarmMgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmResetFoodAdded.class);
alarmIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 14);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, alarmIntent);
}
AlarmResetFoodAdded.java:
#Override
public void onReceive(Context context, Intent intent)
{
pref = context.getSharedPreferences(AppControl.PREF, Activity.MODE_PRIVATE);
prefEditor = pref.edit();
prefEditor.putInt("foodAdded", 0);
prefEditor.commit();
}
I am testing this using the device simulator with API 28. I tried to set the time to about 13:59. Then I'd wait until about 14:01 because it is inExactRepeating, but the key foodAdded in the preferences file stay the same, even when I reopen the app.
I'm pretty sure the alarm is up because when I debug alarmUp, the value is false the first time, then when I reopen it, it is true.
I think you should use prefEditor.apply(); instead of prefEditor.commit(); in your onReceive method.

Why is onReceive() called twice in this Alarm setup?

I'm building an Android pedometer app that sends data to a SQLite database at 30 minute intervals using AlarmManager; if the phone is off when the transfer is supposed to occur, it will do the transfer immediately when the phone is switched back on.
(30 minutes is for testing - the actual interval will be 1 week).
I was told alarms wouldn't persist unless I used "intent.action.BOOT_COMPLETED" in the manifest, so I put that as a filter for my Alarm.java class.
My code to set the alarm (in MainActivity.java) is as follows:
public void insertData(View view) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long time= System.currentTimeMillis();
Date d = new Date(time);
editor.putLong("alarmtime", time); //the next ring time for the alarm is put in SharedPreferences
editor.apply();
Intent intent = new Intent(this, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, time, AlarmManager.INTERVAL_DAY, pendingIntent);
Toast.makeText(MainActivity.this, "Alarm Set", Toast.LENGTH_LONG).show();
}
The code in the Alarm.java class (which extends BroadcastReceiver) is as follows:
Context context; //plus other declared variables
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
preferences = context.getSharedPreferences("MyPreferences", context.MODE_PRIVATE);
editor = preferences.edit();
long startedtime = preferences.getLong("alarmtime", 0); //get the next ring time
Date nextRingTime = new Date(startedtime);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
String action = intent.getAction();
//sometimes the onReceive() is called by the phone turning on
if (action != null && action.equals(Intent.ACTION_BOOT_COMPLETED)) {
//if phone turned on again before the alarm, just re-create the alarm for the same time, don't transfer data
Date currenttime = new Date(System.currentTimeMillis());
if ((currenttime.compareTo(nextRingTime) < 0)) {
Intent newintent = new Intent(context, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
newintent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, startedtime, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent);
return;
}
else if ((currenttime.compareTo(nextRingTime) > 0)) {
//if the phone was off when the alarm was supposed to make the transfer, set the alarm to the next intended ring time and insert data to db immediately
Calendar d = Calendar.getInstance();
d.setTime(nextRingTime);
d.add(Calendar.MINUTE, 30);
long newtime = d.getTimeInMillis();
Intent newintent = new Intent(context, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
newintent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, newtime, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent);
}
}
myDb = new DatabaseHelper(context);
// code for inserting data into database
//finally, update next intended ring time in SharedPreferences now that this particular transfer is done:
Calendar c = Calendar.getInstance();
c.setTime(nextRingTime);
c.add(Calendar.MINUTE, 30);
Log.v("insertdone, alarrm now", c.getTime().toString());
long nexttime = c.getTimeInMillis();
editor.putLong("alarmtime", nextime);
editor.apply();
}
Unfortunately, if the phone is off at the time of a scheduled alarm, when I turn it on afterwards, it calls onReceive() TWICE, meaning it inserts data twice.
How can this be stopped?
A solution would maybe be to use RTC_WAKEUP, but I really don't want to do that unless as an absolute last resort.
N.B. I have a separate bootBroadcastReceiver class that uses "intent.action.BOOT_COMPLETED" to restart a pedometer service, and am wondering whether having 2 different things started by the boot is causing problems...

Having problems with "snoozing" alarm

Does anyone know how to do snooze function and where I am going wrong?
My default snooze time is 5 minutes. It worked fine until restart the
application and then does not work again.
If I clear the cache and data its works again.
public void onClick(View view) {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
int iSnoozeLeft = settings.getInt(PuzzleAlarm.SNOOZE_LEFT, 1);
if (iSnoozeLeft != 0) {
Intent activate = new Intent(AlarmNotification.this, AlarmReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(AlarmNotification.this, 0, activate, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
int iMinutes = Preferences.getSnooze(settings, AlarmNotification.this);
Calendar calendar = Calendar.getInstance();
calendar.roll(Calendar.MINUTE, iMinutes);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), alarmIntent);
SharedPreferences.Editor editor = settings.edit();
editor.putInt(PuzzleAlarm.SNOOZE_LEFT, iSnoozeLeft - 1); // disabling
// Commit the edits!
editor.commit();
}`
PendingIntent.getBroadcast(AlarmNotification.this, 0, activate, 0);
Make sure you aren't using "0" for your other alarm ID. In other case your "snooze" code will overwrite that previous alarm.

Alarm Manager Notification Setting Cookies

I am having some problem when trying to prompt notification based on the timing set by Alarm Manager in Android.
So what I am trying to do is a budget setting application. If the monthly expenses exceed the budget, it will prompt notification. In order to keep the question short, I will just post the part where I interact with Alarm Manager:
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, BudgetAlarm.class);
PendingIntent pi = null;
if (bm.getReminderNotify().equals("Y")
&& percentage >= 90) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 1);
notificationCount = notificationCount + 1;
// Set request flag to 1 so that the same pending intent in broadcastReceiver
notificationIntent.putExtra("NotifyCount", notificationCount);
pi = PendingIntent.getBroadcast(context, 1,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
What I am trying to do here is to check if the expenses exceed the budget. And inside the BudgetAlarm class, I am prompting the notification:
private NotificationManager mNotificationManager;
private Notification notification;
#Override
public void onReceive(Context context, Intent intent) {
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(context, 1,
new Intent(), 0);
notification = new Notification(R.drawable.ic_launcher, "Notification",
System.currentTimeMillis());
notification.setLatestEventInfo(context, "Budget Reminder",
"Your monthly expenses nearly exceed the budget!",
contentIntent);
mNotificationManager.notify(
Integer.parseInt(intent.getExtras().get("NotifyCount")
.toString()), notification);
}
My problem now is it does set and trigger the alarm manager every day. But let's say today I did not run the apps, it does prompt me a notification to notify me. But after a while, I launch the app, then it prompt me again.
I wonder is there any way to set the notification to just notify once per day regardless of how many times I launch the apps. I was actually thinking of something like cookies or shared preference.
Thanks in advance.
EDIT
public class BudgetAlarm extends BroadcastReceiver {
private NotificationManager mNotificationManager;
private Notification notification;
#Override
public void onReceive(Context context, Intent intent) {
saveDay(context);
if (sameDay(context) == false) {
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(context, 1,
new Intent(), 0);
notification = new Notification(R.drawable.ic_launcher,
"Notification", System.currentTimeMillis());
notification.setLatestEventInfo(context, "Budget Reminder",
"Your monthly expenses nearly exceed the budget!",
contentIntent);
mNotificationManager.notify(
Integer.parseInt(intent.getExtras().get("NotifyCount")
.toString()), notification);
saveDay(context);
}
}
private boolean sameDay(Context context) {
boolean isSameDay = false;
SharedPreferences pref = context.getSharedPreferences("PrefKey",
Context.MODE_PRIVATE);
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
Date today = cal.getTime();
String day = format.format(today);
String savedDate = pref.getString("SaveDateKey", "NONE");
if (savedDate.equals(day)) {
isSameDay = true;
} else {
isSameDay = false;
}
return isSameDay;
}
private void saveDay(Context context) {
SharedPreferences pref = context.getSharedPreferences("PrefKey",
Context.MODE_PRIVATE);
Editor editor = pref.edit();
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
Date today = cal.getTime();
String day = format.format(today);
editor.putString("SaveDateKey", day);
editor.commit();
}
}
I think You are on the right way with shared preferences. This could be a scenario in Your app: make a method to know if the same day is equal, if not, save the date when You start the app:
private boolean sameDay(){
boolean isSameDay = false;
SharedPreferences pref = getApplicationContext().getSharedPreferences(YOUR_PREF_KEY, Context.MODE_PRIVAT);
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
Date today = cal.getTime();
String day = format.format(today);
String savedDate = pref.getString(YOUR_SAVED_DATE_KEY,"NONE");
if(savedDate.equals(day){
isSameDay=true;
}else{
isSameDay=false;
}
return isSameDay;
}
And to save the current day:
private void saveDay(){
SharedPreferences pref = getApplicationContext().getSharedPreferences(YOUR_PREF_KEY, Context.MODE_PRIVAT);
Editor editor = pref.edit();
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
Date today = cal.getTime();
String day = format.format(today);
editor.putString(YOUR_SAVED_DATE_KEY,day);
editor.commit();
}
And then You just handling this inside Your onReceive():
if(sameDay==true){
//donĀ“t notify
}else{
//notify and save the day
saveDay();
}

Boot Receiver causing app to stop

I have a boot reciever in my homework planner application to re-set all of the alarms that are created to remind the user of their homework being due in. Everytime my phone boots, I get "Homework planner has stopped", and I cannot figure out why. And I cannot use LogCat as it is not connecting on startup (?).
This is my code in the boot reciever.
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// RE-SET ALL ALARMS FROM THE DATABASE WHEN PHONE IS TURNED ON
HomeworkDatabase db = new HomeworkDatabase(context);
db.open();
ArrayList<HomeworkItem> hwks = db.getHomeworks();
db.close();
int reminders = 0;
for (int e = 0; e < hwks.size(); e++) {
HomeworkItem current = hwks.get(e);
{
Calendar c = Calendar.getInstance();
c.set(current.year, current.month, current.day, 0, 0);
long alarmTime = c.getTimeInMillis();
Intent i = new Intent(HomeworkList.getAppContext(),
RegularAlarm.class);
i.putExtra("title", current.title);
i.putExtra("id", current.id);
PendingIntent pi = PendingIntent.getBroadcast(
HomeworkList.getAppContext(), (int) current.id, i, 0);
AlarmManager mAlarm = (AlarmManager) context
.getApplicationContext().getSystemService(
Context.ALARM_SERVICE);
mAlarm.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
reminders++;
}
if (current.oneDayReminder == true) {
Calendar c = Calendar.getInstance();
c.set(current.year, current.month, current.day, 0, 0);
c.roll(Calendar.DAY_OF_MONTH, -1);
long alarmTime = c.getTimeInMillis();
Intent i = new Intent(HomeworkList.getAppContext(),
OneDayAlarm.class);
i.putExtra("title", current.title);
i.putExtra("id", current.id);
PendingIntent pi = PendingIntent.getBroadcast(
HomeworkList.getAppContext(), (int) current.id, i, 0);
AlarmManager mAlarm = (AlarmManager) context
.getApplicationContext().getSystemService(
Context.ALARM_SERVICE);
mAlarm.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
reminders++;
}
if (current.twoDayReminder == true) {
Calendar c = Calendar.getInstance();
c.set(current.year, current.month, current.day, 0, 0);
c.roll(Calendar.DAY_OF_MONTH, -2);
long alarmTime = c.getTimeInMillis();
Intent i = new Intent(HomeworkList.getAppContext(),
TwoDaysAlarm.class);
i.putExtra("title", current.title);
i.putExtra("id", current.id);
PendingIntent pi = PendingIntent.getBroadcast(
HomeworkList.getAppContext(), (int) current.id, i, 0);
AlarmManager mAlarm = (AlarmManager) context
.getApplicationContext().getSystemService(
Context.ALARM_SERVICE);
mAlarm.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
reminders++;
}
}
Log.d("hwk","BOOT COMPLETE: " + reminders + " alarms created");
Toast.makeText(context, reminders + " alarms created at boot", Toast.LENGTH_LONG);
}
}
Sorry for the long code snippet, but I have no idea what the problem is.
Thanks
EDIT: The source of the error is when creating the intent and getting the context from the for the PendingIntent.
Intent i = new Intent(HomeworkList.getAppContext(), RegularAlarm.class);
This line is causing a null pointer exception. Any idea why?

Categories