Alarm taking too long to trigger - android

I have a mostly working alarm that functions perfectly except for one aspect: The time that I have set for the alarm to trigger is not always the time that it triggers at.
I was thinking for a while that maybe the alarm ran in seconds, not milliseconds. After rereading the documentation, this thought was dismissed.
I'm going to post the important code now before I explain a bit more:
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
startAlarm(findViewById(R.id.reset));
Log.d("test", "reseting");
}
};
public void startAlarm(View view) {
//setup
EditText nameInput = (EditText) this.findViewById(R.id.nameInput);
EditText firstAlertInput = (EditText) this.findViewById(R.id.firstAlertInput);
EditText intervalInput = (EditText) this.findViewById(R.id.intervalInput);
EditText descriptionInput = (EditText) this.findViewById(R.id.descriptionInput);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sp.edit();
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
//Need to parse and format the offset now, before intent is created, then attach it to the intent.
//HERE I NEED TO GET THE OFFSET AND CONVERT INTO MILLISECONDS
EditText hoursInput = (EditText) this.findViewById(R.id.hoursInput);
EditText minutesInput = (EditText) this.findViewById(R.id.minutesInput);
EditText secondsInput = (EditText) this.findViewById(R.id.secondsInput);
int hours = 0;
int minutes = 0;
int seconds = 0;
try {
hours = Integer.parseInt(hoursInput.getText().toString()) * 3600000;
minutes = Integer.parseInt(minutesInput.getText().toString()) * 60000;
seconds = Integer.parseInt(secondsInput.getText().toString()) * 1000;
} catch(NumberFormatException e) {
}
int offsetMillis = hours + minutes + seconds;
//HERE: Randomly Generate the offset, add or subtract it when alarm is set. Make sure to attach the opposite to the PI so that the next alarm will trigger at te correct default time
int offsetRandomMillis = 0;
if(offsetMillis != 0){
Random offsetRandom = new Random();
int oneOrNegativeOne = (offsetRandom.nextBoolean()) ? 1 : -1;
offsetRandomMillis = offsetRandom.nextInt(offsetMillis) * oneOrNegativeOne;
}
//Creates new offset alarm structure
Intent intent = new Intent(this, AlarmReciever.class);
intent.putExtra("name", nameInput.getText().toString());
intent.putExtra("description", descriptionInput.getText().toString());
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 234324243, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//Creates new recursion alarm structure
Intent intent2 = new Intent("RESET_ALARM");
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this.getApplicationContext(), 000, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
//Stores old offset alarm details
editor.putString("oldName", nameInput.getText().toString());
editor.putString("oldDescription", descriptionInput.getText().toString());
editor.apply();
//Finishes new offset alarm details
int[] times = parseTime(firstAlertInput.getText().toString());
times[0] = times[0] * 3600000;
times[1] = times[1] * 60000;
long millis = times[0] + times[1];
int[] intervalTimes = parseTime(intervalInput.getText().toString());
intervalTimes[0] = intervalTimes[0] * 3600000;
intervalTimes[1] = intervalTimes[1] * 60000;
long intervalMillis = intervalTimes[0] + intervalTimes[1];
//New offset alarm set
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis + intervalMillis + offsetRandomMillis, pendingIntent);
//New Recursion alarm set
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis + intervalMillis, pendingIntent2);
Log.d("test", "OFFSET: " + String.valueOf((System.currentTimeMillis() + millis + intervalMillis + offsetRandomMillis) / 1000));
Log.d("test", "NOOFFSET: " + String.valueOf((System.currentTimeMillis() + millis + intervalMillis) / 1000));
}
private int[] parseTime(String input){
int[] times = new int[2];
String[] split = input.split(":");
for(int i = 0; i < times.length; i++){
times[i] = Integer.parseInt(split[i]);
}
return times;
}
NOTE: These are both in the same class, and I am excluding irrelevant parts of that same class.
So here is what is happening: After I clear all alarms and set new ones, those alarms trigger at the correct time. When the broadcastReceiver sets the new alarms, these alarms trigger 5-20 seconds late. All subsequent alarms that it sets also trigger late, but they trigger 5-20 seconds later than the previous alarms. As you can see, the alarm ends up triggering far fewer times than in should.
Also, note that this experiment was tested with an interval value of 1 minute, meaning that each alarm should have triggered one minute after it was set. I know there is also code that determines and parses an offset, but I have no set a value for that, so it doesn't affect the time. I printed this value among many other values, so I know that this works.
I'm frustrated because I see no reason why this should fail. The current log.d statements in the code that I posted are just to show when a new alarm is set and the time in which both alarms should go off (one alarm is suppose to trigger a notification, this one that adds the offset. The other triggers without the offset and sets the new alarms. Sorry if I confused you.). These values are correct, so that is not an issue.
My suspicion at the moment comes from another experiment that I performed. I continually clicked the button that first runs the startAlarm method. This displayed the time in milliseconds that the alarms that were set should have triggered. I expected this number to rise constantly as 1000 milliseconds must pass in one second. This was not the case however. The number rose slowly, about 10 every few. This was a very unexpected behavior. Can someone please explain the reason for this in their answer as well? Thanks.
My current suspicion is that I have failed to convert something into milliseconds or into seconds. But, looking through the code and logging variables, I don't know that this is the issue. The milliseconds that pass seem to be funky, so I don't know what is up.

Related

Alarm manager not pushing data in server periodically for nougat

i am using AlarmManager to call the PendingIntent. which triggers the WakefulBroadcastReceiver to push the data in server periodically. but for nougat, its not pushing in the periodic time. and also draining battery too much.
my code is here->
public static void startSyncAlarm(String enteredTime) {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(context, SyncDbToServerAlarmReceiver.class);
intent.putExtra(Constants.ENTERED_TIME, enteredTime);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(context, SyncDbToServerAlarmReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// alarm needs to be set after 5 minutes of entered time
// convert entered time to milliseconds
// create Date object using string time format with using Simple date format
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss aa");
try {
Date enteredDate = format.parse(enteredTime);
long enteredTimeInMillis = enteredDate.getTime();
long intervalMillis = 30 * 60 * 1000; // 30 minutes gap
long triggerAtMillis = enteredTimeInMillis + intervalMillis;
Log.d(TAG, "start alarm, enteredTimeMillis = " + enteredTimeInMillis + "\ntriggerAtMillis = " + triggerAtMillis);
//long firstMillis = System.currentTimeMillis(); // alarm is set right away
syncDbAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// Setup periodic alarm every 5 minutes
syncDbAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis/*firstMillis*/,
intervalMillis, pIntent);
} catch (ParseException e) {
e.printStackTrace();
}
}

Change textview everyday automatically

First I want to change textview text everyday automatically. For example, first day the textview text is "First text", and second day text is "Second text" at 8 o'clock eveyday. And the text is permanent until next day 8 o'clock.
I try this code but my problem is that the code is not triggered if the user doesn't open this page at 8 o'clock. Texts are inside "mquote array"
SharedPreferences pref =getApplicationContext().getSharedPreferences("myPrefsKey",Context.MODE_PRIVATE) ;
dayCount = pref.getInt("dayCount", 0);
Calendar cal = Calendar.getInstance();
int hour=cal.get(Calendar.HOUR_OF_DAY);
int minute=cal.get(Calendar.MINUTE);
if ((hour==8 && minute==1 ))
updateQuote();
tvQuote.setText(mquote.getQuote(dayCount));
public void updateQuote()
{
if (dayCount==4)
dayCount=0;
dayCount++;
pref.edit().putInt("dayCount",dayCount).apply();
}
You need an AlarmManager implementation:
public class AlarmReceiver extends BroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
public void setAlarm(Context context) {
alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
// set the Alarm's trigger time to 8:00 a.m.
calendar.set(Calendar.HOUR_OF_DAT, 9);
// Set the alarm to fire at approximately 8:30 a.m., according to the device's
// clock, and to repeat once a day.
alarmManager.setInexactRepeating(AlarmManager.RTC,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent);
}
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarm.setAlarm(context);
} else if(Objects.equals(intent, alarmIntent)) {
SharedPreferences pref = getApplicationContext().getSharedPreferences("key", Context.MODE_PRIVATE);
int dayCount = pref.getInt("dayCount", 0);
dayCount %= 4;
pref.edit().putInt("dayCount", ++daysCount).apply();
}
}
}
You should use timestamp to be able to compare two dates. try this code.
But the 1 day happens while user is using the app, it will not trigger. If you want to overcome this, then use AlarmManager to trigger an event every single day.
SharedPreferences pref = getApplicationContext().getSharedPreferences("myPrefsKey",Context.MODE_PRIVATE);
lastDay_ms= pref.getInt("time_ms", 0);
Calendar today = Calendar.getInstance();
long diff = today.getTimeInMillis() - lastDay_ms; //result in millis
final long ONE_DAY = 24*60*60*1000; //millis in a day
if (diff > ONE_DAY)
updateQuote();
Store milliseconds when you last updated preferences in your preferences:
SharedPreferences pref = getApplicationContext().getSharedPreferences("msKey", Context.MODE_PRIVATE);
msCount = pref.getInt("msCount", 0);
long milliseconds = System.currentTimeMillis();
long diff = milliseconds - msCount;
diff += (8 * 60 * 60 * 1000); // compensating difference for 8'O clock condition
int days = (int) Math.ceil(diff / 24 / 60 / 60 / 1000);
days %= 4; // Because you were resetting days count after 4 increments
updateQuote(days);
public void updateQuote(days) {
if(days == 0) {
pref.edit().putInt("msCount", milliseconds).apply();
}
}

Setting notifications based on timepicker and Spinner value

I have two time picker and spinner with value 1 to 10, I want to make number of local notification selected from spinner between time selected from two time picker for eg from 2 pm to 6 pm I want to show six notification and it should notify every day between selected time range with selected value, I have used alarm manager for this but it showing just one notification also used set repeating.
Also I tried to first find time difference from two picker in millis and applied some logic but nothing seems to be good. Please give me some logic or sample code will be appreciated.
Some code snippet is
Intent myIntent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
myIntent, 0);
long next = 0;
for (int i = 0; i < spinnervalue ); i++) {
Log.e("checkkkk ", "" + "checkkkk");
alarmManager.set(AlarmManager.RTC_WAKEUP, next, pendingIntent);
next += diff_millis;
}
This snippet should work, perhaps with a few changes.
Here i am getting the difference between the 2 time pickers in milliseconds and += to the current time in milliseconds. Giving each pending intent a different ID and telling it to update and existing one if available (allows you to reset them if you know the ID (counter).
You have to divide the difference by the spinner chosen in order to get evenly spaced millisecond differences.
//Assume timepicker2 > timepicker1
int hours1 = timePicker1.getCurrentHour();
int minutes1 = timePicker1.getCurrentMinute();
int hours2 = timePicker2.getCurrentHour();
int minutes2 = timePicker2.getCurrentMinute();
int hourDiff = hours2-hours1;
int minutesDiff = minutes2-minutes1
int millisecondsDiff = hourDiff*60*60*1000;
millisecondsDiff += minutesDiff*60*1000;
Double period_diff = millisecondsDiff/spinnerValue;
//This is for the intent difference
int counter = 0;
long next = System.currentTimeMillis() + period_diff;
for (int i = 0; i < spinnervalue ); i++) {
Intent myIntent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, counter++, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Log.e("checkkkk ", "" + "checkkkk");
alarmManager.set(AlarmManager.RTC_WAKEUP, next, pendingIntent);
next += period_diff;
}
Don't take the snippet to work definitively as it's a quick jot down, but it should give you a general idea of where you should be looking.
Another idea would be to use i as the counter so you know in what range the IDs are.
http://developer.android.com/reference/android/app/AlarmManager.html#set%28int,%20long,%20android.app.PendingIntent%29
If the stated trigger time is in the past, the alarm will be triggered
immediately. If there is already an alarm for this Intent scheduled
(with the equality of two intents being defined by
filterEquals(Intent)), then it will be removed and replaced by this
one.
So setting this in a loop isn't what you want.
Your value for millis needs to be a time. 0 would indicate epoch (January 1st, 1970). What you probably want is "SystemClock.elapsedRealtime() + someValue" passed in for that parameter.

Android How To Create Multiple Alarms AlarmClock.ACTION_SET_ALARM

Hi I am trying to create multiple alarms.
I am getting the number of hours left in the day, dividing it by the number of alarms needed, then trying to set alarms with intervals between them.
So for example if num of alarms is 2, and user clicks button at 14:00 there are 10 hours left, so 10 hours / 2 alarms = 5 hours distance between each alarm
So I want to set 2 alarms (one alarm at 19:00 and another at 24:00)
Currently my code is only setting 1 alarm and seems to ignore running the for loop multiple times. I'm not sure why the for loop dosnt create multiple alarms
try {
// Current Time
Time timeNow = new Time();
timeNow.setToNow();
int timeNowHour = timeNow.hour;
int timeNowMins = timeNow.minute;
// Hours left in day
int hoursInADay = 24;
int hoursLeftInDay = hoursInADay - timeNowHour;
// Number of alarms
int numAlarms = 2;
// Calc time between each alarm
int alarmDistance = hoursLeftInDay / numAlarms;
for(int i = 0; i < numAlarms; i++){
int alarmHour = timeNowHour + alarmDistance * (i+1);
Intent alarmIntent = new Intent(AlarmClock.ACTION_SET_ALARM);
alarmIntent.putExtra(AlarmClock.EXTRA_MESSAGE, "TEST ALARM");
alarmIntent.putExtra(AlarmClock.EXTRA_HOUR, alarmHour);
alarmIntent.putExtra(AlarmClock.EXTRA_MINUTES, timeNowMins);
alarmIntent.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(alarmIntent);
}
} catch (Exception e) {
}
Thanks
AlarmManager alarmManager= (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent intent=createPendingResult(ALARM_TIME_ID, new Intent(), 0);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,SystemClock.elapsedRealtime() + alarmDistance,alarmDistance, intent);
And in the onActivityMethod you can do whatever you want.
Example:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ALARM_TIME_ID) {
Toast.makeText(this, "Alarm is running", Toast.LENGTH_LONG).show();
}
Make sure to set the ALARM_TIME_ID to a unique int value.

How to set multiple AlarmManagers?

I have an app that needs to set alarms for multiple days of the week, depending on what days the user sets them. I have it working where it triggers an alarm at the appropriate time, but am wanting to know how to do multiple without them overridding the current AlarmManager. For example, here is my test code:
final int alarmid = (int)System.currentTimeMillis(); //creates unique id for the alarm attached to the object
tempmainfrag.mainObjectList.returnSchedule(i).setAlarmId(alarmid);
for(int j = 0; j < 7; j++)
{
if(tempmainfrag.mainObjectList.returnSchedule(i).returnDays()[j]) //if this day of the week has an alarm
{
int adjustedday = j+2; //makes time for DAY_OF_WEEK where sunday = 1, monday = 2, etc.
if (adjustedday == 8)
adjustedday = 1;
Calendar startcal = Calendar.getInstance();
startcal.set(Calendar.DAY_OF_WEEK, adjustedday);
startcal.set(Calendar.HOUR_OF_DAY, tempmainfrag.mainObjectList.returnSchedule(i).returnTimes()[0]);
startcal.set(Calendar.MINUTE, tempmainfrag.mainObjectList.returnSchedule(i).returnTimes()[1]);
startcal.set(Calendar.SECOND, 0);
Log.i("mydebug","Starting cal day of week: " + adjustedday);
Log.i("mydebug","Starting cal hour of day: " + tempmainfrag.mainObjectList.returnSchedule(i).returnTimes()[0]);
Log.i("mydebug","Starting minute: " + tempmainfrag.mainObjectList.returnSchedule(i).returnTimes()[1]);
Intent intent = new Intent(this, SilenceHandler.class);
intent.putExtra("starttime",tempmainfrag.mainObjectList.returnSchedule(i));
intent.putExtra("alarm_message", "Test!"); //FOR TESTING
PendingIntent pendintent = PendingIntent.getBroadcast(this, alarmid, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmman = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmman.setRepeating(AlarmManager.RTC_WAKEUP, startcal.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendintent);
// startcal.set(Calendar.MINUTE, (tempmainfrag.mainObjectList.returnSchedule(i).returnTimes()[1])+1);
// alarmman.setRepeating(AlarmManager.RTC_WAKEUP, startcal.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendintent);
}
}
The for loop iterates through days of the week, and at each iteration checks to see if the alarm should be set for that day. This code works (presumably), but to test if I could set another alarm on top of it I added in the last two lines of code that are commented out here. This makes the alarm go off a minute later, but not a minute earlier as well. I am doing this to demonstrate that if I want this code to work for multiple days of the week, the way the code is currently set up will just have the alarm set for the last day of the week that returns true. How can I make multiple alarms?
The problem is that although you are creating a unique ID for your PendingIntent, which would set separate alarms, in your test you are reusing the same PendingIntent, which would override your previous one. Create a new PendingIntent with a different alarmid to test this.

Categories

Resources