diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Action.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Action.java deleted file mode 100644 index d537f9f889..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Action.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -/** - * JamOrHam - * - * Alert action interface - */ - -public interface Action { - boolean activate(); -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/BaseAlert.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/BaseAlert.java deleted file mode 100644 index e4975e58e2..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/BaseAlert.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -import com.eveningoutpost.dexdrip.models.UserError.Log; -import com.google.gson.annotations.Expose; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import lombok.Getter; -import lombok.val; - -/** - * JamOrHam - * - * BaseAlert abstract helper class - */ - -public abstract class BaseAlert implements Condition, Action, Pollable { - - @Expose - @Getter - private final String name; - @Expose - @Getter - private final List when = new ArrayList<>(); - - @Expose - private boolean oneShot; - - - protected When lastEvent; - - public BaseAlert(final String name, final When... when) { - this.name = name; - this.when.addAll(Arrays.asList(when)); - } - - public BaseAlert setOneShot(final boolean oneShot) { - this.oneShot = oneShot; - return this; - } - - private void log(final String msg) { - Log.uel("Alert:" + name, msg); // TODO i18n ? - } - - @Override - public PollResult poll(final When event) { - val ret = new PollResult(); - if (when.contains(event)) { - lastEvent = event; - if (isMet()) { - val result = activate(); - ret.triggered = true; - log("Activated: " + result); // TODO i18n? - ret.remove = oneShot; - } - } - return ret; - } - - @Override - public String group() { - return name; - } - - @Override - public int sortPos() { - return 0; - } - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Condition.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Condition.java deleted file mode 100644 index 427dd51365..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Condition.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -/** - * JamOrHam - * - * Alert condition interface - */ - -public interface Condition { - boolean isMet(); -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/PollResult.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/PollResult.java deleted file mode 100644 index ce2edf9976..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/PollResult.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -/** - * JamOrHam - *

- * Data class for returning poll results to caller - */ - -public class PollResult { - - boolean remove; - boolean triggered; - - public void reset() { - remove = false; - triggered = false; - } - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Pollable.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Pollable.java deleted file mode 100644 index c6db7b6fd5..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Pollable.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -/** - * JamOrHam - * - * Alert polling interface - */ - -public interface Pollable { - - enum When { - Reading, - Hour, - ScreenOn, - ChargeChange, - } - - // main polling method - PollResult poll(When event); - - // grouping identifier - String group(); - - // relative sorting position - int sortPos(); - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Poller.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Poller.java deleted file mode 100644 index e4fc17f44a..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Poller.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; - -import com.eveningoutpost.dexdrip.models.UserError.Log; -import com.eveningoutpost.dexdrip.utilitymodels.Inevitable; -import com.eveningoutpost.dexdrip.xdrip; - -import java.util.LinkedList; - -import lombok.val; - -/** - * JamOrHam - *

- * Encapsulated poller providing external methods for trigger events - *

- * Handles its own trigger for screen on events - */ - -public class Poller { - - private static final String TAG = "AlertSchedule"; - - public static void chargerConnectedDisconnected() { - poll(Pollable.When.ChargeChange); - } - - public static void screenOn() { - poll(Pollable.When.ScreenOn); - } - - public static void reading() { - poll(Pollable.When.Reading); - } - - public static void hour() { - poll(Pollable.When.Hour); - } - - // call from application start - public static void init() { - try { - val intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_SCREEN_ON); - - try { - xdrip.getAppContext().unregisterReceiver(screenReceiver); - } catch (Exception e) { - // - } - xdrip.getAppContext().registerReceiver(screenReceiver, intentFilter); - } catch (Exception e) { - Log.wtf(TAG, "Exception in init: " + e); - } - } - - private static void poll(final Pollable.When event) { - Log.d(TAG, "DEBUG POLL: " + event); - val remove = new LinkedList(); - val triggeredGroups = new LinkedList(); - val registry = Registry.getRegistry(); - for (val alert : registry) { - if (triggeredGroups.contains(alert.group())) { - Log.d(TAG, "Skipping due to group match: " + alert.group()); - continue; - } - val result = alert.poll(event); - if (result.remove) { - remove.add(alert); // this one asked to be removed - } - if (result.triggered) { - triggeredGroups.add(alert.group()); - } - } - for (val alert : remove) { - Registry.remove(alert); - } - } - - private static final BroadcastReceiver screenReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(final Context context, final Intent intent) { - final String action = intent.getAction(); - if (action == null) return; - switch (action) { - case Intent.ACTION_SCREEN_ON: - Inevitable.task("screen-on-poll", 6000, () -> screenOn()); - break; - } - } - }; - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Registry.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Registry.java deleted file mode 100644 index e457d8d178..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Registry.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -import android.content.SharedPreferences; - -import com.eveningoutpost.dexdrip.models.UserError.Log; -import com.eveningoutpost.dexdrip.utilitymodels.Pref; - -import java.util.ArrayList; -import java.util.List; - -import lombok.Getter; - -/** - * JamOrHam - *

- * Registry for Pollable alert objects - */ - -public class Registry { - - private static final String TAG = "AlertRegistry"; - - @Getter - private static final List registry = new ArrayList<>(); - - // TODO allow for adjusting list and available vs enabled lists - // TODO add sorting of registry - - static { - refresh(); - } - - public static void refresh() { - synchronized (registry) { - registry.clear(); - if (Pref.getBooleanDefaultFalse("alert_raise_for_sensor_expiry")) { - registry.add(new SensorExpiry()); - } - // addGlucoseAlerts(); - // sort(); - } - } - - public static void remove(final Pollable alert) { - synchronized (registry) { - Log.d(TAG, "Removing " + alert); - registry.remove(alert); - } - } - - private static void clear() { - synchronized (registry) { - registry.clear(); - } - } - - /*private static void addGlucoseAlerts() { - synchronized (registry) { - registry.add(new CustomAlert("Ultra Low") - .setHighAlert(false) - .setSmoothedRate(false) - .setRateOfChange(0d) - .setThreshold(100)); - registry.add(new CustomAlert("Ultra High") - .setHighAlert(true) - .setSmoothedRate(false) - .setRateOfChange(0d) - .setThreshold(300)); - } - }*/ - - public static SharedPreferences.OnSharedPreferenceChangeListener prefListener = (prefs, key) -> { - if (key.startsWith("alert_")) { - Log.d(TAG, "Refreshing due to settings change"); - refresh(); - } - }; - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/SensorExpiry.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/SensorExpiry.java deleted file mode 100644 index f7271a52c7..0000000000 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/SensorExpiry.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.eveningoutpost.dexdrip.alert; - -import static com.eveningoutpost.dexdrip.models.JoH.cancelNotification; -import static com.eveningoutpost.dexdrip.models.JoH.niceTimeScalar; -import static com.eveningoutpost.dexdrip.models.JoH.niceTimeScalarNatural; -import static com.eveningoutpost.dexdrip.models.JoH.showNotification; -import static com.eveningoutpost.dexdrip.models.JoH.tsl; -import static com.eveningoutpost.dexdrip.utilitymodels.Constants.SENSORY_EXPIRY_NOTIFICATION_ID; - -import com.eveningoutpost.dexdrip.g5model.SensorDays; -import com.eveningoutpost.dexdrip.models.Treatments; -import com.eveningoutpost.dexdrip.models.UserError.Log; -import com.eveningoutpost.dexdrip.utilitymodels.Constants; - -import lombok.val; - -/** - * JamOrHam - * - * Sensor Expiry alert. Triggers when passing threshold times when user should be able to notice - */ - -public class SensorExpiry extends BaseAlert { - - private static final String TAG = SensorExpiry.class.getSimpleName(); - private static final long NOT_ALERTED = Long.MAX_VALUE; - private static final long[] THRESHOLDS = { - // need to be in ascending order so first hit is first applicable to avoid multiple triggers - Constants.HOUR_IN_MS * 2, - Constants.HOUR_IN_MS * 6, - Constants.HOUR_IN_MS * 12, - Constants.HOUR_IN_MS * 24, - }; - - private static final Persist.Long remaining = new Persist.Long("PREF_SENSOR_EXPIRE_ALERT"); - private static final Persist.Long alerted = new Persist.Long("PREF_SENSOR_EXPIRE_ALERTED"); - - public SensorExpiry() { - super("Sensor Expiry", When.ChargeChange, When.ScreenOn); - } - - @Override - public boolean activate() { - val expiry = niceTimeScalarNatural(SensorDays.get().getRemainingSensorPeriodInMs(), 1); - val notificationId = SENSORY_EXPIRY_NOTIFICATION_ID; - cancelNotification(notificationId); - val expireMsg = String.format("Sensor will expire in %s", expiry); // TODO i18n and format string - showNotification("Sensor expiring", expireMsg, null, notificationId, null, true, true, null, null, null, true); - Treatments.create_note("Warning: " + expireMsg, tsl()); // TODO i18n but note classifier also needs updating for that - return true; - } - - @Override - public boolean isMet() { - val sd = SensorDays.get(); - if (sd.isValid()) { - val now = sd.getRemainingSensorPeriodInMs(); - val last = remaining.get(); - - try { - if (now > (last + Constants.HOUR_IN_MS)) { - Log.d(TAG, "Period rewound to: " + niceTimeScalar(now) + " was " + niceTimeScalar(last)); - alerted.set(NOT_ALERTED); - } else if (last > now) { - Log.d(TAG, "Period reduced to: " + niceTimeScalar(now) + " was " + niceTimeScalar(last)); - val lastAlerted = alerted.get(); - for (val threshold : THRESHOLDS) { - if (now <= threshold && threshold < lastAlerted) { - alerted.set(threshold); - return true; - } - } - } else { - Log.d(TAG, "Now and last identical: " + niceTimeScalar(now)); - } - - } finally { - remaining.set(now); - } - - } else { - Log.d(TAG, "Cannot evaluate as sensor days invalid"); - } - return false; - } - -} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/UserNotification.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/UserNotification.java index a2c658587e..d60463650f 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/UserNotification.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/UserNotification.java @@ -20,6 +20,7 @@ @Table(name = "Notifications", id = BaseColumns._ID) public class UserNotification extends PlusModel { + public static final String SENSOR_EXPIRY_ALERT_NAME = "sensor_expiry_alert"; // For 'other alerts' this will be the time that the alert should be raised again. // For calibration alerts this is the time that the alert was played. @@ -53,6 +54,9 @@ public class UserNotification extends PlusModel { @Column(name = "bg_fall_alert") public boolean bg_fall_alert; + @Column(name = SENSOR_EXPIRY_ALERT_NAME) + public boolean sensor_expiry_alert; + private final static List legacy_types = Arrays.asList( "bg_alert", "calibration_alert", "double_calibration_alert", "extra_calibration_alert", "bg_unclear_readings_alert", @@ -90,6 +94,14 @@ public static UserNotification lastExtraCalibrationAlert() { .executeSingle(); } + public static UserNotification sensorExpiryAlert() { + return new Select() + .from(UserNotification.class) + .where("sensor_expiry_alert = ?", true) + .orderBy("_ID desc") + .executeSingle(); + } + // the UserNotifcation model is difficult to extend without adding more // booleans which will introduce a database incompatibility and prevent // downgrading. So instead we work around it with shared preferences until @@ -169,6 +181,9 @@ public static UserNotification create(String message, String type, long timestam case "bg_fall_alert": userNotification.bg_fall_alert = true; break; + case SENSOR_EXPIRY_ALERT_NAME: + userNotification.sensor_expiry_alert = true; + break; default: Log.d(TAG, "Saving workaround for: " + type + " " + message); PersistentStore.setString("UserNotification:timestamp:" + type, String.format(Locale.US, "%d", (long) timestamp)); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java index 6380d824e5..92b0fb1659 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java @@ -471,10 +471,15 @@ private PendingIntent snoozeIntent(Context ctx, int minsSinceStartPlaying){ return PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } - static private int getAlertProfile(Context ctx){ + static int getAlertProfile(Context context) + { + return getAlertProfile(context, "bg_alert_profile"); + } + + static int getAlertProfile(Context ctx, String preference) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - String profile = prefs.getString("bg_alert_profile", "ascending"); - if(profile.equals("High")) { + String profile = prefs.getString(preference, "ascending"); + if (profile.compareToIgnoreCase("High") == 0) { Log.i(TAG, "getAlertProfile returning ALERT_PROFILE_HIGH"); return ALERT_PROFILE_HIGH; } @@ -486,11 +491,11 @@ static private int getAlertProfile(Context ctx){ Log.i(TAG, "getAlertProfile returning ALERT_PROFILE_MEDIUM"); return ALERT_PROFILE_MEDIUM; } - if(profile.equals("vibrate only")) { + if (profile.compareToIgnoreCase("vibrate only") == 0) { Log.i(TAG, "getAlertProfile returning ALERT_PROFILE_VIBRATE_ONLY"); return ALERT_PROFILE_VIBRATE_ONLY; } - if(profile.equals("Silent")) { + if (profile.compareToIgnoreCase("Silent") == 0) { Log.i(TAG, "getAlertProfile returning ALERT_PROFILE_SILENT"); return ALERT_PROFILE_SILENT; } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Constants.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Constants.java index 49cd91fc46..8ff9d0cd95 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Constants.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Constants.java @@ -60,7 +60,7 @@ public class Constants { public static final int HEALTH_CONNECT_RESPONSE_ID = 2002; public static final int ZXING_CAM_REQ_CODE = 49374; public static final int ZXING_FILE_REQ_CODE = 49375; // This is created by just incrementing the existing camera scan code from the zxing package - public static final int SENSORY_EXPIRY_NOTIFICATION_ID = 2003; + public static final int SENSOR_EXPIRY_NOTIFICATION_ID = 2003; // increments from this start number diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Notifications.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Notifications.java index ce7d28e1ee..8d3fb1952c 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Notifications.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Notifications.java @@ -1,5 +1,10 @@ package com.eveningoutpost.dexdrip.utilitymodels; +import static com.eveningoutpost.dexdrip.utilitymodels.AlertPlayer.ALERT_PROFILE_HIGH; +import static com.eveningoutpost.dexdrip.utilitymodels.AlertPlayer.ALERT_PROFILE_VIBRATE_ONLY; +import static com.eveningoutpost.dexdrip.utilitymodels.ColorCache.X; +import static com.eveningoutpost.dexdrip.utilitymodels.ColorCache.getCol; + import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; @@ -20,16 +25,19 @@ import android.os.PowerManager; import android.os.SystemClock; import android.preference.PreferenceManager; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; import android.text.SpannableString; import android.widget.RemoteViews; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + import com.eveningoutpost.dexdrip.AddCalibration; import com.eveningoutpost.dexdrip.BestGlucose; import com.eveningoutpost.dexdrip.DoubleCalibrationActivity; import com.eveningoutpost.dexdrip.EditAlertActivity; import com.eveningoutpost.dexdrip.Home; +import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.evaluators.PersistentHigh; import com.eveningoutpost.dexdrip.models.ActiveBgAlert; import com.eveningoutpost.dexdrip.models.AlertType; import com.eveningoutpost.dexdrip.models.BgReading; @@ -39,24 +47,19 @@ import com.eveningoutpost.dexdrip.models.Sensor; import com.eveningoutpost.dexdrip.models.UserError.Log; import com.eveningoutpost.dexdrip.models.UserNotification; -import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.services.ActivityRecognizedService; import com.eveningoutpost.dexdrip.services.MissedReadingService; import com.eveningoutpost.dexdrip.services.SnoozeOnNotificationDismissService; -import com.eveningoutpost.dexdrip.evaluators.PersistentHigh; +import com.eveningoutpost.dexdrip.services.broadcastservice.BroadcastEntry; import com.eveningoutpost.dexdrip.ui.NumberGraphic; import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.eveningoutpost.dexdrip.utils.PowerStateReceiver; import com.eveningoutpost.dexdrip.wearintegration.Amazfitservice; -import com.eveningoutpost.dexdrip.services.broadcastservice.BroadcastEntry; import com.eveningoutpost.dexdrip.xdrip; import java.util.Date; import java.util.List; -import static com.eveningoutpost.dexdrip.utilitymodels.ColorCache.X; -import static com.eveningoutpost.dexdrip.utilitymodels.ColorCache.getCol; - /** * Created by Emma Black on 11/28/14. */ @@ -896,6 +899,79 @@ private void extraCalibrationRequest() { } } + public static void sensorExpiryAlert(final Context context, final long threshold) { + UserNotification userNotification = UserNotification.sensorExpiryAlert(); + if (userNotification == null || userNotification.timestamp <= new Date().getTime()) { + if (userNotification != null) { + try { + userNotification.delete(); + } catch (NullPointerException e) { + // ignore null pointer exception during delete as we emulate database records + } + + Log.d(TAG, "Delete"); + } + + userNotification = UserNotification.create( + "Sensor expiring in " + convertMillisecondsToTimeString(threshold), + UserNotification.SENSOR_EXPIRY_ALERT_NAME, + new Date().getTime()); + final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + final NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(context, "" + Constants.SENSOR_EXPIRY_NOTIFICATION_ID) + .setVisibility(Pref.getBooleanDefaultFalse("public_notifications") ? Notification.VISIBILITY_PUBLIC : Notification.VISIBILITY_PRIVATE) + .setSmallIcon(R.drawable.ic_action_communication_invert_colors_on) + .setContentTitle("Sensor Expiry Alert") + .setContentText(userNotification.message) + .setStyle(new NotificationCompat.BigTextStyle().bigText(userNotification.message)) + .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, Home.class), PendingIntent.FLAG_UPDATE_CURRENT)) + .setLights(0xff00ff00, 300, 1000); + final int alertProfile = AlertPlayer.getAlertProfile(context, "sensor_expiry_alert_profile"); + switch (alertProfile) + { + case ALERT_PROFILE_HIGH: + if (AlertPlayer.notSilencedDueToCall()) { + final Uri sensorExpiryAlertSound = Uri.parse(sharedPreferences.getString("sensor_expiry_alert_sound", "content://settings/system/notification_sound")); + if (sharedPreferences.getBoolean("sensor_expiry_alert_override_silent", false) + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mBuilder.setCategory(NotificationCompat.CATEGORY_ALARM); + mBuilder.setSound(sensorExpiryAlertSound, AudioAttributes.USAGE_ALARM); + } else { + mBuilder.setSound(sensorExpiryAlertSound); + } + } + + break; + case ALERT_PROFILE_VIBRATE_ONLY: + mBuilder.setVibrate(vibratePattern); + break; + default: + throw new IllegalStateException("Alert profile '" + alertProfile + "' is unsupported."); + } + + Log.ueh("Sensor Expiry Alert", userNotification.message); + final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(Constants.SENSOR_EXPIRY_NOTIFICATION_ID, XdripNotificationCompat.build(mBuilder)); + BroadcastEntry.sendAlert(UserNotification.SENSOR_EXPIRY_ALERT_NAME, userNotification.message); + } + } + + private static String convertMillisecondsToTimeString(final long milliseconds) { + final long hours = milliseconds / (1000 * 60 * 60); + final long minutes = (milliseconds / (1000 * 60)) % 60; + if (hours == 1 && minutes == 1) { + return "1 hour and 1 minute"; + } else if (hours == 1) { + return "1 hour and " + minutes + " minutes"; + } else if (minutes == 1) { + return hours + " hours and 1 minute"; + } else if (minutes == 0) { + return hours + " hours"; + } + + return hours + " hours and " + minutes + " minutes"; + } + public static void bgUnclearAlert(Context context) { long otherAlertReraiseSec = MissedReadingService.getOtherAlertReraiseSec(context, "bg_unclear_readings_alert"); OtherAlert(context, "bg_unclear_readings_alert", "Unclear Sensor Readings" + " (@" + JoH.hourMinuteString() + ")", uncleanAlertNotificationId, NotificationChannels.BG_ALERT_CHANNEL, true, otherAlertReraiseSec); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/PowerStateReceiver.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/PowerStateReceiver.java index 5b8e81866b..37481a09e2 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/PowerStateReceiver.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/PowerStateReceiver.java @@ -8,7 +8,6 @@ import android.os.BatteryManager; import android.util.Log; -import com.eveningoutpost.dexdrip.alert.Poller; import com.eveningoutpost.dexdrip.xdrip; /** @@ -67,11 +66,9 @@ public void onReceive(Context context, Intent intent) { if (action.equals(Intent.ACTION_POWER_CONNECTED)) { setInternalPrefsBoolean(PREFS_POWER_STATE, true); Log.d(TAG, "Power connected"); - Poller.chargerConnectedDisconnected(); } else if (action.equals(Intent.ACTION_POWER_DISCONNECTED)) { setInternalPrefsBoolean(PREFS_POWER_STATE, false); Log.d(TAG, "Power disconnected "); - Poller.chargerConnectedDisconnected(); } } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java index f4542a9555..0a5ef76b90 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java @@ -37,11 +37,6 @@ import android.preference.PreferenceScreen; import android.preference.RingtonePreference; import android.preference.SwitchPreference; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.ActivityCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.text.InputFilter; import android.text.TextUtils; import android.view.Menu; @@ -49,6 +44,11 @@ import android.widget.BaseAdapter; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import com.bytehamster.lib.preferencesearch.SearchConfiguration; import com.bytehamster.lib.preferencesearch.SearchPreferenceResult; import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener; @@ -59,15 +59,14 @@ import com.eveningoutpost.dexdrip.ParakeetHelper; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.WidgetUpdateService; -import com.eveningoutpost.dexdrip.alert.Registry; import com.eveningoutpost.dexdrip.calibrations.PluggableCalibration; import com.eveningoutpost.dexdrip.cgm.carelinkfollow.CareLinkFollowService; import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkAuthType; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkAuthenticator; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; import com.eveningoutpost.dexdrip.cgm.nsfollow.NightscoutFollow; import com.eveningoutpost.dexdrip.cgm.sharefollow.ShareFollowService; import com.eveningoutpost.dexdrip.cgm.webfollow.Cpref; -import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkAuthenticator; -import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; import com.eveningoutpost.dexdrip.cloud.jamcm.Pusher; import com.eveningoutpost.dexdrip.g5model.DexSyncKeeper; import com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine; @@ -592,7 +591,6 @@ protected void onResume() PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(BroadcastService.prefListener); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(BlueJayEntry.prefListener); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(uiPrefListener); - PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(Registry.prefListener); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(xDripCloudListener); LocalBroadcastManager.getInstance(this).registerReceiver(mibandStatusReceiver, new IntentFilter(Intents.PREFERENCE_INTENT)); @@ -608,7 +606,6 @@ protected void onPause() PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(BroadcastService.prefListener); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(BlueJayEntry.prefListener); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(uiPrefListener); - PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(Registry.prefListener); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(xDripCloudListener); LocalBroadcastManager.getInstance(this).unregisterReceiver(mibandStatusReceiver); pFragment = null; diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/xdrip.java b/app/src/main/java/com/eveningoutpost/dexdrip/xdrip.java index b207d44f84..a4a2f5bed7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/xdrip.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/xdrip.java @@ -2,37 +2,43 @@ import android.annotation.SuppressLint; import android.app.Application; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Build; import android.preference.PreferenceManager; -import androidx.annotation.StringRes; import android.util.Log; +import androidx.annotation.StringRes; + +import com.eveningoutpost.dexdrip.alert.Persist; +import com.eveningoutpost.dexdrip.calibrations.PluggableCalibration; +import com.eveningoutpost.dexdrip.g5model.SensorDays; import com.eveningoutpost.dexdrip.models.AlertType; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.Reminder; -import com.eveningoutpost.dexdrip.alert.Poller; import com.eveningoutpost.dexdrip.services.ActivityRecognizedService; import com.eveningoutpost.dexdrip.services.BluetoothGlucoseMeter; import com.eveningoutpost.dexdrip.services.MissedReadingService; import com.eveningoutpost.dexdrip.services.PlusSyncService; +import com.eveningoutpost.dexdrip.services.broadcastservice.BroadcastEntry; import com.eveningoutpost.dexdrip.utilitymodels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.utilitymodels.ColorCache; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.IdempotentMigrations; +import com.eveningoutpost.dexdrip.utilitymodels.Notifications; import com.eveningoutpost.dexdrip.utilitymodels.PlusAsyncExecutor; import com.eveningoutpost.dexdrip.utilitymodels.Pref; import com.eveningoutpost.dexdrip.utilitymodels.VersionTracker; -import com.eveningoutpost.dexdrip.calibrations.PluggableCalibration; import com.eveningoutpost.dexdrip.utils.AppCenterCrashReporting; -import com.eveningoutpost.dexdrip.utils.NewRelicCrashReporting; import com.eveningoutpost.dexdrip.utils.jobs.DailyJob; import com.eveningoutpost.dexdrip.utils.jobs.XDripJobCreator; import com.eveningoutpost.dexdrip.watch.lefun.LeFunEntry; import com.eveningoutpost.dexdrip.watch.miband.MiBandEntry; import com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry; -import com.eveningoutpost.dexdrip.services.broadcastservice.BroadcastEntry; import com.eveningoutpost.dexdrip.webservices.XdripWebService; import com.evernote.android.job.JobManager; @@ -49,6 +55,7 @@ public class xdrip extends Application { private static final String TAG = "xdrip.java"; + private static final Persist.Long ALERTED = new Persist.Long("PREF_SENSOR_EXPIRE_ALERTED"); @SuppressLint("StaticFieldLeak") private static volatile Context context; private static boolean fabricInited = false; @@ -132,9 +139,67 @@ public void onCreate() { } Reminder.firstInit(xdrip.getAppContext()); PluggableCalibration.invalidateCache(); - Poller.init(); + setupSensorExpiryAlert(); } + private static void setupSensorExpiryAlert() + { + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); + if (action == null) { + return; + } + + switch (action) { + case Intent.ACTION_TIME_TICK: + if (!SensorDays.get().isValid() || !Pref.getBooleanDefaultFalse("alert_raise_for_sensor_expiry")) + { + return; + } + + final String customSensoryExpiryMinThreshold = "alert_raise_for_sensor_expiry_mins"; + final String customThreshold = Pref.getStringDefaultBlank(customSensoryExpiryMinThreshold); + long lastAlerted = ALERTED.get(); + long remainingSensorPeriodInMs = SensorDays.get().getRemainingSensorPeriodInMs(); + if (remainingSensorPeriodInMs > lastAlerted) + { + lastAlerted = Long.MAX_VALUE; + } + + if (customThreshold.length() > 0) + { + long customThresholdMs = Constants.MINUTE_IN_MS * Long.parseLong(customThreshold); + if (lastAlerted > customThresholdMs && customThresholdMs >= remainingSensorPeriodInMs) + { + ALERTED.set(customThresholdMs); + Notifications.sensorExpiryAlert(context, customThresholdMs); + } + } + else + { + final long[] thresholds = { + // need to be in ascending order so first hit is first applicable to avoid multiple triggers + Constants.HOUR_IN_MS * 2, + Constants.HOUR_IN_MS * 6, + Constants.HOUR_IN_MS * 12, + Constants.HOUR_IN_MS * 24, + }; + for (final long threshold : thresholds) + { + if (lastAlerted > threshold && threshold >= remainingSensorPeriodInMs) + { + ALERTED.set(threshold); + Notifications.sensorExpiryAlert(context, threshold); + } + } + } + + break; + }}}; + xdrip.getAppContext().registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); + } public static synchronized boolean isRunningTest() { if (null == isRunningTestCache) { diff --git a/app/src/main/res/values-bg/strings-bg.xml b/app/src/main/res/values-bg/strings-bg.xml index 8aeaa884c2..1fb1a0dcfd 100644 --- a/app/src/main/res/values-bg/strings-bg.xml +++ b/app/src/main/res/values-bg/strings-bg.xml @@ -1796,7 +1796,6 @@ Избрете yМакс в случай, когато няма по-Големи стойности. Проверете настройките и изчакайте за свързване. Повиши честотата на известията, когато наближи да изтече срокът на ползване на сензора. - Възраст на сензора Затвори Разширени настройки за Libre 2 Показвай raw стойности в графиката diff --git a/app/src/main/res/values-cs/strings-cs.xml b/app/src/main/res/values-cs/strings-cs.xml index 09c325dda7..a397efba0b 100644 --- a/app/src/main/res/values-cs/strings-cs.xml +++ b/app/src/main/res/values-cs/strings-cs.xml @@ -1806,7 +1806,6 @@ môže spôsobovať pády alebo zamrznutia telefónu! Klepnutím otestujte predt Zvolit maximum osy y, pokud se nebudou vyskytovat výšší hodnoty glykémie. Ověřte nastavení a počkejte na připojení. Upozornit, když se blíží konec senzoru. - Senzor bude končit Zavřít Pokročilá nastavení pro Libre 2 Zobrazit neupravené hodnoty na grafu diff --git a/app/src/main/res/values-de/strings-de.xml b/app/src/main/res/values-de/strings-de.xml index a798ba9bed..87949d54cb 100644 --- a/app/src/main/res/values-de/strings-de.xml +++ b/app/src/main/res/values-de/strings-de.xml @@ -1800,7 +1800,6 @@ Wähle yMax, wenn es keine größeren Werte gibt. Einstellungen überprüfen und auf Verbindung warten Benachrichtigen, wenn Sensor bald abläuft - Sensor-Ablauf Schließen Erweiterte Einstellungen für Libre 2 Rohwerte im Graph anzeigen diff --git a/app/src/main/res/values-es/strings-es.xml b/app/src/main/res/values-es/strings-es.xml index f43e654d3f..d3b0b63827 100644 --- a/app/src/main/res/values-es/strings-es.xml +++ b/app/src/main/res/values-es/strings-es.xml @@ -1794,7 +1794,6 @@ Escoge yMin para cuando no hay lecturas superiores. Verificar ajustes y esperar a la conectividad Aumentar las notificaciones cuando el sensor se acerca a la caducidad. - Caducidad sensor Cerrar Ajustes avanzados para Libre 2 Mostrar valores en bruto en el gráfico diff --git a/app/src/main/res/values-et/strings-et.xml b/app/src/main/res/values-et/strings-et.xml index 4beb3c957a..0b1aac9598 100644 --- a/app/src/main/res/values-et/strings-et.xml +++ b/app/src/main/res/values-et/strings-et.xml @@ -1813,7 +1813,6 @@ Vali yMax, kui sellest suuremaid näitusid ei ole. Kontrolli seadeid ja oota ühendust. Käivita teavitused kui sensori kehtivusaeg hakkab lõppema. - Sensori kehtivusaeg Sulge Libre 2 täpsemad sätted Näita graafikus töötlemata väärtuseid diff --git a/app/src/main/res/values-fr/strings-fr.xml b/app/src/main/res/values-fr/strings-fr.xml index 155c2917db..d8421289a6 100644 --- a/app/src/main/res/values-fr/strings-fr.xml +++ b/app/src/main/res/values-fr/strings-fr.xml @@ -1813,7 +1813,6 @@ Choisir yMax lorsqu\'il n\'y a pas de mesures plus grandes. Vérifier les paramètres et attendre la connexion. Notifier lorsque le capteur est proche de l\'expiration. - Expiration du capteur Fermer Paramètres avancés pour Libre 2 Afficher les valeurs brutes dans le graphique diff --git a/app/src/main/res/values-hu/strings-hu.xml b/app/src/main/res/values-hu/strings-hu.xml index 2880f1acea..8d8eabedcb 100644 --- a/app/src/main/res/values-hu/strings-hu.xml +++ b/app/src/main/res/values-hu/strings-hu.xml @@ -1755,7 +1755,6 @@ Állítsa be yMax értékét, amelynél nagyobbat nem mutat a grafikon. Beállítások megerősítése és várakozás a kapcsolatfelvételre. Értesítés szenzor lejárata előtt. - Szenzor lejár Bezárás Libre 2 szenzor további beállításai Nyers adatok mutatása a grafikonon diff --git a/app/src/main/res/values-it/strings-it.xml b/app/src/main/res/values-it/strings-it.xml index 58f1df8f23..07d5468195 100644 --- a/app/src/main/res/values-it/strings-it.xml +++ b/app/src/main/res/values-it/strings-it.xml @@ -1793,7 +1793,6 @@ Scegli yMax per quando non ci sono letture più alte. Verifica le impostazioni e aspetta il collegamento. Genera delle notifiche quando il sensore si avvicina alla scadenza. - Scadenza sensore Chiudi Impostazioni avanzate per Libre 2 Mostra i valori grezzi nel grafico diff --git a/app/src/main/res/values-ja/strings-ja.xml b/app/src/main/res/values-ja/strings-ja.xml index 84db378211..0ef4438f17 100644 --- a/app/src/main/res/values-ja/strings-ja.xml +++ b/app/src/main/res/values-ja/strings-ja.xml @@ -947,7 +947,6 @@ 有効にする 設定を確認し、接続を待ちます。 センサーの有効期限が近づいたら通知します。 - センサーの有効期限 閉じる リブレ2の高度な設定 グラフに生の値を表示 diff --git a/app/src/main/res/values-nb/strings-nb.xml b/app/src/main/res/values-nb/strings-nb.xml index 487a25b088..423f3b1033 100644 --- a/app/src/main/res/values-nb/strings-nb.xml +++ b/app/src/main/res/values-nb/strings-nb.xml @@ -1813,7 +1813,6 @@ Velg yMax for når det ikke er større avlesninger. Bekreft innstillingene og vent på tilkobling. Lag varsel når sensoren nærmer seg utløp. - Sensor-utløp Lukk Avanserte innstillinger for Libre 2 Vis råverdier i grafen diff --git a/app/src/main/res/values-pl/strings-pl.xml b/app/src/main/res/values-pl/strings-pl.xml index a8e7061ccc..93fab3d57e 100644 --- a/app/src/main/res/values-pl/strings-pl.xml +++ b/app/src/main/res/values-pl/strings-pl.xml @@ -1764,7 +1764,6 @@ Wybierz yMax, dla sytuacji, gdy nie ma wyższych odczytów. Sprawdź ustawienia i poczekaj na połączenie. Wyświetl powiadomienia, gdy czujnik zbliża się do wygaśnięcia. - Czas wygaśnięcia czujnika Zamknij Zaawansowane ustawienia dla Libre 2 Pokaż wartości surowe na wykresie diff --git a/app/src/main/res/values-pt-rBR/strings-pt-rBR.xml b/app/src/main/res/values-pt-rBR/strings-pt-rBR.xml index 20a7bbf153..560e622a5d 100644 --- a/app/src/main/res/values-pt-rBR/strings-pt-rBR.xml +++ b/app/src/main/res/values-pt-rBR/strings-pt-rBR.xml @@ -1678,7 +1678,6 @@ nenhum Ligar Emitir notificações quando o sensor estiver perto de expirar. - Expiração do sensor Fechar Configurações avançadas para o Libre 2 Mostrar valores brutos no gráfico diff --git a/app/src/main/res/values-ru/strings-ru.xml b/app/src/main/res/values-ru/strings-ru.xml index 95b2bb7f54..3092fbe4dd 100644 --- a/app/src/main/res/values-ru/strings-ru.xml +++ b/app/src/main/res/values-ru/strings-ru.xml @@ -1802,7 +1802,6 @@ Укажите y max когда нет больших показаний. Проверьте настройки и дождитесь подключения. Выводить уведомления, когда приближается окончание срока работы сенсора. - Пришло окончание срока работы сенсора Закрыть Расширенные настройки для Libre 2 Показать необработанные значения на графике diff --git a/app/src/main/res/values-sv/strings-sv.xml b/app/src/main/res/values-sv/strings-sv.xml index c9d184c1fb..16015e0146 100644 --- a/app/src/main/res/values-sv/strings-sv.xml +++ b/app/src/main/res/values-sv/strings-sv.xml @@ -1673,7 +1673,6 @@ Justera y-axelns minimum Justera y-axelns maximum Verifiera inställningar och vänta på anslutning. - Sensorutgång Stäng Avancerade inställningar för Libre 2 Visa råa värden i grafen diff --git a/app/src/main/res/values-tr/strings-tr.xml b/app/src/main/res/values-tr/strings-tr.xml index 1960152a56..9c8537b97e 100644 --- a/app/src/main/res/values-tr/strings-tr.xml +++ b/app/src/main/res/values-tr/strings-tr.xml @@ -1799,7 +1799,6 @@ Daha büyük okumalar olmadığında yMax\'i seçin. Ayarları doğrulayın ve bağlantının kurulmasını bekleyin. Sensör ömrü bitimine yakın bildirimleri artırın. - Sensörün geçerlilik süresi Kapat Libre 2 için gelişmiş ayarlar Ham değerleri grafikte göster diff --git a/app/src/main/res/values-uk/strings-uk.xml b/app/src/main/res/values-uk/strings-uk.xml index 6f9cd31dde..619aad5a4c 100644 --- a/app/src/main/res/values-uk/strings-uk.xml +++ b/app/src/main/res/values-uk/strings-uk.xml @@ -1798,7 +1798,6 @@ Виберіть максимальне значення для осі y, коли немає більших зчитувань. Перевірте налаштування і чекайте підключення. Увімкнути сповіщення, коли закінчується термін дії сенсору. - Термін сенсору закінчується Закрити Розширені налаштування для Libre 2 Показати не скореговані значення на графіку diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 339e50f0be..8d2177efd2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -181,6 +181,12 @@ Silent + + High + Vibrate only + Silent + + 1 mg/dl/min (0.06 mmol/l/min) 2 mg/dl/min (0.11 mmol/l/min) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7bc4a2e2d1..4154134380 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - + Bluetooth Scan @@ -244,6 +244,7 @@ Forecasted Low Alert Extrapolate data to try to predict lows Alarm at Forecasted Low mins + Minutes before sensor expiry Other xDrip+ alerts xDrip+ Display Settings Display customizations @@ -443,6 +444,7 @@ Forecast Lows Raise alarm on Forecast Low Notify when predicted low time reaches threshold + If unspecified, alert 24, 12, 6, and 2 hours before sensor expiry. Predicted Low Sound Choose sound used for predicted low alarm. Notify when Parakeet device stops checking in @@ -812,6 +814,7 @@ falling rate threshold rising rate threshold Set sound used for BG Alerts. + Set sound used for sensor expiry alarm. Alert Sound Override Silent mode on these alerts Repeating max every (minutes) @@ -1861,7 +1864,7 @@ Choose yMax for when there are no greater readings. Verify settings and wait for connectivity. Raise notifications when the sensor gets close to expiry. - Sensor expiry + Sensor expiry alert Close Advanced settings for Libre 2 Show raw values in graph diff --git a/app/src/main/res/xml/pref_notifications.xml b/app/src/main/res/xml/pref_notifications.xml index 587c470d52..554d4a5356 100644 --- a/app/src/main/res/xml/pref_notifications.xml +++ b/app/src/main/res/xml/pref_notifications.xml @@ -355,6 +355,41 @@ android:summary="@string/choose_sound_used_for_predicted_low_alarm" android:title="@string/predicted_low_sound" /> + + + + + + + -