package com.eveningoutpost.dexdrip.Services; import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.PowerManager; import android.preference.PreferenceManager; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.Models.AlertType; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.DesertSync; import com.eveningoutpost.dexdrip.Models.JoH; import com.eveningoutpost.dexdrip.Models.Reminder; import com.eveningoutpost.dexdrip.Models.Sensor; import com.eveningoutpost.dexdrip.Models.UserError.Log; import com.eveningoutpost.dexdrip.Models.UserNotification; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.Constants; import com.eveningoutpost.dexdrip.UtilityModels.Inevitable; import com.eveningoutpost.dexdrip.UtilityModels.NanoStatus; import com.eveningoutpost.dexdrip.UtilityModels.Notifications; import com.eveningoutpost.dexdrip.UtilityModels.Pref; import com.eveningoutpost.dexdrip.UtilityModels.pebble.PebbleUtil; import com.eveningoutpost.dexdrip.UtilityModels.pebble.PebbleWatchSync; import com.eveningoutpost.dexdrip.insulin.inpen.InPenEntry; import com.eveningoutpost.dexdrip.ui.LockScreenWallPaper; import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.eveningoutpost.dexdrip.watch.lefun.LeFun; import com.eveningoutpost.dexdrip.watch.lefun.LeFunEntry; import com.eveningoutpost.dexdrip.wearintegration.WatchUpdaterService; import com.eveningoutpost.dexdrip.webservices.XdripWebService; import com.eveningoutpost.dexdrip.xdrip; import static com.eveningoutpost.dexdrip.Home.startWatchUpdaterService; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.getLocalServiceCollectingState; public class MissedReadingService extends IntentService { //int otherAlertSnooze; private final static String TAG = MissedReadingService.class.getSimpleName(); private static volatile PendingIntent serviceIntent = null; private static int aggressive_backoff_timer = 120; public MissedReadingService() { super("MissedReadingService"); } @Override protected void onHandleIntent(Intent intent) { final boolean bg_missed_alerts; final PowerManager.WakeLock wl = JoH.getWakeLock("missed-reading-service", 60000); try { final boolean sensorActive = Sensor.isActive(); Log.d(TAG, "MissedReadingService onHandleIntent"); // test debug log final long stale_millis = Home.stale_data_millis(); // send to pebble if (Pref.getBoolean("broadcast_to_pebble", false) && (PebbleUtil.getCurrentPebbleSyncType() != 1) && !BgReading.last_within_millis(stale_millis)) { if (JoH.ratelimit("peb-miss", 120)) { // TODO replace ratelimit with Inevitable.task? JoH.startService(PebbleWatchSync.class); } // update pebble even when we don't have data to ensure missed readings show } if (LeFunEntry.isEnabled() && (!BgReading.last_within_millis(stale_millis))) { LeFun.showLatestBG(); } if ((Pref.getBoolean("aggressive_service_restart", false) || DexCollectionType.isFlakey())) {//!Home.get_enable_wear() && if (!BgReading.last_within_millis(stale_millis) && sensorActive && (!getLocalServiceCollectingState())) { if (JoH.ratelimit("aggressive-restart", aggressive_backoff_timer)) { Log.e(TAG, "Aggressively restarting collector service due to lack of reception: backoff: " + aggressive_backoff_timer); if (aggressive_backoff_timer < 1200) aggressive_backoff_timer += 60; CollectionServiceStarter.restartCollectionServiceBackground(); } else { aggressive_backoff_timer = 120; // reset } } } Reminder.processAnyDueReminders(); BluetoothGlucoseMeter.immortality(); XdripWebService.immortality(); // InPenEntry.immortality(); DesertSync.pullAsEnabled(); NanoStatus.keepFollowerUpdated(); LockScreenWallPaper.timerPoll(); // TODO functionalize the actual checking bg_missed_alerts = Pref.getBoolean("bg_missed_alerts", false); if (!bg_missed_alerts) { // we should not do anything in this case. if the ui, changes will be called again return; } if (!sensorActive) { // sensor not running we should return return; } if (!JoH.upForAtLeastMins(15)) { Log.d(TAG, "Uptime less than 15 minutes so not processing for missed reading"); return; } if ((Home.get_forced_wear()) && Pref.getBoolean("disable_wearG5_on_missedreadings", false)) { int bg_wear_missed_minutes = Pref.getStringToInt("disable_wearG5_on_missedreadings_level", 30); if (BgReading.getTimeSinceLastReading() >= (bg_wear_missed_minutes * 1000 * 60)) { Log.d(TAG, "Request WatchUpdaterService to disable force_wearG5 when wear is connected"); startWatchUpdaterService(xdrip.getAppContext(), WatchUpdaterService.ACTION_DISABLE_FORCE_WEAR, TAG); } } final int bg_missed_minutes = Pref.getStringToInt("bg_missed_minutes", 30); final long now = JoH.tsl(); // check if readings have been missed if (BgReading.getTimeSinceLastReading() >= (bg_missed_minutes * 1000 * 60) && Pref.getLong("alerts_disabled_until", 0) <= now && (BgReading.getTimeSinceLastReading() < (Constants.HOUR_IN_MS * 6)) && inTimeFrame()) { Notifications.bgMissedAlert(xdrip.getAppContext()); checkBackAfterSnoozeTime(xdrip.getAppContext(), now); } else { long disabletime = Pref.getLong("alerts_disabled_until", 0) - now; long missedTime = bg_missed_minutes * 1000 * 60 - BgReading.getTimeSinceLastReading(); long alarmIn = Math.max(disabletime, missedTime); checkBackAfterMissedTime(alarmIn); } } finally { JoH.releaseWakeLock(wl); } } private boolean inTimeFrame() { int startMinutes = Pref.getInt("missed_readings_start", 0); int endMinutes = Pref.getInt("missed_readings_end", 0); boolean allDay = Pref.getBoolean("missed_readings_all_day", true); return AlertType.s_in_time_frame(allDay, startMinutes, endMinutes); } private void checkBackAfterSnoozeTime(Context context, long now) { // This is not 100% accurate, need to take in account also the time of when this alert was snoozed. UserNotification userNotification = UserNotification.GetNotificationByType("bg_missed_alerts"); if (userNotification == null) { // No active alert exists, should not happen, we have just created it. Log.wtf(TAG, "No active alert exists."); setAlarm(getOtherAlertReraiseSec(context, "bg_missed_alerts") * 1000, false); } else { // we have an alert that should be re-raised on userNotification.timestamp long alarmIn = (long) userNotification.timestamp - now; if (alarmIn < 0) { alarmIn = 0; } setAlarm(alarmIn, true); } } private void checkBackAfterMissedTime(long alarmIn) { setAlarm(alarmIn, false); } // alarmIn is relative time ms public void setAlarm(long alarmIn, boolean force) { if (!force && (alarmIn < 5 * 60 * 1000)) { // No need to check more than once every 5 minutes alarmIn = 5 * 60 * 1000; } alarmIn = Math.max(alarmIn, 5000); // don't try to set less than 5 seconds in the future Log.d(TAG, "Setting timer to " + alarmIn / 60000 + " minutes from now"); initializeServiceIntent(); JoH.wakeUpIntent(this, alarmIn, serviceIntent); } // create the static pending intent if needed private void initializeServiceIntent() { if (serviceIntent == null) { synchronized (this) { if (serviceIntent == null) { serviceIntent = PendingIntent.getService(this, Constants.MISSED_READING_SERVICE_ID, new Intent(this, this.getClass()), 0); } } } } static public long getOtherAlertReraiseSec(Context context, String alertName) { boolean enableAlertsReraise = Pref.getBoolean(alertName + "_enable_alerts_reraise", false); if (enableAlertsReraise) { return Pref.getStringToInt(alertName + "_reraise_sec", 60); } else { return 60 * getOtherAlertSnoozeMinutes(PreferenceManager.getDefaultSharedPreferences(context), alertName); } } static public long getOtherAlertSnoozeMinutes(SharedPreferences prefs, String alertName) { int defaultSnooze = Pref.getStringToInt("other_alerts_snooze", 20); return Pref.getStringToInt(alertName + "_snooze", defaultSnooze); } public static void delayedLaunch() { Inevitable.task("launch-missed-readings", 1000, () -> JoH.startService(MissedReadingService.class)); } }