From d91ef8831737edc6860d1bf6c4f895b6b76f293c Mon Sep 17 00:00:00 2001 From: bart2 Date: Tue, 4 Jul 2023 19:30:05 -0700 Subject: [PATCH 01/13] Fetch IoB from Omnipod app --- .../dexdrip/models/Treatments.java | 16 +++ .../dexdrip/services/UiBasedCollector.java | 105 ++++++++++++++++++ .../dexdrip/webservices/WebServicePebble.java | 13 ++- app/src/main/res/values/strings.xml | 4 + .../main/res/xml/pref_advanced_settings.xml | 10 ++ .../services/UiBasedCollectorTest.java | 11 ++ 6 files changed, 157 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java index 06ba55f7bf..ac37bb6bdd 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java @@ -21,6 +21,7 @@ import com.eveningoutpost.dexdrip.models.UserError.Log; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.services.SyncService; +import com.eveningoutpost.dexdrip.services.UiBasedCollector; import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.Pref; import com.eveningoutpost.dexdrip.utilitymodels.UndoRedo; @@ -57,6 +58,7 @@ import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.utilitymodels.Constants.HOUR_IN_MS; import static com.eveningoutpost.dexdrip.utilitymodels.Constants.MINUTE_IN_MS; +import com.eveningoutpost.dexdrip.utilitymodels.Pref; import static java.lang.StrictMath.abs; import static com.eveningoutpost.dexdrip.models.JoH.emptyString; @@ -1292,6 +1294,20 @@ public static List ioBForGraph_old(int number, double startTime) { }*/ public static Double getCurrentIoB() { + if (Pref.getBooleanDefaultFalse("fetch_iob_from_omnipod_app")) { + return getCurrentIoBFromOmnipodApp(); + } else { + return getCurrentIoBFromGraphCalculation(); + } + } + + public static Double getCurrentIoBFromOmnipodApp() { + Double iob = UiBasedCollector.getCurrentIoB(); + + return iob; + } + + public static Double getCurrentIoBFromGraphCalculation() { long now = System.currentTimeMillis(); final List iobInfo = Treatments.ioBForGraph_new(now - 1 * Constants.DAY_IN_MS); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java index 171d6f5908..bedf87fba1 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java @@ -2,6 +2,7 @@ import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.cgm.dex.ClassifierAction.lastReadingTimestamp; +import static com.eveningoutpost.dexdrip.utilitymodels.Constants.MINUTE_IN_MS; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.UiBased; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.getDexCollectionType; import static com.eveningoutpost.dexdrip.xdrip.gs; @@ -37,9 +38,13 @@ import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.eveningoutpost.dexdrip.xdrip; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import lombok.val; @@ -53,10 +58,15 @@ public class UiBasedCollector extends NotificationListenerService { private static final String TAG = UiBasedCollector.class.getSimpleName(); private static final String UI_BASED_STORE_LAST_VALUE = "UI_BASED_STORE_LAST_VALUE"; private static final String UI_BASED_STORE_LAST_REPEAT = "UI_BASED_STORE_LAST_REPEAT"; + private static final String OMNIPOD_IOB_ENABLED_PREFERENCE_KEY = "fetch_iob_from_omnipod_app"; + private static final String OMNIPOD_IOB_VALUE = "OMNIPOD_IOB_VALUE"; + private static final String OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP = "OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP"; private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; private static final HashSet coOptedPackages = new HashSet<>(); + private static final HashSet omnipodIoBPackages = new HashSet<>(); + private static boolean debug = true; @VisibleForTesting String lastPackage; @@ -74,6 +84,8 @@ public class UiBasedCollector extends NotificationListenerService { coOptedPackages.add("com.medtronic.diabetes.guardian"); coOptedPackages.add("com.medtronic.diabetes.minimedmobile.eu"); coOptedPackages.add("com.medtronic.diabetes.minimedmobile.us"); + + omnipodIoBPackages.add("com.insulet.myblue.pdm"); } @Override @@ -93,6 +105,88 @@ public void onNotificationPosted(final StatusBarNotification sbn) { } } } + + if (omnipodIoBPackages.contains(fromPackage)) { + processOmnipodOmnipodNotification(sbn.getNotification()); + } + } + + private void processOmnipodOmnipodNotification(final Notification notification) { + if (notification == null) { + UserError.Log.e(TAG, "Null notification"); + return; + } + if (notification.contentView != null) { + processOmnipodNotificationCV(notification.contentView); + } else { + UserError.Log.e(TAG, "Content is empty"); + } + } + + private void processOmnipodNotificationCV(final RemoteViews cview) { + if (cview == null) return; + val applied = cview.apply(this, null); + val root = (ViewGroup) applied.getRootView(); + val texts = new ArrayList(); + getTextViews(texts, root); + if (debug) UserError.Log.d(TAG, "Text views: " + texts.size()); + Double iob = null; + try { + for (val view : texts) { + val tv = (TextView) view; + String text = tv.getText() != null ? tv.getText().toString() : ""; + val desc = tv.getContentDescription() != null ? tv.getContentDescription().toString() : ""; + if (debug) UserError.Log.d(TAG, "Examining: >" + text + "< : >" + desc + "<"); + iob = parseOmnipodIoB(text); + if (iob != null) { + break; + } + } + + if (iob != null) { + if (debug) UserError.Log.d(TAG, "Inserting new IoB value: " + iob); + PersistentStore.setDouble(OMNIPOD_IOB_VALUE, iob); + long now = System.currentTimeMillis(); + PersistentStore.setLong(OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP, now); + } + } catch (Exception e) { + UserError.Log.e(TAG, "exception in processOmnipodNotificationCV: " + e); + } + + texts.clear(); + } + Double parseOmnipodIoB(final String value) { + if (!value.contains("IOB:")) { + return null; + } + + Pattern pattern = Pattern.compile("IOB: ([\\d\\.]+) U"); + Matcher matcher = pattern.matcher(value); + + if (matcher.find()) { + return Double.parseDouble(matcher.group(1)); + } + + return 0.0; + } + + public static Double getCurrentIoB() { + Long iobWriteTimestamp = PersistentStore.getLong(OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP); + if (debug) { + Date date = new Date(iobWriteTimestamp); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String timestamp = dateFormat.format(date); + UserError.Log.d(TAG, "iow write time: " + timestamp); + } + + Long now = System.currentTimeMillis(); + + if (iobWriteTimestamp < now - 5 * MINUTE_IN_MS) { + return null; + } + + Double iob = PersistentStore.getDouble(OMNIPOD_IOB_VALUE); + return iob; } @Override @@ -257,12 +351,23 @@ public static SharedPreferences.OnSharedPreferenceChangeListener getListener(fin // } } + if (key.equals(OMNIPOD_IOB_ENABLED_PREFERENCE_KEY)) { + try { + enableNotificationService(activity); + } catch (Exception e) { + UserError.Log.e(TAG, "Exception when enabling NotificationService: " + e); + } + } }; } public static void switchToAndEnable(final Activity activity) { DexCollectionType.setDexCollectionType(UiBased); Sensor.createDefaultIfMissing(); + enableNotificationService(activity); + } + + private static void enableNotificationService(final Activity activity) { if (!isNotificationServiceEnabled()) { JoH.show_ok_dialog(activity, gs(R.string.please_allow_permission), "Permission is needed to receive data from other applications. xDrip does not do anything beyond this scope. Please enable xDrip on the next screen", diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java index 560a7e7186..e643277551 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java @@ -1,5 +1,7 @@ package com.eveningoutpost.dexdrip.webservices; +import android.content.Context; +import android.app.Activity; import android.util.Log; import com.eveningoutpost.dexdrip.BestGlucose; @@ -10,6 +12,9 @@ import com.eveningoutpost.dexdrip.models.Treatments; import com.eveningoutpost.dexdrip.dagger.Injectors; import com.eveningoutpost.dexdrip.ui.MicroStatus; +import com.eveningoutpost.dexdrip.utilitymodels.Pref; +import com.eveningoutpost.dexdrip.xdrip; + import org.json.JSONArray; import org.json.JSONException; @@ -83,8 +88,12 @@ public WebResponse request(String query) { } bgs.put("battery", microStatus.gs("bestBridgeBattery")); - Double iob = Treatments.getCurrentIoB(); - bgs.put("iob", (iob == null) ? "unknown" : String.format("%.02f", iob)); + if (!Pref.getBooleanDefaultFalse("enable_iob_in_api_endpoint")) { + bgs.put("iob", 0.0); + } else { + Double iob = Treatments.getCurrentIoB(); + bgs.put("iob", (iob == null) ? "unknown" : String.format("%.02f", iob)); + } // TODO output bwp and bwpo status_array.put(status); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff12cb3734..a7cfb1973a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1241,6 +1241,10 @@ Broadcast Service API Enables xDrip communication with third-party applications by using new API + Enable IoB reporting in API + Enable IoB value reporting in Web Service API endpoint used by some smartwatches + Fetch IoB from the Omnipod 5 App + Fetch IoB value from the Omnipod 5 App running on the same device MiBand Sync Data with MiBand fitness bands diff --git a/app/src/main/res/xml/pref_advanced_settings.xml b/app/src/main/res/xml/pref_advanced_settings.xml index 93e076d224..ee8c586d4b 100644 --- a/app/src/main/res/xml/pref_advanced_settings.xml +++ b/app/src/main/res/xml/pref_advanced_settings.xml @@ -1005,6 +1005,16 @@ android:key="broadcast_service_enabled" android:summary="@string/summary_broadcast_service_api" android:title="@string/title_broadcast_service_api" /> + + Date: Tue, 4 Jul 2023 19:53:31 -0700 Subject: [PATCH 02/13] Update UiBasedCollector.java Turn off debug messages --- .../com/eveningoutpost/dexdrip/services/UiBasedCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java index bedf87fba1..65e0db15c7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java @@ -66,7 +66,7 @@ public class UiBasedCollector extends NotificationListenerService { private static final HashSet coOptedPackages = new HashSet<>(); private static final HashSet omnipodIoBPackages = new HashSet<>(); - private static boolean debug = true; + private static boolean debug = false; @VisibleForTesting String lastPackage; From 18581787a49d4e08d2fd47c1e01445a8878e538f Mon Sep 17 00:00:00 2001 From: bart2 Date: Sat, 8 Jul 2023 19:44:21 -0700 Subject: [PATCH 03/13] Make the feature more generic --- .../dexdrip/models/Treatments.java | 6 +-- .../dexdrip/services/UiBasedCollector.java | 38 +++++++++---------- app/src/main/res/values/strings.xml | 4 +- .../main/res/xml/pref_advanced_settings.xml | 6 +-- .../services/UiBasedCollectorTest.java | 6 +-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java index ac37bb6bdd..a328958dae 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java @@ -1294,14 +1294,14 @@ public static List ioBForGraph_old(int number, double startTime) { }*/ public static Double getCurrentIoB() { - if (Pref.getBooleanDefaultFalse("fetch_iob_from_omnipod_app")) { - return getCurrentIoBFromOmnipodApp(); + if (Pref.getBooleanDefaultFalse("fetch_iob_from_companion_app")) { + return getCurrentIoBFromCompanionApp(); } else { return getCurrentIoBFromGraphCalculation(); } } - public static Double getCurrentIoBFromOmnipodApp() { + public static Double getCurrentIoBFromCompanionApp() { Double iob = UiBasedCollector.getCurrentIoB(); return iob; diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java index bedf87fba1..3706e64475 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java @@ -58,15 +58,15 @@ public class UiBasedCollector extends NotificationListenerService { private static final String TAG = UiBasedCollector.class.getSimpleName(); private static final String UI_BASED_STORE_LAST_VALUE = "UI_BASED_STORE_LAST_VALUE"; private static final String UI_BASED_STORE_LAST_REPEAT = "UI_BASED_STORE_LAST_REPEAT"; - private static final String OMNIPOD_IOB_ENABLED_PREFERENCE_KEY = "fetch_iob_from_omnipod_app"; - private static final String OMNIPOD_IOB_VALUE = "OMNIPOD_IOB_VALUE"; - private static final String OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP = "OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP"; + private static final String COMPANION_APP_IOB_ENABLED_PREFERENCE_KEY = "fetch_iob_from_companion_app"; + private static final String COMPANION_APP_IOB_VALUE = "COMPANION_APP_IOB_VALUE"; + private static final String COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP = "COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP"; private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; private static final HashSet coOptedPackages = new HashSet<>(); - private static final HashSet omnipodIoBPackages = new HashSet<>(); - private static boolean debug = true; + private static final HashSet companionAppIoBPackages = new HashSet<>(); + private static boolean debug = false; @VisibleForTesting String lastPackage; @@ -85,7 +85,7 @@ public class UiBasedCollector extends NotificationListenerService { coOptedPackages.add("com.medtronic.diabetes.minimedmobile.eu"); coOptedPackages.add("com.medtronic.diabetes.minimedmobile.us"); - omnipodIoBPackages.add("com.insulet.myblue.pdm"); + companionAppIoBPackages.add("com.insulet.myblue.pdm"); } @Override @@ -106,24 +106,24 @@ public void onNotificationPosted(final StatusBarNotification sbn) { } } - if (omnipodIoBPackages.contains(fromPackage)) { - processOmnipodOmnipodNotification(sbn.getNotification()); + if (companionAppIoBPackages.contains(fromPackage)) { + processCompanionAppIoBNotification(sbn.getNotification()); } } - private void processOmnipodOmnipodNotification(final Notification notification) { + private void processCompanionAppIoBNotification(final Notification notification) { if (notification == null) { UserError.Log.e(TAG, "Null notification"); return; } if (notification.contentView != null) { - processOmnipodNotificationCV(notification.contentView); + processCompanionAppIoBNotificationCV(notification.contentView); } else { UserError.Log.e(TAG, "Content is empty"); } } - private void processOmnipodNotificationCV(final RemoteViews cview) { + private void processCompanionAppIoBNotificationCV(final RemoteViews cview) { if (cview == null) return; val applied = cview.apply(this, null); val root = (ViewGroup) applied.getRootView(); @@ -137,7 +137,7 @@ private void processOmnipodNotificationCV(final RemoteViews cview) { String text = tv.getText() != null ? tv.getText().toString() : ""; val desc = tv.getContentDescription() != null ? tv.getContentDescription().toString() : ""; if (debug) UserError.Log.d(TAG, "Examining: >" + text + "< : >" + desc + "<"); - iob = parseOmnipodIoB(text); + iob = parseIoB(text); if (iob != null) { break; } @@ -145,17 +145,17 @@ private void processOmnipodNotificationCV(final RemoteViews cview) { if (iob != null) { if (debug) UserError.Log.d(TAG, "Inserting new IoB value: " + iob); - PersistentStore.setDouble(OMNIPOD_IOB_VALUE, iob); + PersistentStore.setDouble(COMPANION_APP_IOB_VALUE, iob); long now = System.currentTimeMillis(); - PersistentStore.setLong(OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP, now); + PersistentStore.setLong(COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP, now); } } catch (Exception e) { - UserError.Log.e(TAG, "exception in processOmnipodNotificationCV: " + e); + UserError.Log.e(TAG, "exception in processCompanionAppIoBNotificationCV: " + e); } texts.clear(); } - Double parseOmnipodIoB(final String value) { + Double parseIoB(final String value) { if (!value.contains("IOB:")) { return null; } @@ -171,7 +171,7 @@ Double parseOmnipodIoB(final String value) { } public static Double getCurrentIoB() { - Long iobWriteTimestamp = PersistentStore.getLong(OMNIPOD_IOB_VALUE_WRITE_TIMESTAMP); + Long iobWriteTimestamp = PersistentStore.getLong(COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP); if (debug) { Date date = new Date(iobWriteTimestamp); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -185,7 +185,7 @@ public static Double getCurrentIoB() { return null; } - Double iob = PersistentStore.getDouble(OMNIPOD_IOB_VALUE); + Double iob = PersistentStore.getDouble(COMPANION_APP_IOB_VALUE); return iob; } @@ -351,7 +351,7 @@ public static SharedPreferences.OnSharedPreferenceChangeListener getListener(fin // } } - if (key.equals(OMNIPOD_IOB_ENABLED_PREFERENCE_KEY)) { + if (key.equals(COMPANION_APP_IOB_ENABLED_PREFERENCE_KEY)) { try { enableNotificationService(activity); } catch (Exception e) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a7cfb1973a..b2d1c88094 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1243,8 +1243,8 @@ Enables xDrip communication with third-party applications by using new API Enable IoB reporting in API Enable IoB value reporting in Web Service API endpoint used by some smartwatches - Fetch IoB from the Omnipod 5 App - Fetch IoB value from the Omnipod 5 App running on the same device + Fetch IoB from companion app + Fetch IoB value from companion app running on the same device, such as the Omnipod 5 App MiBand Sync Data with MiBand fitness bands diff --git a/app/src/main/res/xml/pref_advanced_settings.xml b/app/src/main/res/xml/pref_advanced_settings.xml index ee8c586d4b..4f2480ce36 100644 --- a/app/src/main/res/xml/pref_advanced_settings.xml +++ b/app/src/main/res/xml/pref_advanced_settings.xml @@ -1012,9 +1012,9 @@ android:title="@string/title_enable_iob_in_api_endpoint" /> + android:key="fetch_iob_from_companion_app" + android:summary="@string/summary_fetch_iob_from_companion_app" + android:title="@string/title_fetch_iob_from_companion_app" /> Date: Sat, 8 Jul 2023 20:12:10 -0700 Subject: [PATCH 04/13] Use DoubleTimeout and regex list for IoB matching --- .../dexdrip/services/UiBasedCollector.java | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java index 3706e64475..0e560b4e32 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java @@ -27,11 +27,13 @@ import androidx.annotation.VisibleForTesting; import com.eveningoutpost.dexdrip.BestGlucose; +import com.eveningoutpost.dexdrip.alert.Persist; import com.eveningoutpost.dexdrip.models.BgReading; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.Sensor; import com.eveningoutpost.dexdrip.models.UserError; import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.PersistentStore; import com.eveningoutpost.dexdrip.utilitymodels.Unitized; import com.eveningoutpost.dexdrip.cgm.dex.BlueTails; @@ -59,13 +61,14 @@ public class UiBasedCollector extends NotificationListenerService { private static final String UI_BASED_STORE_LAST_VALUE = "UI_BASED_STORE_LAST_VALUE"; private static final String UI_BASED_STORE_LAST_REPEAT = "UI_BASED_STORE_LAST_REPEAT"; private static final String COMPANION_APP_IOB_ENABLED_PREFERENCE_KEY = "fetch_iob_from_companion_app"; - private static final String COMPANION_APP_IOB_VALUE = "COMPANION_APP_IOB_VALUE"; - private static final String COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP = "COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP"; + private static final Persist.DoubleTimeout iob_store = + new Persist.DoubleTimeout("COMPANION_APP_IOB_VALUE", Constants.MINUTE_IN_MS * 5); private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; private static final HashSet coOptedPackages = new HashSet<>(); private static final HashSet companionAppIoBPackages = new HashSet<>(); + private static final HashSet companionAppIoBRegexes = new HashSet<>(); private static boolean debug = false; @VisibleForTesting @@ -86,6 +89,10 @@ public class UiBasedCollector extends NotificationListenerService { coOptedPackages.add("com.medtronic.diabetes.minimedmobile.us"); companionAppIoBPackages.add("com.insulet.myblue.pdm"); + + // The IoB value should be captured into the first match group. + // English localization of the Omnipod 5 App + companionAppIoBRegexes.add(Pattern.compile("IOB: ([\\d\\.,]+) U")); } @Override @@ -145,9 +152,7 @@ private void processCompanionAppIoBNotificationCV(final RemoteViews cview) { if (iob != null) { if (debug) UserError.Log.d(TAG, "Inserting new IoB value: " + iob); - PersistentStore.setDouble(COMPANION_APP_IOB_VALUE, iob); - long now = System.currentTimeMillis(); - PersistentStore.setLong(COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP, now); + iob_store.set(iob); } } catch (Exception e) { UserError.Log.e(TAG, "exception in processCompanionAppIoBNotificationCV: " + e); @@ -156,37 +161,19 @@ private void processCompanionAppIoBNotificationCV(final RemoteViews cview) { texts.clear(); } Double parseIoB(final String value) { - if (!value.contains("IOB:")) { - return null; - } - - Pattern pattern = Pattern.compile("IOB: ([\\d\\.]+) U"); - Matcher matcher = pattern.matcher(value); + for (Pattern pattern : companionAppIoBRegexes) { + Matcher matcher = pattern.matcher(value); - if (matcher.find()) { - return Double.parseDouble(matcher.group(1)); + if (matcher.find()) { + return JoH.tolerantParseDouble(matcher.group(1)); + } } - return 0.0; + return null; } public static Double getCurrentIoB() { - Long iobWriteTimestamp = PersistentStore.getLong(COMPANION_APP_IOB_VALUE_WRITE_TIMESTAMP); - if (debug) { - Date date = new Date(iobWriteTimestamp); - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - String timestamp = dateFormat.format(date); - UserError.Log.d(TAG, "iow write time: " + timestamp); - } - - Long now = System.currentTimeMillis(); - - if (iobWriteTimestamp < now - 5 * MINUTE_IN_MS) { - return null; - } - - Double iob = PersistentStore.getDouble(COMPANION_APP_IOB_VALUE); - return iob; + return iob_store.get(); } @Override From 5fa14cce6040921a8433021b6d99b0c9eca57b8d Mon Sep 17 00:00:00 2001 From: Jamorham Date: Wed, 5 Jul 2023 18:25:01 +0000 Subject: [PATCH 05/13] Update plugin error reporting --- libkeks/src/main/java/jamorham/keks/Context.java | 2 ++ libkeks/src/main/java/jamorham/keks/Plugin.java | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/libkeks/src/main/java/jamorham/keks/Context.java b/libkeks/src/main/java/jamorham/keks/Context.java index 49fa63452a..09fb7a7774 100644 --- a/libkeks/src/main/java/jamorham/keks/Context.java +++ b/libkeks/src/main/java/jamorham/keks/Context.java @@ -30,6 +30,7 @@ public class Context { public byte[] challenge; public volatile byte[] savedKey; public volatile Packet[] packet = new Packet[4]; + public volatile int sequence; @Getter @Setter private volatile byte[] partA; @@ -48,6 +49,7 @@ public void reset() { } public void resetIfNotReady() { + sequence = 0; if (savedKey == null && getRound3Packet() == null) { reset(); } diff --git a/libkeks/src/main/java/jamorham/keks/Plugin.java b/libkeks/src/main/java/jamorham/keks/Plugin.java index 88a9c9dd0a..8237942fb2 100644 --- a/libkeks/src/main/java/jamorham/keks/Plugin.java +++ b/libkeks/src/main/java/jamorham/keks/Plugin.java @@ -237,7 +237,12 @@ public boolean receivedResponse(final byte[] data) { switch (state) { case RequestAuth: if (!verifyChallenge(data)) { - throw new SecurityException("Mismatch"); + context.reset(); + if (context.sequence > 1) { + throw new SecurityException("Mismatch - wait"); + } else { + return false; + } } context.challenge = new byte[8]; arraycopy(data, 9, context.challenge, 0, context.challenge.length); @@ -291,7 +296,7 @@ public boolean receivedResponse(final byte[] data) { changeState(SendCertificate1); return true; } else { - return false; + throw new InvalidParameterException("Invalid QR code 1"); } case SendCertificate2: val rep2 = new CertInfoRxMessage(data); @@ -300,15 +305,17 @@ public boolean receivedResponse(final byte[] data) { changeState(SendCertificate2); return true; } else { - return false; + throw new InvalidParameterException("Invalid QR code 2"); } case SendKeyChallenge: + if (data.length > 2 && data[1] != 0) { + throw new InvalidParameterException("Invalid QR code 3"); + } presponse = Calc.challenger(context.getPartC(), data); return true; case SendKeyChallengeOut: - // TODO validate return true; } @@ -532,6 +539,7 @@ private boolean verifyChallenge(byte[] data) { } private byte[][] sequencePacket(final byte[] Packet) { + context.sequence++; val param = parameterFromState(); val command = (param < 0) ? null : new byte[]{KEYCMD.bytes[0], param}; return new byte[][]{command, Packet}; From 7835cd5a591ca928fcd98bcb6554466ac709aea4 Mon Sep 17 00:00:00 2001 From: Jamorham Date: Thu, 6 Jul 2023 12:51:56 +0000 Subject: [PATCH 06/13] OB1: tidy up persistence and service handling --- .../dexdrip/g5model/Ob1G5StateMachine.java | 2 + .../services/Ob1G5CollectionService.java | 82 ++++++++++++------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java index 84d37d733f..a5d0e87be7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java @@ -202,6 +202,7 @@ private static void doNext(final Ob1G5CollectionService parent, final RxBleConne System.arraycopy(data, i, buf, 0, size); UserError.Log.d(TAG, "Sending auth data: " + bytesToHex(buf)); connection.writeCharacteristic(ExtraData, nn(buf)).subscribe(); + threadSleep(40); } // TODO wait for completion? threadSleep(500); @@ -281,6 +282,7 @@ public static boolean doCheckAuth2(final Ob1G5CollectionService parent, final Rx parent.resetSomeInternalState(); } else if (e instanceof SecurityException) { parent.logFailure(); + parent.clearPersistStore(); parent.changeState(SCAN); } else { e.printStackTrace(); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java index a32ac4992c..e9f3b4f613 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java @@ -9,6 +9,7 @@ import static com.eveningoutpost.dexdrip.g5model.CalibrationState.Unknown; import static com.eveningoutpost.dexdrip.g5model.G6CalibrationParameters.getCurrentSensorCode; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.CLOSED_OK_TEXT; +import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.doKeepAlive; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.evaluateG6Settings; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.pendingCalibration; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.pendingStart; @@ -162,7 +163,7 @@ public class Ob1G5CollectionService extends G5BaseService { private static final String BUGGY_SAMSUNG_ENABLED = "buggy-samsung-enabled"; private static final String STOP_SCAN_TASK_ID = "ob1-g5-scan-timeout_scan"; private static final String KEKS = "keks"; - private static final String KEKS_ONE = "keks1"; + private static final String KEKS_ONE = "keks1_"; private static volatile STATE state = INIT; private static volatile STATE last_automata_state = CLOSED; @@ -496,7 +497,12 @@ public STATE getState() { } public void changeState(STATE new_state) { - changeState(new_state, DEFAULT_AUTOMATA_DELAY); + if (shouldServiceRun()) { + changeState(new_state, DEFAULT_AUTOMATA_DELAY); + } else { + UserError.Log.d(TAG, "Stopping service due to having being disabled in preferences"); + stopSelf(); + } } public void changeState(STATE new_state, int timeout) { @@ -737,13 +743,12 @@ private synchronized void do_create_bond() { unbondIfAllowed(); changeState(CLOSE); } else { - try { - Ob1G5StateMachine.doKeepAlive(this, connection, () -> { - weInitiatedBondConfirmation = 1; - instantCreateBondIfAllowed(); - }); - + if (transmitterID.length() > 4) { + doKeepAlive(this, connection, this::startInitiateBondReal); + } else { + startInitiateBondReal(); + } //background_automata(10000); } catch (Exception e) { UserError.Log.wtf(TAG, "Got exception in do_create_bond() " + e); @@ -751,6 +756,15 @@ private synchronized void do_create_bond() { } } + private void startInitiateBondReal() { + try { + weInitiatedBondConfirmation = 1; + instantCreateBondIfAllowed(); + } catch (Exception e) { + UserError.Log.wtf(TAG, "Got exception in startInitiateBondReal() " + e); + } + } + private synchronized void send_reset_command() { hardResetTransmitterNow = false; getBatteryStatusNow = true; @@ -794,7 +808,6 @@ private void tryLoadingSavedMAC() { // should this service be running? Used to decide when to shut down private static boolean shouldServiceRun() { - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false; if (!Pref.getBooleanDefaultFalse(OB1G5_PREFS)) return false; if (!(DexCollectionType.getDexCollectionType() == DexcomG5)) return false; @@ -942,12 +955,17 @@ private synchronized void prepareToWakeup() { public synchronized void savePersist() { if (plugin != null) { - PersistentStore.setBytes(KEKS_ONE, plugin.getPersistence(1)); + PersistentStore.cleanupOld(KEKS_ONE); + PersistentStore.setBytes(KEKS_ONE + transmitterMAC, plugin.getPersistence(1)); } } + public static void clearPersistStore() { + PersistentStore.removeItem(KEKS_ONE + transmitterMAC); + } + public static void clearPersist() { - PersistentStore.removeItem(KEKS_ONE); + clearPersistStore(); expireFailures(true); } @@ -1397,27 +1415,33 @@ public void tryGattRefresh() { // We have connected to the device! private void onConnectionReceived(RxBleConnection this_connection) { msg("Connected"); - static_last_connected = tsl(); - lastConnectFailed = false; - preScanFailureMarker = false; - DexSyncKeeper.store(transmitterID, static_last_connected); - // TODO check connection already exists - close etc? - if (connection_linger != null) JoH.releaseWakeLock(connection_linger); - connection = this_connection; + if (shouldServiceRun()) { + static_last_connected = tsl(); + lastConnectFailed = false; + preScanFailureMarker = false; - if (state == CONNECT_NOW) { - connectNowFailures = -3; // mark good - } - if (state == CONNECT) { - connectFailures = -1; // mark good - } + DexSyncKeeper.store(transmitterID, static_last_connected); + // TODO check connection already exists - close etc? + if (connection_linger != null) JoH.releaseWakeLock(connection_linger); + connection = this_connection; - scanTimeouts = 0; // reset counter - clearRetries(); + if (state == CONNECT_NOW) { + connectNowFailures = -3; // mark good + } + if (state == CONNECT) { + connectFailures = -1; // mark good + } + + scanTimeouts = 0; // reset counter + clearRetries(); - if (JoH.ratelimit("g5-to-discover", 1)) { - changeState(DISCOVER); + if (JoH.ratelimit("g5-to-discover", 1)) { + changeState(DISCOVER); + } + } else { + msg("Shutdown"); + stopSelf(); } } @@ -1491,7 +1515,7 @@ private void onServicesDiscovered(RxBleDeviceServices services) { UserError.Log.wtf(TAG, msg); JoH.static_toast_long(msg); } else { - plugin.setPersistence(2, PersistentStore.getBytes(KEKS_ONE)); + plugin.setPersistence(2, PersistentStore.getBytes(KEKS_ONE + transmitterMAC)); try { for (int i = 1; i < 4; i++) { plugin.setPersistence(7 + i, tolerantHexStringToByteArray(Pref.getStringDefaultBlank(KEKS + "_p" + i))); From 7cb980b71fff1cfc6f3f82e9e6dffdb384d7af73 Mon Sep 17 00:00:00 2001 From: Jamorham Date: Thu, 6 Jul 2023 17:14:43 +0000 Subject: [PATCH 07/13] Persist: add Double support --- .../eveningoutpost/dexdrip/alert/Persist.java | 47 +++++++++++++++++++ .../dexdrip/alert/PersistTest.java | 31 ++++++++++++ 2 files changed, 78 insertions(+) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java index b51b1ad368..2d022821cc 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java @@ -2,11 +2,16 @@ import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.models.JoH.tsl; +import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.getDouble; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.getLong; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.getString; +import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.setDouble; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.setLong; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.setString; +import java.security.InvalidParameterException; + +import lombok.NonNull; import lombok.RequiredArgsConstructor; /** @@ -45,6 +50,19 @@ public void set(final long value) { } } + @RequiredArgsConstructor + public static class Double { + private final java.lang.String pref; + + public java.lang.Double get() { + return getDouble(pref); + } + + public void set(final double value) { + setDouble(pref, value); + } + } + public static class LongTimeout extends Long { private final long millis; @@ -63,6 +81,35 @@ public boolean expired() { } } + public static class DoubleTimeout extends Double { + + private final LongTimeout timeout; + + public DoubleTimeout(final java.lang.String pref, final long millis) { + super(pref); + timeout = new LongTimeout(PREF_TIMEOUT + pref, millis); + } + + @Override + public void set(final double value) { + super.set(value); + timeout.set(); + } + + @Override + public java.lang.Double get() { + return !timeout.expired() ? super.get() : null; + } + + public void set(@NonNull final java.lang.Double value) { + if (value == null) { + throw new InvalidParameterException("Sorry null not permitted here"); + } + set((double)value); + } + + } + public static class StringTimeout extends String { private final LongTimeout timeout; diff --git a/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java b/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java index 35ee313ba4..d2c984d15e 100644 --- a/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java +++ b/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java @@ -50,4 +50,35 @@ public void testTimeoutString() { assertWithMessage("test ok 4").that(store.get()).isEqualTo(testString); } + @Test + public void testTimeoutDouble() { + + val PREF_NAME = "TEST_TIMEOUT_DOUBLE"; + val testDouble = Double.valueOf(123.123); + + // setup + PersistentStore.removeItem(PREF_NAME); + ShadowSystemClock.advanceBy(Duration.ofHours(100)); + + val store = + new Persist.DoubleTimeout(PREF_NAME, Constants.MINUTE_IN_MS * 21); + + assertWithMessage("Time not zero").that(JoH.tsl()).isGreaterThan(Constants.HOUR_IN_MS); + assertWithMessage("test empty null").that(store.get()).isNull(); + + store.set(testDouble); + assertWithMessage("test ok 1").that(store.get()).isEqualTo(testDouble); + ShadowSystemClock.advanceBy(Duration.ofMinutes(1)); + assertWithMessage("test ok 2").that(store.get()).isEqualTo(testDouble); + ShadowSystemClock.advanceBy(Duration.ofMinutes(10)); + assertWithMessage("test ok 3").that(store.get()).isEqualTo(testDouble); + ShadowSystemClock.advanceBy(Duration.ofMinutes(11)); + assertWithMessage("test expired 4").that(store.get()).isNull(); + ShadowSystemClock.advanceBy(Duration.ofMinutes(11)); + assertWithMessage("test expired 5").that(store.get()).isNull(); + store.set(testDouble); + ShadowSystemClock.advanceBy(Duration.ofMinutes(10)); + assertWithMessage("test ok 4").that(store.get()).isEqualTo(testDouble); + } + } \ No newline at end of file From 2ae33575dcd14e450b842703f02f77694214b2f0 Mon Sep 17 00:00:00 2001 From: JamOrHam Date: Thu, 6 Jul 2023 10:59:49 +0100 Subject: [PATCH 08/13] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings-cs.xml | 78 +++++++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values-cs/strings-cs.xml b/app/src/main/res/values-cs/strings-cs.xml index c1e1643740..7fb5347dab 100644 --- a/app/src/main/res/values-cs/strings-cs.xml +++ b/app/src/main/res/values-cs/strings-cs.xml @@ -318,8 +318,8 @@ " Jednotky" jednotky sacharidy - když - Prosím řekněte ošetření jako:\nx.x jednotek inzulínu / xx gramů sacharidů + kdy (čas HHMM) + Řekněte ošetření jako:\nx.x jednotek inzulínu / xx gramů sacharidů - pro zadání v češtině: 3 jednotky inzulínu = \"tři u/ů/jů\" a 55 gramů sacharidů = \"padesát gé\" Rozpoznávání hlasu není podporováno Poznámka ošetření xDrip používá funkci Usnadnění pouze k úpravě obrazovky, když je zařízení uzamčené. Toto je pravděpodobně užitečné pouze v případě, že máš telefon s funkcí \"Always On Display\" a máš ji aktivovanou. xDrip nekomunikuje s Androidem jiným způsobem. @@ -472,10 +472,10 @@ Nesprávné hodnoty glykémie Obnovit výchozí nastavení Filtrované hodnoty - Barva ošetření - Tmavá barva ošetření + Aktivní inzulín (IOB) + Aktivita inzulínu Barva předpovědi - Tmavá barva předpovědi + Aktivní sacharidy Čáry pro průměr a cíl 8 hodinová průměrná hodnota Čára 24 hodinového průměru @@ -749,19 +749,19 @@ Tlačítka pro vypnutí alarmu Vytvořit zástupce seznamu výstrah glykémií v hlavním menu Zástupce seznamu výstrah glykémií - Potlačit odložené a aktivní výstrahy po přednastaveném časovém úseku chybějících měření - Potlačit odložené a aktivní výstrahy po .. minutách (minimálně 10) + Potlačit odložené a aktivní výstrahy po před nastavenou dobu chybějících hodnot + Potlačit odložené a aktivní výstrahy po .. minutách (minimálně 10 min) Upozornit, když je požadována kalibrace - Kolik hodin mezi požadavky na kalibraci + Kolik hodin mezi upozorněními na kalibraci Hodiny mezi kalibracemi Nastavit zvuk žádosti o kalibraci. Přebít tichý mód telefonu I při nabíjení - Odznač, pokud nechceš dostávat žádosti o kalibraci v době nabíjení telefonu - Pokračuj v upozorňování pokud se neprovede žádná kalibrace + Zrušit zaškrnutí, nechcete-li dostávat upozornění na kalibrace při nabíjení telefonu + Opakovat upozornění dokud se neprovede kalibrace Opakovat výstrahy Počet minut k opětovné žádosti o kalibraci. - Počet minut mezi opakovanými upozorněními + Počet minut mezi opakováním upozornění Upozornění po x minutách zašuměných hodnot Odložení výstrahy Počet minut před spuštěním stejné výstrahy po odložení. @@ -799,23 +799,37 @@ Pokiud požaduješ jinou jazykovou verzi v aplikaci xDrip+ Vyberte konkrétní jazyk Linky rozsahu ve widgetu - Zobrazíit linku spodní a vrchní hranice ve widgetu. + Zobrazit linku spodní a vrchní hranice ve widgetu. Zpožděné, ale stabilnější, když je senzor rušen (nedoporučeno) Zobrazit náhled průvodce bolusem Zobrazit výpočty inzulín/sacharidů Zobrazit výpočty inzulín/sacharidů po celou dobu Vždy zobrazovat náhled průvodce bolusem + Zobrazit extra a alpha testovací funkce Zobrazit matematické simulace na základě profilu a sacharidových/inzulínových záznamů Prediktivní simulace Hodnoty predikce nízkoúrovňové Jméno WiFi se musí shodovat ANO, odeslat! + Detekce pohybu a režim vozidla + xDrip+ sledování pohybu + Použít senzory ke zjištění druhu pohybu + Povolit sledování pohybu + Uložit nedávné druhy pohybu a zobrazit je na hlavním grafu + Zaznamenat a zobrazit pohyb + Data o pohybu vzít z vnějšího senzoru místo lokální detekce + Použít vnější senzor pohybu Když je detekováno vozidlo, povolit extra funkce Povolit režim vozidla + Zvýšit úroveň pro výstrahy nízké glykémie v režimu vozidla + Zvýšit limit nízké glykémie + Zvuk oznámení při zjištění režimu vozidla Přehrát zvuk + Opakovat zvuk oznámení každých 90 minut v režimu vozidla Opakovat zvuk Blucon Tomato + Bubble Připomenutí chyba Zastavit senzor @@ -826,6 +840,7 @@ Kalibrace mimo rozsah Hledání poznámek Upravit poznámku + Zde upravte text poznámky. Úpravu není možné vrátit zpět Hledaný výraz nebyl nalezen Skenování Bluetooth Úrovně výstrah @@ -836,17 +851,24 @@ Další poznámky: Odstranit budoucí data Zobrazit pouze nepřeložené + Sběr prvotních dat Kolektor dat je spuštěný + Přijata nová data + Přijato dostatek dobrých dat pro kalibraci Přehrát zvuk, když je připraveno ke kalibraci Přijímání dat od %s kolektoru Rychlý pokles Pokles Mírný pokles + Stabilní Mírný nárůst Nárůst Rychlý nárůst - Výška mluvení + Přečíst trend změny + Přečti všechno dvakrát + Rychlost řeči + Výška hlasu Přečíst výstrahy Přečíst i detaily výstrah Rozšířené nastavení blueReader @@ -872,6 +894,7 @@ Jaký software používá Wixel? %s hodin Zadejte ID vašeho vysílače přesně tak, jak je vytištěno + Povolit synchronizaci do Android Wear OS? AndroidAPS Android Wear OS LibreAlarm @@ -881,8 +904,13 @@ Neplatný nebo nepodporovaný kód! Kód senzoru G6 Zadejte prosím vytištěný kalibrační kód, 4 číslice, např.: 5678 + Povolit kontrolu aktualizací? + Pokud nejste spokojeni s nejnovější verzí aplikace xDrip+, lze aplikaci snadno vrátit na předchozí verze pomocí spuštění APK instalátoru pro zvolenou starší verzi ve složce Downloads.\n\nChcete povolit kontrolu aktualizací? + Ahoj... + Kontrola aktualizací je vypnuta!\n\nSnažíme se neustále opravovat chyby a přidávat nové funkce do aplikace xDrip+.\n\nToto upozornění na zapnutí kontroly aktualizací se objeví každých 60 dní. Kontrola aktualizací povolena! Zrušit budík? + Opravdu zrušit výstrahu? Ano, zrušit automatická zpráva z xDrip+ 0000 @@ -898,12 +926,16 @@ Výstraha vysoké glykémie nepotvrzená Neaktivita zařízení %d vybraných kontaktů pro nouzové zprávy + Verze aplikace xDrip+ se změnila z: %1$s na %2$s + Přepnout od režimu vozidla při spojení s jedním z následujících audio zařízení. Vyberte zařízení Pokud chcete používat pouze Wear kolektor a nikdy váš telefon. Použít pouze Wear Zobrazit v případě potřeby BWP (Bolus Wizard Preview) hodnotu inzulínu na hodinkách Zobrazit BWP inzulín Senzor tepové frekvence + Když se změní o + Nebo za počet minut Potvrdit kalibrace xDrip Web Service Zobrazit stav z jiných aplikací jako je AndroidAPS @@ -958,9 +990,11 @@ hodiny dny“ týdny“ + Výstrahy vypnuty Restartovat ráno Rozšířený režim Název připomenutí + V kolik hodin? VŠECHNY VÝSTRAHY VYPNUTY VÝSTRAHY NÍZKÉ A VYSOKÉ GLYKÉMIE JSOU VYPNUTÉ VÝSTRAHY NÍZKÉ GLYKÉMIE JSOU VYPNUTÉ @@ -1043,6 +1077,13 @@ Zobrazit QR kód Použít HTTPS Experimentální funkce + Přehrát zvuky + Použít funkci notifikačního kanálu pro Android 8+ + Notifikační kanály + Posunout začátek zvukové výstrahy o 3 minuty pro zvukový profil vzestupné hlasitosti. Vibrace (pokud jsou povoleny) začnou ihned po spuštění výstrahy bez ohledu na toto nastavení. + Zpozdit vzestupnou hlasitost + Přehrát zvuk při požadavku na výchozí kalibraci + Výchozí výstraha Pokles/nárůst glykemie Nastavení výstrah (pro tyto výstrahy) HLEDAT @@ -1123,8 +1164,19 @@ Vždy se připojovat k Libre2 senzorům Pokaždé se zeptat Nikdy se nepřipojovat k Libre2 senzorům + 10 minut + 15 minut Znovu se neptat NE, nikdy + Zobrazit akční tlačítka (jako Potvrdit) v oznámení výstrah + Tlačítka výstrahy + Zobraz konec senzoru + Neplatný čas + před %s + Přizpůsobit rozsah osy y Povolit + Zde můžete upravit rozsah svislé osy (y).\nPokud však máte hodnoty mimo tento rozsah, osa se automaticky prodlouží o několik hodin, aby bylo možné tyto hodnoty zobrazit. + Upravit minimum osy y + Upravit maximum osy y Ověřte nastavení a počkejte na připojení. From 4079411f06dce2fa7725f36dbc82bfda117a7c49 Mon Sep 17 00:00:00 2001 From: Jamorham Date: Sat, 8 Jul 2023 17:13:05 +0000 Subject: [PATCH 09/13] WebService: use CR+LF linefeed type for protocol headers --- .../dexdrip/webservices/XdripWebService.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/XdripWebService.java b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/XdripWebService.java index 24a5c8126a..e41146a35f 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/XdripWebService.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/XdripWebService.java @@ -5,12 +5,12 @@ import android.text.TextUtils; import android.util.Log; +import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.dagger.Singleton; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.UserError; -import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.Pref; -import com.eveningoutpost.dexdrip.dagger.Singleton; import com.eveningoutpost.dexdrip.utils.TriState; import com.eveningoutpost.dexdrip.xdrip; import com.google.common.base.Charsets; @@ -20,17 +20,18 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; -import java.time.format.DateTimeFormatter; -import java.time.ZonedDateTime; import java.time.ZoneOffset; -import java.util.concurrent.atomic.AtomicInteger; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLServerSocketFactory; @@ -217,6 +218,24 @@ public void run() { } } + // Makes \n be \r\n for HTTP specification compliance + static class CRLFPrintStream extends PrintStream { + + public CRLFPrintStream(OutputStream out) { + super(out); + } + + @Override + public void println(String x) { + super.println(x + "\r"); + } + + @Override + public void println() { + println(""); + } + } + /** * Respond to a request from a client. * @@ -226,7 +245,7 @@ public void run() { private void handle(Socket socket) throws IOException { final PowerManager.WakeLock wl = JoH.getWakeLock("webservice-handler", 20000); BufferedReader reader = null; - PrintStream output = null; + CRLFPrintStream output = null; try { socket.setSoTimeout((int) (Constants.SECOND_IN_MS * 10)); try { @@ -288,7 +307,7 @@ private void handle(Socket socket) throws IOException { } // Output stream that we send the response to - output = new PrintStream(socket.getOutputStream()); + output = new CRLFPrintStream(socket.getOutputStream()); // Prepare the content to send. if (null == route) { From c67848645e1e7dea095e763a148a372f0bfafc90 Mon Sep 17 00:00:00 2001 From: Jamorham Date: Sat, 8 Jul 2023 17:36:57 +0000 Subject: [PATCH 10/13] MockDataSource: add support for pre-calibration --- .../dexdrip/services/Ob1G5CollectionService.java | 8 +++++++- .../com/eveningoutpost/dexdrip/services/WixelReader.java | 3 +++ .../dexdrip/utilitymodels/VoiceCommands.java | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java index e9f3b4f613..b23f9fe0a7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java @@ -1970,7 +1970,13 @@ public static boolean usingNativeMode() { } public static boolean onlyUsingNativeMode() { - return usingNativeMode() && !fallbackToXdripAlgorithm(); + return (usingNativeMode() && !fallbackToXdripAlgorithm()) + || usingMockPreCalibrated(); + } + + public static boolean usingMockPreCalibrated() { + return Pref.getBooleanDefaultFalse("fake_data_pre_calibrated") + && DexCollectionType.getDexCollectionType() == DexCollectionType.Mock; } public static boolean isProvidingNativeGlucoseData() { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/WixelReader.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/WixelReader.java index dd2c8625e0..9af3896892 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/WixelReader.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/WixelReader.java @@ -137,6 +137,9 @@ private static List readFake() { trd.CaptureDateTime = System.currentTimeMillis() - trd.RelativeTime; final List l = new ArrayList<>(); l.add(trd); + if (Ob1G5CollectionService.usingMockPreCalibrated()) { + BgReading.bgReadingInsertFromG5(trd.RawValue / 1000.0, trd.getCaptureDateTime(), "Mock"); + } return l; } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java index 1abefc67fc..a6bf4caba3 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java @@ -46,6 +46,13 @@ public static void processVoiceCommand(final String allWords, final Activity mAc Pref.setString(DexCollectionType.DEX_COLLECTION_METHOD, DexCollectionType.Mock.toString()); JoH.static_toast_long("YOU ARE NOW USING FAKE DATA!!!"); MockDataSource.defaults(); + CollectionServiceStarter.restartCollectionServiceBackground(); + } else if (get_engineering_mode() && allWords.equals("fake data source automatic calibration")) { + Pref.setBoolean("fake_data_pre_calibrated", true); + JoH.static_toast_long("Fake data pre-calibration ON"); + } else if (get_engineering_mode() && allWords.equals("fake data source manual calibration")) { + Pref.setBoolean("fake_data_pre_calibrated", false); + JoH.static_toast_long("Fake data pre-calibration OFF"); } else if (get_engineering_mode() && allWords.equals("break fake data source")) { JoH.static_toast_long("Breaking fake data source"); MockDataSource.breakRaw(); From 22674853b1bf82c364e75616b4a4795f61ad0944 Mon Sep 17 00:00:00 2001 From: bart2 Date: Tue, 4 Jul 2023 19:30:05 -0700 Subject: [PATCH 11/13] Fetch IoB from companion app --- .../dexdrip/models/Treatments.java | 16 ++++ .../dexdrip/services/UiBasedCollector.java | 92 +++++++++++++++++++ .../dexdrip/webservices/WebServicePebble.java | 13 ++- app/src/main/res/values/strings.xml | 4 + .../main/res/xml/pref_advanced_settings.xml | 10 ++ .../services/UiBasedCollectorTest.java | 11 +++ 6 files changed, 144 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java index 06ba55f7bf..a328958dae 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java @@ -21,6 +21,7 @@ import com.eveningoutpost.dexdrip.models.UserError.Log; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.services.SyncService; +import com.eveningoutpost.dexdrip.services.UiBasedCollector; import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.Pref; import com.eveningoutpost.dexdrip.utilitymodels.UndoRedo; @@ -57,6 +58,7 @@ import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.utilitymodels.Constants.HOUR_IN_MS; import static com.eveningoutpost.dexdrip.utilitymodels.Constants.MINUTE_IN_MS; +import com.eveningoutpost.dexdrip.utilitymodels.Pref; import static java.lang.StrictMath.abs; import static com.eveningoutpost.dexdrip.models.JoH.emptyString; @@ -1292,6 +1294,20 @@ public static List ioBForGraph_old(int number, double startTime) { }*/ public static Double getCurrentIoB() { + if (Pref.getBooleanDefaultFalse("fetch_iob_from_companion_app")) { + return getCurrentIoBFromCompanionApp(); + } else { + return getCurrentIoBFromGraphCalculation(); + } + } + + public static Double getCurrentIoBFromCompanionApp() { + Double iob = UiBasedCollector.getCurrentIoB(); + + return iob; + } + + public static Double getCurrentIoBFromGraphCalculation() { long now = System.currentTimeMillis(); final List iobInfo = Treatments.ioBForGraph_new(now - 1 * Constants.DAY_IN_MS); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java index 171d6f5908..0e560b4e32 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/UiBasedCollector.java @@ -2,6 +2,7 @@ import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.cgm.dex.ClassifierAction.lastReadingTimestamp; +import static com.eveningoutpost.dexdrip.utilitymodels.Constants.MINUTE_IN_MS; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.UiBased; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.getDexCollectionType; import static com.eveningoutpost.dexdrip.xdrip.gs; @@ -26,20 +27,26 @@ import androidx.annotation.VisibleForTesting; import com.eveningoutpost.dexdrip.BestGlucose; +import com.eveningoutpost.dexdrip.alert.Persist; import com.eveningoutpost.dexdrip.models.BgReading; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.Sensor; import com.eveningoutpost.dexdrip.models.UserError; import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.PersistentStore; import com.eveningoutpost.dexdrip.utilitymodels.Unitized; import com.eveningoutpost.dexdrip.cgm.dex.BlueTails; import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.eveningoutpost.dexdrip.xdrip; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import lombok.val; @@ -53,10 +60,16 @@ public class UiBasedCollector extends NotificationListenerService { private static final String TAG = UiBasedCollector.class.getSimpleName(); private static final String UI_BASED_STORE_LAST_VALUE = "UI_BASED_STORE_LAST_VALUE"; private static final String UI_BASED_STORE_LAST_REPEAT = "UI_BASED_STORE_LAST_REPEAT"; + private static final String COMPANION_APP_IOB_ENABLED_PREFERENCE_KEY = "fetch_iob_from_companion_app"; + private static final Persist.DoubleTimeout iob_store = + new Persist.DoubleTimeout("COMPANION_APP_IOB_VALUE", Constants.MINUTE_IN_MS * 5); private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; private static final HashSet coOptedPackages = new HashSet<>(); + private static final HashSet companionAppIoBPackages = new HashSet<>(); + private static final HashSet companionAppIoBRegexes = new HashSet<>(); + private static boolean debug = false; @VisibleForTesting String lastPackage; @@ -74,6 +87,12 @@ public class UiBasedCollector extends NotificationListenerService { coOptedPackages.add("com.medtronic.diabetes.guardian"); coOptedPackages.add("com.medtronic.diabetes.minimedmobile.eu"); coOptedPackages.add("com.medtronic.diabetes.minimedmobile.us"); + + companionAppIoBPackages.add("com.insulet.myblue.pdm"); + + // The IoB value should be captured into the first match group. + // English localization of the Omnipod 5 App + companionAppIoBRegexes.add(Pattern.compile("IOB: ([\\d\\.,]+) U")); } @Override @@ -93,6 +112,68 @@ public void onNotificationPosted(final StatusBarNotification sbn) { } } } + + if (companionAppIoBPackages.contains(fromPackage)) { + processCompanionAppIoBNotification(sbn.getNotification()); + } + } + + private void processCompanionAppIoBNotification(final Notification notification) { + if (notification == null) { + UserError.Log.e(TAG, "Null notification"); + return; + } + if (notification.contentView != null) { + processCompanionAppIoBNotificationCV(notification.contentView); + } else { + UserError.Log.e(TAG, "Content is empty"); + } + } + + private void processCompanionAppIoBNotificationCV(final RemoteViews cview) { + if (cview == null) return; + val applied = cview.apply(this, null); + val root = (ViewGroup) applied.getRootView(); + val texts = new ArrayList(); + getTextViews(texts, root); + if (debug) UserError.Log.d(TAG, "Text views: " + texts.size()); + Double iob = null; + try { + for (val view : texts) { + val tv = (TextView) view; + String text = tv.getText() != null ? tv.getText().toString() : ""; + val desc = tv.getContentDescription() != null ? tv.getContentDescription().toString() : ""; + if (debug) UserError.Log.d(TAG, "Examining: >" + text + "< : >" + desc + "<"); + iob = parseIoB(text); + if (iob != null) { + break; + } + } + + if (iob != null) { + if (debug) UserError.Log.d(TAG, "Inserting new IoB value: " + iob); + iob_store.set(iob); + } + } catch (Exception e) { + UserError.Log.e(TAG, "exception in processCompanionAppIoBNotificationCV: " + e); + } + + texts.clear(); + } + Double parseIoB(final String value) { + for (Pattern pattern : companionAppIoBRegexes) { + Matcher matcher = pattern.matcher(value); + + if (matcher.find()) { + return JoH.tolerantParseDouble(matcher.group(1)); + } + } + + return null; + } + + public static Double getCurrentIoB() { + return iob_store.get(); } @Override @@ -257,12 +338,23 @@ public static SharedPreferences.OnSharedPreferenceChangeListener getListener(fin // } } + if (key.equals(COMPANION_APP_IOB_ENABLED_PREFERENCE_KEY)) { + try { + enableNotificationService(activity); + } catch (Exception e) { + UserError.Log.e(TAG, "Exception when enabling NotificationService: " + e); + } + } }; } public static void switchToAndEnable(final Activity activity) { DexCollectionType.setDexCollectionType(UiBased); Sensor.createDefaultIfMissing(); + enableNotificationService(activity); + } + + private static void enableNotificationService(final Activity activity) { if (!isNotificationServiceEnabled()) { JoH.show_ok_dialog(activity, gs(R.string.please_allow_permission), "Permission is needed to receive data from other applications. xDrip does not do anything beyond this scope. Please enable xDrip on the next screen", diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java index 560a7e7186..e643277551 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/webservices/WebServicePebble.java @@ -1,5 +1,7 @@ package com.eveningoutpost.dexdrip.webservices; +import android.content.Context; +import android.app.Activity; import android.util.Log; import com.eveningoutpost.dexdrip.BestGlucose; @@ -10,6 +12,9 @@ import com.eveningoutpost.dexdrip.models.Treatments; import com.eveningoutpost.dexdrip.dagger.Injectors; import com.eveningoutpost.dexdrip.ui.MicroStatus; +import com.eveningoutpost.dexdrip.utilitymodels.Pref; +import com.eveningoutpost.dexdrip.xdrip; + import org.json.JSONArray; import org.json.JSONException; @@ -83,8 +88,12 @@ public WebResponse request(String query) { } bgs.put("battery", microStatus.gs("bestBridgeBattery")); - Double iob = Treatments.getCurrentIoB(); - bgs.put("iob", (iob == null) ? "unknown" : String.format("%.02f", iob)); + if (!Pref.getBooleanDefaultFalse("enable_iob_in_api_endpoint")) { + bgs.put("iob", 0.0); + } else { + Double iob = Treatments.getCurrentIoB(); + bgs.put("iob", (iob == null) ? "unknown" : String.format("%.02f", iob)); + } // TODO output bwp and bwpo status_array.put(status); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff12cb3734..b2d1c88094 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1241,6 +1241,10 @@ Broadcast Service API Enables xDrip communication with third-party applications by using new API + Enable IoB reporting in API + Enable IoB value reporting in Web Service API endpoint used by some smartwatches + Fetch IoB from companion app + Fetch IoB value from companion app running on the same device, such as the Omnipod 5 App MiBand Sync Data with MiBand fitness bands diff --git a/app/src/main/res/xml/pref_advanced_settings.xml b/app/src/main/res/xml/pref_advanced_settings.xml index 93e076d224..4f2480ce36 100644 --- a/app/src/main/res/xml/pref_advanced_settings.xml +++ b/app/src/main/res/xml/pref_advanced_settings.xml @@ -1005,6 +1005,16 @@ android:key="broadcast_service_enabled" android:summary="@string/summary_broadcast_service_api" android:title="@string/title_broadcast_service_api" /> + + Date: Tue, 11 Jul 2023 11:03:37 +0200 Subject: [PATCH 12/13] CareLink new countries added: AD, BD, BW, PF, JM, KZ, KE, PF, TT, UZ --- app/src/main/res/values/arrays.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 0b042e9fcc..857d2b4a4d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -237,6 +237,7 @@ Albania Algeria + Andorra Argentina Armenia Aruba @@ -245,12 +246,14 @@ Azerbaijan Bahamas Bahrain + Bangladesh Barbados Belarus Belgium Bermuda Bolivia Bosnia and Herzegovina + Botswana Brazil Bulgaria Cambodia @@ -272,6 +275,7 @@ Estonia Finland France + French Polynesia Georgia Germany Greece @@ -287,13 +291,17 @@ Ireland Israel Italy + Jamaica Japan Jordan + Kazakhstan + Kenya Kosovo Kuwait Latvia Lebanon Libya + Liechtenstein Lithuania Luxembourg Macau @@ -340,6 +348,7 @@ Switzerland Taiwan Thailand + Trinidad and Tobago Tunisia Turkey Ukraine @@ -347,6 +356,7 @@ United Kingdom United States Uruguay + Uzbekistan Venezuela Vietnam @@ -354,6 +364,7 @@ al dz + ad ar am aw @@ -362,12 +373,14 @@ az bs bh + bd bb by be bm bo ba + bw br bg kh @@ -389,6 +402,7 @@ ee fi fr + pf ge de gr @@ -404,13 +418,17 @@ ie il it + jm jp jo + kz + ke xk kw lv lb ly + pf lt lu mo @@ -457,6 +475,7 @@ ch tw th + tt tn tr ua @@ -464,6 +483,7 @@ gb us uy + uz ve vn From f364f1cdec1e9296e1f4e27c151ba4f25b3da38b Mon Sep 17 00:00:00 2001 From: benceszasz Date: Tue, 11 Jul 2023 11:12:43 +0200 Subject: [PATCH 13/13] CareLink new countries added: AD, BD, BW, PF, JM, KZ, KE, PF, TT, UZ --- .../carelinkfollow/client/CountryUtils.java | 162 ++++++++++-------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CountryUtils.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CountryUtils.java index 87ad34f73e..d7672e175d 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CountryUtils.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CountryUtils.java @@ -5,120 +5,130 @@ public class CountryUtils { public static final String[] supportedCountryCodes = { + "al", "dz", - "ba", - "eg", - "za", - "ca", - "cr", - "mx", - "ma", - "pa", - "pr", - "us", + "ad", "ar", - "br", - "cl", - "co", - "ve", - "hk", - "in", - "id", - "il", - "jp", - "kw", - "lb", - "my", - "ph", - "qa", - "sa", - "sg", - "kr", - "tw", - "th", - "tn", - "tr", - "ae", - "vn", - "at", - "be", - "bg", - "hr", - "cz", - "dk", - "ee", - "fi", - "fr", - "de", - "gr", - "hu", - "is", - "ie", - "it", - "lv", - "lt", - "lu", - "nl", - "no", - "pl", - "pt", - "ro", - "ru", - "rs", - "sk", - "si", - "es", - "se", - "ch", - "ua", - "gb", - "au", - "nz", - "bh", - "om", - "cn", - "cy", - "al", "am", + "aw", + "au", + "at", "az", "bs", + "bh", + "bd", "bb", "by", + "be", "bm", "bo", + "ba", + "bw", + "br", + "bg", "kh", + "ca", + "ky", + "cl", + "cn", + "co", + "cr", + "hr", + "cw", + "cy", + "cz", + "dk", "do", "ec", + "eg", "sv", + "ee", + "fi", + "fr", + "pf", "ge", + "de", + "gr", "gt", "hn", + "hk", + "hu", + "is", + "in", + "id", "ir", "iq", + "ie", + "il", + "it", + "jm", + "jp", "jo", + "kz", + "ke", "xk", + "kw", + "lv", + "lb", "ly", + "li", + "lt", + "lu", "mo", "mk", + "my", "mv", "mt", "mu", "yt", + "mx", "md", "me", + "ma", "na", + "nl", "nc", + "nz", "ni", "ng", + "no", + "om", "pk", + "pa", "py", + "pe", + "ph", + "pl", + "pt", + "pr", + "qa", + "ro", + "ru", + "sa", + "rs", + "sg", + "sk", + "si", + "za", + "kr", + "es", "mf", "sd", + "se", + "ch", + "tw", + "th", + "tt", + "tn", + "tr", + "ua", + "ae", + "gb", + "us", "uy", - "aw", - "ky", - "cw", - "pe" + "uz", + "ve", + "vn" }; public static boolean isSupportedCountry(String countryCode) {