Setting up alarm for android application using AlarmReceiver and OnBootReceiver - android

I've browsed through numerous StackOverflow and Youtube tutorials to create an alarm service (to be used as a part of a larger app that I am building), yet it seems that all of them give different, non-working answers or rely on deprecated methods that do not work anymore.
My current issue with the following code is that when I reach the following code: alrmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendInt); it does not seem to send the appropriate time to the alarm manager (it more or less always sends the current time).
However, I know for a fact that calendar.getTimeInMillis() gives me the time that I have set (the setAlarmText textView changes properly). I was wondering if anyone has any experience with this?
Furthermore, the AlarmReceiver class never seems to be called even though I was under the impression that the AlarmManager takes care of that for you.
Code is attached below:
public class AlarmStartPage extends Activity {
AlarmManager alrmMgr;
PendingIntent pendInt;
private TimePicker alrmTimePicker;
private static AlarmStartPage inst;
Intent myIntent;
private TextView alrmStatusView;`
protected static AlarmStartPage instance() {
return inst; // returns an instance of the current Activity
}
#Override
public void onStart() {
super.onStart(); // calls the super classes onStart, and then sets the instance to the current one
inst = this;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm_start_page); // sets the various buttons and other containers on the website
alrmTimePicker = (TimePicker) findViewById(R.id.alarmTimePicker);
ToggleButton alrmTogg = (ToggleButton) findViewById(R.id.toggleAlarmButton);
alrmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alrmStatusView = (TextView) findViewById(R.id.alarmStatus);
setVolumeControlStream(AudioManager.STREAM_ALARM); // sets the volume to be controlled to the audiomanager so that the user can control the alarm's volume
}
public void onToggleClicked(View view) {
if (((ToggleButton) view).isChecked()) {
Log.d("MyActivity", "Alarm On!");
int hourToSet, minuteToSet; // if the toggle button is pushed, then it creates an alarm. Otherwise it cancels a previously created alarm
Calendar calendar = Calendar.getInstance();
if (Build.VERSION.SDK_INT >= 23) // the code here and the one below in the else statement are identical except for which API they cater to
{
hourToSet = alrmTimePicker.getHour();
minuteToSet = alrmTimePicker.getMinute(); // gets the TimePicker's time that the user wants if using Android Marshmallow
} else {
hourToSet = alrmTimePicker.getCurrentHour(); // gets the TimePicker's time that the user wants if using any Android Lolipop or below
minuteToSet = alrmTimePicker.getCurrentMinute();
}
// this is the code to actually do the "magic" of the REM time
int currhr = calendar.get(Calendar.HOUR_OF_DAY); // gets the current time from the system's clock
int currmin = calendar.get(Calendar.MINUTE);
boolean lessThan90 = false; // boolean to check if the current alarm is less than 90 Minutes away (1 REM cycle)
int hrDiff = 0;
int minDiff = 0;
if (hourToSet >= currhr) {
hrDiff = hourToSet - currhr; // calculating the difference between the current hour and the hour of the alarm to get the difference in the time
if (hrDiff == 0) {
if (minuteToSet > currmin) // if the alarm is for after the current time, but same hour, then it is less than 1 hour away
minDiff = minuteToSet - currmin;
else {
hrDiff = 23; // otherwise the alarm us for more than 23 hours away (same hour, but earlier time)
minDiff = 60 - (currmin - minuteToSet);
}
} else {
if (minuteToSet > currmin)
minDiff = minuteToSet - currmin;
else {
hrDiff--;
minDiff = 60 - (currmin - minuteToSet);
}
}
if (60 * hrDiff + minDiff < 90) // if prior to the 15 min shift, the alarm time is less than 90 minutes away, then it will be set as the alarm time
lessThan90 = true;
}
currmin += 15; // add 15 min to the current time, and below, change the hour and minute accordingly
if (currmin >= 60) {
currmin = currmin % 60;
currhr++;
if (currhr >= 24)
currhr = currhr % 24;
}
if (!lessThan90) // only if the alarm time is more than 90 minutes away, it will try to do this (which it will try to do
{ // by defualt since lessThan90 is initalized to false (or it is set to true by the above if else statement
if (hourToSet >= currhr) {
hrDiff = hourToSet - currhr;
if (hrDiff == 0) // same logic as earlier, checks if the same hour as the alarm, then checks if the alarm is before or after the current time
{
if (minuteToSet > currmin) // if the alarm is set for a later time (which means that it is less than 90 minutes away)
minDiff = minuteToSet - currmin;
else // otherwise the alarm is set for 23 hours and some minutes away
{
minDiff = 60 - (currmin - minuteToSet);
hrDiff = 23;
}
} else {
if (minuteToSet > currmin)
minDiff = minuteToSet - currmin;
else {
hrDiff--;
minDiff = 60 - (currmin - minuteToSet);
}
}
} else if (hourToSet < currhr) // if the alarm time is before the current time (then it must loop over midnight and restart from 0 again)
hrDiff = 24 - (currhr - hourToSet);
}
int totalMinutesInBetween = 60 * hrDiff + minDiff;
if (totalMinutesInBetween < 90) // if the total minutes between the alarm and the current time (after the 15 min shift) is less than 90 minutes
lessThan90 = true; // it is less than 1 REM shift away
if (!lessThan90) // If there are more than 90 minutes of difference, then a REM cycle is ACTUALLY possible
{
int possibleRem = totalMinutesInBetween / 90; // the possible amount of REM cycles between now and the alarm time
for (int i = 0; i < possibleRem; i++) {
currhr++; // the time is altered by 90 minute cycles (looping around after 60 minutes or after 24 hours) to get the appropiate REM time
if (currhr >= 24)
currhr = currhr % 24;
currmin += 30;
if (currmin >= 60) {
currmin = currmin % 60; // looping the minutes over 60
currhr++;
if (currhr >= 24)
currhr = currhr % 24; // looping the hours after 24 hours
}
}
hourToSet = currhr;
minuteToSet = currmin;
}
calendar.set(Calendar.HOUR_OF_DAY, hourToSet); // the calendar sets the final REM time
calendar.set(Calendar.MINUTE, minuteToSet);
myIntent = new Intent(AlarmStartPage.this, AlarmReceiver.class);
pendInt = PendingIntent.getBroadcast(AlarmStartPage.this, 0, myIntent, 0); // new intent as well as a pending intent to notify the system of the alarm (uses Alarm Receiver and Alarm Service)
alrmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendInt); // alarmmanager is used to set the alarm
if (minuteToSet > 9)
setAlarmText("An alarm has been placed for " + hourToSet + ":" + minuteToSet + " (in military time). If you shut down" +
" this app, please do not open it again until the alarm that you set is over (otherwise the alarm will reset itself)."); // alarm text is changed to notify the user
else
setAlarmText("An alarm has been placed for " + hourToSet + ":0" + minuteToSet + " (in military time). If you shut down" +
" this app, please do not open it again until the alarm that you set is over (otherwise the alarm will reset itself).");
} else {
alrmMgr.cancel(pendInt); //cancels the current Intent (effectively stopping the alarm)
stopService(myIntent);
setAlarmText("The previous alarm was canceled."); // changes the text on the textbox under the time picker
Log.d("MyActivity", "Alarm OFF");
}
}
public void setAlarmText(String textToShow) {
alrmStatusView.setText(textToShow); // sets the text for the textbox below the TimePicker
}
#Override
public void onDestroy() {
super.onDestroy(); // calls the super classes destroy method to destroy the activity
}
}
AlarmReceiver:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {`
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); //this will sound the alarm tone
Log.d("Creating Alarm", "Used ALARM for ringtone " + alarmUri);
System.out.println("logging that it got to this part");
if (alarmUri == null) {
Log.d("Creating Alarm", "Used the notification instead of alarm for ringtone");
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
ringtone.play(); // plays the ringtone of the phone as the alarm
Intent service_intent = new Intent(context, AlarmService.class);
context.startService(service_intent);
//ComponentName comp = new ComponentName(context.getPackageName(), AlarmService.class.getName());
//startWakefulService(context, (intent.setComponent(comp))); // sends the notification message and wakes up the phone
setResultCode(Activity.RESULT_OK);
}
}
AlarmService.java:
public class AlarmService extends IntentService {
private NotificationManager alarmNotificationManager;`
public AlarmService() {
super("AlarmService");
}
#Override
public void onHandleIntent(Intent intent) {
sendNotification("Wake Up! Your alarm has been rung!!!!"); // sends the notification to the phone that the alarm is ringing
}
private void sendNotification(String msg) {
Log.d("AlarmService", "Preparing to send notification...: " + msg);
alarmNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AlarmStartPage.class), 0); // creates the notification and sets the icon for the notification
NotificationCompat.Builder alarmNotificationBuilder = new NotificationCompat.Builder(
this).setContentTitle("Alarm").setSmallIcon(R.mipmap.ic_launcher).setStyle(new NotificationCompat.BigTextStyle().bigText(msg)).setContentText(msg);
alarmNotificationBuilder.setContentIntent(contentIntent);
alarmNotificationManager.notify(1, alarmNotificationBuilder.build());
Log.d("AlarmService", "Notification sent.");
}
}
OnBootReceiver:
public class OnBootReceiver extends BroadcastReceiver {
private static final int WAITING_PERIOD = 10000; // 10 seconds (aka 10000 milliseconds)`
#Override
public void onReceive(Context context, Intent intent)
{
AlarmManager aMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); // allows previously created alarms to still exist on bootup.
Intent i = new Intent(context, AlarmReceiver.class);
PendingIntent pI = PendingIntent.getBroadcast(context, 0, i, 0);
aMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), WAITING_PERIOD, pI);
}
}
Code can also be found on https://github.com/sahilmgandhi/REM.my/tree/master/app/src/main/java/com/sahilmgandhi/remmy
Any help would be appreciated.
Edit:
This is the solution we came up with in case if it helps anyone:
Nick: So the broadcast does start the service which runs the notification, but the issue is where the sound is supposed to be changed and it doesn't?
Nick: Ok so is there any reason you are having the sound play from the Receiver and not have the sound play straight from the notification object? I think that would work
Me: Hmm, i was following several different tutorials and all of them seemed to have it in the receiver.
Nick: in the service sendNotification method try to change to this:
private void sendNotification(String msg) {
Log.d("AlarmService", "Preparing to send notification...: " + msg);
alarmNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AlarmStartPage.class), 0); // creates the notification and sets the icon for the notification
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
NotificationCompat.Builder alarmNotificationBuilder = new NotificationCompat.Builder(
this)
.setContentTitle("Alarm")
.setSmallIcon(R.mipmap.ic_launcher)
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg)
.setSound(soundUri);
alarmNotificationBuilder.setContentIntent(contentIntent);
alarmNotificationManager.notify(1, alarmNotificationBuilder.build());
Log.d("AlarmService", "Notification sent.");
}
Nick: Pretty sure that's the correct way to do notification sounds now.. old way with Ringtone is probably deprecated. Comment the ringtone part out for now
Nick: And the correct way to run a service from AlarmManager is to have it fire a BroadcastReceiver which then starts your IntentService.
Nick: I forget exactly why that's the case.. but you definitely want to do it that way. I've thought about running everything in the receiver but its best to do it that way, alarm -> receiver -> IntentService with all the code in it

To get your alarm to fire at the correct time, add this line after you instantiate your Calendar object in your AlarmStartPage activity:
calendar.setTimeInMillis(System.currentTimeMillis());
So your code should now look like this when you create your Calendar:
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
This will make sure the other Calendar parameters are set correctly, because before you had it just setting the hour/minute but the rest was not fully configured.

Related

Xamarin AlarmManager not firing when app sleeps

I have following code to fire an alarm at exact time. When my app goes to sleep/background, Alarm does not FIRE, however as soon as I unlock my phone, then it fires right away.
Intent i = new Intent(this, typeof(MyReceiver));
i.PutExtra("Speaker", txtSpeaker.Text);
PendingIntent pi = PendingIntent.GetBroadcast(this, 0, i, 0);
string _date = DateTime.Today.ToString("MM-dd-yyyy") ;
string _time = tpick.Hour + ":" + tpick.Minute;
DateTime scheduleAt = Convert.ToDateTime(_date).Add(TimeSpan.Parse(_time));
DateTimeOffset dateOffsetValue = DateTimeOffset.Parse(scheduleAt.ToString());
var millisec = dateOffsetValue.ToUnixTimeMilliseconds();
AlarmManager alarmManager = (AlarmManager)GetSystemService(AlarmService);
alarmManager.SetExact(AlarmType.RtcWakeup, millisec, pi);
and here is my Receiver Class...
[BroadcastReceiver]
public class MyReceiver : BroadcastReceiver
{
public async override void OnReceive(Context context, Intent intent)
{
Console.WriteLine("FIRED");
String result = intent.Extras.GetString("Speaker");
Toast.MakeText(context, "Alarm Ringing!", ToastLength.Short).Show();
}
}
If the App is running on API 23 or newer, you might want to look into using the SetExactAndAllowWhileIdle method. According to the documentation it appears to me that this method will ignore whether the Device is in some low-power state or doze is enabled.

New alarm with different data overrides already set alarm

I'm creating a simple app that will turn my wifi off/on at specified time in future. For example I have wifi on and I want to switch its state (turn it off) at 10PM and switch back and 8 AM (Turn on).
To do that, I've written some block of code which has one bug.
Well, when I set first alarm (let's accord to the example above) at 10PM and second at 8AM only the second one will fire.
According to the answer here:
Editing scheduled pending intends
about pending intents I checked those alarms' pending intents while setting them and they were different.
Okay, I'll provide some code, I will not give whole bunch of code to set up calendar because the bug is not there.
WiFi.class
//This class also stores and loads scheduled alarm from database,
//that's why here is variable Uri = mUri.
private void setAlarm(){
ContentValues values = new ContentValues();
values.put(AlarmReminderEntry.KEY_TITLE, title);
values.put(AlarmReminderEntry.KEY_DATE, mDate);
values.put(AlarmReminderEntry.KEY_TIME, mTime);
values.put(AlarmReminderEntry.KEY_REPEAT, repeat);
values.put(AlarmReminderEntry.KEY_YEAR, mYear);
values.put(AlarmReminderEntry.KEY_MONTH, mMonth);
values.put(AlarmReminderEntry.KEY_DAY, mDay);
values.put(AlarmReminderEntry.KEY_HOUR, mHour);
values.put(AlarmReminderEntry.KEY_MINUTE, mMinute);
values.put(AlarmReminderEntry.KEY_REPEAT_NO, mRepeatNo);
if (mUri == null) {
Uri newUri =
getContentResolver().insert(AlarmReminderEntry.CONTENT_URI, values);
if (newUri == null) {
Toast.makeText(context, "error saving alarm",
Toast.LENGTH_SHORT).show();
} else {
//WiFiScheduler() is an instance of another class, see code below
//setAlarm(Context context, long timeInMilis, Uri uri)
new WiFiScheduler().setAlarm(getApplicationContext(),
calendar.getTimeInMillis(), mUri);
Toast.makeText(context, "Alarm will fire one time
only", Toast.LENGTH_SHORT).show();
Log.v("Alarm time",
String.valueOf(calendar.getTime()));
}
WifiScheduler.class --> class where the method setAlarm is called from.
public class WiFiScheduler {
public void setAlarm(Context context, long alarmTime, Uri
reminderTask) {
AlarmManager alarmManager = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
Log.v("AlarmManager", "Initializing AM");
//Here in PendingIntent instance there is another class called called
// WiFiService
PendingIntent operation =
WiFiService.getReminderPendingIntent
(context,reminderTask);
if (Build.VERSION.SDK_INT >= 23) {
assert alarmManager != null;
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
alarmTime, operation);
} else {
assert alarmManager != null;
alarmManager.setExact(AlarmManager.RTC_WAKEUP,
alarmTime,operation);
}
}
}
And finally WiFiService.class
public class WiFiService extends IntentService {
private static final String TAG = WiFiService.class.getSimpleName();
public static PendingIntent getReminderPendingIntent(Context context,
Uri uri) {
Intent action = new Intent(context, WiFiService.class);
action.setData(uri);
Log.v(TAG, "uri passed into intent");
String pi = String.valueOf(PendingIntent.getService(context, 0,
action, PendingIntent.FLAG_UPDATE_CURRENT));
Log.v(TAG, pi); <--
Those outputs of 2 alarms I'll give in the end of this post, but they were different
return PendingIntent.getService(context, 0, action,
PendingIntent.FLAG_UPDATE_CURRENT);
}
public WiFiService() {
super(TAG);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
WifiManager wifiManager = (WifiManager)
getApplicationContext().getSystemService(WIFI_SERVICE);
if (wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(false);
Log.v(TAG, "Wifi is turning off");
} else {
wifiManager.setWifiEnabled(true);
Log.v(TAG, "Wifi is turning on");
}
Calendar calendar = Calendar.getInstance();
int h = calendar.get(Calendar.HOUR_OF_DAY);
int m = calendar.get(Calendar.MINUTE);
int ms = calendar.get(Calendar.SECOND);
Log.v("Time", String.valueOf(h) + ":" + String.valueOf(m) + ":" +
String.valueOf(ms));
}
}
WiFiService.class defined in Manifest as:
service android:name=".WiFiScheduler.WiFiService"
android:exported="false"
No brackets above, page didn't want to accept them, in the code obviously they are.
And now outputs of PendingIntent.getBroadcast, read twice,
first after first alarm was set and second after second was set.
V/WiFiService: PendingIntent{d3aacca: android.os.BinderProxy#dd4e3b}
V/WiFiService: PendingIntent{3acdbb: android.os.BinderProxy#d5ac2d8}
So, where may I did an error?
And for the end, may I ask where can I find Oreo stock alarm app source code or app alike? (It must be written for Marshmallow and above, most apps on GitHub are written for Lollipop and above, I found just 2 for Nougat)
Bests
Check the Uri that you put in the Intent and ensure that the 2 Uris are different for the 2 different calls. Also, make absolutely sure that the times are correct that you pass to AlarmManager.
The other thing you can do is, after you set each alarm, do adb shell dumpsys alarm and check that the alarm has been set with the correct values. See this question if you need help understanding how to read the output of adb shell dumpsys alarm

Understanding My AlarmManager Logic

I need help in understanding the logic that I have implemented in my app using AlarmManager, since the logic is not working as I expected. I am new to Android development and a self-learner.
The Logic :
User selects a time in Main Activity using a button.
That time value is used for setting repeating alarm in Another activity.
When the alarm goes of A dialog box appears.
Main Activity:
static long ATfrom_to_millisec;
case R.id.ATfrom:
final Calendar ATfromC = Calendar.getInstance();
ATfromHour = ATfromC.get(Calendar.HOUR_OF_DAY);
ATfromMinute = ATfromC.get(Calendar.MINUTE);
// Launch Time Picker Dialog
TimePickerDialog ATfromtpd = new TimePickerDialog(MainActivity.this,
new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
//Converting to milli seconds to use in AlarmManager
ATfrom_to_millisec = ((hourOfDay * 60 * 60 * 1000) + (minute * 60 * 1000));
String AM_PM;
if (hourOfDay < 12) {
AM_PM = "AM";
} else {
AM_PM = "PM";
if (hourOfDay > 12) {
hourOfDay = hourOfDay - 12;
}
}
if (minute < 10) {
ATfrom.setText(hourOfDay + ":0" + minute + " " + AM_PM);
} else {
ATfrom.setText(hourOfDay + ":" + minute + " " + AM_PM);
}
In the above logic the time selected by the user is converted to milliseconds and stored in a public static variable so that it can be used by other activities.
Alarm Activity :
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch (arg0.getId()){
case R.id.bReminder:
try {
//Receiving the static variable's value from MainActivity
long AT_from = MainActivity.ATfrom_to_millisec;
float cov = (float)(AT_from/1000/60/60);
//Toast to check if the time value is passed correctly
toast.makeText(getApplicationContext(), String.valueOf(cov), toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, Notifier.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(),
12345, intent, 0);
//Logic for Repeating Alarm that uses the time value from MainActivty as starting time and repeating it every 10 seconds.
// i.e. the alarm should go off after 10 seconds from the use selected time and repeat after 10 seconds.
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, AT_from + (10*1000), 10*1000, pendingIntent);
} catch (Exception e) {}
break;
case R.id.bReminderStop:
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.cancel(pendingIntent);
break;
}
}
When alarm is set with the above implemented logic , The alarm goes of immediately once it is set and repeats with an interval of 1 minute.
But I implemented my logic expecting the alarm to go of 10 seconds from the time set by user and repeat after every 10 seconds
I am not able to understand the mistake implemented in my logic.
Also in the toast I am not getting the decimal value of float cov
(eg : 11:30 PM must be displayed as 23.5 .But it is displayed as 23.0)
Dialog Activity when Alarm goes off :
public class Notifier extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("HydrateTime");
alertDialogBuilder.setMessage("Enter the amount of water");
alertDialogBuilder.setPositiveButton("Update", null);
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
}
I want the above Activity to be invoked when the Alarm goes off. Is the above implemented logic enough to make a Dialog window appear when an activity goes of?
Note : I am using a general activity definition in the Android Manifest.
like :
<activity
android:name=".Notifier"
android:label="#string/app_name">
</activity
Kindly request answers for the below questions :
Am I passing the time value set in the MainActivity to the AlarmManager in another activity correctly?
What is the mistake in my Alarm logic and how to rectify it such that the alarm goes of as per my requirement?
Is the definition for the dialog box correct? should there any more definitions in Manifest? should I create any layout file for the dialog activity? Should I extend my Notifier class with Dialog class instead of Activity class?
The logic has a minor flaw. You see, with alarms if the time to fire has already passed, then the alarm will fire immediately. For eg: if I set an alarm with milli seconds for 2 Jan 1983, it would fire immediately. If you notice, you are doing something similar.
To fix it, simply add this in your Alarm Activity:
// Will trigger after 10 secs from set time
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AT_from + (10*1000), 10*1000, pendingIntent);
I am not sure what is causing the interval problem though. It seems fine to me.

Setting repeating alarm when creating Application - why does it trigger immediately

I have an application object declared in the manifest and this code runs when the application runs. What I want to accomplish is to set an alarm:
#Override
public void onCreate() {
super.onCreate();
singleton = this;
persister = new Persister();
am = (AlarmManager) getSystemService(ALARM_SERVICE);
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
sharedPref.registerOnSharedPreferenceChangeListener(this);
registerAlarms();
}
public void registerAlarms() {
String sleepString = sharedPref.getString("time_sleepLog", "08:00");
String[] pieces = sleepString.split(":");
int sleepHour = Integer.parseInt(pieces[0]);
int sleepMinute = Integer.parseInt(pieces[1]);
String eveningString = sharedPref.getString("time_eveningLog", "20:00");
pieces = null;
pieces = eveningString.split(":");
int eveningHour = Integer.parseInt(pieces[0]);
Log.v(TAG, "eveningHour in registerAlarms: " + eveningHour);
int eveningMinute = Integer.parseInt(pieces[1]);
// create calendar objects pointing to the next time this clock will
// occur
Calendar sleepCal = Calendar.getInstance();
sleepCal.set(Calendar.HOUR_OF_DAY, sleepHour);
sleepCal.set(Calendar.MINUTE, sleepMinute);
sleepCal.set(Calendar.SECOND, 0);
Calendar eveningCal = Calendar.getInstance();
eveningCal.set(Calendar.HOUR_OF_DAY, eveningHour);
eveningCal.set(Calendar.MINUTE, eveningMinute);
sleepCal.set(Calendar.SECOND, 0);
Intent syncIntent = new Intent(this, SleepNotificationReceiver.class);
syncIntent.putExtra("MoodSleepLogAlarm", 0);
PendingIntent sleepPending = PendingIntent.getBroadcast(this, 0,
syncIntent, 0);
// then set the alarms
am.setRepeating(AlarmManager.RTC_WAKEUP, sleepCal.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, sleepPending);
Log.v(TAG, "Alarm for sleep registered at " + sleepCal.getTime());
}
I log in the broadcastreceiver to check if it runs with:
Log.v(TAG, "Context: " + context.getClass().getName())
I can see this in logcat 4 seconds after starting my application as 0
6-27 17:59:29.492: V/SleepNotificationReceiver(2609): Context: android.app.ReceiverRestrictedContext
When I call the registerAlarms() via a button it doesn't happend. So it is only when called onCreate.
Why does it run the broadcast receiver after 4 seconds? (It does also run the broadcastreceiver with the same context given at the given times in my settings screen - but I need it to not run when I set it.)
When you are setting the alarm repeating, the second parameter indicates when the alarm has to go off the first time, so I think that if you type sleepCal.getTimeInMillis() and you are in the "future" compared to this time, the alarm is going off instantly.
Check that second parameter if it's in the future because I think that you are not setting the day or month in your calendar.

Techniques to implement a notification service

I have a main activity where the user can enable/disable notifications, set the notification interval, and set the base time the notification interval will use. Notifications will typically trigger about 2 hours from each other. After a certain time, an accumulator will reach a maximum value and notifications will no longer be needed.
What is the standard way of implementing such a notification scheme? I tried using a handler inside of a service using postAtTime, but it seems that there are a lot of conditions that can cause it to never run. I looked at a timer inside of the service, but putting the phone in standby will stop any timers, plus it just seems like a bad idea.
The only other option I came across I have yet to explore, but it involves using an AlarmManager and a BroadcastReceiver. Should I just ditch the service and schedule a repeating alarm instead? I need to be able to disable all remaining alarms once my accumulator has reached max value.
Thanks for any input.
What if you start a service that spawns a thread like this:
thread t = new thread(new Runnable(){
public void Run(){
boolean notified = false;
while( !notified ){
if( notify_time - time > 1000 ){
Thread.sleep(999);
else if( notify_time - time <= 0 ){
// START NOTIFICATION ACTIVITY
notified = true;
}
}
}
}
t.start();
I have not done anything like this personally, so I am not sure what service can do to notify the user or start an activity, but it does have the full panoply of options available to an activity, so yeah.
Oh but it just occured to me you'll need to use a handler for that because of the multithreaded aspect here.
Since I will always have a finite number of notifications and I can calculate the elapsed time in advance, it seems the combination of AlarmManager and a BroadcastReceiver work pretty well. Here is how I implemented this:
I first created a BroadcastReceiver
public class NotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Get handle to system notification manager
NotificationManager mNM = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
//Get message from intent
Bundle bundle = intent.getExtras();
CharSequence text = bundle.getString("notification_message");
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.notification_icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(context, context.getText(R.string.app_name),text, contentIntent);
// Set Flags
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Send the notification.
mNM.notify(R.string.notification, notification);
}
}
I then created a class that used a AlarmManager to create/cancel alarms that send a message to the BroadcastReceiver
public class NotificationSender {
private AlarmManager mAlarmManager;
private Context mContext;
private Intent mIntent;
public NotificationSender(Context context){
this.mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.mIntent = new Intent(context, NotificationReceiver.class);
this.mContext = context;
}
public void setAlarm(Long etaMillis, int accumulator){
//Create intent to send to Receiver
this.mIntent.putExtra("notification_message","Message");
//Use accumulator as requestCode so we can cancel later
PendingIntent sender = PendingIntent.getBroadcast(this.mContext, accumulator, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Set Alarm
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, etaMillis, sender);
}
public void cancelAlarms(){
//requestCode (accumulator) will always be a multiple of 10 and less than 100
for (int x = 10; x <= 100; x += 10){
PendingIntent operation = PendingIntent.getBroadcast(this.mContext, x, this.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(operation);
}
}
public void createAlarms(PreferenceHelper prefs){
//Calculate time notifications are due and set an alarm for each one
//PreferenceHelper is a class to help pull values from shared preferences
Date currentTime = new Date();
for (int i = prefs.getNotificationInterval(); i <= 100; i += prefs.getNotificationInterval()) {
if (i > prefs.getAccumulator()) {
this.setAlarm(SystemClock.elapsedRealtime() + calculateETA(i, prefs).getTime() - currentTime.getTime(), i);
}
}
}
public void refreshAlarms(PreferenceHelper prefs){
this.cancelAlarms();
if (prefs.isNotificationsEnabled()) this.createAlarms(prefs);
}
}
The important part is to use the accumulator as the requestCode so we can cancel all of our alarms later.
Finally I used the NotificationSender class in my activity by calling refreshAlarms() in onCreate() and whenever the user modifies preferences that are relevant to scheduling notifications. Rebooting the phone will clear all alarms so the app must be restarted before notifications will begin. If the system happens to kills the process, the alarms will still trigger at the appropriate time.

Categories

Resources