I want to track the tower location of the user hourly. So for that I am now using an alarm manager. With alarm manager sometimes the alarm is getting triggered sometimes it does not.
Also there is problem in xiomi devices, if the app is killed from ram then the alarm does not trigger.
Is there any other way to do this? Can I run a service continuous and track the location hourly?
I want a solution where in a service I should be able to run this task hourly, may be using an handler or timer??
How can I use Job Scheduler for this?
Right Now my attempt with alarm manager :
public static boolean setAlarm(Context context) {
Calendar calendarNow = Calendar.getInstance();
Intent intent = new Intent(context, SaveLocationReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1321, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendarNow.getTimeInMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
ComponentName receiver = new ComponentName(context, SaveLocationReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
return true;
}
SaveLocationReceiver.java:
public class SaveLocationReceiver extends BroadcastReceiver {
private SharedPreferences preferences;
String networkSubType="";
SessionData mSessionData;
#Override
public void onReceive(final Context context, Intent intent) {
Log.d("myapp", "service started");
mSessionData = new SessionData(context);
if(ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
{
Calendar calendarSet = Calendar.getInstance();
Calendar calendarNow = Calendar.getInstance();
calendarSet.set(Calendar.HOUR_OF_DAY, 7); // hour
calendarSet.set(Calendar.MINUTE, 00); // minute
calendarSet.set(Calendar.SECOND, 0); // second
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.set(Calendar.HOUR_OF_DAY, 20); // hour
calendarEnd.set(Calendar.MINUTE, 00); // minute
calendarEnd.set(Calendar.SECOND, 0); // second
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(calendarNow.getTime());
// Output "Wed Sep 26 14:23:28 EST 2012"
String currentTime = format1.format(calendarNow.getTime());
System.out.println(currentTime);
if (calendarNow.compareTo(calendarSet) >= 0 && calendarNow.compareTo(calendarEnd) <= 0) {
TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation cellLocation = (GsmCellLocation) telephonyManager.getCellLocation();
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);//?????????
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
if (cellLocation != null) {
int cellid = cellLocation.getCid();
int celllac = cellLocation.getLac();
if(activeNetInfo != null) {
networkSubType = activeNetInfo.getSubtypeName();
}
String networkOperator = telephonyManager.getNetworkOperator();
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
int mnc = Integer.parseInt(networkOperator.substring(3));
String networkOperatorName = telephonyManager.getNetworkOperatorName();
int type = telephonyManager.getNetworkType();
Log.d("CellLocation", cellLocation.toString());
Log.d("GSM CELL ID", String.valueOf(cellid));
Log.d("GSM Location Code", String.valueOf(celllac));
Log.d("MCC", String.valueOf(mcc));
Log.d("MNC", String.valueOf(mnc));
Log.d("NetworkOperatorName", networkOperatorName);
Log.d("radioType", String.valueOf(type));
Log.d("Network subtype name", networkSubType);
AddLocationAsyncTask addLocationAsyncTask = new AddLocationAsyncTask(context);
addLocationAsyncTask.execute(mSessionData.getString("user_id",""), String.valueOf(cellid), String.valueOf(celllac), String.valueOf(mcc), String.valueOf(mnc),
networkOperatorName, networkSubType, currentTime);
}
}
}
}
}
So I want above task of fetching the location to run exactly after half an hour without miss for any reason.
Related
I am developing an application to schedule payment dates, one of its functions is to send you a notification on each date that you must make your payments. To achieve this, I check daily if it is a payment date, if so, I activate the notification so that it is shown later.
The application is already in production and the Play Console tells me that the following error has occurred 90 times:
Type: java.lang.RuntimeException
java.lang.RuntimeException:
at android.app.ActivityThread.handleReceiver (ActivityThread.java:3990)
at android.app.ActivityThread.access$1500 (ActivityThread.java:233)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2006)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loop (Looper.java:241)
at android.app.ActivityThread.main (ActivityThread.java:7582)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:941)
Caused by: java.lang.IllegalStateException:
at androidx.room.RoomOpenHelper.onUpgrade (RoomOpenHelper.java:117)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade (FrameworkSQLiteOpenHelper.java:177)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked (SQLiteOpenHelper.java:417)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase (SQLiteOpenHelper.java:317)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase (FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase (FrameworkSQLiteOpenHelper.java:106)
at androidx.room.RoomDatabase.inTransaction (RoomDatabase.java:622)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction (RoomDatabase.java:399)
at com.mypackagename.crud.PaymentDao_Impl.getPaymentsOfDate (PaymentDao_Impl.java:447)
at com.mypackagename.AlarmReceiver.aviactivateTodayReminders (AlarmReceiver.java:95)
at com.mypackagename.AlarmReceiver.onReceive (AlarmReceiver.java:35)
at android.app.ActivityThread.handleReceiver (ActivityThread.java:3981)
I don't know what the problem could be but I have read that other people have had problems accessing the database from BroadcastReceiver, am I doing something wrong? This is my code:
AlarmReceiver:
public class AlarmReceiver extends BroadcastReceiver {
static AppDataBase appDataBase;
#Override
public void onReceive(Context context, Intent intent) {
if (appDataBase == null){
appDataBase = Room.databaseBuilder(context, AppDataBase.class, Constants.DB_NAME)
.allowMainThreadQueries()
.build();
}
String action = intent.getAction();
if (action != null && context != null) {
if("ACTIVATOR".equals(action)) {
aviactivateTodayReminders(context, appDataBase);
} else if (action.equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {//Reactivate after restart
NotificationScheduler.enableDailyActivator(context);
aviactivateTodayReminders(context, appDataBase);
return;
}
}
if (action == null){//Show notification
//Code omitted for better understanding...
}
}
static void aviactivateTodayReminders(Context context, AppDataBase appDataBase){//Enable today's notifications
List<Long> idPaymentList = appDataBase.paymentDao().getPaymentsOfDate(todayDate());
for (int i = 0; i < idPaymentList.size(); i++){
long idPayment = idPaymentList.get(i);
NotificationScheduler.setNotification(context, (int) idPayment);
}
}
static String todayDate(){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(new Date());
}
}
getPaymentsOfDate:
#Query("SELECT _id FROM Payment WHERE date =:xDate ")
List<Long> getPaymentsOfDate(String xDate);
Notification Scheduler (I don't see a need to include it but here it is)
public class NotificationScheduler {
public static final String NOTIFICATION_CHANNEL_ID = "10000";
static void enableDailyActivator(Context context){//Enable activator service
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("ACTIVATOR");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cHourActivation = Calendar.getInstance();
cHourActivation.set(Calendar.HOUR_OF_DAY, Constants.HOUR_ACTIVATION);
cHourActivation.set(Calendar.MINUTE, 0);
cHourActivation.set(Calendar.SECOND, 0);
cHourActivation.set(Calendar.MILLISECOND, 0);
if (cHourActivation.getTimeInMillis() <= System.currentTimeMillis())
cHourActivation.add(Calendar.DATE, 1);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pendingIntent);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cHourActivation.getTimeInMillis(), (24 * 60 * 60 * 1000), pendingIntent);
}
public static void setNotification(Context context, int idPayment){
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("idPayment", idPayment);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, idPayment, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
am.cancel(pendingIntent);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 14);
calendar.set(Calendar.MINUTE, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else {
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
}
public static void showNotification(Context context, String title, String content, int idTanda){
//Code omitted for better understanding...
}
}
Important details:
EnableDailyActivator runs when starting the application for the first time and restarting the device.
"ACTIVATOR" It should be executed every day at approximately 00:00, this will call the aviactivateTodayReminders method that aims to obtain (from the database) the id of the current day's payments to schedule the notifications, unfortunately there is the error I see in Play Console
List<Long> idPaymentList = appDataBase.paymentDao().getPaymentsOfDate(todayDate());
What is the origin of the problem and how can I solve it?
I'm trying to create 3 notifications after the desired date and time is selected from a datepicker and a timepicker. I want to send notifications for the last 3 days for the selected value. But after the 3 notifications I want to cancel the notification process. I set up my pending notification and an alarm manager from my activity like
TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
//last 3 days add notification
Calendar calendar = Calendar.getInstance();
calendar.set(year, month, day, hourOfDay, minute, 0);
long when = calendar.getTimeInMillis();
when = when -3*(24*60*60*1000)+3000; //set startup time 3 days before
GetTime(when);
long diff = when - Calendar.getInstance().getTimeInMillis();
while (diff < 0){
when+= (24*60*60*1000); //increment 24h if selected time is closer then 3 days
diff = when - Calendar.getInstance().getTimeInMillis();
GetTime(when);
}
if (diff >0) {
Intent notifyIntent = new Intent(getApplicationContext() ,NotificationReceiver.class);
notifyIntent.putExtra("ctg", categorySelected);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), new Utils().getIdForCategory(categorySelected), notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when, 60*1000, pendingIntent); //setting for testing purposes for 60 seconds
}
}
};
My receiver class is :
protected void onHandleIntent(Intent intent) {
String categorySelected = intent.getStringExtra("ctg");
SharedPreference pref = new SharedPreference(getApplicationContext());
int alarmCount = pref.getAlarmCount(categorySelected);
if (alarmCount<=3){
Log.d("alarmCount", String.valueOf(alarmCount));
pref.saveAlarmCount(categorySelected,alarmCount+1);
showNotification(intent);
}
else //we reached the 3 times
{
new Utils().clearAllPendingIntent(getApplicationContext(),categorySelected);
}
}
and my clear method
public void clearAllPendingIntent(Context context, String categorySelected){
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent notifyIntent = new Intent(context ,NotificationReceiver.class);
notifyIntent.putExtra("ctg",categorySelected);
PendingIntent pendingUpdateIntent = PendingIntent.getBroadcast(context, new Utils().getIdForCategory(categorySelected), notifyIntent, PendingIntent.FLAG_CANCEL_CURRENT);
SharedPreference pref = new SharedPreference(context);
pref.saveAlarmCount(categorySelected,1);
// Cancel alarms
try {
alarmManager.cancel(pendingUpdateIntent);
} catch (Exception e) {
}
}
My problem is that the pending intent is not canceled after 3 times, which means I get notification each minute. What am I doing wrong?
The actual problem was with my new Utils().getIdForCategory(categorySelected) function which should return a specific value based on the category.
public int getIdForCategory(String category){
if (category.equals("A")) return 1; //before it was category=="A"
if (category.equals("B")) return 2;
if (category.equals("C")) return 3;
if (category.equals("D")) return 4;
if (category.equals("E")) return 5;
return 0;
}
I am trying to create a Scheduler in AKKA.
Requirement is :-
It will start every day 5:00AM
It will stop at 10:00 PM every day
It will execute job in every 1 hr (Frequency).
I find the solution for 3rd point (Frequency) but not able to find the solution for 1st and 2nd point.
system.scheduler().schedule(Duration.create(10, TimeUnit.SECONDS), Duration.create(1, TimeUnit.HOURS), actorRef, "Hello", system.dispatcher(), null);
// For 5.00 am time period.
int InHrs = 17;
int InMinutes = 00;
scheduler = Akka.system().scheduler().schedule(Duration.create(nextExecutionInSeconds(InHrs, InMinutes), TimeUnit.SECONDS),
Duration.create(24, TimeUnit.HOURS),
new Runnable() {
#Override
public void run() {
// Call your method
System.out.println("EVERY 24:00 Later --- " + System.currentTimeMillis());
}
},
Akka.system().dispatcher());
public static int nextExecutionInSeconds(int hour, int minute){
return Seconds.secondsBetween(
new DateTime(),
nextExecution(hour, minute)
).getSeconds();
}
public static DateTime nextExecution(int hour, int minute){
DateTime next = new DateTime()
.withHourOfDay(hour)
.withMinuteOfHour(minute)
.withSecondOfMinute(0)
.withMillisOfSecond(0);
return (next.isBeforeNow())
? next.plusHours(24)
: next;
}
Repeat this for 10 AM time period which will be 22 inHrs. Hopefully it solves the problem.
// I am using this for scheduling and repeating, it is working fine
//**I am also searching for to Stop Scheduler after Specific Time**
public static void setReminder(Context context, Class<?> cls, int hour, int min, int uniqueAlertID)
{
Calendar calendar = Calendar.getInstance();
Calendar setcalendar = Calendar.getInstance();
setcalendar.set(Calendar.HOUR_OF_DAY, hour);
setcalendar.set(Calendar.MINUTE, min);
setcalendar.set(Calendar.SECOND, 0);
// cancel already scheduled reminders
cancelReminder(context,cls, uniqueAlertID);
if(setcalendar.before(calendar))
setcalendar.add(Calendar.DATE,1);
// Enable a receiver
ComponentName receiver = new ComponentName(context, cls);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Intent intent1 = new Intent(context, cls);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, uniqueAlertID, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
am.setInexactRepeating(AlarmManager.RTC_WAKEUP, setcalendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
}
public static void cancelReminder(Context context,Class<?> cls, int uniqueAlertID)
{
// Disable a receiver
ComponentName receiver = new ComponentName(context, cls);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Intent intent1 = new Intent(context, cls);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
uniqueAlertID, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
am.cancel(pendingIntent);
pendingIntent.cancel();
}
I am having some problem with alarm manager in Android. So what I am trying to do is set the alarm to repeat to run the DB insertion every day around 12.01AM.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
calendar.set(Calendar.MINUTE, 1);
notificationCount = notificationCount + 1;
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context,
ReminderAlarm.class);
notificationIntent.putExtra("NotifyCount", notificationCount);
PendingIntent pi = PendingIntent.getBroadcast(context,
notificationCount, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
So basically I've came up with these code. However, the alarm manager execute again after the minute I set it.
Let's say I run the apps on 01/10/2014 5.48PM. I wanted this to run the DB insertion when onReceive every day after I set it around 12.01AM only. But somehow, the alarm manager execute at 01/10/2014 5.49PM which is one minute after I set it and it stopped working.
I wonder which part I did wrongly.
Thanks in advance.
EDIT
Recurring class
For this class, it will trigger the alarm manager everyday and pass the variables along to reminder alarm class for DB insertion.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recurring);
context = this;
buildListView();
if(!alarmInitialized(this)) {
scheduleAlarms(this);
}
}
// And the few methods you suggested to schedule the alarm
public static void scheduleAlarms(Context context) {
Calendar calendar = Calendar.getInstance();
if (hasRunnedToday(context)) { // if the alarm has run this day
calendar.add(Calendar.DATE, 1); // schedule it to run again starting
// tomorrow
}
long firstRunTime = calendar.getTimeInMillis();
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, ReminderAlarm.class);
PendingIntent pi = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstRunTime,
AlarmManager.INTERVAL_DAY, pi);
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
BootReceiver class
public void onReceive(Context context, Intent i) {
if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Recurring.scheduleAlarms(context);
}
}
ReminderAlarm class
Basically for this class it just grab the variable passed from Recurring class and execute the DB insertion. I did inserted some Toast.makeText to test if it is retrieving but no luck by testing it.
public class ReminderAlarm extends BroadcastReceiver {
private NotificationManager mNotificationManager;
private Notification notification;
#Override
public void onReceive(Context context, Intent intent) {
String recurID = null;
String recurStartDate = null;
String currentDate = null;
String description = null;
String type = null;
String amount = null;
String categoryName = null;
String frequencyStr = null;
String nextPaymentDate = null;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
DatabaseAdapter mDbHelper = new DatabaseAdapter(context);
mDbHelper.createDatabase();
mDbHelper.open();
RecurringController rc = new RecurringController(mDbHelper.open());
ArrayList<RecurringModel> recur_list = rc.getAllRecurring();
// THIS PART TO GET DATA FROM DATABASE
for (int i = 0; i < recur_list.size(); i++) {
recurID = recur_list.get(i).getRecurringID();
recurStartDate = recur_list.get(i).getRecurringStartDate();
currentDate = dateFormat.format(new Date());
description = recur_list.get(i).getRecurringDesc();
type = recur_list.get(i).getRecurringType();
amount = Float.toString(recur_list.get(i).getRecurringAmount());
categoryName = recur_list.get(i).getCategoryID();
frequencyStr = recur_list.get(i).getFrequency();
Toast.makeText(context,
description, Toast.LENGTH_LONG)
.show();
Toast.makeText(context,
recurStartDate Toast.LENGTH_LONG)
.show();
Calendar cal = Calendar.getInstance();
try {
cal.setTime(dateFormat.parse(recurStartDate));
if (frequencyStr.equals("Daily")) {
cal.add(Calendar.DAY_OF_MONTH, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.DAY_OF_MONTH, -1);
} else if (frequencyStr.equals("Weekly")) {
cal.add(Calendar.WEEK_OF_YEAR, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.WEEK_OF_YEAR, -1);
} else if (frequencyStr.equals("Monthly")) {
cal.add(Calendar.MONTH, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.MONTH, -1);
} else if (frequencyStr.equals("Yearly")) {
cal.add(Calendar.YEAR, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.YEAR, -1);
}
} catch (ParseException e) {
e.printStackTrace();
}
// If dates match then execute the SQL statements
if (currentDate.equals(nextPaymentDate)) {
// mDbHelper.createDatabase();
// mDbHelper.open();
TransactionRecModel trm = new TransactionRecModel();
CategoryController cc = new CategoryController(mDbHelper.open());
trm.setDate(currentDate);
trm.setTransDescription(description);
trm.setType(type);
trm.setAmount(Float.parseFloat(amount));
// Get the categoryID based on categoryName
String catID = cc.getCatIDByName(categoryName);
trm.setCategory(catID);
// Check if the recurring record exists before insert new
// transaction record
boolean recurExist = rc.checkRecurExist(recurStartDate,
description, catID);
if (recurExist == true) {
TransactionRecController trc = new TransactionRecController(
mDbHelper.open());
// Check if the transaction record exists to prevent
// duplication
boolean moveNext = trc.checkTransExist(trm);
if (moveNext == false) {
if (trc.addTransactionRec(trm)) {
// Update recurring start date after insertion of
// transaction
RecurringModel rm = new RecurringModel();
rm.setRecurringID(recurID);
rm.setRecurringStartDate(currentDate);
if (rc.updateRecurringDate(rm)) {
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent
.getActivity(
context,
Integer.parseInt(intent
.getExtras()
.get("NotifyCount")
.toString()),
new Intent(), 0);
notification = new Notification(
R.drawable.ic_launcher, "Notification",
System.currentTimeMillis());
notification.setLatestEventInfo(context,
description, nextPaymentDate,
contentIntent);
mNotificationManager
.notify(Integer.parseInt(intent
.getExtras().get("NotifyCount")
.toString()), notification);
mDbHelper.close();
}
}
}
}
mDbHelper.close();
}
}
mDbHelper.close();
Recurring.updateAlarmLastRun(context);
}
}
I've added this part of codes in the part you suggested to schedule the alarm to call the BootReceiver class. Then from BootReceiver class, I will call back to the Recurring class and Reminder Alarm class:
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
The problem is in calendar.getTimeInMillis() in
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
The second argument to setInexactRepeating quoting the doc
triggerAtMillis time in milliseconds that the alarm should first go off, using the appropriate clock (depending on the alarm type). This is inexact: the alarm will not fire before this time, but there may be a delay of almost an entire alarm interval before the first invocation of the alarm.
Meaning it will run the first time aproximally one minute after you set it because of
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
calendar.set(Calendar.MINUTE, 1);
If you wan't the first run of the alarm to be the next day do a
calendar.add(Calendar. DATE, 1);`
As to the it stopped working, did you reboot de device ?
AlarmCalendar alarms don't persist to device reboot, you can register a BroadcastReceiver to receive BOOT_COMPLETED event and register the alarm again check
does Alarm Manager persist even after reboot?
Update: as you requested here is some help after reviewing your code
In your BOOT_COMPLETED Receiver class:
public void onReceive(Context context, Intent i) {
if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
ReminderAlarm.scheduleAlarms(this);
}
}
In your ReminderAlarm class
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recurring);
if(!alarmInitialized(this) {
scheduleAlarms(this);
}
}
public static void scheduleAlarms(Context context) {
Calendar calendar = Calendar.getInstance();
if(hasRunnedToday(context)) { //if the alarm has run this day
calendar.add(Calendar.DATE, 1); //schedule it to run again starting tomorrow
}
long firstRunTime = calendar.getTimeInMillis();
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, ReminderAlarm.class);
PendingIntent pi = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
firstRunTime, AlarmManager.INTERVAL_DAY, pi);
}
public static boolean alarmInitialized(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
long alarmLastRun = preferences.getLong("AlarmLastRun", -1);
return alarmLastRun != -1;
}
public static void updateAlarmLastRun(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
preferences.edit()
.putLong("AlarmLastRun", new Date().getTime())
.apply();
}
public static boolean hasRunnedToday(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
long alarmLastRun = preferences.getLong("AlarmLastRun", -1);
if(alarmLastRun == -1) {
return false;
}
//check by comparing day, month and year
Date now = new Date();
Date lastRun = new Date(alarmLastRun);
return now.getTime() - lastRun.getTime() < TimeUnit.DAYS.toMillis(1);
}
Each time your Reminder class alarm runs you should call updateAlarmLastRun to update the last time the alarm has run, this is necessary because the alarm may be schedule to be run on a day and the user reboots the device before the alarm has run in that case we don't want to
use calendar.add(Calendar.DATE, 1); since that would skip a day.
On your Manifest.xml
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="false" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Notes:
You shouldn't do context = this if context is a class field since the object holds a reference to its field context and context field holds a reference to the object that would leak
Your Receiver 'onReceive` doesn't has the extras you assumed to have like "notificationCount" onReceive by the system when your device finish boot.
Once your alarm runs call updateAlarmLastRun
Hope any of this helps
I am having some problem with Alarm Manager in Android. I am setting the alarm manager to execute every midnight at 12am. Here are my codes:
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
notificationCount = notificationCount + 1;
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context,
ReminderAlarm.class);
notificationIntent.putExtra("RecurID", recurID);
notificationIntent.putExtra("RecurStartDate", _recurlist.get(position)
.getRecurringStartDate());
notificationIntent.putExtra("Date", dateFormat.format(new Date()));
notificationIntent.putExtra("Type", _recurlist.get(position).getRecurringType());
notificationIntent.putExtra("Amount", formatAmount);
notificationIntent.putExtra("NextPaymentDate", viewHolder.txt_ddate.getText());
notificationIntent.putExtra("NotifyCount", notificationCount);
PendingIntent pi = PendingIntent.getBroadcast(context,
notificationCount, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pi);
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
return convertView;
And when onReceive, the program will execute the insert and update SQL statement if it matches the conditions:
ReminderAlarm class
public void onReceive(Context context, Intent intent) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
String recurID = intent.getStringExtra("RecurID");
String recurStartDate = intent.getStringExtra("RecurStartDate");
String date = intent.getStringExtra("Date");
String type = intent.getStringExtra("Type");
String amount = intent.getStringExtra("Amount");
String nextPaymentDate = intent.getStringExtra("NextPaymentDate");
String currentDate = "Next Payment On: "
+ dateFormat.format(new Date());
// If dates match then execute the SQL statements
if (currentDate.equals(nextPaymentDate)) {
DatabaseAdapter mDbHelper = new DatabaseAdapter(
context.getApplicationContext());
mDbHelper.createDatabase();
mDbHelper.open();
TransactionRecModel trm = new TransactionRecModel();
CategoryController cc = new CategoryController(mDbHelper.open());
trm.setDate(date);
trm.setTransDescription(description);
trm.setType(type);
trm.setAmount(Float.parseFloat(amount));
// Get the categoryID based on categoryName
String catID = cc.getCatIDByName(categoryName);
trm.setCategory(catID);
// Check if the recurring record exists before insert new
// transaction record
RecurringController rc1 = new RecurringController(mDbHelper.open());
boolean recurExist = rc1.checkRecurExist(recurStartDate,
description, catID);
if (recurExist == true) {
TransactionRecController trc = new TransactionRecController(
mDbHelper.open());
// Check if the transaction record exists to prevent duplication
boolean moveNext = trc.checkTransExist(trm);
if (moveNext == false) {
if (trc.addTransactionRec(trm)) {
// Update recurring start date after insertion of
// transaction
RecurringModel rm = new RecurringModel();
rm.setRecurringID(recurID);
rm.setRecurringStartDate(date);
RecurringController rc = new RecurringController(
mDbHelper.open());
if (rc.updateRecurringDate(rm)) {
mDbHelper.close();
}
}
}
}
}
In my manifest file:
<receiver android:name="ReminderAlarm"></receiver>
<receiver
android:name="BootReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" >
</action>
</intent-filter>
</receiver>
Boot Receiver class:
public void onReceive(Context context, Intent i) {
scheduleAlarms(context);
}
static void scheduleAlarms(Context context) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 1);
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, ReminderAlarm.class);
PendingIntent pi = PendingIntent.getService(context, 0,
notificationIntent, 0);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
}
However, the alarm manager does not execute when it get passed 12am everyday. The insert and update SQL statement will only be execute when I run the applications.
Any ideas? Thanks in advance.
Problem is in setting time for alarm to execute. replace the top three lines with this code
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 1);
Change your calendar value to Calendar calendar = Calendar.getInstance();
calendar .set(Calendar.HOUR_OF_DAY, 0);
calendar .set(Calendar.MINUTE, 0);
calendar .set(Calendar.SECOND, 0); and set alarm.