I created a simple application to try out setAlarmClock(). There is only a single button in the MainActivity, and it calls setAlarm when clicked.
The code is below.
MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = "alarmclock.MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setAlarm(View view) {
Context context = getApplicationContext();
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
long nextTrigger = SystemClock.elapsedRealtime()+10*1000;
Log.i(TAG, SystemClock.elapsedRealtime() + ": scheduling next alarm at " + nextTrigger);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
AlarmManager.AlarmClockInfo ac = new AlarmManager.AlarmClockInfo(nextTrigger, null);
alarmManager.setAlarmClock(ac, pendingIntent);
}
}
AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "alarmclock.AlarmReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, SystemClock.elapsedRealtime() + " in AlarmReceiver onReceive()");
}
}
I am basically triggering the alarm for 10seconds later, but for some reason it's triggering immediately.
2020-09-21 22:09:56.664 17910-17910/com.example.alarmclock I/alarmclock.MainActivity: 3362358: scheduling next alarm at 3372358
2020-09-21 22:09:56.687 17910-17910/com.example.alarmclock I/alarmclock.AlarmReceiver: 3362382 in AlarmReceiver onReceive()
If I use setExactAndAllowWhileIdle the same code seems to work fine. But I am specifically wanting to try out setAlarmClock.
The AlarmClockInfo requires a wall time:
time at which the underlying alarm is triggered in wall time milliseconds since the epoch
but you are using elapsedRealtime() time so use System.currentTimeMillis to fix the issue.
It worked after I changed from SystemClock.elapsedRealtime() to System.currentTimeMillis().
long nextTrigger = System.currentTimeMillis()+10*1000
After reading the documentation in greater detail, I realise that the first parameter of the AlarmClockInfo constructor takes
long: time at which the underlying alarm is triggered in wall time
milliseconds since the epoch
which is what System.currentTimeMillis() gives.
On the other hand, SystemClock.elapsedRealtime() returns the
time since the system was booted...
which will always be a much smaller number than wall time since epoch. As a result, the alarm gets scheduled immediately because
If the stated trigger time is in the past, the alarm will be triggered immediately.
Related
The below code is from my MainActivity onCreate() method where I set an alarm to run a service ScheduleAlarm immediately and only once. sPrefs is the SharedPreference object that takes care of setting this alarm only once. The service ScheduleAlarm is an IntentService which is getting fired perfectly.
if(sPrefs.getBoolean("SettingAlarmForFirstTime",true)) {
//Creating alarm for showing notifications.
Calendar calendar = Calendar.getInstance();
//calendar.setTimeInMillis(System.currentTimeMillis());
//calendar.set(Calendar.HOUR_OF_DAY, 8);
Intent intent = new Intent(MainActivity.this, ScheduleAlarms.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);//Use AlarmManager.INTERVAL_DAY instead of int number here.
editor.putBoolean("SettingAlarmForFirstTime", false);
editor.commit();
}
Now this is my ScheduleAlarms service class. When this service is started onHandleIntent() method is called from where I am calling setWeeklyAlarms() method. Now my problem is from inside this method I want to set 7 alarms for a complete week based on timings that I will obtain from an API server call. Right now I am unable to even execute a single alarm perfectly. The alarm that i am setting is set to start after 3 secs delay which will call another service named NotificationService but the alarms gets fired immediately instead of waiting for 3 secs. Please analyze and tell me why this is happening.
public class ScheduleAlarms extends IntentService {
private boolean notificationsEnabled = true;
private String notificationTimings = "";
public ScheduleAlarms() {
super("ScheduleAlarms");
}
#Override
protected void onHandleIntent(Intent intent) {
setWeeklyAlarm();
}
private void setWeeklyAlarm() {
Intent i = new Intent(ScheduleAlarms.this, NotificationService.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getService(ScheduleAlarms.this, 10001, i, 0);
AlarmManager alarmManager = (AlarmManager) ScheduleAlarms.this.getSystemService(Context.ALARM_SERVICE);
/* Setting this alarmManager to fire up the alarm after 3 mins delay, but this alarm is getting fired immediately*/
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
}
}
Change the following line:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3*60*1000, pendingIntent);
to:
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, c.getTimeInMillis() + 3 * 60 * 1000, 3 * 60 * 1000, pendingIntent);
The alarm is fired up immediately, because
AlarmManager.ELAPSED_REALTIME_WAKEUP + 0, ...
So, the first time, it is fired up after 0 seconds. To change this, you need to add some time here.
AlarmManager.ELAPSED_REALTIME_WAKEUP + time
And, also it should be repeating, so you need to use setRepeating.
Note : 3 * 60 * 1000 == 3 minutes, not 3 seconds
I'm developing an app that should perform a certain task every 60 seconds. Since there's some accuracy problems with alarms in Android 4.4+, where all alarms are inexact, I've opted for the chained model: A BroadcastReceiver fires the first alarm, and each alarm in turn sets the next alarm.
The problem is that, even though I'm setting the alarms at intervals of 60 seconds (60000 ms), the alarms trigger at 5 second intervals, and sometimes even less. I've tested the code on my Nexus 5 (Android 5.1.1) and on an Android 5.0.1 emulator, both giving the same result.
I should point out that both receivers are registered on the AndroidManifest and my application has the RECEIVE_BOOT_COMPLETED permission.
EDIT: setExact() causes exactly the same problem
StartupReceiver.java (BroadcastReceiver for BOOT_COMPLETED):
public class StartupReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Got the BOOT_COMPLETED signal");
// Get the first alarm to be invoked immediately
AlarmReceiver.setNextScanAlarm(context, 0);
}
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Start the service
Intent startServiceIntent = new Intent(context, BackgroundService.class);
startServiceIntent.putExtra("interval", 60000);
startServiceIntent.putExtra("action", "scan");
context.startService(startServiceIntent);
// Schedule the next alarm
setNextScanAlarm(context, 60000);
}
public static void setNextScanAlarm(Context context, int interval) {
Intent scanIntent = new Intent(context, AlarmReceiver.class);
scanIntent.putExtra("interval", interval);
scanIntent.putExtra("action", "scan");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
scanIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval,
pendingIntent);
}
}
What could be the problem?
I believe because this is an alarm clock when calling
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval,
pendingIntent);
The variable you are calling interval is the amount of time you want to elapse UNTIL the next alarm , but when you think about this when does it know to start? More so, when does time actually equal zero? When you create it? No. When you call .set()? No. It is actually zero upon BOOT. So you are asking it to launch 60 seconds after boot, and your asking for this everytime, this time will have already elapsed.
This is where the confusion is, and where you should probably just use a call like new
Handler.postDelayed(Runnnable r, 60000) instead of an alarm Manager. It will be much more accurate and will not be subject to some problems with understanding the Android Operating System and its alarms/clocks/etc/etc.
But for your specific case I believe you could solve it by accessing System function calls/variables. So inside of your function setNextScanAlarm() I believe it would look like this:
public static void setNextScanAlarm(Context context, int interval) {
//create the intent the same way as before
Intent scanIntent = new Intent(context, AlarmReceiver.class);
scanIntent.putExtra("interval", interval);
scanIntent.putExtra("action", "scan");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
scanIntent,
PendingIntent.FLAG_ONE_SHOT);
//create new variables to calculate the correct time for this to go off
long timeRightNow = System.elapsedRealTime() //use something else if you change AlarmManager type
long timeWhenIShouldGoOff = timeRightNow + interval;
//use the new timeWhenIShouldGoOff variable instead of interval
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
timeWhenIShouldGoOff,
pendingIntent);
}
See my answer to a similar question.
I use postDelayed() instead of AlarmManager for the short time intervals (less than 1 minute), and AlarmManager for long.
I am using the following code to execute the Alarm at 10:30 AM and then keep executing every 8seconds, but the problem is, this code execute the Alarm manager even when the time is not 10:30.
I have tried everything but the code keep executing the Alarm regardless of what time is set,
public class MainActivity extends Activity {
private PendingIntent pendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Retrieve a PendingIntent that will perform a broadcast */
Intent alarmIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, alarmIntent, 0);
startAt10();
}
public void startAt10() {
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
/* Set the alarm to start at 10:30 AM */
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 10);
calendar.set(Calendar.MINUTE, 30);
/* Repeating on every 8 seconds interval */
manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 8, pendingIntent);
}
}
and this is my AlarmReceiver class,
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// For our recurring task, we'll just display a message
Toast.makeText(context, "Welcome Back ! ", Toast.LENGTH_SHORT).show();
}
I have also added this in my AndroidManifest.xml,
<receiver android:name="com.example.test2.AlarmReceiver" android:enabled="true" >
</receiver>
How to make it to only execute at 10:30AM ?
Your code is correct. The alarm will start at 10:30 am and repeats at every 8 seconds. But once check the time in the device or emulator in which your app is running. If the time is already past then the alarm starts executing immediately.
If the time is not completed then once uninstall the application and run again it may work.
Sorry if answered before, but i looked everywhere but didn't get the proper solution
I am using AlarmManager to automatically fire a notification at 9am everyday, but when i try to run it on emulator it executes immediately, and every half hour (31-32min to be precise) after that instead of only once at 9am everyday.
Any ideas why? help appreciated.
code is as below:
public class Home extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bsheet);
notificationAlert(savedInstanceState);
}
private void notificationAlert(Bundle savedInstanceState) {
AlarmManager manager;
PendingIntent pendingIntent;
Intent intent=new Intent(Home.this, Notify.class);
manager=(AlarmManager)getSystemService(ALARM_SERVICE);
pendingIntent=PendingIntent.getService(Home.this,
0, intent, 0);
GregorianCalendar gcal = new GregorianCalendar();
gcal.set(Calendar.HOUR_OF_DAY, 9);
gcal.set(Calendar.MINUTE, 0);
gcal.set(Calendar.SECOND, 0);
gcal.set(Calendar.MILLISECOND, 0);
long initTime = gcal.getTimeInMillis();
manager.setRepeating(AlarmManager.RTC_WAKEUP, initTime,
24*60*60*1000, pendingIntent);
}
}
cheers,
EDIT: my intention is that, once the app is installed, it fires this alarm at 9am. i have put the alarm in the onCreate, so im not sure if the alarm is only being created everytime i start the app and something weird is happening when i hide the app... again insight would be appreciated!
manager.setRepeating(AlarmManager.RTC_WAKEUP, initTime, 24*60*60*1000, pendingIntent);
Alarm manager will fire immidiatly if initTime < System.currentTimeMillis()
from docs:
If the time occurs in the past, the alarm will be triggered immediately, with an alarm count depending on how far in the past the trigger time is relative to the repeat interval.
According to the code you've provided, gcal.getTimeInMillis() will return millisecods corresponding to Today 9.00. So if you'll try to run your code after this time, initTime will be less then current system time which triggers immidiate run of AlarmManager.
To fix this, for example, you can add one day to your calendar before passing its gcal.getTimeInMillis() if it is already in past so it will point to Tomorrow 9.00 to let it run tomorrows morning
Update1
Tried code like this and it worked as expected for me - fired service every 10 seconds:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initManager();
}
private void initManager() {
AlarmManager manager;
PendingIntent pendingIntent;
Intent intent = new Intent(this, NotifyService.class);
manager=(AlarmManager)getSystemService(ALARM_SERVICE);
pendingIntent=PendingIntent.getService(this, 0, intent, 0);
long initTime = System.currentTimeMillis() + 5000;
manager.setRepeating(AlarmManager.RTC_WAKEUP, initTime,
10*1000, pendingIntent);
}
However you can use another option: there is AlarmManager.set() method
You can fire it like this
long runTime = /* your calendar time + 24 hours in millis*/
manager.set(AlarmManager.RTC_WAKEUP, runTime, pendingIntent);
so it will fire your service at runTime. And then in the service invoke the same method with recalculated runTime for another day so it could look something like that:
public MainActivity extends Activity{
protected onCreate(Bundle bundle){
....
initManager();
}
private initManager(){
...
long runTime = /* time of your first alarm/notification/whatever you develope */
Intent intent = new Intent(this, NotifyService.class);
pendingIntent=PendingIntent.getService(this, 0, intent, 0);
manager.set(AlarmManager.RTC_WAKEUP, runTime, pendingIntent);
}
}
And the service:
public NotifyService extends Service{
...
public int onStartCommand(...){
...
/* do stuff */
Intent intent = new Intent(getApplicationContext(), NotifyService.class);
pendingIntent=PendingIntent.getService(this, 0, intent, 0);
long runTime = /* recalculate it according to the next time of firing this service*/
manager.set(AlarmManager.RTC_WAKEUP, runTime, pendingIntent);
}
}
So your service will register an intent in AlarmManager itself everytime the service fires.
try this :
Calendar currentTime = Calendar.getInstance();
Calendar alarmTime = Calendar.getInstance();
currentTime.set(Calendar.HOUR_OF_DAY, 9;
currentTime.set(Calendar.MINUTE, 0);
currentTime.set(Calendar.SECOND, 0);
currentTime.set(Calendar.MILLISECOND, 0);
if (alarmTime.compareTo(currentTime) <= 0) {
// check for time
alarmTime.add(Calendar.DATE, 1);
}
Intent intent = new Intent(getBaseContext(), Receiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getBaseContext(), 1, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, alarmTime.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
I took some code from some questions here in SO as well as some other website and I came up with a reasonable solution.
What I am trying to do: I need to shutdown the app after 2 minutes of inactivity. So The idea is to start up the alarm service when our app goes in into 'onPause' and cancel the service when our app goes into 'onResume'.
What I currently Have: Here is the relevant code that is currently running. My issue is that the TimeoutService java file is never being 'onCreated'. Does simply calling AlarmManager.set NOT start up the pending intent?
The Timeout.java File
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class Timeout
{
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TIMEOUT = 2 * 60 * 1000; // 2 minutes
public static final String INTENT_TIMEOUT = "timeoutintent";
public static void start(Context ctx)
{
//long triggerTime = System.currentTimeMillis() + DEFAULT_TIMEOUT;
long triggerTime = System.currentTimeMillis() + (5000);
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ctx, TimeoutService.class);
PendingIntent pi = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, 0);
am.set(AlarmManager.RTC, triggerTime, pi);
}
public static void cancel(Context ctx)
{
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ctx, TimeoutService.class);
PendingIntent pi = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, 0);
am.cancel(pi);
}
}
LockingActivity File. This is used as a superclass to all of my Activities.
import android.app.Activity;
import android.widget.Toast;
public class LockingActivity extends Activity
{
#Override
protected void onPause()
{
super.onPause();
Timeout.start(this);
}
#Override
protected void onResume()
{
super.onResume();
Timeout.cancel(this);
checkShutdown();
}
private void checkShutdown()
{
if ( AppVM.isShutDown() )
{
Toast.makeText(this, "Shuting Down", 1).show();
finish();
}
}
}
I could send over the TimeoutService file as well, but it's just a typical service file. The problem is the TimeoutService class is never being instanced, so I can't imagine the problem would lie in that class.
I think you are complicating things with an alarm. Use a Handler and postdelayed() to set a Runnable in two minutes, all in your main activity. Any user interaction cancels the post and sets a new one for the next two minutes. The runnable needs only yourActivity.finish();
Follow this answer here: Count for 45 seconds, pause for 20 then repeat with different title for an example of a timer and how to remove the callbacks.
Hi Try using PendingIntent.FLAG_CANCEL_CURRENT flag for your Pending Intent.
PendingIntent pi = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent,PendingIntent.FLAG_CANCEL_CURRENT);
Make Alarm manager as RTC_WAKEUP to wake up device if it is going off some where and check.
With this you need to register a broadcast receiver and fire intent with some action when alarm goes off. Set action to same intent you are using for pending intent.
Start your service in your receiver.
Your this broadcast receiver should be able to listen to action fired by this Intent when Alarm goes OFF.
Intent intent = new Intent(SCH_ALARM_ACTION);
intent.setClass(getBaseContext(), SchAlarmReciever.class);
registerReceiver(new SchAlarmReciever(), new IntentFilter(SCH_ALARM_ACTION));
PendingIntent pi = PendingIntent.getBroadcast(getBaseContext(),
0,
intent,
PendingIntent.FLAG_CANCEL_CURRENT);