This is my first question on stackoverflow. I have read many similar topics but have been unable to find the problem and I am getting a bit desperate.
I develop an app with an alarm clock. This alarm clock only rings under certain circumstances. If they don't apply, I send a notification to the user and set the next alarm 10 minutes later using setAlarmClock(). If the conditions don't apply until a user specified time, the alarm never goes off.
Now when I leave the phone unobserved for a while and check the notifications, I see that they didn't come in every 10 minutes, but very irregularly. Sometimes it came after 10 minutes, other times 11, 15, 30, or whatever.
The problem only occurs when the phone is not plugged in, and so I figured it has something to do with Android's doze mode. setAlarmClock() should help with this problem but it doesn't. I also tried setExactAndAllowWhileIdle(), but that didn't work either. So if anyone has any idea what the issue might be, I would be really greatful to hear about it.
This is my code:
First, the two methods inside my AlarmController
//this is called the first time an alarm is set
public void registerAlarm(int id, AlarmClock alarm) {
if (alarmManager == null)
alarmManager = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Calendar alarmTime = alarm.getAlarmCalendar();
//create alarm intent
Intent intent = new Intent("START_ALARM");
intent.putExtra("alarm_id",id);
PendingIntent alarmIntent = PendingIntent.getBroadcast(ctx, id, intent, 0);
AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(alarmTime.getTimeInMillis(),alarmIntent);
alarmManager.setAlarmClock(info, alarmIntent);
//alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,alarmTime.getTimeInMillis(),alarmIntent);
}
// This is called if the conditions didn't apply
public void registerSnoozeAlarm(int id, int minutes) {
if (alarmManager == null)
alarmManager = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
long alarmTime = System.currentTimeMillis() + 1000*60*minutes;
//create alarm intent
Intent intent = new Intent("START_ALARM");
intent.putExtra("alarm_id",id);
PendingIntent alarmIntent = PendingIntent.getBroadcast(ctx, id, intent, 0);
AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(alarmTime, alarmIntent);
alarmManager.setAlarmClock(info, alarmIntent);
//alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime,alarmIntent);
}
The Receiver in the manifest:
<receiver android:name="packagename.AlarmReceiver"
android:exported="false">
<intent-filter>
<action android:name="START_ALARM" >
</action>
</intent-filter>
</receiver>
This is my AlarmReceiver:
public class AlarmReceiver extends WakefulBroadcastReceiver implements Observer{
private Context context;
private AlarmClock alarm;
private Intent intent;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
this.intent = intent;
//request data from server
Bundle extras = intent.getExtras();
int alarmID = extras.getInt("alarm_id");
alarm = DataHandler.getInstance(context).getAlarm(alarmID);
DataHandler.getInstance(context).requestDataForAlarm(alarm,this);
}
#Override
public void update(Observable observable, Object data) {
Measurement measurement = (Measurement) data;
if (*someConditionapplies*){
//play ringtone
AlarmSoundController sc = AlarmSoundController.getInstance(context);
sc.playSound(alarm.getAlarmSoundPath());
//send a notification message
ComponentName comp = new ComponentName(context.getPackageName(), AlarmNotificationService.class.getName());
intent.putExtra("alarm_id",alarm.getId());
intent.putExtra("description", alarm.getDescription());
intent.putExtra("data", measurement.getData());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
else {
//if user specified end time is reached, send final notification, else try again in 10 minutes
Calendar alarmEndTime = alarm.getAlarmEndCalendar();
Calendar now = Calendar.getInstance();
if (now.before(alarmEndTime)) {
//schedule new alarm in 10 minutes
DataHandler.getInstance(context).registerSnoozeAlarm(alarm.getId(),10);
sendNoAlarmNotification(measurement, false);
setResultCode(Activity.RESULT_OK);
}
else{
//send the last notification message
sendNoAlarmNotification(measurement, true);
setResultCode(Activity.RESULT_OK);
}
}
DataHandler.getInstance(context).removeObserver(this);
}
private void sendNoAlarmNotification(Measurement measurement, boolean lastNotification) {
ComponentName comp = new ComponentName(context.getPackageName(), NoAlarmNotificationService.class.getName());
intent.putExtra("alarm_id", alarm.getId());
intent.putExtra("description", alarm.getDescription());
intent.putExtra("wind", measurement.windAvg);
intent.putExtra("last_notification", lastNotification);
intent.setComponent(comp);
startWakefulService(context, intent);
}
}
Thanks a lot in advance!
Related
I've built one small reminder app demo. Now the problem is sometimes reminder coming or sometimes not? Now, I have this feature that user can set the alarm for day, month, week, year.
Now how can I check for the month or year that notification is coming or not?
Obviously, when I manually go and change the Android date and time settings, alarm not coming. (Even for some minutes also)
See: When I change Android phone date and time, notification not getting, otherwise, if I do nothing, I get notification on time
Edit:
Code:
public void setRepeatAlarm(Context context, Calendar calendar, int ID, long RepeatTime, int requestCode) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// Put Reminder ID in Intent Extra
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra(REMINDER_ID, Integer.toString(ID));
mPendingIntent = PendingIntent.getBroadcast(context, ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
mPendingIntent = PendingIntent.getBroadcast(context, requestCode, new Intent(context, AlarmReceiver.class),
PendingIntent.FLAG_NO_CREATE);
isAlarmSet = (mPendingIntent != null);
if (isAlarmSet) {
showLog("isAlarmSet: " + isAlarmSet);
} else {
showLog("isAlarmSet: " + isAlarmSet);
}
// Calculate notification timein
Calendar c = Calendar.getInstance();
long currentTime = c.getTimeInMillis();
long diffTime = calendar.getTimeInMillis() - currentTime;
// Start alarm using initial notification time and repeat interval time
mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + diffTime,
RepeatTime, mPendingIntent);
// Restart alarm if device is rebooted
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
You can check for existing alarm using this :
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode,
new Intent(this, AlarmReceiver.class),
PendingIntent.FLAG_NO_CREATE);
boolean isAlarmSet = (pendingIntent!=null);
Note that the requestCode here should be the same one used while setting the alarm. Also, AlarmReceiver is your BroadcastReceiver class for alarm
I have not tested this in the scenario you mentioned (changing the phone time), but you can refer this and set your alarm again according to the new time and cancel the previous one.
You have pretty knowledge about Design Pattern. Please follow the Observer Pattern to mange below thing and get always new time when you change time manually or not. It will check current time is changed or not. More details Please read Observer Pattern Documentation.
// Calculate notification timein
Calendar c = Calendar.getInstance();
long currentTime = c.getTimeInMillis();
long diffTime = calendar.getTimeInMillis() - currentTime;
You should listen to time changes in the android system and act accordingly
<receiver android:name=".TimeChangedReceiver">
<intent-filter >
<action android:name="android.intent.action.TIME_SET"/>
</intent-filter>
</receiver>
Broadcast receiver class
public class TimeChangedReceiver extends BroadcastReceiver{
#Override
public void onReceive(final Context arg0, Intent arg1) {
//Call your Alarm setting code
// change your alarm accordingly
}
}
This is an example
But I need to make a called for 5 minutes or 10 minute automatically. How I can make it?
Intent in=new Intent(Intent.ACTION_CALL,Uri.parse("0000000000"))
try{
startActivity(in);
}
you can use broadcast receiver and set action as Time change.And set Alarm for every 5 min to send action to broadcast receiver.
Add this code in your activity:
PendingIntent service = null;
Intent intentForService = new Intent(context.getApplicationContext(), YourService.class);
final AlarmManager alarmManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
final Calendar time = Calendar.getInstance();
time.set(Calendar.MINUTE, 0);
time.set(Calendar.SECOND, 0);
time.set(Calendar.MILLISECOND, 0);
if (service == null) {
service = PendingIntent.getService(context, 0,
intentForService, PendingIntent.FLAG_CANCEL_CURRENT);
}
alarmManager.setRepeating(AlarmManager.RTC, time.getTime()
.getTime(), 60000, service);
Create Broadcast Receiver and add code for Make Call in onReceive() of receiver.
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("BroadcastReceiver", "debut receive");
Intent in=new Intent(Intent.ACTION_CALL,Uri.parse("0000000000"))
startActivity(in);
}
}
I hope its helpful to you.
Happy Coding..
I could not explain my problem in the Title..Here's the detailed explanation of my problem :
I have an application where arrival time of trains are displayed.
I want to add a feature where User can create an alert if the train is 10-5 minutes away from station.
A form is displayed to create an alert (all the values are stored in DB).
alertID = db.insert_alerts(name,starttime,endtime,booleanrepeat);
I am using alarm manager to create the notification :
if (is_repeat.equals("true")) {
new AlertsReceiver().setRepeatAlarm(getApplicationContext(), mCalendar, (int)alertID ,10000);
} else if (is_repeat.equals("false")) {
new AlertsReceiver().setAlarm(getApplicationContext(), mCalendar,(int)alertID);
}
below is the Receiver class :
public class AlertsReceiver extends WakefulBroadcastReceiver {
private int alertId;
AlarmManager mAlarmManager;
PendingIntent mPendingIntent;
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if(extras == null) {
Log.d("Service","null");
} else {
alertId= extras.getInt("alertId");
}
Intent intent1 = new Intent(context,MService.class);
intent1.putExtra("alertId", alertId);
context.startService(intent1);
}
public void setAlarm(Context context, Calendar calendar, int ID) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context,AlertsReceiver.class);
intent.putExtra("alertId", ID);
mPendingIntent = PendingIntent.getBroadcast(context, ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// Calculate notification time
Calendar c = Calendar.getInstance();
long currentTime = c.getTimeInMillis();
long diffTime = calendar.getTimeInMillis() - currentTime;
// Start alarm using notification time
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + diffTime,
mPendingIntent);
}
public void setRepeatAlarm(Context context, Calendar calendar, int ID, long RepeatTime) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Log.e("asetRepeat",ID+"");
Intent intent = new Intent(context, AlertsReceiver.class);
intent.putExtra("alertId", ID);
mPendingIntent = PendingIntent.getBroadcast(context, ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// Calculate notification timein
Calendar c = Calendar.getInstance();
long currentTime = c.getTimeInMillis();
long diffTime = calendar.getTimeInMillis() - currentTime;
// Start alarm using initial notification time and repeat interval time
mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + diffTime,
RepeatTime , mPendingIntent);
}
public void cancelAlarm(Context context, int ID) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Toast.makeText(context,"Cancelled"+ID,Toast.LENGTH_LONG).show();
// Cancel Alarm using Reminder ID
mPendingIntent = PendingIntent.getBroadcast(context, ID, new Intent(context, RiderAlertsReceiver.class), 0);
mAlarmManager.cancel(mPendingIntent);
}
}
In receiver I am starting a service where I am making a webservice call :
Once an alarm manager is set..A webservice call is made in background service to check the minutes in which train will arrive :
if the minutes < 10 minutes notification is displayed else the webservice call should run again
So, i place the webservice in timer which runs every 1 minute.
The issue is how to stop the timer once the notification is received.
Also, the every time the receiver is starting the same service so only one alert is working at one time.
The user can create multiple alerts and all alerts are stored in DB.
Please can any one tell me the best way to implement this feature.
Appreciate Help...!
I'm failing to see why this alarm is going off on a reboot...I am setting it 7 days ahead here -
Intent intent = new Intent(MainActivity.this, Reminder.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
MainActivity.this, 1, intent, 1);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
try {
am.cancel(pendingIntent);
} catch (Exception e) {
System.out.println("Derp");
}
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 7);
long time = calendar.getTimeInMillis();
am.set(AlarmManager.RTC_WAKEUP, time,
pendingIntent);
Here is my manifest that I have set for alarm to stick around on a reboot - Reminder is the class receiving the alarm-
<receiver android:name="com.practicum.notifications.Reminder" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
By default, all alarms are canceled when a device shuts down. To prevent this from happening, you can design your application to automatically restart a repeating alarm if the user reboots the device. This ensures that the AlarmManager will continue doing its task without the user needing to manually restart the alarm.
You have to manually reset the alarm once again in Bootup Receiver
public class SampleBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
// Set the alarm here.
}
}
All alarms are shut off when you power off the Android device.
You need to call setRepeating method
public class AlarmReceiver extends BroadcastReceiver {
private static final int PERIOD=5000;
#Override
public void onReceive(Context ctxt, Intent i) {
scheduleAlarms(ctxt);
}
static void scheduleAlarms(Context ctxt) {
AlarmManager am = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctxt, YourService.class);
PendingIntent pi = PendingIntent.getService(ctxt, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + PERIOD, PERIOD, pi);
}
}
Check this answer from CommonsWare.
For future reference, I misunderstood how receving the boot complete action worked. I had the intent filter in both of my receiver classes so they were both running, when instead I needed an intent filter on a new broadcastreceiver class to RESET my alarmmanagers.
I am currently starting a service using a Broadcast Receiver which fires 60 seconds after the device boots up. This broadcast receiver triggers an alarm so that my service runs every 60 seconds. Code for this is as follows:
public class ScheduleReceiver extends BroadcastReceiver {
// Restart service every 60 seconds
private static final long REPEAT_TIME = 1000 * 60;
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, StartServiceReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
// Start 60 seconds after boot completed
cal.add(Calendar.SECOND, 60);
// Fetch every 60 seconds
// InexactRepeating allows Android to optimize the energy consumption
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), REPEAT_TIME, pending);
}
}
The above starts the service as follows:
public class StartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, PhotoService.class);
context.startService(service);
}
}
I would like to somehow modify this so that my service runs every 60 seconds when the phone screen is on and every 20 minutes when off.
What is the best way to do this ? Can I modify the alarm dynamically when the screen is switched off/on ?
Thanks for any help,
Regards,
Fido
Okay I have found out how to do this so I thought I would put this here in case anyone else is ever curious. (More elegant solutions are always welcome):
First thing is that in my service I added the following to the OnCreate:
#Override
public void onCreate() {
super.onCreate();
// REGISTER RECEIVER THAT HANDLES SCREEN ON AND SCREEN OFF LOGIC
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScheduleReceiver();
registerReceiver(mReceiver, filter);
}
This is done so that my broadcast receiver can handle those events. Apparently you cannot register these via intent-filters in the xml.
This means that when the service is created it ensures that these additional system actions can be handled.
I then modified my broadcast receiver to be:
public class ScheduleReceiver extends BroadcastReceiver {
private static final int ALARM_ID = 909;
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, StartServiceReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, ALARM_ID, i,PendingIntent.FLAG_CANCEL_CURRENT);
service.cancel(pending);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 60); //Delay startup by one minute - This is useful to prevent over utilization of resources at boot
// Start alarm. Set a long repeat time if the screen is off and a short repeat time if the screen is on
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON) || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 60000, pending);
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 600000, pending);
}
}
}
From the above I cancel any current alarms with the same pending intent and then I set a new alarm whenever the screen if switched off/on.
This seems to be working for me. As always any improvements are very welcome.