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.
Related
I am currently developing an app on android studio using java where I want the user to be able to receive notifications from calendar events that they create. However, my notifications are not coming on time as they are either lagging or just not showing.
Here is my coding for the alarm receiver which sets up the notification:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String event = intent.getStringExtra("event");
String time = intent.getStringExtra("time");
int notId = intent.getIntExtra("id", 0);
Intent activityIntent = new Intent(context, CalendarActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, activityIntent, PendingIntent.FLAG_ONE_SHOT);
String channelId = "channel_id";
CharSequence name = "channel_name";
String description = "description";
NotificationChannel channel = new NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentTitle(event)
.setContentText(time)
.setDeleteIntent(pendingIntent)
.setGroup("Group_calendar_view")
.build();
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
notificationManagerCompat.notify(notId,notification);
}
}
Here is my CustomCalendarView Activity that sets up the alarm:
private void setAlarm(Calendar calendar, String event, String time, int RequestCode){
Intent intent = new Intent(context.getApplicationContext(), AlarmReceiver.class);
intent.putExtra("event",event);
intent.putExtra("time",time);
intent.putExtra("id",RequestCode);
#SuppressLint("UnspecifiedImmutableFlag") PendingIntent pendingIntent = PendingIntent.getBroadcast(context,RequestCode,intent,PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager)context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis() ,pendingIntent);
}
For example: When I set an alarm for 10:20 am, the notification either doesn't pop up or pops up really late like at 10:22 am. Please help! Let me know if I need to provide any more information.
You should use exact alarms for firing alarm on time,
use alarmManager.setExact() instead of alarmManager.set().
setExact() method is like set(), but does not permit the OS to adjust the delivery time. The alarm will be delivered as nearly as possible to the requested trigger time.
Put setExact() instead of set
alarmManager.setExact(...);
But with different devices and interface updates, the performance of the application work differed from one device to another, in this case you must put some settings in your application such as Disable Software Battery Optimizations, make the application run in the background even after closing, give Permissions Autostart, turn on all notifications and display them etc. Finally, call the functions in the onStop() event of the Activity.
Yes, correct. You should ask user to allow Battery Optimization and in Android 12 or higher you should ask user to enable Alarms & Reminders permission as well.
But, when your app wants to fire alarms exactly on time you have to use exact alarms.
I currently have a small project running in Android Studio that I need help with. I want to include a reminder function with notifications.
I have a total of 4 time pickers for each of which I want to set an alarm at the corresponding selected time.
With my current code the planned feature works only halfway, with the following problem:
If I select one time, then I sometimes get a notification at this time as desired. However, not always on time. Most of the time the alert doesn't appear and if it does, then half a minute to a minute later. And if I set all 4 alarms, in the best case I get a notification at the last selected time. In the worst case nothing happens.
But all I want is to get daily notifications at the selected 4 times.
About my code, I use an alarm manager which I call 4 times for the 4 alarms. I also use a broadcast receiver in which the notification is triggered. And I use a different RequestCode for each PendingIntent.
I really searched all relevant posts on SO, but not one of them worked for me. Maybe I have included it in the wrong way. I hope someone can help me. Here are my methods:
Alarm method from Activity.class
(UhrzeitInMillis describes the chosen time by timepicker, for example 16.03):
public void SetAlarm(Context context, long UhrzeitInMillis) {
Intent intent = new Intent(context, Optionen_Alarm.class);
final int id = (int) System.currentTimeMillis();
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), id, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, UhrzeitInMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
}
BroadcastReceiver (Optionen_Alarm.java):
public class Optionen_Alarm extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myapp:mywakelocktag");
wl.acquire();
createNotificationChannel(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "1");
builder.setContentTitle("titel");
builder.setContentText("text!");
builder.setSmallIcon(R.drawable.picture);
builder.setColor(context.getResources().getColor(R.color.red));
builder.setVibrate(new long[]{0, 300, 300, 300});
builder.setLights(Color.WHITE, 1000, 5000);
builder.setAutoCancel(true);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setStyle(new NotificationCompat.BigTextStyle().bigText("text!"));
Intent notifyIntent = new Intent(context, Activity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 2, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
Notification notificationCompat = builder.build();
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);
managerCompat.notify(15, notificationCompat);
wl.release();
private void createNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "name";
String description = "description";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel("1", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}
```java
For battery reason, from android 6.0 the time you set on the alarm manager will not be guaranteed to trigger at the exact same time you have set. You could use the method '''setExactAndAllowWhileIdle()''' to make the alarm behave like you want.
You can read more about this here https://developer.android.com/training/scheduling/alarms
So far I've been using AlarmManager and BroadcastReceiver like this:
private void setUpStreakResetAlarm() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.add(Calendar.DAY_OF_MONTH, 1);
Intent intent = new Intent(getApplicationContext(), DailyCounterCheckReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 100, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
And in my DailyCounterCheckReceiver class:
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
int counterOld = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, 0);
int counterToday = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_KEY, 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
// If user has increased counter on this day, increase the old check for tomorrow
if (counterToday > counterOld) {
counterOld = counterToday;
editor.putInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, counterOld);
editor.putBoolean("increasedOld", true);
editor.putBoolean("reset", false);
}
// etc.....
But with new Android versions, background tasks like that just get killed and it's very unreliable.
So what can I use instead? Work Manager, Foreground Service, something else?
And don't I still need AlarmManager to trigger them?
My use case is extremely simple, so I don't think I need some super complex solution, but there are so many options out there. What is better for my simple case?
Edit:
Angel's comment would solve my reset problem, but I also do the same for triggering notifications at certain times:
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("lastTimeOfNotifTrigger", System.currentTimeMillis());
editor.apply();
repository = new NotificationRepository(context);
repository.startGetNextNotificationAsync(this);
}
How could I solve that in the easiest way?
You could use alarmManager.setExact to schedule the alarms (so that your receiver is called exactly at the time you want even if the phone is sleeping or in Doze mode). Once your receiver is called you can schedule the next. Remember to also add receivers detecting BOOT and PACKAGE_CHANGED so that you can re-schedule the alarms in case the phone is rebooted or the app updated.
You could also use Work Manager as you said to schedule a job, if you don't need it to be run at an exact time.
Your code shouldn't have any other issue running in newer versions of Android if you are not trying to launch an Activity from that receiver (you can't launch an activity anymore from the background).
I started working with Notifications and to improve user expirirence I added button to notification "MARK AS READ", so when it is pressed - message will hide;
I had a few problems, in database each message has "read" field which is boolean, in method onDataChange() I loop and check if any of messages have this field as false if they have I call makeNotification(), looks fine, where is problem??? - e.x. I have 2 notifications, I cancel one(the app goes to database and change field to true, so now onDataChange() will be called), but another isn't cancelled and because onDataChange() was called now I have same notification appeared twice.....
I tried to fix this by building array of messages that are read and already displayed, not the best solution I think, but even previous problem was somehow solved this is a wierd one.
When notitifcation appears in notification list it has button "MARK AS READ", when I press it notififcation hides, but only if I start from the highest to the lower ones, so if I press second notification first foldes, if I press third first folds..
In onCreate() counter is set to 1;
public void makeNotification()
{
Intent activityIntent = new Intent(this, Notifications.class);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, activityIntent, 0);
Intent broadcastIntent = new Intent(getApplicationContext(), NotifUpdater.class);
broadcastIntent.putExtra("seconds", message.getSeconds());
broadcastIntent.putExtra("id", String.valueOf(counter));
PendingIntent actionIntent = PendingIntent.getBroadcast(this, 0,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, App.CHANNEL_ID)
.setVibrate(new long[]{100, 0, 100})
.setSmallIcon(R.drawable.ic_shield)
.setContentTitle(message.getTime() + " | " + message.getClient())
.setContentText(message.getMessage())
.setContentIntent(contentIntent)
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.addAction(0, "MARK AS READ", actionIntent)
.build();
notificationManager.notify(counter, notification);
counter += 1;
}
// Broadcast which is triggered by pressing button
public class NotifUpdater extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
FirebaseDatabase database = FirebaseDatabase.getInstance();
String seconds = intent.getStringExtra("seconds");
int id = Integer.parseInt(intent.getStringExtra("id"));
database.getReference("latest").child(seconds).child("read").setValue(true);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.cancel(id);
}
}
You should have : FLAG_CANCEL_CURRENT
like :
PendingIntent actionIntent = PendingIntent.getBroadcast(this, NOTIFICATION_ID, buttonIntent, PendingIntent.FLAG_CANCEL_CURRENT);
not FLAG_UPDATE_CURRENT
Regarding your first issue, I'd just set an indicator for each notification, "wasShown" or something of the sort that is only set to true once you have showed the notification and when ever you are iterating your notifications, only show those who has "wasShown" set to false.
Regarding your 2nd problem, I'm not familiar with FirebaseDatabase but it sounds like its an ID problem. Did you make sure the ID you are getting from the intent is the right one?
I'm currently working on a notification that has to show at a specific time chosen by the user.
BUT when I run it, notifications show but not at the selected time, only when I ask the time in a timepickerdialog appear, before even choosing a time.
Does anyone know how to change the code so that notifications only appear at the time selected?
showDialog(DIALOG_ID); //this is the dialog that asks for an hour and minute.
alarmMethod1();
private void alarmMethod1(){
Calendar calendari1 = Calendar.getInstance();
calendari1.set(Calendar.HOUR_OF_DAY, hour_x);
calendari1.set(Calendar.MINUTE, minute_x);
calendari1.set(Calendar.SECOND, 00);
Intent myIntent1 = new Intent(Main2Activity.this, NotifyService1.class);
AlarmManager alarmManager1 = (AlarmManager) getSystemService(ALARM_SERVICE);
pendingIntent1 = PendingIntent.getService(Main2Activity.this, 0, myIntent1, 0);
alarmManager1.setRepeating(AlarmManager.RTC_WAKEUP, calendari1.getTimeInMillis(), 24 * 60 * 60 * 1000, pendingIntent1);
}
Then here is the class where the intent goes:
public class NotifyService1 extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onCreate(){
Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager nNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent intent1 = new Intent(this.getApplicationContext(),Main3Activity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent1, 0);
Notification mNotify = new Notification.Builder(this)
.setContentTitle("Hora d'entrenar!")
.setContentText("Clica per començar entrenament de pit i braços")
.setSmallIcon(R.drawable.logofinal)
.setContentIntent(pIntent)
.setSound(sound)
.build();
nNM.notify(1,mNotify);
}
}
Since Android API 19, all alarms are inexact in order to reduce battery consumption, that means that your alarms will not be triggered exactly when you want, but if you really need to do it, you need to use setExact and then write the repeat alarm handler by yourself, there's no longer a "set exact and repeat". Please, see:
http://developer.android.com/reference/android/app/AlarmManager.html#setRepeating(int, long, long, android.app.PendingIntent)
http://developer.android.com/reference/android/app/AlarmManager.html#setExact(int, long, android.app.PendingIntent)