I have this method called "createAlarms()" which is on initial app setup, sets an alarm for a specific time. On the running of this alarm, it creates a notification which I am creating using a Broadcast Receiver Class.
public void createAlarms() {
cal = Calendar.getInstance();
cal.add(Calendar.HOUR, alarmintervalint);
calintent = new Intent(this, AlarmBroadcastReceiver.class);
calpendingintent = PendingIntent.getBroadcast(this.getApplicationContext(), 12345, calintent, 0);
am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarmintervalint, calpendingintent);
}
I want this alarm to repeat every "alarmintervalint" time period. I could do this by using the "am.setRepeating()" function, but my problem is a bit more complicated then that. After sending a certain amount of alarms (such as 50. will be calculated by the program), I want all the values to change, so that the alarmintervalint will change.
private void showNotification(Context context) {
PendingIntent notifpi = PendingIntent.getActivity(context, 0, new Intent(context, Main.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Hello!")
.setContentText("Welcome!")
.addAction(R.drawable.ic_launcher, "Open App", notifpi);
mBuilder.setContentIntent(notifpi);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
System.out.println("6");
}
For example, right now, an alarm goes off every 2 hours, and I get a notification every 2 hours. After getting 50 notifications, I want to set the Alarm to go off after 3 hours. And again after 50 notifications, make it 4 hours. (This is just an example, it will be a bit more complicated.
How would I do this? Right now what I think is to have some sort of counter in my broadcastreceiver class, and after the counter reaches 50 (in this example's case), it will call upon the createAlarms() class and change the timings and stuff. Would this work?
Simplest way is to use the Timer. Set TimerTask in your initial setup method, main method or whatever.
Example :
import java.util.TimerTask;
public class Schedular extends TimerTask {
#Override
public void run() {
//your implementation.
System.out.println("Run Me ~");
}
}
import java.util.Timer;
import java.util.TimerTask;
public class Main{
public static void main(String[] args){
TimerTask task = new Schedular();
Timer timer = new Timer();
//miliseconds
timer.schedule(task, 1000, 60000);
}
}
For Ref : Timer
I'm not totally sure what you're using the alarm for, but it looks like you want people to gradually smoke less often.
In that case I'd like to add to the previous answers, that you might want to persist your last used interval in case your app crashes or the phone is rebooted. You can easily archieve that by using SharedPreferences.
Related
I've set an alarm manager to download web content every 24h at 10:40am but apart from executing at proper time, apparently it launches few seconds after successfully running the app. I want this to run exclusively at the set time.
#Override
public void onReceive(Context context, Intent intent) {
mAuth = FirebaseAuth.getInstance();
currentUser = mAuth.getCurrentUser();
DownloadTask task = new DownloadTask();
task.execute("https://api.apify.com/v2/key-value-stores/3Po6TV7wTht4vIEid/records/LATEST?disableRedirect=true");
String contentText = "Nowe zakażenia: " +infectionsInfo+ " Nowe śmierci: "+deathsInfo;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "notifyLemubit1")
.setSmallIcon(R.drawable.unnamed)
.setContentTitle("Punkty zostały przyznane!")
.setContentText(contentText)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(200, builder.build());
and
calendar1.set(Calendar.HOUR_OF_DAY,10);
calendar1.set(Calendar.MINUTE,40);
Intent intent1 = new Intent(MenuActivity.this, PointsBroadcast.class);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(MenuActivity.this, 0, intent1, 0);
AlarmManager alarmManager1 = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager1.setRepeating(AlarmManager.RTC_WAKEUP,calendar1.getTimeInMillis(),AlarmManager.INTERVAL_DAY, pendingIntent1);```
All repeating alarms are inexact in order not to easily let developers abuse them.
If you want the action to be done in that exact time you need to use one-time alarms. To make them "repeating" you need to schedule the first one initially and always schedule the next one once the alarm is fired.
Although your issue might be due to this plus some other issue.
Exact alarms should only be used for extremely important tasks so I suggest considering using WorkManager so you don't affect the battery of the device as much.
I am working on an Android Project that requires some tasks to be run at some times. The task to be performed is selected by user along with the time at which it needs to be performed. I have tried using BroadcastReceiver and AlarmManager to perform the task at its time. But the problem is - task stop performing when the app is closed. Below is my code for BroadcastReceiver and AlarmManager classes.
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: Alarm received");
// perform the task here using the values received by Intent
}
}
I called this AlarmReceiver using AlarmManager as:
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("foo", foo);
alarmIntent.putExtra("bar", bar);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, date.getTime(), pendingIntent);
where date.getTime() is the time at which the task is to be performed.
All works fine. But I want a solution that makes this work even if the app is force closed.
I also want the solution to be able to perform multiple such tasks scheduled one after another or even for the next day. IF POSSIBLE
Note: I thought of using a Service class to accomplish this but couldn't figure out how to write the Service class.
I don't think a forced closed will allow tasks to keep working. You could however register a BroadcastReceiver on system events to let the application come back to live (e.g. android.intent.action.BOOT_COMPLETED and QUICKBOOT_POWERON)
For tasks you should really look at the WorkManager library.
See: https://developer.android.com/reference/androidx/work/package-summary and https://developer.android.com/topic/libraries/architecture/workmanager
It does a lot of work for you to listen to system events. It offers a PeriodicWorkRequest and OneTimeWorkRequest for repetitive and single tasks.
Latest lib: androidx.work:work-runtime:2.2.0
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
private fun startTask(interval: Int) {
val flexInterval = when {
interval > 200 -> 30L
interval > 59 -> 15L
else -> 5L
}
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
val fileFinderWork = PeriodicWorkRequest.Builder(
FileFindWorker::class.java,
interval.toLong(), TimeUnit.MINUTES,
flexInterval, TimeUnit.MINUTES
).setConstraints(constraints).addTag(WORK_TAG).build()
Logger.e("Enqueueing file finder worker ($interval min.)")
val manager: WorkManager = WorkManager.getInstance(ctx)
manager.enqueueUniquePeriodicWork(WORK_NAME, REPLACE, fileFinderWork)
}
Or in Java
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
private UUID startTask() {
int interval = 99 /* Just a value from configuration */;
TimeUnit unit;
int flexInterval;
if (interval % 60 == 0) {
interval = interval / 60;
unit = TimeUnit.HOURS;
} else {
unit = TimeUnit.MINUTES;
}
if (interval >= 240) {
flexInterval = 30;
} else if (interval >= 60) {
flexInterval = 15;
} else {
flexInterval = 5;
}
final Constraints constraints = new Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build();
final PeriodicWorkRequest fileFinderWork = new PeriodicWorkRequest.Builder(
FileFindWorker.class, interval, unit, flexInterval, TimeUnit.MINUTES)
.setConstraints(constraints)
.addTag(WORK_TAG)
.build();
lastPeriodicUuid = fileFinderWork.getId();
WorkManager manager = WorkManager.getInstance(ctx)
manager.enqueueUniquePeriodicWork(WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, fileFinderWork);
return fileFinderWork.getId();
}
I have an app which requires a code to be executed every minute. But the issue is that the code has to be executed at every minute change of the clock. Which means,
If its 12:34 then the code will execute at 12:35 and goes on. But my current code works but it includes the seconds. Meaning,
If its 12:34:30 and the alarm starts, the code is executed. But the code is then executed at 12:35:30.
I want the code to be executed each minute according to the clock of the phone. Below is the current code.
Intent intent2 = new Intent(MainActivity.this, MyABService.class);
PendingIntent pintent = PendingIntent.getService(MainActivity.this, 0, intent2, 0);
AlarmManager alarm_manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm_manager.setRepeating(AlarmManager.RTC, c.getTimeInMillis(), 1 * 1000, pintent);
Im making it execute every second so that the effect takes place at the exact time. Instead of having it every second i need it to repeat itself at every minute change of the clock (every minute)
How do i go about this
Use Intent.ACTION_TIME_TICK This is a broadcast intent fired every minute by the Android OS. Register to it as you would register to a normal system broadcast in code (doesn't work from manifest)
tickReceiver=new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().compareTo(Intent.ACTION_TIME_TICK)==0)
{
//do something
}
};
//Register the broadcast receiver to receive TIME_TICK
registerReceiver(tickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
This article describes the complete process.
Use a Calendar to set the time to trigger to the next full minute, and repeat it every minute (60*1000ms)
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MINUTE, 1);
calendar.set(Calendar.SECOND, 0);
long triggerAt = calendar.getTimeInMillis();
long repeatAfter = 60 * 1000;
alarm_manager.setRepeating(AlarmManager.RTC, triggerAt, repeatAfter, pintent);
Calendar calendar = Calendar.getInstance();
Intent intent = new Intent(context, AnyClass.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 60*1000, pendingIntent);
Hope this code solved your problem
Use the Quartz Schedular API, make the corn job which run at every min.
“0 0/1 * * * ?” // Corn expression run at every min
Build the Schedular and trigger like that
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
// Trigger the job to run now, and then every 1 min
Trigger trigger =trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0 0/1 * * * ?"))
.forJob("myJob", "group1")
.build();
// Tell quartz to schedule the job using our trigger
sched.scheduleJob(job, trigger);
and write your code in HelloJob class
public class HelloJob implements Job {
public void execute(JobExecutionContext context)
throws JobExecutionException;
// Here you write your code which exceute on every min
}
You can use Intent.ACTION_TIME_TICK.
Broadcast Action: The current time has changed. Sent every minute. You cannot receive this through components declared in manifests, only by explicitly registering for it with Context.registerReceiver().
Source:https://developer.android.com/reference/android/content/Intent.html#ACTION_TIME_TICK
IntentFilter tickIntent = new IntentFilter(Intent.ACTION_TIME_TICK);
private BroadcastReceiver receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context c, Intent i) {
// perform per minute task
}
};
registerReceiver(receiver, tickIntent);
You can use below code for your requirement,
Calendar initialCalendar = Calendar.getInstance();
initialCalendar.setTimeInMillis(System.currentTimeMillis());
initialCalendar.set(Calendar.SECOND, 0);
long triggerAtMillis = initialCalendar.getTimeInMillis();
long intervalMillis = 60 * 1000;
alarm_manager.setRepeating(AlarmManager.RTC, triggerAtMillis, intervalMillis, pintent);
Below is a pretty simple method that takes a Date and an id for an alarm to be fired which starts a countdown. For some reason I don't understand, if I call it once with one date and id 0 and call it again with another date and id 1 (i.e., two different countdowns), Android will fire both alarms at the same time (specifically the first date passed with id 0) so both countdowns start at the same time.
Can anyone tell me why and how to fix it? Thanks!
public void setCountdownAlarm(Date fireTime, int id)
{
// startCountdown will be called at fireTime
BroadcastReceiver startCountdown = new BroadcastReceiver() {
#Override public void onReceive( Context context, Intent theIntent )
{
countdownTimer = new Timer();
countdownTimer.schedule(new TimerTask() {
#Override
public void run() {
onSecondTick(showtime);
}
}, 0, 100); // call every 10th of a second
}
};
this.registerReceiver( startCountdown, new IntentFilter("com.counter.app.CountActivity.COUNT") );
Intent intent = new Intent("com.counter.app.CountActivity.COUNT");
PendingIntent pintent = PendingIntent.getBroadcast( this, id, intent, 0 );
AlarmManager manager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
if (Build.VERSION.SDK_INT >= 19)
manager.setExact(AlarmManager.RTC_WAKEUP, fireTime.getTime(), pintent);
else
manager.set(AlarmManager.RTC_WAKEUP, fireTime.getTime(), pintent);
}
Editing to say that when I waited for the second alarm to fire, Android actually calls startCountdown twice -- once again for each alarm. Help!
I figured out what I did wrong. As explained in answers to similar questions, the second parameter of PendingIntent.getBroadcast (requestCode) must be unique if you want to get a unique pending intent. I took care of that by passing "id".
The second problem was that I registered the BroadcastReceiver each time I called setCountdownAlarm. The BroadcastReceiver should only be registered once, typically in the onCreate method of the activity.
I'm trying out alarms and have hit a wall. I don't think my alarm is setting up properly because I never get a confirmation after the alarm is supposed to go off. Here's how I call on the alarm manager:
long alarmtime=new GregorianCalendar().getTimeInMillis()+10*1000;//run after 10 seconds
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmman = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmman.cancel(pi); // cancel any existing alarms
alarmman.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
alarmtime, 10 * 1000, pi);//run every 10 seconds
And here's my AlarmReceiver.java:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "TEST", Toast.LENGTH_LONG).show();
}
}
However, the TEST text does not appear, and I can't figure out why.
Since you are using the AlarmManager.ELAPSED_REALTIME_WAKEUP argument, your initial alarm-time should be base on the elapsed real time of the device:
long alarmtime = SystemClock.elapsedRealtime() + 10 * 1000;
alarmman.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmtime, 10 * 1000, pi);
(see this link).
For a BroadcastReceiver it's probably PendingIntent.getBroadcast() instead of PendingIntent.getService(). You are also cancelling the alarm, just update your PendingIntent like this and try not cancelling before:
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
Read the documentation thoroughly for further information.
Make sure that the BroadcastReceiver is being called by doing a System.out.println("TEST"); instead of Toast. If you are able to see that in your logcat, then the problem probably is that you need to run the Toast in UI thread.