Skip to content

Commit

Permalink
Merge pull request #2928 from bart2/fetch_omnipod_iob
Browse files Browse the repository at this point in the history
Fetch IoB from Omnipod app
  • Loading branch information
jamorham authored Jul 13, 2023
2 parents 617cdb3 + cef0158 commit 011820a
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -1292,6 +1294,20 @@ public static List<Iob> 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<Iob> iobInfo = Treatments.ioBForGraph_new(now - 1 * Constants.DAY_IN_MS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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<String> coOptedPackages = new HashSet<>();
private static final HashSet<String> companionAppIoBPackages = new HashSet<>();
private static final HashSet<Pattern> companionAppIoBRegexes = new HashSet<>();
private static boolean debug = false;

@VisibleForTesting
String lastPackage;
Expand All @@ -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
Expand All @@ -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<TextView>();
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
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,10 @@

<string name="title_broadcast_service_api">Broadcast Service API</string>
<string name="summary_broadcast_service_api">Enables xDrip communication with third-party applications by using new API</string>
<string name="title_enable_iob_in_api_endpoint">Enable IoB reporting in API</string>
<string name="summary_enable_iob_in_api_endpoint">Enable IoB value reporting in Web Service API endpoint used by some smartwatches</string>
<string name="title_fetch_iob_from_companion_app">Fetch IoB from companion app</string>
<string name="summary_fetch_iob_from_companion_app">Fetch IoB value from companion app running on the same device, such as the Omnipod 5 App</string>

<string name="title_miband">MiBand</string>
<string name="summary_miband_enable">Sync Data with MiBand fitness bands</string>
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/xml/pref_advanced_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,16 @@
android:key="broadcast_service_enabled"
android:summary="@string/summary_broadcast_service_api"
android:title="@string/title_broadcast_service_api" />
<SwitchPreference
android:defaultValue="true"
android:key="enable_iob_in_api_endpoint"
android:summary="@string/summary_enable_iob_in_api_endpoint"
android:title="@string/title_enable_iob_in_api_endpoint" />
<SwitchPreference
android:defaultValue="false"
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" />
<PreferenceScreen
android:title="Google Health Connect"
android:summary="Send and receive data from Google Health Connect and Google Fit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,15 @@ public void filterStringTest() {
assertWithMessage("gte 1").that(i.filterString(spec2)).isEqualTo(spec2p);
assertWithMessage("lte 1").that(i.filterString(spec3)).isEqualTo(spec3p);
}

@Test
public void parseIoBTest() {
val i = new UiBasedCollector();

val valid = "Automated Mode (IOB: 5.1 U)";
val invalid = "Foobar";

assertWithMessage("valid IoB message").that(i.parseIoB(valid)).isEqualTo(5.1);
assertWithMessage("invalid IoB message").that(i.parseIoB(invalid)).isNull();
}
}

0 comments on commit 011820a

Please sign in to comment.