make alarms survive boot - java

I have created an app, that allows a user to input assignment deadlines and then a time to be notified that the assignment is due, this all works fine, however, I can't find a clear example of how to make the alarms survive a reboot.
The database stuff on the receiver is just to populate the notification with the specific assignments information. The alarms themselves are not saved in the database, the times and dates are set by user input and directly passed to alarm manager.
Alarm creation page:
Long nIdLong = System.currentTimeMillis();
String nId = nIdLong.toString();
final int _id = (int) System.currentTimeMillis();
Intent alarmIntent = new Intent(context, AlarmReceiver.class);
alarmIntent.putExtra("nID", nIdLong);
// make sure the intent have id's
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, _id, alarmIntent , 0);
AlarmManager alarmManager = (AlarmManager)getActivity().getSystemService(getActivity().ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);
This is my receiver:
public void onReceive(Context context, Intent intent)
{
long dbId = intent.getLongExtra("nID", 0);
String notificationId = Long.toString(dbId);
Log.i("App", "called receiver method");
try{
userDbHelper = new UserDbHelper(context.getApplicationContext());
sqLiteDatabase = userDbHelper.getReadableDatabase();
cursor = userDbHelper.getAssignmentNotification(notificationId, sqLiteDatabase);
if (cursor.moveToFirst()) {
do {
id = cursor.getString(0);
name = cursor.getString(1);
subject = cursor.getString(2);
date = cursor.getString(3);
time = cursor.getString(4);
} while (cursor.moveToNext());
}
NotificationGenerator.generateNotification(context, date, time, name, subject);
}catch(Exception e){
e.printStackTrace();
}
}

https://examples.javacodegeeks.com/android/core/activity/android-start-service-boot-example/
This example shows how to start services on boot, save the alarms into a database and have the on boot service run through all the saved alarms to have them recreated upon each boot.

Related

How would I set multiple notification trigger times from one Activity?

I have a menu case that allows me to set a notification trigger based on the "term" start date. This takes user input where they select the start date for a term and creates a notification alert based on their choice:
case R.id.notify:
String startDateFromScreen = editTextStartDate.getText().toString();
String endDateFromScreen = editTextEndDate.getText().toString();
Date startDate = null;
Date endDate = null;
try {
startDate = sdf.parse(startDateFromScreen);
endDate = sdf.parse(endDateFromScreen);
} catch (ParseException e) {
e.printStackTrace();
}
Intent intent = new Intent(AddEditTermActivity.this,
MyReceiver.class);
Long triggerStartDate = startDate.getTime();
Long triggerEndDate = endDate.getTime();
intent.putExtra("start", editTextTermTitle.getText().toString() + " starts today!");
intent.putExtra("end", editTextTermTitle.getText().toString() + " ends today!");
PendingIntent startSender = PendingIntent.getBroadcast(AddEditTermActivity.this, MainActivity.termStartAlertNum++, intent, 0);
PendingIntent endSender = PendingIntent.getBroadcast(AddEditTermActivity.this, MainActivity.termEndAlertNum++, intent, 0);
AlarmManager startAlerm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
startAlerm.set(AlarmManager.RTC_WAKEUP, triggerStartDate, startSender);
return true;
I also want to be able to create a separate notification based on their selected end time, as you can see from some of the code in that section. This intent is sent to a receiver class:
public class MyReceiver extends BroadcastReceiver {
String startChannelID = "test";
static int notificationID;
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, intent.getStringExtra("start"), Toast.LENGTH_LONG).show();
createNotificationChannel(context, startChannelID);
Notification start = new NotificationCompat.Builder(context, startChannelID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentText(intent.getStringExtra("start"))
.setContentTitle("Starting").build();
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(notificationID++, start);
}
private void createNotificationChannel(Context context, String CHANNEL_ID) {
CharSequence name = context.getResources().getString(R.string.channel_name);
String description = context.getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
But when creating a separate notification/notification manager, it only shows the last notification line in the code. How can I make it so that both notifications are set on the respective dates? Time does not matter in this example.

Setting multiple alarms in Android Studio with an alarmmanager causes problems

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

How to call a method from dbhelper in broadcastReceiver

I want to excute a method in my dbhelper immediately my alarm service run, the if that code runs successfully I want to display a notification based on the result of the method execution. I want this to run even if my app is idle or in the background
this is my broadcastReceiver
public class ExpiryBroadcast extends BroadcastReceiver {
//db helper
private DbHelper dbHelper;
private final String CHANNEL_ID = "expiring_items";
private final int NOTIFICATION_ID = 200;
#Override
public void onReceive(Context context, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
dbHelper.updateExpiryRow();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_item_expiring)
.setContentTitle("ProExm Product Expiry")
.setContentText("Some products will soon expire, check now...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
}
this is my alarmmanager
public void startAlertAtParticularTime() {
// alarm first vibrate at 14 hrs and 40 min and repeat itself at ONE_HOUR interval
intent = new Intent(this, ExpiryBroadcast.class);
pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 280192, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 02);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
Toast.makeText(this, "Alarm will vibrate at time specified",
Toast.LENGTH_SHORT).show();
}
I have this code in my dbhelper class which I want to run before notification even if my application is asleep or in the background
public void updateExpiryRow(){
int daysToExpiry = 0;
String selectQuery = "SELECT * FROM " + Constants.ITEMS_TABLE;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
//looping through all records and add to list
if(cursor.moveToFirst()){
do{
ModelItems modelItems = new ModelItems(
""+cursor.getInt(cursor.getColumnIndex(Constants.C_ID)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_ITEM_NAME)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_ITEM_IMAGE)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_ITEM_PRICE)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_ITEM_MANUFACTURER)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_DESC)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_EXPIRY_DATE)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_MANUFACTURE_DATE)),
Integer.parseInt(""+cursor.getInt(cursor.getColumnIndex(Constants.C_DAYS_TO_EXPIRY)))-1,
""+cursor.getString(cursor.getColumnIndex(Constants.C_ADDED_TIMESTAMP)),
""+cursor.getString(cursor.getColumnIndex(Constants.C_UPDATED_TIMESTAMP))
);
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("dd-MM-yyyy");
try {
Date date = formatter.parse(modelItems.getItemExp());
LocalDate now = LocalDate.now();
String text = now.format(formatter2);
Date dateNow = formatter.parse(text);
long diffInMillies = Math.abs(date.getTime() - dateNow.getTime());
long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
daysToExpiry = (int) (long) diff;
} catch (ParseException e) {
e.printStackTrace();
}
ContentValues values = new ContentValues();
// id will be inserted automatically as we set AUTOINCREMENT in query
//insert data
String timestamp = ""+System.currentTimeMillis();
values.put(Constants.C_ID, modelItems.getId());
values.put(Constants.C_ITEM_NAME, modelItems.getItemName());
values.put(Constants.C_ITEM_IMAGE, modelItems.getItemImage());
values.put(Constants.C_ITEM_PRICE, modelItems.getItemPrice());
values.put(Constants.C_ITEM_MANUFACTURER, modelItems.getItemManufacturer());
values.put(Constants.C_DESC, modelItems.getItemDesc());
values.put(Constants.C_EXPIRY_DATE, modelItems.getItemExp());
values.put(Constants.C_MANUFACTURE_DATE, modelItems.getItemMfd());
values.put(Constants.C_DAYS_TO_EXPIRY, daysToExpiry);
values.put(Constants.C_ADDED_TIMESTAMP, modelItems.getAddedTime());
values.put(Constants.C_UPDATED_TIMESTAMP, timestamp);
//insert row, it will return record id of saved record
db.update(Constants.ITEMS_TABLE, values, Constants.C_ID +" = ?", new String[] {modelItems.getId()});
//add record to list
}while (cursor.moveToNext());
}
//close db connection
db.close();
}
First of all you have to initialize the dbHelper
dbHelper = new DbHelper();
Then just call your method.
And this broadcast receiver will work if your app is idle or even closed
Try to use a foreground service and start it from your broadcast receiver, it will work even if the app is killed, and in your service, you can instantiate your DbHelper and execute what you want, after finishing tasks just stop it.

Why is onReceive() called twice in this Alarm setup?

I'm building an Android pedometer app that sends data to a SQLite database at 30 minute intervals using AlarmManager; if the phone is off when the transfer is supposed to occur, it will do the transfer immediately when the phone is switched back on.
(30 minutes is for testing - the actual interval will be 1 week).
I was told alarms wouldn't persist unless I used "intent.action.BOOT_COMPLETED" in the manifest, so I put that as a filter for my Alarm.java class.
My code to set the alarm (in MainActivity.java) is as follows:
public void insertData(View view) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long time= System.currentTimeMillis();
Date d = new Date(time);
editor.putLong("alarmtime", time); //the next ring time for the alarm is put in SharedPreferences
editor.apply();
Intent intent = new Intent(this, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, time, AlarmManager.INTERVAL_DAY, pendingIntent);
Toast.makeText(MainActivity.this, "Alarm Set", Toast.LENGTH_LONG).show();
}
The code in the Alarm.java class (which extends BroadcastReceiver) is as follows:
Context context; //plus other declared variables
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
preferences = context.getSharedPreferences("MyPreferences", context.MODE_PRIVATE);
editor = preferences.edit();
long startedtime = preferences.getLong("alarmtime", 0); //get the next ring time
Date nextRingTime = new Date(startedtime);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
String action = intent.getAction();
//sometimes the onReceive() is called by the phone turning on
if (action != null && action.equals(Intent.ACTION_BOOT_COMPLETED)) {
//if phone turned on again before the alarm, just re-create the alarm for the same time, don't transfer data
Date currenttime = new Date(System.currentTimeMillis());
if ((currenttime.compareTo(nextRingTime) < 0)) {
Intent newintent = new Intent(context, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
newintent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, startedtime, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent);
return;
}
else if ((currenttime.compareTo(nextRingTime) > 0)) {
//if the phone was off when the alarm was supposed to make the transfer, set the alarm to the next intended ring time and insert data to db immediately
Calendar d = Calendar.getInstance();
d.setTime(nextRingTime);
d.add(Calendar.MINUTE, 30);
long newtime = d.getTimeInMillis();
Intent newintent = new Intent(context, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
0,
newintent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC, newtime, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent);
}
}
myDb = new DatabaseHelper(context);
// code for inserting data into database
//finally, update next intended ring time in SharedPreferences now that this particular transfer is done:
Calendar c = Calendar.getInstance();
c.setTime(nextRingTime);
c.add(Calendar.MINUTE, 30);
Log.v("insertdone, alarrm now", c.getTime().toString());
long nexttime = c.getTimeInMillis();
editor.putLong("alarmtime", nextime);
editor.apply();
}
Unfortunately, if the phone is off at the time of a scheduled alarm, when I turn it on afterwards, it calls onReceive() TWICE, meaning it inserts data twice.
How can this be stopped?
A solution would maybe be to use RTC_WAKEUP, but I really don't want to do that unless as an absolute last resort.
N.B. I have a separate bootBroadcastReceiver class that uses "intent.action.BOOT_COMPLETED" to restart a pedometer service, and am wondering whether having 2 different things started by the boot is causing problems...

How to implement multiple timed notifications in Android

I have this final feature for the app that I am creating. The app that I have made is a calendar that saves events and notifies the user when the time arrives. The problem that I encounter is that when I create multiple notifications (or multiple events), it only notifies the very latest that was created. I tried to make different IDs for the notifications but to no success. Here the codes that I modified. It was from a tutorial that I've seen.
AlarmTask.java
public class AlarmTask implements Runnable{
// The date selected for the alarm
private final Calendar date;
// The android system alarm manager
private final AlarmManager am;
// Your context to retrieve the alarm manager from
private final Context context;
private final long alarmID;
public AlarmTask(Context context, Calendar date, long id) {
this.context = context;
this.am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.date = date;
this.alarmID = id;
}
#Override
public void run() {
// Request to start are service when the alarm date is upon us
// We don't start an activity as we just want to pop up a notification into the system bar not a full activity
Intent intent = new Intent(context, NotifyService.class);
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("alarmID", alarmID);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC, date.getTimeInMillis(), pendingIntent);
}
}
NotifyService.java
public class NotifyService extends Service {
/**
* Class for clients to access
*/
public class ServiceBinder extends Binder {
NotifyService getService() {
return NotifyService.this;
}
}
// Unique id to identify the notification.
private static final int NOTIFICATION = 143;
// Name of an intent extra we can use to identify if this service was started to create a notification
public static final String INTENT_NOTIFY = "com.gpplsmje.mac.calendar.utils.INTENT_NOTIFY";
// The system notification manager
private NotificationManager mNM;
#Override
public void onCreate() {
Log.i("NotifyService", "onCreate()");
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// If this service was started by out AlarmTask intent then we want to show our notification
if(intent.getBooleanExtra(INTENT_NOTIFY, false)){
showNotification(intent.getLongExtra("alarmID", 0));
}
// We don't care if this service is stopped as we have already delivered our notification
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients
private final IBinder mBinder = new ServiceBinder();
/**
* Creates a notification and shows it in the OS drag-down status bar
*/
#SuppressWarnings("deprecation")
private void showNotification(long alarmID) {
SaveEvent event = new SaveEvent(this);
event.open();
Log.d("Notification: ID", alarmID + "");
// This is the 'title' of the notification
CharSequence title = event.getEventName(alarmID);
// This is the icon to use on the notification
int icon = R.drawable.icon_reminder;
// This is the scrolling text of the notification
CharSequence text = event.getEventDesc(alarmID);
// What time to show on the notification
long time = System.currentTimeMillis();
event.close();
Intent backToEventDetail = new Intent(this, CalendarEventDetail.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, backToEventDetail, 0);
Notification notify = new Notification.Builder(this)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(icon)
.setContentIntent(contentIntent).getNotification();
notify.defaults = Notification.DEFAULT_SOUND;
notify.flags = Notification.FLAG_AUTO_CANCEL;
// Send the notification to the system.
mNM.notify(Integer.parseInt(String.valueOf(alarmID)), notify);
// Stop the service when we are finished
stopSelf();
}
}
From what I understand with the code, the AlarmTask.java receives the alarm date and sets the it to notify on that date. The ID that I passed is the ID of the event that I saved in the phone's database. But I couldn't get it to add multiple notifications. It only receives the latest that I saved. I would want it to get all the events and set notification for each of those events. Can somebody help me with it?
Create pending intent like this
PendingIntent contentIntent = PendingIntent.getActivity(this, (int)(Math.random() * 100), backToEventDetail, PendingIntent.FLAG_UPDATE_CURRENT);
Create Pending intent with below code
PendingIntent contentIntent = PendingIntent.getActivity(this, Integer.parseInt(String.valueOf(alarmID)), backToEventDetail, Intent.FLAG_ACTIVITY_NEW_TASK );

Categories