diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 194e2e589..73df543f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -150,6 +150,12 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> + + { dialog.dismiss(); startActivity(new Intent(context, Preferences.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/NewDataObserver.java b/app/src/main/java/com/eveningoutpost/dexdrip/NewDataObserver.java index 6d8ed221c..86cef53f3 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/NewDataObserver.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/NewDataObserver.java @@ -4,6 +4,7 @@ import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.LibreBlock; import com.eveningoutpost.dexdrip.models.UserError; +import com.eveningoutpost.dexdrip.receiver.InfoContentProvider; import com.eveningoutpost.dexdrip.sharemodels.BgUploader; import com.eveningoutpost.dexdrip.sharemodels.models.ShareUploadPayload; import com.eveningoutpost.dexdrip.utilitymodels.Inevitable; @@ -58,6 +59,7 @@ public static void newBgReading(BgReading bgReading, boolean is_follower) { sendToBlueJay(); sendToRemoteBlueJay(); Notifications.start(); + InfoContentProvider.ping("bg"); uploadToShare(bgReading, is_follower); textToSpeech(bgReading, null); LibreBlock.UpdateBgVal(bgReading.timestamp, bgReading.calculated_value); @@ -87,6 +89,7 @@ public static void newExternalStatus(boolean receivedLocally) { GcmActivity.push_external_status_update(JoH.tsl(), statusLine); } + InfoContentProvider.ping("status"); } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/StartNewSensor.java b/app/src/main/java/com/eveningoutpost/dexdrip/StartNewSensor.java index 82da7f965..c01bb1791 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/StartNewSensor.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/StartNewSensor.java @@ -61,17 +61,22 @@ public class StartNewSensor extends ActivityWithMenu { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!Sensor.isActive()) { - JoH.fixActionBar(this); - setContentView(R.layout.activity_start_new_sensor); - button = (Button) findViewById(R.id.startNewSensor); - //dp = (DatePicker)findViewById(R.id.datePicker); - //tp = (TimePicker)findViewById(R.id.timePicker); - addListenerOnButton(); - } else { - Intent intent = new Intent(this, StopSensor.class); - startActivity(intent); + if (DexCollectionType.getBestCollectorHardwareName().equals("G7")) { + JoH.static_toast_long(getString(R.string.g7_should_start_automatically)); finish(); + } else { + if (!Sensor.isActive()) { + JoH.fixActionBar(this); + setContentView(R.layout.activity_start_new_sensor); + button = (Button) findViewById(R.id.startNewSensor); + //dp = (DatePicker)findViewById(R.id.datePicker); + //tp = (TimePicker)findViewById(R.id.timePicker); + addListenerOnButton(); + } else { + Intent intent = new Intent(this, StopSensor.class); + startActivity(intent); + finish(); + } } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cloud/backup/BackupActivity.java b/app/src/main/java/com/eveningoutpost/dexdrip/cloud/backup/BackupActivity.java index 002396f95..f1887c7eb 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/cloud/backup/BackupActivity.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cloud/backup/BackupActivity.java @@ -17,6 +17,7 @@ import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.UserError; import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.receiver.InfoContentProvider; import com.eveningoutpost.dexdrip.utilitymodels.Inevitable; import com.eveningoutpost.dexdrip.utilitymodels.PrefsViewImpl; import com.eveningoutpost.dexdrip.databinding.ActivityBackupPickerBinding; @@ -212,6 +213,7 @@ private synchronized void restoreNowReal() { if (metaData.exception != null) { status(getString(R.string.error_exclamation) + " " + metaData.exception); } + InfoContentProvider.ping("pref"); } finally { idle.set(true); } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/BaseGlucoseRxMessage.java b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/BaseGlucoseRxMessage.java index 41d94825b..b39e37f42 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/BaseGlucoseRxMessage.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/BaseGlucoseRxMessage.java @@ -1,5 +1,6 @@ package com.eveningoutpost.dexdrip.g5model; +import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.services.G5CollectionService; import lombok.NoArgsConstructor; @@ -50,4 +51,9 @@ public Integer getPredictedGlucose() { return null; // stub } + public long getRealTimestamp() { + return JoH.tsl(); // default behavior is received now + } + + } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/DexSyncKeeper.java b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/DexSyncKeeper.java index ecd8fe5c9..31e0c28b7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/DexSyncKeeper.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/DexSyncKeeper.java @@ -41,6 +41,16 @@ public static void store(final String transmitterId, final long when) { UserError.Log.d(TAG, "Sync time updated to: " + JoH.dateTimeText(when)); } + public static void store(final String transmitterId, final long when, final long last_conection_time, final long last_glucose_processed) { + final long latency = (last_glucose_processed - last_conection_time); + UserError.Log.d(TAG, "Update time from glucose rx glucose: " + JoH.dateTimeText(when) + " latency:" + latency + " ms"); + if (latency < 8000) { // roughly half preempt + store(transmitterId, when); + } else { + UserError.Log.e(TAG, "Refusing to update stored timestamp due to excessive processing latency: " + latency + "ms"); + } + } + public static void clear(final String transmitterId) { if (PersistentStore.getLong(DEX_SYNC_STORE + transmitterId) > 0) { UserError.Log.e(TAG, "Clearing stored timing sync information for: " + transmitterId); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/EGlucoseRxMessage2.java b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/EGlucoseRxMessage2.java index 4b6611689..358b0766c 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/EGlucoseRxMessage2.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/EGlucoseRxMessage2.java @@ -91,6 +91,11 @@ public Long getRealSessionStartTime() { return null; } + @Override + public long getRealTimestamp() { + return JoH.tsl() - (age * Constants.SECOND_IN_MS); + } + public String getRealSessionStartTimeString() { val t = getRealSessionStartTime(); if (t != null) { 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 adb22b6a8..26017ca41 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/g5model/Ob1G5StateMachine.java @@ -82,6 +82,8 @@ import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.STATE.SCAN; import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.android_wear; import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.getTransmitterID; +import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.ignoreBonding; +import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.immediateBonding; import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.onlyUsingNativeMode; import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.resetSomeInternalState; import static com.eveningoutpost.dexdrip.services.Ob1G5CollectionService.wear_broadcast; @@ -133,6 +135,7 @@ public class Ob1G5StateMachine { private static final int OLDEST_RAW = 300 * 24 * 60 * 60; // 300 days public static long maxBackfillPeriod_MS = 0; + public static long maxBackfillPeriod_MS() { maxBackfillPeriod_MS = MAX_BACKFILL_PERIOD_MS; if (shortTxId()) { // If using G7 @@ -140,6 +143,7 @@ public static long maxBackfillPeriod_MS() { } return maxBackfillPeriod_MS; } + public static int backfillCheckLarge() { return (int) (maxBackfillPeriod_MS() / DEXCOM_PERIOD); } @@ -272,7 +276,7 @@ public static boolean doCheckAuth2(final Ob1G5CollectionService parent, final Rx connection.setupNotification(ExtraData) .timeout(15, TimeUnit.SECONDS) // WARN .doOnNext(notificationObservable -> { - UserError.Log.d(TAG,"Extra data notifications enabled"); + UserError.Log.d(TAG, "Extra data notifications enabled"); connection.setupIndication(Authentication) .timeout(15, TimeUnit.SECONDS) // WARN .doOnNext(notificationObservable2 -> doNext(parent, connection)) @@ -289,14 +293,14 @@ public static boolean doCheckAuth2(final Ob1G5CollectionService parent, final Rx doNext(parent, connection); } } catch (Exception e) { - UserError.Log.e(TAG,"Got exception in plugin: " + e); + UserError.Log.e(TAG, "Got exception in plugin: " + e); parent.lastSensorStatus = e.getMessage(); if (e instanceof InvalidParameterException) { parent.resetSomeInternalState(); } else if (e instanceof SecurityException) { - parent.logFailure(); - parent.clearPersistStore(); - parent.changeState(SCAN); + parent.logFailure(); + parent.clearPersistStore(); + parent.changeState(SCAN); } else { e.printStackTrace(); } @@ -323,7 +327,7 @@ public static boolean doCheckAuth2(final Ob1G5CollectionService parent, final Rx private static void handleAuthenticationWrite(final Ob1G5CollectionService parent, final RxBleConnection connection) { final int specifiedSlot = Pref.getBooleanDefaultFalse("engineering_mode") ? Pref.getStringToInt("dex_specified_slot", -1) : -1; final AuthRequestTxMessage authRequest = (specifiedSlot == -1) ? new AuthRequestTxMessage(getTokenSize(), usingAlt()) - : new AuthRequestTxMessage(getTokenSize(), specifiedSlot); + : new AuthRequestTxMessage(getTokenSize(), specifiedSlot); lastAuthPacket = authRequest; UserError.Log.i(TAG, "AuthRequestTX: " + bytesToHex(authRequest.byteSequence)); @@ -882,7 +886,7 @@ public static boolean doGetData(Ob1G5CollectionService parent, RxBleConnection c } else { parent.msg("Invalid Glucose"); } - break; + break; case CalibrateRxMessage: @@ -1108,15 +1112,15 @@ private static void backFillIfNeeded(Ob1G5CollectionService parent, RxBleConnect UserError.Log.d(TAG, "Requesting backfill between: " + JoH.dateTimeText(startTime) + " " + JoH.dateTimeText(endTime)); - if (!shortTxId()) { - enqueueUniqueCommand( - BackFillTxMessage.get(getTransmitterID(), startTime, endTime), - "Get backfill since: " + JoH.hourMinuteString(startTime)); - } else { - enqueueUniqueCommand( - BackFillTxMessage2.get(getTransmitterID(), startTime, endTime), - "Get backfill2 since: " + JoH.hourMinuteString(startTime)); - } + if (!shortTxId()) { + enqueueUniqueCommand( + BackFillTxMessage.get(getTransmitterID(), startTime, endTime), + "Get backfill since: " + JoH.hourMinuteString(startTime)); + } else { + enqueueUniqueCommand( + BackFillTxMessage2.get(getTransmitterID(), startTime, endTime), + "Get backfill2 since: " + JoH.hourMinuteString(startTime)); + } } else { nextBackFillCheckSize = BACKFILL_CHECK_SMALL; } @@ -1563,8 +1567,9 @@ private static void processGlucoseRxMessage(Ob1G5CollectionService parent, final DexTimeKeeper.updateAge(getTransmitterID(), glucose.timestamp); if (glucose.usable() || (glucose.insufficient() && Pref.getBoolean("ob1_g5_use_insufficiently_calibrated", true))) { UserError.Log.d(TAG, "Got usable glucose data from transmitter!!"); - final long rxtimestamp = tsl(); + final long rxtimestamp = glucose.getRealTimestamp(); checkAndActivateSensor(); + DexSyncKeeper.store(getTransmitterID(), rxtimestamp, parent.static_last_connected, lastGlucosePacket); final BgReading bgReading = BgReading.bgReadingInsertFromG5(glucose.glucose, rxtimestamp); if (bgReading != null) { try { @@ -1590,7 +1595,7 @@ private static void processGlucoseRxMessage(Ob1G5CollectionService parent, final if (glucose.getPredictedGlucose() != null) { // not really supported on wear yet if (!android_wear) { - Prediction.create(tsl(), glucose.getPredictedGlucose(), "EGlucoseRx").save(); + Prediction.create(rxtimestamp, glucose.getPredictedGlucose(), "EGlucoseRx").save(); } } @@ -1981,7 +1986,8 @@ private static synchronized byte[] calculateChallengeHash(final byte[] challenge @SuppressLint("GetInstance") final Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS7Padding"); aesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); return Arrays.copyOfRange(aesCipher.doFinal(plainText, 0, plainText.length), 0, 8); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) { + } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | + BadPaddingException | InvalidKeyException e) { UserError.Log.wtf(TAG, "System Encryption problem: " + e); return null; } @@ -2106,7 +2112,7 @@ private static boolean getEGlucose(final Ob1G5CollectionService parent) { public static boolean usingAlt() { return (android_wear && !Pref.getBooleanDefaultFalse("only_ever_use_wear_collector")) - || WholeHouse.isLive(); + || (immediateBonding() && !ignoreBonding()) || WholeHouse.isLive(); } private static class OperationSuccess extends RuntimeException { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/JoH.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/JoH.java index 99d3101b9..584cea0e3 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/JoH.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/JoH.java @@ -1881,6 +1881,17 @@ public static byte[] splitBytes(final byte[] source, final int start, final int return newBytes; } + public static byte[] joinBytes(final byte[] first, final byte[] second) { + if (first == null || second == null) { + throw new IllegalArgumentException("Input arrays cannot be null"); + } + final int totalLength = first.length + second.length; + final byte[] result = new byte[totalLength]; + System.arraycopy(first, 0, result, 0, first.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + public static long checksum(byte[] bytes) { if (bytes == null) return 0; diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/UserError.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/UserError.java index c6b69ff1f..95bfbdc73 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/UserError.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/UserError.java @@ -9,6 +9,7 @@ import com.activeandroid.annotation.Table; import com.activeandroid.query.Delete; import com.activeandroid.query.Select; +import com.eveningoutpost.dexdrip.receiver.InfoContentProvider; import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.Pref; import com.google.gson.annotations.Expose; @@ -17,7 +18,6 @@ import java.util.Hashtable; import java.util.List; -//import com.bugfender.sdk.Bugfender; /** * Created by Emma Black on 8/3/15. @@ -236,6 +236,15 @@ public static List bySeverityOlderThanID(long id, Integer[] levels, i } + public static UserError newestBySeverity(int level) { + return new Select() + .from(UserError.class) + .where("severity == "+level) + .orderBy("timestamp desc") + .limit(1) + .executeSingle(); + } + public static UserError getForTimestamp(UserError error) { try { return new Select() @@ -331,6 +340,7 @@ public static void uel(String tag, String b) { public static void ueh(String tag, String b) { android.util.Log.i(tag, b); UserError.UserEventHigh(tag, b); + InfoContentProvider.ping("info"); } public static void d(String tag, String b) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/receiver/InfoContentProvider.java b/app/src/main/java/com/eveningoutpost/dexdrip/receiver/InfoContentProvider.java new file mode 100644 index 000000000..16205b28b --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/receiver/InfoContentProvider.java @@ -0,0 +1,310 @@ +package com.eveningoutpost.dexdrip.receiver; + +import static com.eveningoutpost.dexdrip.utilitymodels.ColorCache.getCol; +import static com.eveningoutpost.dexdrip.utilitymodels.NanoStatus.nanoStatus; +import static com.eveningoutpost.dexdrip.utilitymodels.Pref.getBooleanDefaultFalse; +import static com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry.isNative; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.Bitmap; + +import android.net.Uri; +import android.os.Looper; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.eveningoutpost.dexdrip.BestGlucose; +import com.eveningoutpost.dexdrip.BuildConfig; +import com.eveningoutpost.dexdrip.eassist.EmergencyAssist; +import com.eveningoutpost.dexdrip.models.UserError; +import com.eveningoutpost.dexdrip.utilitymodels.AlertPlayer; +import com.eveningoutpost.dexdrip.utilitymodels.BgGraphBuilder; +import com.eveningoutpost.dexdrip.utilitymodels.BgSparklineBuilder; + +import com.eveningoutpost.dexdrip.utilitymodels.CollectionServiceStarter; +import com.eveningoutpost.dexdrip.utilitymodels.ColorCache; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; +import com.eveningoutpost.dexdrip.utilitymodels.Pref; +import com.eveningoutpost.dexdrip.utils.BlobCache; +import com.eveningoutpost.dexdrip.xdrip; + +import java.io.ByteArrayOutputStream; + +/** + * xDrip content provider + * JamOrHam + */ +public class InfoContentProvider extends ContentProvider { + + private static final String TAG = "jamorham-content"; + private static final BlobCache dgCache = new BlobCache(60_000); + private static final BlobCache graphCache = new BlobCache(60_000); + + { + xdrip.setContext(getContext()); + } + + @Override + public boolean onCreate() { + xdrip.setContext(getContext()); + return enabled(); + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + if (!enabled()) return null; + try { + if (selection == null) return null; + + switch (selection) { + + case "ping": + return matrixRow( + "version", BuildConfig.VERSION_NAME, + "versioncode", BuildConfig.buildVersion); + + case "alarms": + if (selectionArgs.length > 0 && selectionArgs[0].equals("snooze")) { + AlertPlayer.getPlayer().OpportunisticSnooze(); + Log.d(TAG, "Opportunistic snooze"); + } + break; + + case "eassist": + if (enabledWrite()) { + EmergencyAssist.test(EmergencyAssist.Reason.REQUESTED_ASSISTANCE, Constants.MINUTE_IN_MS); + UserError.Log.ueh(TAG, "Emergency assist triggered remotely"); + } + break; + + case "bg": { + BestGlucose.DisplayGlucose dg = (BestGlucose.DisplayGlucose) dgCache.get(); + if (dg == null) { + dg = BestGlucose.getDisplayGlucose(); + dgCache.set(dg); + } + if (dg == null) return null; + + int chint = getCol(ColorCache.X.color_inrange_bg_values); + if (dg.isHigh()) { + chint = getCol(ColorCache.X.color_high_bg_values); + } else if (dg.isLow()) { + chint = getCol(ColorCache.X.color_low_bg_values); + } + return matrixRow( + "timestamp", dg.timestamp, + "mgdl", dg.mgdl, + "delta_mgdl", dg.delta_mgdl, + "delta_arrow", dg.delta_arrow, + "delta_name", dg.delta_name, + "mssince", dg.mssince, + "unitized", dg.unitized, + "unitized_delta", dg.unitized_delta, + "unitized_delta_no_units", dg.unitized_delta_no_units, + "chint", chint, + "humansummary", dg.humanSummary(), + "ishigh", boolInt(dg.isHigh()), + "islow", boolInt(dg.isLow()), + "isstale", boolInt(dg.isStale()), + "isreallystale", boolInt(dg.isReallyStale()) + ); + } + + case "status-line": { + } + break; + + case "nano-status": { + return matrixRow( + "collector", nanoStatus("collector"), + "sensor-expiry", nanoStatus("sensor-expiry") + ); + } + + case "info": + return matrixRow("last_ueh", UserError.newestBySeverity(6)); + + case "iob-info": { + } + break; + + case "graph": { + + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + final int width = Math.min(1000, Integer.parseInt(selectionArgs[0])); + final int height = Math.min(1000, Integer.parseInt(selectionArgs[1])); + + byte[] blob = (byte[]) graphCache.get(width, height); + if (blob == null) { + String backgroundColor = "#80000000"; + if (selectionArgs.length >= 3) { + backgroundColor = selectionArgs[2]; + } + + final BgGraphBuilder bgGraphBuilder = new BgGraphBuilder(xdrip.getAppContext()); + + final Bitmap bitmap = new BgSparklineBuilder(xdrip.getAppContext()) + .setBgGraphBuilder(bgGraphBuilder) + .setHeight(height) + .setDotSize(5) + .setWidth(width) + .showHighLine(true) + .showLowLine(true) + .build(); + blob = convertBitmapToPNGByteArray(bitmap); + bitmap.recycle(); + graphCache.set(blob, width, height); + } + + final String[] columnNames = {"blob"}; + final MatrixCursor cursor = new MatrixCursor(columnNames); + cursor.addRow(new Object[]{blob}); + return cursor; + } + + case "config": + if (enabledWrite()) { + try { + if (selectionArgs != null && projection != null + && selectionArgs.length == projection.length) { + for (int i = 0; i < projection.length; i++) { + final String item = projection[i]; + switch (sortOrder) { + case "String": + Pref.setString(item, selectionArgs[i]); + break; + case "Integer": + Pref.setInt(item, Integer.parseInt(selectionArgs[i])); + break; + case "Long": + Pref.setLong(item, Long.parseLong(selectionArgs[i])); + break; + case "Boolean": + Pref.setBoolean(item, Boolean.parseBoolean(selectionArgs[i])); + break; + case "NewString": + if (Pref.getString(item, "defaultvalue").equals("defaultvalue")) { + Pref.setString(item, selectionArgs[i]); + } + break; + case "NewInteger": + if (Pref.getInt(item, -1000) == -1000) { + Pref.setInt(item, Integer.parseInt(selectionArgs[i])); + } + break; + case "NewLong": + if (Pref.getLong(item, -1000) == -1000) { + Pref.setLong(item, Long.parseLong(selectionArgs[i])); + } + break; + case "NewBoolean": + if (Pref.isPreferenceSet(item)) { + Pref.setBoolean(item, Boolean.parseBoolean(selectionArgs[i])); + } + break; + case "Remove": + Pref.removeItem(item); + break; + } + } + CollectionServiceStarter.restartCollectionServiceBackground(); + return matrixRow("processed", projection.length); + } + } catch (Exception e) { + UserError.Log.e(TAG, "config error: " + e); + } + } + return null; + + } // end switch + } catch (Exception e) { + Log.d(TAG, "Got exception: " + e); + e.printStackTrace(); + } + return null; + } + + private static MatrixCursor matrixRow(Object... x) { + final String[] columnNames = new String[x.length / 2]; + final Object[] values = new Object[x.length / 2]; + for (int i = 0; i < x.length; i += 2) { + columnNames[i / 2] = (String) x[i]; + values[i / 2] = x[i + 1]; + } + try ( + MatrixCursor cursor = new MatrixCursor(columnNames)) { + cursor.addRow(values); + return cursor; + } catch (Exception e) { + Log.d(TAG, "Error with cursor: " + e); + } + return null; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) { + if (!enabled()) return null; + try { + getContext().getContentResolver().notifyChange(uri, null); + } catch (Exception e) { + Log.e(TAG, "Got exception during insert " + e); + } + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) { + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) { + return 0; + } + + private int boolInt(final boolean bool) { + return bool ? 1 : 0; + } + + private static boolean enabled() { + return isNative() || xdrip.getAppContext() != null && getBooleanDefaultFalse("host_content_provider"); + } + + private static boolean enabledWrite() { + return enabled() && (isNative() || getBooleanDefaultFalse("content_provider_write")); + } + + public static void ping(String channel) { + if (enabled() && channel != null) { + if (channel.equals("bg")) { + dgCache.clear(); + graphCache.clear(); + } + xdrip.getAppContext().getContentResolver().insert(Uri.parse("content://" + BuildConfig.APPLICATION_ID + ".contentprovider/" + channel), null); + } + } + + public static byte[] convertBitmapToPNGByteArray(Bitmap bitmap) { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + return outputStream.toByteArray(); + } + +} 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 d3c34c176..e66ad5439 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/services/Ob1G5CollectionService.java @@ -14,8 +14,10 @@ import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.pendingCalibration; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.pendingStart; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.pendingStop; +import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.shortTxId; import static com.eveningoutpost.dexdrip.g5model.Ob1G5StateMachine.usingAlt; import static com.eveningoutpost.dexdrip.models.JoH.emptyString; +import static com.eveningoutpost.dexdrip.models.JoH.joinBytes; import static com.eveningoutpost.dexdrip.models.JoH.msSince; import static com.eveningoutpost.dexdrip.models.JoH.niceTimeScalar; import static com.eveningoutpost.dexdrip.models.JoH.tolerantHexStringToByteArray; @@ -46,7 +48,12 @@ import static com.eveningoutpost.dexdrip.utilitymodels.StatusItem.Highlight.NOTICE; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.DexcomG5; import static com.eveningoutpost.dexdrip.utils.bt.Subscription.addErrorHandler; +import static com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry.isNative; import static com.eveningoutpost.dexdrip.xdrip.gs; +import static com.polidea.rxandroidble2.scan.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; +import static com.polidea.rxandroidble2.scan.ScanSettings.CALLBACK_TYPE_FIRST_MATCH; +import static com.polidea.rxandroidble2.scan.ScanSettings.SCAN_MODE_BALANCED; +import static com.polidea.rxandroidble2.scan.ScanSettings.SCAN_MODE_LOW_LATENCY; import android.annotation.TargetApi; import android.app.PendingIntent; @@ -66,7 +73,9 @@ import android.os.ParcelUuid; import android.os.PowerManager; import android.preference.PreferenceManager; + import androidx.annotation.NonNull; + import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -181,7 +190,7 @@ public class Ob1G5CollectionService extends G5BaseService { public static volatile CalibrationState lastSensorState = null; public static volatile long lastUsableGlucosePacketTime = 0; private static volatile String static_connection_state = null; - private static volatile long static_last_connected = 0; + public static volatile long static_last_connected = 0; @Setter @Getter private static long last_transmitter_timestamp = 0; @@ -229,8 +238,8 @@ public class Ob1G5CollectionService extends G5BaseService { private static volatile int skippedConnects = 0; private static final boolean d = false; - private static final boolean allow_scan_by_mac = false; - private static boolean use_auto_connect = false; + private static volatile boolean allow_scan_by_mac = false; + private static volatile boolean use_auto_connect = false; private static volatile boolean minimize_scanning = false; // set by preference private static volatile boolean always_scan = false; private static volatile boolean scan_next_run = true; @@ -368,7 +377,7 @@ private synchronized void automata() { connect_to_device(true); break; case DISCOVER: - if ((wear_broadcast && usingAlt()) || specialPairingWorkaround()){ + if ((wear_broadcast && usingAlt()) || specialPairingWorkaround()) { msg("Pausing"); UserError.Log.d(TAG, "Pausing for alt: "); JoH.threadSleep(1000); @@ -560,11 +569,12 @@ private synchronized void scan_for_device() { historicalTransmitterMAC = PersistentStore.getString(OB1G5_MACSTORE + transmitterID); // "" if unset + boolean macFilter = false; ScanFilter filter; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && historicalTransmitterMAC.length() > 5 && allow_scan_by_mac) { - filter = new ScanFilter.Builder() - .setDeviceAddress(historicalTransmitterMAC) - .build(); + filter = new ScanFilter.Builder().setDeviceAddress(historicalTransmitterMAC).build(); + UserError.Log.d(TAG, "Using mac filter " + historicalTransmitterMAC); + macFilter = true; } else { final String localTransmitterID = transmitterID; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && localTransmitterID != null && localTransmitterID.length() > 4) { @@ -581,19 +591,19 @@ private synchronized void scan_for_device() { } scanSubscription = new Subscription(rxBleClient.scanBleDevices( - new ScanSettings.Builder() - //.setScanMode(static_last_timestamp < 1 ? ScanSettings.SCAN_MODE_LOW_LATENCY : ScanSettings.SCAN_MODE_BALANCED) - //.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH) - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setScanMode(android_wear ? ScanSettings.SCAN_MODE_BALANCED : - historicalTransmitterMAC.length() <= 5 ? ScanSettings.SCAN_MODE_LOW_LATENCY : - minimize_scanning ? ScanSettings.SCAN_MODE_BALANCED : ScanSettings.SCAN_MODE_LOW_LATENCY) - // .setScanMode(ScanSettings.SCAN_MODE_BALANCED) - .build(), - - // scan filter doesn't work reliable on android sdk 23+ - filter - ) + new ScanSettings.Builder() + //.setScanMode(static_last_timestamp < 1 ? ScanSettings.SCAN_MODE_LOW_LATENCY : ScanSettings.SCAN_MODE_BALANCED) + //.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH) + .setCallbackType(macFilter ? CALLBACK_TYPE_FIRST_MATCH : CALLBACK_TYPE_ALL_MATCHES) + .setScanMode(android_wear ? SCAN_MODE_BALANCED : + historicalTransmitterMAC.length() <= 5 ? SCAN_MODE_LOW_LATENCY : + minimize_scanning ? SCAN_MODE_BALANCED : SCAN_MODE_LOW_LATENCY) + // .setScanMode(ScanSettings.SCAN_MODE_BALANCED) + .build(), + + // scan filter doesn't work reliable on android sdk 23+ + filter + ) // observe on? // do unsubscribe? //.doOnUnsubscribe(this::clearSubscription) @@ -849,6 +859,27 @@ public static synchronized boolean isDeviceLocallyBonded() { return false; } + public static boolean immediateBonding() { + return Pref.getBooleanDefaultFalse("engineering_ob1_bonding_test") || isNative(); + } + + public static boolean ignoreBonding() { + return Pref.getBooleanDefaultFalse("engineering_ob1_ignore_bonding"); + } + + private byte[] fwChalCache(boolean prefix) { + final long chal = Pref.getLong("engineering_ob1_chal_cache" + (prefix ? "_1" : ""), 0); + if (chal != 0 && shortTxId()) { + final byte[] bytes = new byte[8]; + for (int i = 0; i < 8; i++) { + bytes[i] = (byte) (chal >> (8 - i - 1) * 8); + } + return bytes; + } else { + return new byte[0]; + } + } + private synchronized void checkAndEnableBT() { try { if (Pref.getBoolean("automatically_turn_bluetooth_on", true)) { @@ -1119,7 +1150,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { } minimize_scanning = Pref.getBooleanDefaultFalse("ob1_minimize_scanning"); - + allow_scan_by_mac = Build.VERSION.SDK_INT >= 32 && shortTxId(); automata(); // sequence logic UserError.Log.d(TAG, "Releasing service start"); @@ -1233,7 +1264,7 @@ private synchronized void onScanResult(final ScanResult bleScanResult) { transmitterMAC = bleScanResult.getBleDevice().getMacAddress(); transmitterIDmatchingMAC = transmitterID; if (search_name != null) { - saveTransmitterMac(); + saveTransmitterMac(); } //if (JoH.ratelimit("ob1-g5-scan-to-connect-transition", 3)) { if (state == SCAN) { @@ -1255,7 +1286,7 @@ private synchronized void onScanResult(final ScanResult bleScanResult) { } public void saveTransmitterMac() { - UserError.Log.d(TAG,"Saving transmitter mac: " + transmitterID + " = " + transmitterMAC); + UserError.Log.d(TAG, "Saving transmitter mac: " + transmitterID + " = " + transmitterMAC); PersistentStore.cleanupOld(OB1G5_MACSTORE); PersistentStore.setString(OB1G5_MACSTORE + transmitterID, transmitterMAC); } @@ -1516,6 +1547,8 @@ private void onServicesDiscovered(RxBleDeviceServices services) { JoH.static_toast_long(msg); } else { plugin.setPersistence(2, PersistentStore.getBytes(KEKS_ONE + transmitterMAC)); + if (immediateBonding()) + needsBonding(!isDeviceLocallyBonded() || ignoreBonding()); try { for (int i = 1; i < 4; i++) { plugin.setPersistence(7 + i, tolerantHexStringToByteArray(Pref.getStringDefaultBlank(KEKS + "_p" + i))); @@ -1533,7 +1566,7 @@ private void onServicesDiscovered(RxBleDeviceServices services) { } if (specialPairingWorkaround()) { - UserError.Log.d(TAG,"Samsung additional delay"); + UserError.Log.d(TAG, "Samsung additional delay"); Inevitable.task("samsung delay", 1000, () -> changeState(STATE.CHECK_AUTH)); } else { changeState(STATE.CHECK_AUTH); @@ -1788,7 +1821,7 @@ public static void processCalibrationStateLite(final CalibrationState state, fin } } - public static boolean processCalibrationStateLite(final CalibrationState state) { + public static boolean processCalibrationStateLite(final CalibrationState state) { if (state == CalibrationState.Unknown) { UserError.Log.d(TAG, "Not processing push of unknown state as this is the unset state"); return false; @@ -2064,6 +2097,10 @@ void setPurdah(final long duration) { } } + void needsBonding(final boolean required) { + plugin.setPersistence(6, joinBytes(new byte[]{(byte) ((required ? 0 : 1) << 1)}, fwChalCache(required))); + } + private static void handleUnknownFirmwareClick() { UserError.Log.d(TAG, "handleUnknownFirmwareClick()"); if (UpdateActivity.testAndSetNightly(true)) { @@ -2306,9 +2343,9 @@ public void run() { l.add(new StatusItem("Voltage A", parsedBattery.voltageA(), parsedBattery.voltageAWarning() ? BAD : NORMAL)); l.add(new StatusItem("Voltage B", parsedBattery.voltageB(), parsedBattery.voltageBWarning() ? BAD : NORMAL)); if (vr != null && FirmwareCapability.isFirmwareResistanceCapable(vr.firmware_version_string)) { - if (parsedBattery.resistance() != 0) { - l.add(new StatusItem("Resistance", parsedBattery.resistance(), parsedBattery.resistanceStatus().highlight)); - } + if (parsedBattery.resistance() != 0) { + l.add(new StatusItem("Resistance", parsedBattery.resistance(), parsedBattery.resistanceStatus().highlight)); + } } if (vr != null && FirmwareCapability.isFirmwareTemperatureCapable(vr.firmware_version_string)) { if (parsedBattery.temperature() > 0) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/tables/BgReadingTable.java b/app/src/main/java/com/eveningoutpost/dexdrip/tables/BgReadingTable.java index 7639d7f2d..a3ffaa759 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/tables/BgReadingTable.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/tables/BgReadingTable.java @@ -5,7 +5,9 @@ import android.content.DialogInterface; import android.graphics.Color; import android.os.Bundle; + import androidx.drawerlayout.widget.DrawerLayout; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,14 +21,19 @@ import com.eveningoutpost.dexdrip.NavigationDrawerFragment; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.utilitymodels.BgGraphBuilder; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; import com.eveningoutpost.dexdrip.utilitymodels.Pref; +import com.eveningoutpost.dexdrip.utils.DexCollectionType; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import static com.eveningoutpost.dexdrip.xdrip.gs; +import lombok.val; + public class BgReadingTable extends BaseListActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "BG Data Table"; @@ -55,9 +62,39 @@ public void onNavigationDrawerItemSelected(int position) { private void getData() { final List latest = BgReading.latest(5000); + parseDataForStats(latest); ListAdapter adapter = new BgReadingAdapter(this, latest); - this.setListAdapter(adapter); + try { + if (total > 0) { + this.getActionBar().setSubtitle(String.format(Locale.getDefault(), "%d in 24h, bf:%d%% mis:%d", total, ((backfilled * 100) / total), missing)); + } + } catch (NullPointerException e) { + // + } + } + + private int missing; + private int backfilled; + private int total; + + private void parseDataForStats(List list) { + long cutoff = JoH.tsl() - Constants.DAY_IN_MS; + long oldest = 0; + for (val item : list) { + if (item.timestamp < cutoff) break; + oldest = item.timestamp; + total++; + if (item.source_info != null && item.source_info.contains("Backfill")) { + backfilled++; + } + } + + if (total > 0) { + val expectedReadings = (JoH.tsl() - oldest) / DexCollectionType.getCurrentSamplePeriod(); + missing = (int) (expectedReadings - total); + } + } public static class BgReadingCursorAdapterViewHolder { @@ -75,12 +112,12 @@ public BgReadingCursorAdapterViewHolder(View root) { } public static class BgReadingAdapter extends BaseAdapter { - private final Context context; + private final Context context; private final List readings; public BgReadingAdapter(Context context, List readings) { this.context = context; - if(readings == null) + if (readings == null) readings = new ArrayList<>(); this.readings = readings; @@ -119,19 +156,21 @@ public boolean onLongClick(View v) { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - switch (which){ + switch (which) { case DialogInterface.BUTTON_POSITIVE: bgReading.ignoreForStats = true; bgReading.save(); notifyDataSetChanged(); - if (Pref.getBooleanDefaultFalse("wear_sync")) BgReading.pushBgReadingSyncToWatch(bgReading, false); + if (Pref.getBooleanDefaultFalse("wear_sync")) + BgReading.pushBgReadingSyncToWatch(bgReading, false); break; case DialogInterface.BUTTON_NEGATIVE: bgReading.ignoreForStats = false; bgReading.save(); notifyDataSetChanged(); - if (Pref.getBooleanDefaultFalse("wear_sync")) BgReading.pushBgReadingSyncToWatch(bgReading, false); + if (Pref.getBooleanDefaultFalse("wear_sync")) + BgReading.pushBgReadingSyncToWatch(bgReading, false); break; } } 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 5a168db92..6380d824e 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/AlertPlayer.java @@ -4,6 +4,7 @@ import static com.eveningoutpost.dexdrip.models.JoH.delayedMediaPlayerRelease; import static com.eveningoutpost.dexdrip.models.JoH.setMediaDataSource; import static com.eveningoutpost.dexdrip.models.JoH.stopAndReleasePlayer; +import static com.eveningoutpost.dexdrip.receiver.InfoContentProvider.ping; import android.app.Notification; import android.app.NotificationManager; @@ -188,6 +189,7 @@ public synchronized void startAlert(Context ctx, boolean trendingToAlertEnd, Al ActiveBgAlert.Create(newAlert.uuid, start_snoozed, nextAlertTime); if (!start_snoozed) VibrateNotifyMakeNoise(ctx, newAlert, bgValue, 0); + ping("alarm"); AlertTracker.evaluate(); } @@ -213,6 +215,7 @@ public synchronized void stopAlert(Context ctx, boolean ClearData, boolean clear } revertCurrentVolume(streamType); releaseAudioFocus(); + ping("alarm"); } // only do something if an alert is active - only call from interactive @@ -246,6 +249,7 @@ public synchronized void Snooze(Context ctx, int repeatTime) { } BroadcastEntry.cancelAlert(); + ping("alarm"); } public synchronized void Snooze(Context ctx, int repeatTime, boolean from_interactive) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/CollectionServiceStarter.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/CollectionServiceStarter.java index 2a89e319e..8f5de0272 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/CollectionServiceStarter.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/CollectionServiceStarter.java @@ -510,7 +510,7 @@ private void startServiceCompat(final Class service) { @SuppressWarnings("ConstantConditions") private void startServiceCompat(final Intent intent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && BuildConfig.targetSDK >= Build.VERSION_CODES.N + // && BuildConfig.targetSDK >= Build.VERSION_CODES.N && ForegroundServiceStarter.shouldRunCollectorInForeground()) { try { Log.d(TAG, String.format("Starting oreo foreground service: %s", intent.getComponent().getClassName())); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/ColorCache.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/ColorCache.java index f11bb521b..aca930945 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/ColorCache.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/ColorCache.java @@ -26,17 +26,23 @@ public static void invalidateCache() { } public static int getCol(final X color) { - if (!the_cache.containsKey(color)) { - try { - the_cache.put(color, Pref.getInt(color.internalName, 0xABCDEF)); - } catch (ClassCastException e) { - UserError.Log.wtf(TAG, "Cannot set initial value - preference type likely wrong for: " + color.internalName + " " + e); - the_cache.put(color, Color.GRAY); + + if (!the_cache.containsKey(color)) { + try { + the_cache.put(color, Pref.getInt(color.internalName, 0xABCDEF)); + } catch (ClassCastException e) { + UserError.Log.wtf(TAG, "Cannot set initial value - preference type likely wrong for: " + color.internalName + " " + e); + the_cache.put(color, Color.GRAY); + } + if (debug) + UserError.Log.d(TAG, "Setting cache for color: " + color.internalName + " / " + Pref.getInt(color.internalName, 1234)); } - if (debug) - UserError.Log.d(TAG, "Setting cache for color: " + color.internalName + " / " + Pref.getInt(color.internalName, 1234)); - } - return the_cache.get(color); + int col = the_cache.get(color); + if (col == 0xABCDEF && color.internalName.equals("color_low_values")) { + the_cache.clear(); // preferences init hasn't run yet as is local default + } + return col; + } public enum X { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/InstalledApps.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/InstalledApps.java index 8be5bd1b7..6e4cc5898 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/InstalledApps.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/InstalledApps.java @@ -1,5 +1,7 @@ package com.eveningoutpost.dexdrip.utilitymodels; +import static com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry.isNative; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -23,7 +25,7 @@ public class InstalledApps { private static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; public static boolean isGooglePlayInstalled(Context context) { - return checkPackageExists(context, GOOGLE_PLAY_SERVICES_PACKAGE); + return isNative() || checkPackageExists(context, GOOGLE_PLAY_SERVICES_PACKAGE); } public static boolean checkPackageExists(Context context, String packageName) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Pref.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Pref.java index c6bc342ef..d7aa09301 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Pref.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Pref.java @@ -30,7 +30,11 @@ private static void initializePrefs() { } } else { if (JoH.ratelimit("prefs-failure2", 20)) { - UserError.Log.wtf(TAG, "Could not initialize preferences due to missing context!!"); + try { + UserError.Log.wtf(TAG, "Could not initialize preferences due to missing context!!"); + } catch (NullPointerException e) { + android.util.Log.wtf(TAG, "Could not initialize preferences due to missing context and then got null pointer!!"); + } } } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SendFeedBack.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SendFeedBack.java index fa1750d96..7f0538590 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SendFeedBack.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SendFeedBack.java @@ -16,6 +16,7 @@ import com.eveningoutpost.dexdrip.BaseAppCompatActivity; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.R; +import com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry; import com.squareup.okhttp.FormEncodingBuilder; import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.MediaType; @@ -33,6 +34,7 @@ import okio.Okio; import static com.eveningoutpost.dexdrip.utils.DexCollectionType.getBestCollectorHardwareName; +import static com.eveningoutpost.dexdrip.watch.thinjam.BlueJayEntry.isNative; import androidx.appcompat.app.AlertDialog; @@ -54,7 +56,7 @@ public class SendFeedBack extends BaseAppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_send_feed_back); - send_url = getString(R.string.wserviceurl) + "/joh-feedback"; + send_url = getString(isNative() ? R.string.qserviceurl : R.string.wserviceurl) + "/joh-feedback"; myrating = (RatingBar) findViewById(R.id.ratingBar); ratingtext = (TextView) findViewById(R.id.ratingtext); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SourceWizard.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SourceWizard.java index 703f4defe..96f13f5e1 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SourceWizard.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/SourceWizard.java @@ -87,7 +87,7 @@ public static void start(Activity activity) { } public synchronized static void start(Activity activity, boolean force) { - if (sw == null) sw = new SourceWizard(activity); + if (sw == null || sw.activity != activity) sw = new SourceWizard(activity); if (force) { if (sw.showing()) sw.dismiss(); } 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 6f1138b2b..977a82fe7 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/VoiceCommands.java @@ -197,6 +197,8 @@ public static void processVoiceCommand(final String allWords, final Activity mAc case "database administration": JoH.startActivity(DatabaseAdmin.class); break; + case "simulate crash": + throw new RuntimeException("Test crash"); } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/BlobCache.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/BlobCache.java new file mode 100644 index 000000000..7d2ae2e14 --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/BlobCache.java @@ -0,0 +1,66 @@ +package com.eveningoutpost.dexdrip.utils; + +import android.os.SystemClock; + +// JamOrHam +public class BlobCache { + + private final Object lock = new Object(); + private volatile Object blobCache = null; + private static volatile long blobTime = 0; + static volatile long blobParamX = 0; + static volatile long blobParamY = 0; + + private final long timeout; + + public BlobCache(long timeout) { + this.timeout = timeout; + } + + public void set(Object o) { + synchronized (lock) { + blobCache = o; + blobTime = SystemClock.elapsedRealtime(); + } + } + + public Object get() { + synchronized (lock) { + if (isExpired()) { + blobCache = null; + } + return blobCache; + } + } + + public void set(Object o, long x, long y) { + synchronized (lock) { + blobParamX = x; + blobParamY = y; + blobCache = o; + blobTime = SystemClock.elapsedRealtime(); + } + } + + public Object get(long x, long y) { + synchronized (lock) { + if (x != blobParamX || y != blobParamY) { + return null; + } + return get(); + } + } + + public void clear() { + blobCache = null; + blobTime = 0; + } + + public boolean isExpired() { + synchronized (lock) { + return (blobCache == null || SystemClock.elapsedRealtime() - blobTime > timeout); + } + } + +} + 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 8db254ccb..afbb490e9 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java @@ -79,6 +79,7 @@ import com.eveningoutpost.dexdrip.models.UserNotification; import com.eveningoutpost.dexdrip.plugin.Dialog; import com.eveningoutpost.dexdrip.profileeditor.ProfileEditor; +import com.eveningoutpost.dexdrip.receiver.InfoContentProvider; import com.eveningoutpost.dexdrip.services.ActivityRecognizedService; import com.eveningoutpost.dexdrip.services.BluetoothGlucoseMeter; import com.eveningoutpost.dexdrip.services.DexCollectionService; @@ -299,6 +300,7 @@ private void installxDripPlusPreferencesFromQRCode(SharedPreferences prefs, Stri Toast.makeText(getApplicationContext(), "Loaded " + Integer.toString(changes) + " preferences from QR code", Toast.LENGTH_LONG).show(); PlusSyncService.clearandRestartSyncService(getApplicationContext()); DesertSync.settingsChanged(); // refresh + InfoContentProvider.ping("pref"); if (prefs.getString("dex_collection_method", "").equals("Follower")) { PlusSyncService.clearandRestartSyncService(getApplicationContext()); GcmActivity.last_sync_request = 0; diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/SdcardImportExport.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/SdcardImportExport.java index afcfcbaed..6b527a858 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/SdcardImportExport.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/SdcardImportExport.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.List; +import static com.eveningoutpost.dexdrip.receiver.InfoContentProvider.ping; import static com.eveningoutpost.dexdrip.utils.FileUtils.getExternalDir; @@ -162,6 +163,7 @@ public void loadPreferencesToSD(View myview) { public static void storePreferencesFromBytes(byte[] bytes, Context context) { if (dataFromBytes(bytes, PREFERENCES_FILE, context)) { + ping("pref"); Log.i(TAG, "Restarting as new preferences loaded from bytes"); hardReset(); } else { @@ -291,6 +293,7 @@ public static void restoreSettingsNow(Activity activity) { JoH.static_toast_long("Restoring Settings"); if (copyPreferencesFileBack(activity, results.get(0))) { Log.e(TAG, "Restoring preferences succeeded from first match: " + results.get(0)); + ping("pref"); hardReset(); } else { JoH.static_toast_long("Couldn't restore preferences from: " + results.get(0)); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/framework/WakeLockTrampoline.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/framework/WakeLockTrampoline.java index fea5f5e36..1c34b46c5 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/framework/WakeLockTrampoline.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/framework/WakeLockTrampoline.java @@ -64,7 +64,7 @@ public void onReceive(final Context context, final Intent broadcastIntent) { ComponentName startResult; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && BuildConfig.targetSDK >= Build.VERSION_CODES.N + // && BuildConfig.targetSDK >= Build.VERSION_CODES.N && ForegroundServiceStarter.shouldRunCollectorInForeground()) { try { UserError.Log.d(TAG, String.format("Starting oreo foreground service: %s", serviceIntent.getComponent().getClassName())); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/watch/thinjam/BlueJayEntry.java b/app/src/main/java/com/eveningoutpost/dexdrip/watch/thinjam/BlueJayEntry.java index 2c0584307..053c4dbf0 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/watch/thinjam/BlueJayEntry.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/watch/thinjam/BlueJayEntry.java @@ -1,6 +1,7 @@ package com.eveningoutpost.dexdrip.watch.thinjam; import android.content.SharedPreferences; +import android.os.Build; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.utilitymodels.CollectionServiceStarter; @@ -124,6 +125,10 @@ public static void sendPngIfEnabled(final byte[] bytes, final String parameters, } } + public static boolean isNative() { + return Build.MODEL.startsWith("BlueJay U"); + } + static void startWithRefresh() { Inevitable.task("bluejay-preference-changed", 1000, () -> JoH.startService(BlueJayService.class, "function", "refresh")); } diff --git a/app/src/main/res/values/internal.xml b/app/src/main/res/values/internal.xml index 554a572aa..bc7838a5e 100644 --- a/app/src/main/res/values/internal.xml +++ b/app/src/main/res/values/internal.xml @@ -27,6 +27,7 @@ xDrip+ xDrip+ https://xdrip-plus-updates.appspot.com + https://xdrip-plus-updates.cflare-509.workers.dev all_settings_wizard wizard_uuid wizard_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b5b4139d1..a55737e4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1902,4 +1902,5 @@ Pause all other sounds Use Camera Light Flash camera light during alerts when connected to a charger + G7 should start automatically diff --git a/libkeks/src/main/java/jamorham/keks/Plugin.java b/libkeks/src/main/java/jamorham/keks/Plugin.java index 8237942fb..6b849b4a7 100644 --- a/libkeks/src/main/java/jamorham/keks/Plugin.java +++ b/libkeks/src/main/java/jamorham/keks/Plugin.java @@ -121,6 +121,7 @@ private static String stateToName(final int state) { private volatile AuthRequestTxMessage2 lastAuthTx2; private volatile int state = 0; private volatile byte[] accumulator = new byte[0]; + private volatile byte[] newFwchal = new byte[0]; private volatile byte[] keyToTestAgainst = null; static { @@ -336,7 +337,7 @@ public boolean receivedResponse3(byte[] data) { private boolean alt = false; private AuthRequestTxMessage2 getAuthRequestTx2() { - lastAuthTx2 = new AuthRequestTxMessage2(8, alt); + lastAuthTx2 = new AuthRequestTxMessage2(8, alt, newFwchal); return lastAuthTx2; } @@ -480,6 +481,7 @@ public boolean setPersistence(int channel, byte[] data) { } if (channel == 6) { alt = Arrays.equals(data, SPARAM.bytes); + newFwchal = data; } if (channel == 7) { dontClearAccumulator.clear(); diff --git a/libkeks/src/main/java/jamorham/keks/message/AuthRequestTxMessage2.java b/libkeks/src/main/java/jamorham/keks/message/AuthRequestTxMessage2.java index ec4dfa862..d635f00bd 100644 --- a/libkeks/src/main/java/jamorham/keks/message/AuthRequestTxMessage2.java +++ b/libkeks/src/main/java/jamorham/keks/message/AuthRequestTxMessage2.java @@ -17,11 +17,12 @@ public class AuthRequestTxMessage2 extends BaseMessage { private static final byte endByteAlt = 0x1; public AuthRequestTxMessage2(int token_size) { - this(token_size, false); + this(token_size, false, new byte[0]); } - public AuthRequestTxMessage2(int token_size, boolean alt) { - this(token_size, alt ? endByteAlt : endByteStd); + public AuthRequestTxMessage2(int token_size, boolean alt, byte[] chal) { + this(token_size, (alt ? endByteAlt : endByteStd) + + (chal.length > 2 ? chal[2] : 0)); } public AuthRequestTxMessage2(int token_size, int slot) {