I have an Android application that allows a user to add an alarm that will log their location. For example, every day at 8am I want to log where I am. When the alarm is received, it starts a service that gets the user's location and saves it to a db. The feature works fine when the app is running (in the background or foreground).
The problem is, the alarm doesn't seem to go off if I go into the task manager and force the app to close. This is important as the user could reboot their phone and kill the app.
Here is the method that sets the alarm:
private void setNewAlarm(int hour, int minute, int id) {
Intent alarmIntent = new Intent(AutoLoggerActivity.this, AlarmReciever.class);
alarmIntent.putExtra(Constants.ALARMID_FIELD_NAME, id);
PendingIntent sender = PendingIntent.getBroadcast(AutoLoggerActivity.this, id, alarmIntent, 0);
Calendar alarmCal = Calendar.getInstance();
alarmCal.set(Calendar.HOUR_OF_DAY, hour); // set user selection
alarmCal.set(Calendar.MINUTE, minute); // set user selection
alarmCal.set(Calendar.SECOND, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, alarmCal.getTimeInMillis(), 120000, sender);
}
Here is the BroadcastReceiver:
public class AlarmReciever extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
int alarmId = bundle.getInt(Constants.ALARMID_FIELD_NAME);
Toast.makeText(context, "Alarm Running: ID = " + alarmId, Toast.LENGTH_LONG).show();
Log.d(TAG, "AlarmReceiver: onRecieve. ID = " + alarmId);
Intent serviceIntent = new Intent(context, LocationRecordingService.class);
serviceIntent.putExtra(Constants.ALARMID_FIELD_NAME, alarmId);
context.startService(serviceIntent);
}
}
Any idea why this is happening?
This is actually a design feature of the Android operating system, and isn't something you can cirumvent. An application that is in a "stopped" state cannot receive any broadcasts. It is only in this state under two conditions:
When the application is first installed before it is ever launched
When the user actively goes into Settings and does a force close
The problem is, the alarm doesn't seem to go off if I go into the task manager and force the app to close. This is important as the user could reboot their phone and kill the app.
This is not quite true, because if the device is rebooted the application is not considered to be in this state of being "stopped". However, alarms do not persist reboots, so your application will need to listen for ACTION_BOOT_COMPLETED broadcast by the system when the device starts up so you can reschedule any pending alarms.
If the user actively kills your app, you must respect that choice of theirs.
Related
I'm writing my android application, which should always show a notification every day at 10:00 AM, no matter what. I use an alarm service to fire notifications at a specific time.
I set notifications to fire in two cases:
when I open the app, I set the alarm service to fire at the nearest 10:00 AM
when I receive notifications, I set the alarm service to fire the next day at 10:00 AM
The problem is: When the interval between the moment when I set alarm service and the actual alarm is greater than approx 12 hours, I just don't receive that notification. If the interval is smaller - I do. But I checked the logs, the alarm service is scheduled properly and for the correct time no matter how I set notifications.
I expect the problem is in the battery optimizer, but I don't know how to verify (and fix) that.
What I tried
In Android I've added my app to exceptions, allowing it to run in the background.
Tested on Huawei P30 Pro, Android 10.
Attaching the code how notifications are scheduled
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// display notification
NotificationController.scheduleNextNotification(context);
}
}
public class NotificationController {
public static void scheduleNextNotification(Context context) {
Intent myIntent = new Intent(context, AlarmBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
long notificationTime = getNotificationTime(new Date());
int alarmType = AlarmManager.RTC_WAKEUP;
alarmManager.setExactAndAllowWhileIdle(alarmType, notificationTime, pendingIntent);
}
}
my mean goal is to run a task periodically at midnight (00:00:00)
but the user can set the period based on the interval (daily, weekly , monthly)
let's assume that this job is a backup Scheduling.
this task will triggred at midnight but based on the user preference (midnight everyday, every week , or monthly ), and if the phone was in the doze mode or even off , wait untill the next start and start backup.
when i start implementing the code , i started with JobService and JobScheduler , but unfortunately i learned that i can set the repetitive but i can't set the exact time, so the last solution was to work with alarmmanager.
i use this code for triggering the alarm :
public static void setTheTimeToStartBackup(Context context,String periode) {
int DATA_FETCHER_RC = 123;
AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
Intent intent = new Intent(context, BackUpAlarmRecevier.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 0;
switch (periode){
case "never":
return;
case "daily":
alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
interval = AlarmManager.INTERVAL_DAY;
break;
case "weekly":
alarmStartTime.set(Calendar.DAY_OF_WEEK, 1);
interval = AlarmManager.INTERVAL_DAY*7;
break;
case "monthly":
alarmStartTime.set(Calendar.WEEK_OF_MONTH, 1);
interval = AlarmManager.INTERVAL_DAY*30;
break;
}
alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
alarmStartTime.set(Calendar.MINUTE, 0);
alarmStartTime.set(Calendar.SECOND, 0);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),interval, pendingIntent);
Log.e("Alarm","Set for midnight");
}
this is my receiver :
public class BackUpAlarmRecevier extends BroadcastReceiver {
SharedPreferences preferences;
#Override
public void onReceive(Context context, Intent intent) {
Log.e("BackUpAlarmReciver","Triggred");
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG:APP");
wl.acquire();
sendNotification(context);// simple notification...
Toast.makeText(context, "Alarm !!", Toast.LENGTH_LONG).show();
startBackupProcess();
wl.release();
}}
the problem is task never start.
so i went to test it with less time interval (15min as the minimum possible as i read ), so i change my first function setTheTimeToStartBackup to this :
public static void setTheTimeToStartBackup(Context context,String periode) {
int DATA_FETCHER_RC = 123;
AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 55);
Intent intent = new Intent(context, BackUpAlarmRecevier.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
Log.e("Alarm","Set for midnight");
}
and exactly the same problem , nothing started, no log , no notification , nothing.
and i already set the Receiver in my manifest with all permission like that :
<receiver android:name=".job.BackUpAlarmRecevier"
android:enabled="true"
android:process=":remote"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
what im doing wrong in both cases ? and if it work , this code will persist for ever or i need to set it again each time ?
thanks :)
EDIT:
i call my function setTheTimeToStartBackup in the MainActivity.
You could set it to occur at midnight if you did the appropriate time calculations. Dynamically get the current time and date, calculate when to register the broadcast for the alarm manager. Customize the onReceive method to set another alarm at 12pm again.
Either way you can trigger a broadcast receiver by registering your receiver manually.
Broadcast receiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("Alarm received!! ");
// Register alarm again here.
}
}
Code to register a receiver with a custom intent filter.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AlarmManager mAlarmManager = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
getApplicationContext().registerReceiver(new AlarmReceiver(), new IntentFilter("AlarmAction"));
PendingIntent broadcast = PendingIntent.getBroadcast(this, 0, new Intent("AlarmAction"), 0);
// Add dynamic time calculations. For testing just +100 milli.
mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 100, broadcast);
;
}
You could achieve what you wanted through a background service.
My suggestion would be to turn the problem around a bit.
Create three topics on Firebase (daily, weekly, monthly). Subscribe users to appropriate topics. Have a Firebase function that is triggered by CRON job which sends the data notification down to the device, this data notification should schedule one-time WorkManager job for the update. This way you can control everything from server-side, if the phone is off, it will still execute as soon as it turns on and you don't need to manually take care of catching the Boot completed with alarm manager etc.
I'm making an android app to be used to help recover a lost phone. The app uses a Service that is constantly running and when a text message (SMS) is received by the phone, a BroadcastReceiver's (already registered in the service) onReceive() event is triggered. The functionality that I want is that the BroadcastReceiver be able to make the phone produce a loud noise even if the phone was on silent. The most logical way I can think of doing this is to use and alarm clock set one minute from the current time.
This is the code I'm currently using inside of the broadcast receiver:
Calendar c = Calendar.getInstance();
Intent alarmIntent = new Intent(AlarmClock.ACTION_SET_ALARM);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.putExtra(AlarmClock.EXTRA_MESSAGE, "Polo!");
alarmIntent.putExtra(AlarmClock.EXTRA_HOUR, c.get(Calendar.HOUR_OF_DAY));
alarmIntent.putExtra(AlarmClock.EXTRA_MINUTES, c.get(Calendar.MINUTE) + 1);
context.startActivity(alarmIntent);
Fortunately, this code works, but it only ever works once. But that I mean, the first time this code is run, the phone's flow is interrupted and the clock app is opened with a new alarm set. When you attempt to do this again, the clock app is opened, but no alarm is set. I have to restart my phone to get this to work again.
From what I can tell, the issue has to do with the clock app and I need to restart the clock app to get my service to work. This is a really sketchy solution, but I don't know what else to do.
Any recommendations?
The trick is to develop your own Alarm screen and re-set the alarm while onCreate.
Like that, your app will continuously ring up.
for that :
initialise a WakefulBroadcastReceiver
manifest.xml
<receiver android:name="com.mycompany.myapp.AlarmManagerBroadcastReceiver"></receiver>
AlarmManagerBroadcastReceiver.java
public class AlarmManagerBroadcastReceiver extends WakefulBroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Calendar c = Calendar.getInstance();
Intent alarmIntent = new Intent(context, MyAlarm.class);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.putExtra(AlarmClock.EXTRA_MESSAGE, "Polo!");
alarmIntent.putExtra(AlarmClock.EXTRA_HOUR, c.get(Calendar.HOUR_OF_DAY));
alarmIntent.putExtra(AlarmClock.EXTRA_MINUTES, c.get(Calendar.MINUTE) + 1);
context.startActivity(alarmIntent);
}
}
MyAlarm.java :
public class MyAlarm extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
// doing UI stuff
AlarmUtil.setAlarm(this);
}
}
AlarmUtil.java
public class AlarmUtil {
#SuppressLint("NewApi")
public static void setAlarm(Context context){
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
int requestCode = 1234;
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
am.setExact(AlarmManager.RTC_WAKEUP, 60 * 1000, pi);
}
else{
am.set(AlarmManager.RTC_WAKEUP, 60 * 1000, pi);
}
}
}
I tried to develop a sample Alarm Application. I searched Google and SC, most of their examples confused. How can I create an alarm application with the following requirements,
In My Home screen i have a button, like "START ALARM", when i click the button a time picker must enable.
I select the time as I wish, once I pick the time, the alarm icon will enabled on widget. (For example if we set the alarm in default mobile Alarm application, the icon will be enabled, that indicates the alarm is set).
When the set time is reached (the time which is set form the TimePicker app), the alarm will beep.
These are my requirements, I finished the first two points, but I'm still struggling on setting the alarm.
Take a look at AlarmManager. And, If you want to use alarm simultaneously you must use Service class for that. And, see below sample code -
public class OnBootReceiver extends BroadcastReceiver {
private static final int PERIOD=300000; // 5 minutes
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager mgr =
(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, OnAlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+60000, PERIOD, pi);
}
This will repeat the alarm with every 6 Mins. See Scheduling Repeating Alarms document.
when you enable the alarm you have to call inbuilt alarm manager and use the alarmmanager.set to set the alarm time in the manager.
Once the alarm time (in milliseconds) is given to the alarm manager it will send message and you can retrive the message through reciever class
//creating and assigning value to alarm manager class
Intent AlarmIntent = new Intent(MainActivity.this, AlarmReciever.class);
AlarmManager AlmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent Sender = PendingIntent.getBroadcast(MainActivity.this, 0, AlarmIntent, 0);
AlmMgr.set(AlarmManager.RTC_WAKEUP, Alarm.getTimeInMillis(), Sender);
For recieving the alarm you have to make a new class which extends reciever where in onrecieve you can set the intent to the activity u want to call on alarm time , you can also provide notification.
public class AlarmReciever extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{ //Build pending intent from calling information to display Notification
PendingIntent Sender = PendingIntent.getBroadcast(context, 0, intent, 0);
NotificationManager manager = (NotificationManager)context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
Notification noti = new Notification(android.R.drawable.stat_notify_more, "Wake up alarm", System.currentTimeMillis());
noti.setLatestEventInfo(context, "My Alarm", "WAKE UP...!!!", Sender);
noti.flags = Notification.FLAG_AUTO_CANCEL;
manager.notify(R.string.app_name, noti);
//intent to call the activity which shows on ringing
Intent myIntent = new Intent(context, Alarmring.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);
//display that alarm is ringing
Toast.makeText(context, "Alarm Ringing...!!!", Toast.LENGTH_LONG).show();
}}
If you still get any problem ask again..:)
If you want to make things interesting, you can try to create one without a possibility of dismissing/snoozing. I made this a while ago, you can read about it in this tutorial:
Alarm Application in Android (Tutorial using AlarmManager)
And test the app functionality by downloading this app:
Oversleeper on Google Play
To finish your last point you need to do Date Comparision and use AlaramManager Alaram Doc and again you need to use Service
to compare next date and time. Hope it will helpful for you.
You need to use RingtoneManageror the NotificationManager(to show any text or image to the user for notification at the top of screen), Or you can use MediaPlayerto set to play sound when alarm time is reached. You have to set <receiver> tag in manifest file, that must include a class extending BroadCastReceiver. In the receiver class you can write your code to wake your device up.
I would like to display an alert dialog when the alarm goes off. Here is where i am so far. Im not sure if im doing it right.
#Override
void doTaskWork(Intent intent){
String taskId = intent.getStringExtra(TaskHelper._id);
NotificationManager mgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, TaskDetails.class);
notificationIntent.putExtra(TaskHelper._id, taskId);
PendingIntent pi = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
Notification note = new Notification(R.drawable.stat_sys_warning, );
}
}
Alarm:
You can schedule a pending intent that drives what you want when the alarm fires. The process is:
Determine how often you want the alarm to fire. You can fire at an exact time, a specific time from now (in 10 seconds..), or a specific repeat at an interval (every x seconds/minutes/etc.). You can also set a specific time to start the repeat process. The interval isn't variable. Then you have to do one shots and set another alarm for the next time. You can also set flags that determine the time format (millis, RTC, ...). Finally, you can have the alarm firing wake up the device or let it sleep and get scheduled the next time the phone is awake.
Now, as to what is scheduled. A pending intent is scheduled. The pending intent wakes up a broadcast receiver. Here's some clips of code I use to fire a timer at 1 minute past midnight daily. (It updates a widget that has to update daily.)
Intent intent = new Intent(context, DaysReceiver.class);
PendingIntent receiverIntent = PendingIntent.getBroadcast(context,
DaysConstants.UPDATE_ALARM,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Schedule the alarm!
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.cancel(receiverIntent);
if (cancelAlarm) {
MyLog.d(TAG, "setAlarm cancel");
return;
}
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
JodaTime jtime = new JodaTime();
am.set(AlarmManager.RTC_WAKEUP, jtime.afterMidnight(), receiverIntent);
//am.setRepeating(AlarmManager.RTC_WAKEUP, jtime.nowPlusMillis(30 * 1000),
// 30 * 1000, receiverIntent);
MyLog.d(TAG, "setAlarm set");
}
The JodaTime class does date and time calculations. the afterMidnight() bit above returns 1 minute after midnight tonight. The routine can be used to just cancel an outstanding alarm.
The receiver is just a normal broadcast receiver and you can do anything in it that you can do in any other broadcast receiver. (Don't forget to put the usual stuff in the manifest. Permissions, and such like.
Here's the receiver I'm using less the imports. It's pretty straight forward. It grabs all the widgets that are on home screens and updates them. The update routine is a static function in the widget provider. It's a class because it is driven from two places. The widget config and the widget provider. The timer is rescheduled every 24 hours. The alarm won't live through a boot, but the provider's on update is driven at reboot. (All that's happening is the new day calculations are performed and the widget display is updated.) You could drop my code and put in a startActivity.
Ooops. Almost forgot. Use PendingIntent.FLAG_UPDATE_CURRENT so you don't have multiple intents stacked up accidentally...
public class DaysReceiver extends BroadcastReceiver {
static String TAG = "DaysReceiver";
#Override
public void onReceive(Context context, Intent intent) {
MyLog.d(TAG, "onReceive");
updateWidgets(context);
}
private void updateWidgets(Context context) {
MyLog.d(TAG, "updateWidgets");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName componentName = new ComponentName(context, DaysProvider.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);
final int N = appWidgetIds.length;
if (N < 1) {
MyLog.d(TAG, "No widgets");
return;
}
for (int i = 0; i < N; i++) {
MyLog.d(TAG, "Update widget " + Integer.toString(appWidgetIds[i]));
DaysProvider.updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
}
}
}
Hope I haven't rambled to much, but I'm in a rush to get back to some other business. I don't have the time to really edit the post. Hope this helped...
Do you really need a notification? Why not fire off an activity that can do the alarm notification and disappear. You can sound an alarm, vibrate the phone, whatever. Even do a notification if you still want to...
Intent intent = new Intent(context.MyAlarmResponse);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("REASONFORALARM", "What ever you want");
context.startActivity(intent);
In the manifest, use the following theme to look like a dialog:
<activity android:name=".MyAlarmResponse"
android:theme="#android:style/Theme.Dialog">
</activity>
It doesn't have to look like a dialog. You can do a full court press with a full screen display, animation, vibrate, and sound. The user than hits your cancel key and it all goes away.