commit
b950a10938
|
@ -21,7 +21,7 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="sugar.free.sightremote.HISTORY_BROADCASTS" />
|
||||
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
|
||||
|
||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
|
@ -88,10 +88,8 @@
|
|||
<action android:name="com.eveningoutpost.dexdrip.NS_EMULATOR"/>
|
||||
<!-- Receiver from glimp -->
|
||||
<action android:name="it.ct.glicemia.ACTION_GLUCOSE_MEASURED"/>
|
||||
<!-- Receiver from DexcomG5 -->
|
||||
<action android:name="com.dexcom.cgm.DATA"/>
|
||||
<action android:name="com.dexcom.cgm.AndroidAPSEVGCallback.BROADCAST"/>
|
||||
<action android:name="com.dexcom.cgm.g5.AndroidAPSEVGCallback.BROADCAST"/>
|
||||
<!-- Receiver from Dexcom -->
|
||||
<action android:name="com.dexcom.cgm.EXTERNAL_BROADCAST"/>
|
||||
<!-- Receiver from Poctech -->
|
||||
<action android:name="com.china.poctech.data"/>
|
||||
<!-- Receiver from Tomato -->
|
||||
|
@ -284,6 +282,7 @@
|
|||
android:name=".plugins.pump.insight.activities.InsightPairingInformationActivity"
|
||||
android:label="@string/pairing_information"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".activities.RequestDexcomPermissionActivity" />
|
||||
|
||||
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||
|
||||
|
|
|
@ -72,8 +72,7 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
|||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceEversensePlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceMM640gPlugin;
|
||||
|
@ -192,8 +191,7 @@ public class MainApp extends Application {
|
|||
pluginsList.add(SourceNSClientPlugin.getPlugin());
|
||||
pluginsList.add(SourceMM640gPlugin.getPlugin());
|
||||
pluginsList.add(SourceGlimpPlugin.getPlugin());
|
||||
pluginsList.add(SourceDexcomG5Plugin.getPlugin());
|
||||
pluginsList.add(SourceDexcomG6Plugin.getPlugin());
|
||||
pluginsList.add(SourceDexcomPlugin.INSTANCE);
|
||||
pluginsList.add(SourcePoctechPlugin.getPlugin());
|
||||
pluginsList.add(SourceTomatoPlugin.getPlugin());
|
||||
pluginsList.add(SourceEversensePlugin.getPlugin());
|
||||
|
|
|
@ -39,9 +39,9 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
|||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
|
||||
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
|
||||
import info.nightscout.androidaps.utils.LocaleHelper;
|
||||
import info.nightscout.androidaps.utils.OKDialog;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
|
@ -144,7 +144,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
|
|||
|
||||
addPreferencesFromResource(R.xml.pref_overview);
|
||||
|
||||
addPreferencesFromResourceIfEnabled(SourceDexcomG5Plugin.getPlugin(), PluginType.BGSOURCE);
|
||||
addPreferencesFromResourceIfEnabled(SourceDexcomPlugin.INSTANCE, PluginType.BGSOURCE);
|
||||
addPreferencesFromResourceIfEnabled(CareportalPlugin.getPlugin(), PluginType.GENERAL);
|
||||
addPreferencesFromResourceIfEnabled(SafetyPlugin.getPlugin(), PluginType.CONSTRAINTS);
|
||||
if (Config.APS) {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package info.nightscout.androidaps.activities
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin
|
||||
|
||||
class RequestDexcomPermissionActivity : AppCompatActivity() {
|
||||
|
||||
private val requestCode = "AndroidAPS <3".map { it.toInt() }.sum()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(arrayOf(SourceDexcomPlugin.PERMISSION), requestCode)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
|
@ -108,8 +108,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
|
|||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceXdripPlugin;
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||
import info.nightscout.androidaps.plugins.treatments.fragments.ProfileViewerDialog;
|
||||
|
@ -661,8 +660,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean xdrip = SourceXdripPlugin.getPlugin().isEnabled(PluginType.BGSOURCE);
|
||||
boolean g5 = SourceDexcomG5Plugin.getPlugin().isEnabled(PluginType.BGSOURCE);
|
||||
boolean g6 = SourceDexcomG6Plugin.getPlugin().isEnabled(PluginType.BGSOURCE);
|
||||
boolean dexcom = SourceDexcomPlugin.INSTANCE.isEnabled(PluginType.BGSOURCE);
|
||||
String units = ProfileFunctions.getInstance().getProfileUnits();
|
||||
|
||||
FragmentManager manager = getFragmentManager();
|
||||
|
@ -685,10 +683,16 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
if (xdrip) {
|
||||
CalibrationDialog calibrationDialog = new CalibrationDialog();
|
||||
calibrationDialog.show(manager, "CalibrationDialog");
|
||||
} else if (g5 || g6) {
|
||||
} else if (dexcom) {
|
||||
try {
|
||||
Intent i = new Intent("com.dexcom.cgm.activities.MeterEntryActivity");
|
||||
startActivity(i);
|
||||
String packageName = SourceDexcomPlugin.INSTANCE.findDexcomPackageName();
|
||||
if (packageName != null) {
|
||||
Intent i = new Intent("com.dexcom.cgm.activities.MeterEntryActivity");
|
||||
i.setPackage(packageName);
|
||||
startActivity(i);
|
||||
} else {
|
||||
ToastUtils.showToastInUiThread(getActivity(), MainApp.gs(R.string.dexcom_app_not_installed));
|
||||
}
|
||||
} catch (ActivityNotFoundException e) {
|
||||
ToastUtils.showToastInUiThread(getActivity(), MainApp.gs(R.string.g5appnotdetected));
|
||||
}
|
||||
|
@ -697,14 +701,14 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
case R.id.overview_cgmbutton:
|
||||
if (xdrip)
|
||||
openCgmApp("com.eveningoutpost.dexdrip");
|
||||
else if (g5 && units.equals(Constants.MGDL))
|
||||
openCgmApp("com.dexcom.cgm.region5.mgdl");
|
||||
else if (g5 && units.equals(Constants.MMOL))
|
||||
openCgmApp("com.dexcom.cgm.region5.mmol");
|
||||
else if (g6 && units.equals(Constants.MGDL))
|
||||
openCgmApp("com.dexcom.g6.region3.mgdl");
|
||||
else if (g6 && units.equals(Constants.MMOL))
|
||||
openCgmApp("com.dexcom.g6.region3.mmol");
|
||||
else if (dexcom) {
|
||||
String packageName = SourceDexcomPlugin.INSTANCE.findDexcomPackageName();
|
||||
if (packageName != null) {
|
||||
openCgmApp(packageName);
|
||||
} else {
|
||||
ToastUtils.showToastInUiThread(getActivity(), MainApp.gs(R.string.dexcom_app_not_installed));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R.id.overview_treatmentbutton:
|
||||
NewTreatmentDialog treatmentDialogFragment = new NewTreatmentDialog();
|
||||
|
@ -1188,10 +1192,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
|
||||
// **** Calibration & CGM buttons ****
|
||||
boolean xDripIsBgSource = MainApp.getSpecificPlugin(SourceXdripPlugin.class) != null && MainApp.getSpecificPlugin(SourceXdripPlugin.class).isEnabled(PluginType.BGSOURCE);
|
||||
boolean g5IsBgSource = MainApp.getSpecificPlugin(SourceDexcomG5Plugin.class) != null && MainApp.getSpecificPlugin(SourceDexcomG5Plugin.class).isEnabled(PluginType.BGSOURCE);
|
||||
boolean dexcomIsSource = SourceDexcomPlugin.INSTANCE.isEnabled(PluginType.BGSOURCE);
|
||||
boolean bgAvailable = DatabaseHelper.actualBg() != null;
|
||||
if (calibrationButton != null) {
|
||||
if ((xDripIsBgSource || g5IsBgSource) && bgAvailable && SP.getBoolean(R.string.key_show_calibration_button, true)) {
|
||||
if ((xDripIsBgSource || dexcomIsSource) && bgAvailable && SP.getBoolean(R.string.key_show_calibration_button, true)) {
|
||||
calibrationButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
calibrationButton.setVisibility(View.GONE);
|
||||
|
@ -1200,7 +1204,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
if (cgmButton != null) {
|
||||
if (xDripIsBgSource && SP.getBoolean(R.string.key_show_cgm_button, false)) {
|
||||
cgmButton.setVisibility(View.VISIBLE);
|
||||
} else if (g5IsBgSource && SP.getBoolean(R.string.key_show_cgm_button, false)) {
|
||||
} else if (dexcomIsSource && SP.getBoolean(R.string.key_show_cgm_button, false)) {
|
||||
cgmButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
cgmButton.setVisibility(View.GONE);
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.source;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.db.BgReading;
|
||||
import info.nightscout.androidaps.db.CareportalEvent;
|
||||
import info.nightscout.androidaps.interfaces.BgSourceInterface;
|
||||
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.logging.L;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
|
||||
import info.nightscout.androidaps.services.Intents;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
|
||||
/**
|
||||
* Created by mike on 28.11.2017.
|
||||
*/
|
||||
|
||||
public class SourceDexcomG5Plugin extends PluginBase implements BgSourceInterface {
|
||||
private static Logger log = LoggerFactory.getLogger(L.BGSOURCE);
|
||||
|
||||
private static SourceDexcomG5Plugin plugin = null;
|
||||
|
||||
public static SourceDexcomG5Plugin getPlugin() {
|
||||
if (plugin == null)
|
||||
plugin = new SourceDexcomG5Plugin();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private SourceDexcomG5Plugin() {
|
||||
super(new PluginDescription()
|
||||
.mainType(PluginType.BGSOURCE)
|
||||
.fragmentClass(BGSourceFragment.class.getName())
|
||||
.pluginName(R.string.DexcomG5)
|
||||
.shortName(R.string.dexcomG5_shortname)
|
||||
.preferencesId(R.xml.pref_bgsource)
|
||||
.description(R.string.description_source_dexcom_g5)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advancedFilteringSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNewData(Intent intent) {
|
||||
// onHandleIntent Bundle{ data => [{"m_time":1511939180,"m_trend":"NotComputable","m_value":335}]; android.support.content.wakelockid => 95; }Bundle
|
||||
|
||||
if (!isEnabled(PluginType.BGSOURCE)) return;
|
||||
|
||||
if (intent.getAction().equals(Intents.DEXCOMG5_BG))
|
||||
handleNewDataOld(intent);
|
||||
|
||||
if (intent.getAction().equals(Intents.DEXCOMG5_BG_NEW))
|
||||
handleNewDataNew(intent);
|
||||
}
|
||||
|
||||
public void handleNewDataOld(Intent intent) {
|
||||
// onHandleIntent Bundle{ data => [{"m_time":1511939180,"m_trend":"NotComputable","m_value":335}]; android.support.content.wakelockid => 95; }Bundle
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
if (bundle == null) return;
|
||||
|
||||
BgReading bgReading = new BgReading();
|
||||
|
||||
String data = bundle.getString("data");
|
||||
if (L.isEnabled(L.BGSOURCE))
|
||||
log.debug("Received Dexcom Data", data);
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
try {
|
||||
JSONArray jsonArray = new JSONArray(data);
|
||||
if (L.isEnabled(L.BGSOURCE))
|
||||
log.debug("Received Dexcom Data size:" + jsonArray.length());
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject json = jsonArray.getJSONObject(i);
|
||||
bgReading.value = json.getInt("m_value");
|
||||
bgReading.direction = json.getString("m_trend");
|
||||
bgReading.date = json.getLong("m_time") * 1000L;
|
||||
bgReading.raw = 0;
|
||||
boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG5");
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
|
||||
NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG5");
|
||||
}
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
|
||||
NSUpload.sendToXdrip(bgReading);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
log.error("Exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleNewDataNew(Intent intent) {
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
if (bundle == null) return;
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
if (bundle.containsKey("transmitterSystemTime"))
|
||||
log.debug("transmitterSystemTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterSystemTime")));
|
||||
if (bundle.containsKey("transmitterRemainingTime"))
|
||||
log.debug("transmitterRemainingTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterRemainingTime")));
|
||||
log.debug("transmitterId: " + bundle.getString("transmitterId"));
|
||||
if (bundle.containsKey("transmitterActivatedOn"))
|
||||
log.debug("transmitterActivatedOn: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterActivatedOn")));
|
||||
log.debug("transmitterVersion: " + bundle.getString("transmitterVersion"));
|
||||
log.debug("transmitterSoftwareNumber: " + bundle.getString("transmitterSoftwareNumber"));
|
||||
log.debug("transmitterStorageTimeDays: " + bundle.getInt("transmitterStorageTimeDays"));
|
||||
log.debug("transmitterApiVersion: " + bundle.getInt("transmitterApiVersion"));
|
||||
log.debug("transmitterMaxRuntimeDays: " + bundle.getInt("transmitterMaxRuntimeDays"));
|
||||
log.debug("transmitterMaxStorageTimeDays: " + bundle.getInt("transmitterMaxStorageTimeDays"));
|
||||
log.debug("transmitterCGMProcessorFirmwareVersion: " + bundle.getString("transmitterCGMProcessorFirmwareVersion"));
|
||||
log.debug("transmitterBleRadioFirmwareVersion: " + bundle.getString("transmitterBleRadioFirmwareVersion"));
|
||||
log.debug("transmitterHardwareVersion: " + bundle.getInt("transmitterHardwareVersion"));
|
||||
log.debug("transmitterBleSoftDeviceVersion: " + bundle.getString("transmitterBleSoftDeviceVersion"));
|
||||
log.debug("transmitterNordicAsicHwID: " + bundle.getInt("transmitterNordicAsicHwID"));
|
||||
log.debug("transmitterSessionTimeDays: " + bundle.getInt("transmitterSessionTimeDays"));
|
||||
log.debug("transmitterFeatureFlags: " + bundle.getInt("transmitterFeatureFlags"));
|
||||
}
|
||||
|
||||
if (bundle.containsKey("sensorInsertionTime")) {
|
||||
long sensorInsertionTime = bundle.getLong("sensorInsertionTime");
|
||||
if (L.isEnabled(L.BGSOURCE))
|
||||
log.debug("sensorInsertionTime: " + DateUtil.dateAndTimeFullString(sensorInsertionTime));
|
||||
if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false)) {
|
||||
try {
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("enteredBy", "AndroidAPS-DexcomG5");
|
||||
data.put("created_at", DateUtil.toISOString(sensorInsertionTime));
|
||||
data.put("eventType", CareportalEvent.SENSORCHANGE);
|
||||
NSUpload.uploadCareportalEntryToNS(data);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey("glucoseValues")) {
|
||||
int[] glucoseValues = bundle.getIntArray("glucoseValues");
|
||||
int[] glucoseRecordIDs = bundle.getIntArray("glucoseRecordIDs");
|
||||
long[] glucoseRecordedTimestamps = bundle.getLongArray("glucoseRecordedTimestamps");
|
||||
long[] glucoseSessionStartTimes = bundle.getLongArray("glucoseSessionStartTimes");
|
||||
long[] glucoseSystemTimestamps = bundle.getLongArray("glucoseSystemTimestamps");
|
||||
String[] glucoseTransmitterIDS = bundle.getStringArray("glucoseTransmitterIDS");
|
||||
long[] glucoseTransmitterTimestamps = bundle.getLongArray("glucoseTransmitterTimestamps");
|
||||
String[] glucoseTrendsArrows = bundle.getStringArray("glucoseTrendsArrows");
|
||||
boolean[] glucoseWasBackfilled = bundle.getBooleanArray("glucoseWasBackfilled");
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
log.debug("glucoseValues", Arrays.toString(glucoseValues));
|
||||
log.debug("glucoseRecordIDs", Arrays.toString(glucoseRecordIDs));
|
||||
log.debug("glucoseRecordedTimestamps", Arrays.toString(glucoseRecordedTimestamps));
|
||||
log.debug("glucoseSessionStartTimes", Arrays.toString(glucoseSessionStartTimes));
|
||||
log.debug("glucoseSystemTimestamps", Arrays.toString(glucoseSystemTimestamps));
|
||||
log.debug("glucoseTransmitterIDS", Arrays.toString(glucoseTransmitterIDS));
|
||||
log.debug("glucoseTransmitterTimestamps", Arrays.toString(glucoseTransmitterTimestamps));
|
||||
log.debug("glucoseTrendsArrows", Arrays.toString(glucoseTrendsArrows));
|
||||
log.debug("glucoseWasBackfilled", Arrays.toString(glucoseWasBackfilled));
|
||||
}
|
||||
|
||||
for (int i = 0; i < glucoseValues.length; i++) {
|
||||
BgReading bgReading = new BgReading();
|
||||
bgReading.value = glucoseValues[i];
|
||||
bgReading.direction = glucoseTrendsArrows[i];
|
||||
bgReading.date = glucoseTransmitterTimestamps[i];
|
||||
bgReading.raw = 0;
|
||||
boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG5");
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
|
||||
NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG5");
|
||||
}
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
|
||||
NSUpload.sendToXdrip(bgReading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey("meterValues")) {
|
||||
String[] meterEntryTypes = bundle.getStringArray("meterEntryTypes");
|
||||
long[] meterTimestamps = bundle.getLongArray("meterTimestamps");
|
||||
int[] meterValues = bundle.getIntArray("meterValues");
|
||||
long[] meterRecordedTimestamps = bundle.getLongArray("meterRecordedTimestamps");
|
||||
int[] meterTransmitterIDs = bundle.getIntArray("meterTransmitterIDs");
|
||||
long[] meterTransmitterTimestamps = bundle.getLongArray("meterTransmitterTimestamps");
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
log.debug("meterValues", Arrays.toString(meterValues));
|
||||
log.debug("meterEntryTypes", Arrays.toString(meterEntryTypes));
|
||||
log.debug("meterTimestamps", Arrays.toString(meterTimestamps));
|
||||
log.debug("meterTransmitterTimestamps", Arrays.toString(meterTransmitterTimestamps));
|
||||
log.debug("meterRecordedTimestamps", Arrays.toString(meterRecordedTimestamps));
|
||||
log.debug("meterTransmitterIDs", Arrays.toString(meterTransmitterIDs));
|
||||
}
|
||||
|
||||
for (int i = 0; i < meterValues.length; i++) {
|
||||
try {
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(meterTimestamps[i]) == null) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("enteredBy", "AndroidAPS-DexcomG5");
|
||||
data.put("created_at", DateUtil.toISOString(meterTimestamps[i]));
|
||||
data.put("eventType", CareportalEvent.BGCHECK);
|
||||
data.put("glucoseType", "Finger");
|
||||
data.put("glucose", meterValues[i]);
|
||||
data.put("units", Constants.MGDL);
|
||||
NSUpload.uploadCareportalEntryToNS(data);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.source;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.db.BgReading;
|
||||
import info.nightscout.androidaps.db.CareportalEvent;
|
||||
import info.nightscout.androidaps.interfaces.BgSourceInterface;
|
||||
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.logging.L;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
|
||||
/**
|
||||
* Created by mike on 30.11.2018.
|
||||
*/
|
||||
|
||||
public class SourceDexcomG6Plugin extends PluginBase implements BgSourceInterface {
|
||||
private static Logger log = LoggerFactory.getLogger(L.BGSOURCE);
|
||||
|
||||
private static SourceDexcomG6Plugin plugin = null;
|
||||
|
||||
public static SourceDexcomG6Plugin getPlugin() {
|
||||
if (plugin == null)
|
||||
plugin = new SourceDexcomG6Plugin();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private SourceDexcomG6Plugin() {
|
||||
super(new PluginDescription()
|
||||
.mainType(PluginType.BGSOURCE)
|
||||
.fragmentClass(BGSourceFragment.class.getName())
|
||||
.pluginName(R.string.DexcomG6)
|
||||
.shortName(R.string.dexcomG6_shortname)
|
||||
.preferencesId(R.xml.pref_bgsource)
|
||||
.description(R.string.description_source_dexcom_g6)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advancedFilteringSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNewData(Intent intent) {
|
||||
if (!isEnabled(PluginType.BGSOURCE)) return;
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
if (bundle == null) return;
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
if (bundle.containsKey("transmitterSystemTime"))
|
||||
log.debug("transmitterSystemTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterSystemTime")));
|
||||
log.debug("transmitterId: " + bundle.getString("transmitterId"));
|
||||
if (bundle.containsKey("transmitterActivatedOn"))
|
||||
log.debug("transmitterActivatedOn: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterActivatedOn")));
|
||||
log.debug("transmitterVersion: " + bundle.getString("transmitterVersion"));
|
||||
log.debug("transmitterSoftwareNumber: " + bundle.getString("transmitterSoftwareNumber"));
|
||||
log.debug("transmitterStorageTimeDays: " + bundle.getInt("transmitterStorageTimeDays"));
|
||||
log.debug("transmitterApiVersion: " + bundle.getInt("transmitterApiVersion"));
|
||||
log.debug("transmitterMaxRuntimeDays: " + bundle.getInt("transmitterMaxRuntimeDays"));
|
||||
log.debug("transmitterMaxStorageTimeDays: " + bundle.getInt("transmitterMaxStorageTimeDays"));
|
||||
log.debug("transmitterCGMProcessorFirmwareVersion: " + bundle.getString("transmitterCGMProcessorFirmwareVersion"));
|
||||
log.debug("transmitterBleRadioFirmwareVersion: " + bundle.getString("transmitterBleRadioFirmwareVersion"));
|
||||
log.debug("transmitterHardwareVersion: " + bundle.getInt("transmitterHardwareVersion"));
|
||||
log.debug("transmitterBleSoftDeviceVersion: " + bundle.getString("transmitterBleSoftDeviceVersion"));
|
||||
log.debug("transmitterNordicAsicHwID: " + bundle.getInt("transmitterNordicAsicHwID"));
|
||||
log.debug("transmitterSessionTimeDays: " + bundle.getInt("transmitterSessionTimeDays"));
|
||||
log.debug("transmitterFeatureFlags: " + bundle.getInt("transmitterFeatureFlags"));
|
||||
|
||||
if (bundle.containsKey("sensorCode"))
|
||||
log.debug("sensorCode: " + bundle.getString("sensorCode"));
|
||||
}
|
||||
|
||||
if (bundle.containsKey("sensorInsertionTime")) {
|
||||
long sensorInsertionTime = bundle.getLong("sensorInsertionTime");
|
||||
if (L.isEnabled(L.BGSOURCE))
|
||||
log.debug("sensorInsertionTime: " + DateUtil.dateAndTimeFullString(sensorInsertionTime));
|
||||
if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false)) {
|
||||
try {
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("enteredBy", "AndroidAPS-DexcomG6");
|
||||
data.put("created_at", DateUtil.toISOString(sensorInsertionTime));
|
||||
data.put("eventType", CareportalEvent.SENSORCHANGE);
|
||||
NSUpload.uploadCareportalEntryToNS(data);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey("evgTimestamps")) {
|
||||
long[] timestamps = bundle.getLongArray("evgTimestamps");
|
||||
long[] transmitterTimes = bundle.getLongArray("transmitterTimes");
|
||||
int[] evgs = bundle.getIntArray("evgs");
|
||||
int[] predictiveEVGs = bundle.getIntArray("predictiveEVGs");
|
||||
String[] trendArrows = bundle.getStringArray("trendArrows");
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
log.debug("timestamps", Arrays.toString(timestamps));
|
||||
log.debug("transmitterTimes", Arrays.toString(transmitterTimes));
|
||||
log.debug("evgs", Arrays.toString(evgs));
|
||||
log.debug("predictiveEVGs", Arrays.toString(predictiveEVGs));
|
||||
log.debug("trendArrows", Arrays.toString(trendArrows));
|
||||
}
|
||||
|
||||
for (int i = 0; i < transmitterTimes.length; i++) {
|
||||
BgReading bgReading = new BgReading();
|
||||
bgReading.value = evgs[i];
|
||||
bgReading.direction = trendArrows[i];
|
||||
bgReading.date = timestamps[i];
|
||||
bgReading.raw = 0;
|
||||
boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG6");
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
|
||||
NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG6");
|
||||
}
|
||||
if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
|
||||
NSUpload.sendToXdrip(bgReading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey("meterValues")) {
|
||||
int[] meterValues = bundle.getIntArray("meterValues");
|
||||
String[] meterEntryTypes = bundle.getStringArray("meterEntryTypes");
|
||||
long[] meterTimestamps = bundle.getLongArray("meterTimestamps");
|
||||
long[] meterTransmitterTimestamps = bundle.getLongArray("meterTransmitterTimestamps");
|
||||
long[] meterRecordedTimestamps = bundle.getLongArray("meterRecordedTimestamps");
|
||||
int[] meterRecordIDs = bundle.getIntArray("meterRecordIDs");
|
||||
|
||||
if (L.isEnabled(L.BGSOURCE)) {
|
||||
log.debug("meterValues", Arrays.toString(meterValues));
|
||||
log.debug("meterEntryTypes", Arrays.toString(meterEntryTypes));
|
||||
log.debug("meterTimestamps", Arrays.toString(meterTimestamps));
|
||||
log.debug("meterTransmitterTimestamps", Arrays.toString(meterTransmitterTimestamps));
|
||||
log.debug("meterRecordedTimestamps", Arrays.toString(meterRecordedTimestamps));
|
||||
log.debug("meterRecordIDs", Arrays.toString(meterRecordIDs));
|
||||
}
|
||||
|
||||
for (int i = 0; i < meterValues.length; i++) {
|
||||
try {
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(meterTimestamps[i]) == null) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("enteredBy", "AndroidAPS-DexcomG6");
|
||||
data.put("created_at", DateUtil.toISOString(meterTimestamps[i]));
|
||||
data.put("eventType", CareportalEvent.BGCHECK);
|
||||
data.put("glucoseType", "Finger");
|
||||
data.put("glucose", meterValues[i]);
|
||||
data.put("units", Constants.MGDL);
|
||||
NSUpload.uploadCareportalEntryToNS(data);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package info.nightscout.androidaps.plugins.source
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.MainApp
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity
|
||||
import info.nightscout.androidaps.db.BgReading
|
||||
import info.nightscout.androidaps.db.CareportalEvent
|
||||
import info.nightscout.androidaps.interfaces.BgSourceInterface
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.logging.L
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.SP
|
||||
import org.json.JSONObject
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
object SourceDexcomPlugin : PluginBase(PluginDescription()
|
||||
.mainType(PluginType.BGSOURCE)
|
||||
.fragmentClass(BGSourceFragment::class.java.name)
|
||||
.pluginName(R.string.dexcom_app_patched)
|
||||
.shortName(R.string.dexcom_short)
|
||||
.preferencesId(R.xml.pref_bgsource)
|
||||
.description(R.string.description_source_dexcom)), BgSourceInterface {
|
||||
|
||||
private val log = LoggerFactory.getLogger(L.BGSOURCE)
|
||||
|
||||
private val PACKAGE_NAMES = arrayOf("com.dexcom.cgm.region1.mgdl", "com.dexcom.cgm.region1.mmol",
|
||||
"com.dexcom.cgm.region2.mgdl", "com.dexcom.cgm.region2.mmol",
|
||||
"com.dexcom.g6.region1.mmol", "com.dexcom.g6.region2.mgdl",
|
||||
"com.dexcom.g6.region3.mgdl", "com.dexcom.g6.region3.mmol")
|
||||
|
||||
const val PERMISSION = "com.dexcom.cgm.EXTERNAL_PERMISSION"
|
||||
|
||||
override fun advancedFilteringSupported(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(MainApp.instance(), PERMISSION) != PackageManager.PERMISSION_GRANTED) {
|
||||
val intent = Intent(MainApp.instance(), RequestDexcomPermissionActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
MainApp.instance().startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
fun findDexcomPackageName(): String? {
|
||||
val packageManager = MainApp.instance().packageManager;
|
||||
for (packageInfo in packageManager.getInstalledPackages(0)) {
|
||||
if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun handleNewData(intent: Intent) {
|
||||
if (!isEnabled(PluginType.BGSOURCE)) return
|
||||
try {
|
||||
val glucoseValues = intent.getBundleExtra("glucoseValues")
|
||||
for (i in 0 until glucoseValues.size()) {
|
||||
val glucoseValue = glucoseValues.getBundle(i.toString())
|
||||
val bgReading = BgReading()
|
||||
bgReading.value = glucoseValue!!.getInt("glucoseValue").toDouble()
|
||||
bgReading.direction = glucoseValue.getString("trendArrow")
|
||||
bgReading.date = glucoseValue.getLong("timestamp") * 1000
|
||||
bgReading.raw = 0.0
|
||||
if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom")) {
|
||||
if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
|
||||
NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG6")
|
||||
}
|
||||
if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
|
||||
NSUpload.sendToXdrip(bgReading)
|
||||
}
|
||||
}
|
||||
}
|
||||
val meters = intent.getBundleExtra("meters")
|
||||
for (i in 0 until meters.size()) {
|
||||
val meter = meters.getBundle(i.toString())
|
||||
val timestamp = meter.getLong("timestamp") * 1000
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) != null) continue
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("enteredBy", "AndroidAPS-Dexcom")
|
||||
jsonObject.put("created_at", DateUtil.toISOString(timestamp))
|
||||
jsonObject.put("eventType", CareportalEvent.BGCHECK)
|
||||
jsonObject.put("glucoseType", "Finger")
|
||||
jsonObject.put("glucose", meter.getInt("meterValue"))
|
||||
jsonObject.put("units", Constants.MGDL)
|
||||
NSUpload.uploadCareportalEntryToNS(jsonObject)
|
||||
}
|
||||
if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false) && intent.hasExtra("sensorInsertionTime")) {
|
||||
val sensorInsertionTime = intent.extras.getLong("sensorInsertionTime") * 1000
|
||||
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) {
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("enteredBy", "AndroidAPS-Dexcom")
|
||||
jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime))
|
||||
jsonObject.put("eventType", CareportalEvent.SENSORCHANGE)
|
||||
NSUpload.uploadCareportalEntryToNS(jsonObject)
|
||||
}
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
log.error("Error while processing intent from Dexcom App", e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,8 +25,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific
|
|||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
|
||||
import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRNSHistorySync;
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceEversensePlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin;
|
||||
import info.nightscout.androidaps.plugins.source.SourceMM640gPlugin;
|
||||
|
@ -69,12 +68,8 @@ public class DataService extends IntentService {
|
|||
SourceMM640gPlugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.GLIMP_BG.equals(action)) {
|
||||
SourceGlimpPlugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.DEXCOMG5_BG.equals(action)) {
|
||||
SourceDexcomG5Plugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.DEXCOMG5_BG_NEW.equals(action)) {
|
||||
SourceDexcomG5Plugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.DEXCOMG6_BG.equals(action)) {
|
||||
SourceDexcomG6Plugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.DEXCOM_BG.equals(action)) {
|
||||
SourceDexcomPlugin.INSTANCE.handleNewData(intent);
|
||||
} else if (Intents.POCTECH_BG.equals(action)) {
|
||||
SourcePoctechPlugin.getPlugin().handleNewData(intent);
|
||||
} else if (Intents.TOMATO_BG.equals(action)) {
|
||||
|
|
|
@ -48,9 +48,7 @@ public interface Intents {
|
|||
|
||||
String GLIMP_BG = "it.ct.glicemia.ACTION_GLUCOSE_MEASURED";
|
||||
|
||||
String DEXCOMG5_BG = "com.dexcom.cgm.DATA";
|
||||
String DEXCOMG5_BG_NEW = "com.dexcom.cgm.g5.AndroidAPSEVGCallback.BROADCAST";
|
||||
String DEXCOMG6_BG = "com.dexcom.cgm.AndroidAPSEVGCallback.BROADCAST";
|
||||
String DEXCOM_BG = "com.dexcom.cgm.EXTERNAL_BROADCAST";
|
||||
String EVERSENSE_BG = "com.senseonics.AndroidAPSEventSubscriber.BROADCAST";
|
||||
|
||||
String POCTECH_BG = "com.china.poctech.data";
|
||||
|
|
|
@ -991,6 +991,7 @@
|
|||
<string name="fabric_upload">Fabric Upload</string>
|
||||
<string name="allow_automated_crash_reporting">Allow automated crash reporting and feature usage data to be sent to the developers via the fabric.io service.</string>
|
||||
<string name="g5appnotdetected">Please update your Dexcom app to supported version</string>
|
||||
<string name="dexcom_app_not_installed">Dexcom app is not installed.</string>
|
||||
<string name="start_activity_tt">Start Activity TT</string>
|
||||
<string name="start_eating_soon_tt">Start Eating soon TT</string>
|
||||
<string name="temptargetshort">TT</string>
|
||||
|
@ -1335,6 +1336,10 @@
|
|||
<string name="very_old_version">very old version</string>
|
||||
<string name="new_version_warning">New version for at least %1$d days available! Fallback to LGS after 60 days, loop will be disabled after 90 days</string>
|
||||
|
||||
<string name="dexcom_app_patched">Dexcom App (patched)</string>
|
||||
<string name="dexcom_short">DXCM</string>
|
||||
<string name="description_source_dexcom">Receive BG values from the patched Dexcom app.</string>
|
||||
|
||||
<plurals name="objective_days">
|
||||
<item quantity="one">%1$d day</item>
|
||||
<item quantity="other">%1$d days</item>
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.source;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
public class SourceDexcomG5PluginTest {
|
||||
|
||||
@Test
|
||||
public void getPlugin() {
|
||||
Assert.assertNotEquals(null, SourceDexcomG5Plugin.getPlugin());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void advancedFilteringSupported() {
|
||||
Assert.assertEquals(true, SourceDexcomG5Plugin.getPlugin().advancedFilteringSupported());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue