simplify graph series
This commit is contained in:
parent
8934d2d6b9
commit
dd7e777a24
9 changed files with 282 additions and 555 deletions
|
@ -2,20 +2,24 @@ package info.nightscout.androidaps.db;
|
||||||
|
|
||||||
import com.j256.ormlite.field.DatabaseField;
|
import com.j256.ormlite.field.DatabaseField;
|
||||||
import com.j256.ormlite.table.DatabaseTable;
|
import com.j256.ormlite.table.DatabaseTable;
|
||||||
import com.jjoe64.graphview.series.DataPointInterface;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.Constants;
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv;
|
import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.OverviewPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
import info.nightscout.utils.DecimalFormatter;
|
import info.nightscout.utils.DecimalFormatter;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
@DatabaseTable(tableName = DatabaseHelper.DATABASE_BGREADINGS)
|
@DatabaseTable(tableName = DatabaseHelper.DATABASE_BGREADINGS)
|
||||||
public class BgReading implements DataPointInterface {
|
public class BgReading implements DataPointWithLabelInterface {
|
||||||
private static Logger log = LoggerFactory.getLogger(BgReading.class);
|
private static Logger log = LoggerFactory.getLogger(BgReading.class);
|
||||||
|
|
||||||
@DatabaseField(id = true)
|
@DatabaseField(id = true)
|
||||||
|
@ -39,6 +43,8 @@ public class BgReading implements DataPointInterface {
|
||||||
|
|
||||||
public static String units = Constants.MGDL;
|
public static String units = Constants.MGDL;
|
||||||
|
|
||||||
|
public boolean isPrediction = false; // true when drawing predictions as bg points
|
||||||
|
|
||||||
public BgReading() {}
|
public BgReading() {}
|
||||||
|
|
||||||
public BgReading(NSSgv sgv) {
|
public BgReading(NSSgv sgv) {
|
||||||
|
@ -106,6 +112,7 @@ public class BgReading implements DataPointInterface {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------ DataPointWithLabelInterface ------------------
|
||||||
@Override
|
@Override
|
||||||
public double getX() {
|
public double getX() {
|
||||||
return date;
|
return date;
|
||||||
|
@ -116,4 +123,51 @@ public class BgReading implements DataPointInterface {
|
||||||
return valueToUnits(units);
|
return valueToUnits(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setY(double y) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PointsWithLabelGraphSeries.Shape getShape() {
|
||||||
|
return PointsWithLabelGraphSeries.Shape.POINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSize() {
|
||||||
|
boolean isTablet = MainApp.sResources.getBoolean(R.bool.isTablet);
|
||||||
|
return isTablet ? 8 : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColor() {
|
||||||
|
Double lowLine = SP.getDouble("low_mark", 0d);
|
||||||
|
Double highLine = SP.getDouble("high_mark", 0d);
|
||||||
|
if (lowLine < 1) {
|
||||||
|
lowLine = Profile.fromMgdlToUnits(OverviewPlugin.bgTargetLow, units);
|
||||||
|
}
|
||||||
|
if (highLine < 1) {
|
||||||
|
highLine = Profile.fromMgdlToUnits(OverviewPlugin.bgTargetHigh, units);
|
||||||
|
}
|
||||||
|
String units = MainApp.getConfigBuilder().getProfile().getUnits();
|
||||||
|
int color = MainApp.sResources.getColor(R.color.inrange);
|
||||||
|
if (isPrediction)
|
||||||
|
color = MainApp.sResources.getColor(R.color.prediction);
|
||||||
|
else if (valueToUnits(units) < lowLine)
|
||||||
|
color = MainApp.sResources.getColor(R.color.low);
|
||||||
|
else if (valueToUnits(units) > highLine)
|
||||||
|
color = MainApp.sResources.getColor(R.color.high);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ package info.nightscout.androidaps.db;
|
||||||
* Created by mike on 21.05.2017.
|
* Created by mike on 21.05.2017.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
import com.j256.ormlite.field.DatabaseField;
|
import com.j256.ormlite.field.DatabaseField;
|
||||||
import com.j256.ormlite.table.DatabaseTable;
|
import com.j256.ormlite.table.DatabaseTable;
|
||||||
|
|
||||||
|
@ -20,6 +22,8 @@ import info.nightscout.androidaps.interfaces.InsulinInterface;
|
||||||
import info.nightscout.androidaps.interfaces.Interval;
|
import info.nightscout.androidaps.interfaces.Interval;
|
||||||
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
||||||
import info.nightscout.androidaps.data.Profile;
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
import info.nightscout.utils.DecimalFormatter;
|
import info.nightscout.utils.DecimalFormatter;
|
||||||
import info.nightscout.utils.Round;
|
import info.nightscout.utils.Round;
|
||||||
|
@ -29,7 +33,7 @@ import info.nightscout.utils.Round;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@DatabaseTable(tableName = DatabaseHelper.DATABASE_EXTENDEDBOLUSES)
|
@DatabaseTable(tableName = DatabaseHelper.DATABASE_EXTENDEDBOLUSES)
|
||||||
public class ExtendedBolus implements Interval {
|
public class ExtendedBolus implements Interval, DataPointWithLabelInterface {
|
||||||
private static Logger log = LoggerFactory.getLogger(ExtendedBolus.class);
|
private static Logger log = LoggerFactory.getLogger(ExtendedBolus.class);
|
||||||
|
|
||||||
@DatabaseField(id = true)
|
@DatabaseField(id = true)
|
||||||
|
@ -196,4 +200,49 @@ public class ExtendedBolus implements Interval {
|
||||||
return "E " + DecimalFormatter.to2Decimal(absoluteRate()) + "U/h ("
|
return "E " + DecimalFormatter.to2Decimal(absoluteRate()) + "U/h ("
|
||||||
+ getRealDuration() + "/" + durationInMinutes + ") ";
|
+ getRealDuration() + "/" + durationInMinutes + ") ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------- DataPointWithLabelInterface --------
|
||||||
|
@Override
|
||||||
|
public double getX() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default when no sgv around available
|
||||||
|
private double yValue = 0;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getY() {
|
||||||
|
return yValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setY(double y) {
|
||||||
|
yValue = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel() {
|
||||||
|
return toStringMedium();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() {
|
||||||
|
return durationInMinutes * 60 * 1000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PointsWithLabelGraphSeries.Shape getShape() {
|
||||||
|
return PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSize() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColor() {
|
||||||
|
return Color.CYAN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package info.nightscout.androidaps.db;
|
package info.nightscout.androidaps.db;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
import com.j256.ormlite.field.DatabaseField;
|
import com.j256.ormlite.field.DatabaseField;
|
||||||
import com.j256.ormlite.table.DatabaseTable;
|
import com.j256.ormlite.table.DatabaseTable;
|
||||||
|
|
||||||
|
@ -10,6 +12,7 @@ import java.util.Date;
|
||||||
|
|
||||||
import info.nightscout.androidaps.interfaces.Interval;
|
import info.nightscout.androidaps.interfaces.Interval;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
|
|
||||||
@DatabaseTable(tableName = DatabaseHelper.DATABASE_PROFILESWITCHES)
|
@DatabaseTable(tableName = DatabaseHelper.DATABASE_PROFILESWITCHES)
|
||||||
|
@ -118,11 +121,36 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface {
|
||||||
return yValue;
|
return yValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setY(double y) {
|
||||||
|
yValue = y;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
return profileName;
|
return profileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PointsWithLabelGraphSeries.Shape getShape() {
|
||||||
|
return PointsWithLabelGraphSeries.Shape.PROFILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSize() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColor() {
|
||||||
|
return Color.CYAN;
|
||||||
|
}
|
||||||
|
|
||||||
public String log() {
|
public String log() {
|
||||||
return "ProfileSwitch{" +
|
return "ProfileSwitch{" +
|
||||||
"date=" + date +
|
"date=" + date +
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package info.nightscout.androidaps.db;
|
package info.nightscout.androidaps.db;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
import com.j256.ormlite.field.DatabaseField;
|
import com.j256.ormlite.field.DatabaseField;
|
||||||
import com.j256.ormlite.table.DatabaseTable;
|
import com.j256.ormlite.table.DatabaseTable;
|
||||||
|
|
||||||
|
@ -16,6 +18,7 @@ import info.nightscout.androidaps.interfaces.InsulinInterface;
|
||||||
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
||||||
import info.nightscout.androidaps.data.Profile;
|
import info.nightscout.androidaps.data.Profile;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
import info.nightscout.utils.DecimalFormatter;
|
import info.nightscout.utils.DecimalFormatter;
|
||||||
|
|
||||||
|
@ -103,14 +106,29 @@ public class Treatment implements DataPointWithLabelInterface {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setYValue(List<BgReading> bgReadingsArray) {
|
@Override
|
||||||
Profile profile = MainApp.getConfigBuilder().getProfile();
|
public long getDuration() {
|
||||||
for (int r = bgReadingsArray.size() - 1; r >= 0; r--) {
|
return 0;
|
||||||
BgReading reading = bgReadingsArray.get(r);
|
}
|
||||||
if (reading.date > date) continue;
|
|
||||||
yValue = Profile.fromMgdlToUnits(reading.value, profile.getUnits());
|
@Override
|
||||||
break;
|
public PointsWithLabelGraphSeries.Shape getShape() {
|
||||||
}
|
return PointsWithLabelGraphSeries.Shape.BOLUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSize() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColor() {
|
||||||
|
return Color.CYAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setY(double y) {
|
||||||
|
yValue = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------- DataPointInterface end --------------------
|
// ----------------- DataPointInterface end --------------------
|
||||||
|
|
|
@ -100,6 +100,7 @@ public class DetermineBasalResultAMA extends APSResult {
|
||||||
BgReading bg = new BgReading();
|
BgReading bg = new BgReading();
|
||||||
bg.value = iob.getInt(i);
|
bg.value = iob.getInt(i);
|
||||||
bg.date = startTime + i * 5 * 60 * 1000L;
|
bg.date = startTime + i * 5 * 60 * 1000L;
|
||||||
|
bg.isPrediction = true;
|
||||||
array.add(bg);
|
array.add(bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +110,7 @@ public class DetermineBasalResultAMA extends APSResult {
|
||||||
BgReading bg = new BgReading();
|
BgReading bg = new BgReading();
|
||||||
bg.value = iob.getInt(i);
|
bg.value = iob.getInt(i);
|
||||||
bg.date = startTime + i * 5 * 60 * 1000L;
|
bg.date = startTime + i * 5 * 60 * 1000L;
|
||||||
|
bg.isPrediction = true;
|
||||||
array.add(bg);
|
array.add(bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +120,7 @@ public class DetermineBasalResultAMA extends APSResult {
|
||||||
BgReading bg = new BgReading();
|
BgReading bg = new BgReading();
|
||||||
bg.value = iob.getInt(i);
|
bg.value = iob.getInt(i);
|
||||||
bg.date = startTime + i * 5 * 60 * 1000L;
|
bg.date = startTime + i * 5 * 60 * 1000L;
|
||||||
|
bg.isPrediction = true;
|
||||||
array.add(bg);
|
array.add(bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ import com.jjoe64.graphview.Viewport;
|
||||||
import com.jjoe64.graphview.series.BarGraphSeries;
|
import com.jjoe64.graphview.series.BarGraphSeries;
|
||||||
import com.jjoe64.graphview.series.DataPoint;
|
import com.jjoe64.graphview.series.DataPoint;
|
||||||
import com.jjoe64.graphview.series.LineGraphSeries;
|
import com.jjoe64.graphview.series.LineGraphSeries;
|
||||||
import com.jjoe64.graphview.series.PointsGraphSeries;
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -61,7 +60,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import info.nightscout.androidaps.BuildConfig;
|
|
||||||
import info.nightscout.androidaps.Config;
|
import info.nightscout.androidaps.Config;
|
||||||
import info.nightscout.androidaps.Constants;
|
import info.nightscout.androidaps.Constants;
|
||||||
import info.nightscout.androidaps.MainApp;
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
@ -109,11 +107,11 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog;
|
||||||
import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification;
|
import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification;
|
||||||
import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification;
|
import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.AreaGraphSeries;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.AreaGraphSeries;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DoubleDataPoint;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.DoubleDataPoint;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.FixedLineGraphSeries;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.FixedLineGraphSeries;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.TimeAsXAxisLabelFormatter;
|
import info.nightscout.androidaps.plugins.Overview.graphExtensions.TimeAsXAxisLabelFormatter;
|
||||||
import info.nightscout.androidaps.plugins.Overview.graphExtensions.VerticalTextsGraphSeries;
|
|
||||||
import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripPlugin;
|
import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripPlugin;
|
||||||
import info.nightscout.utils.BolusWizard;
|
import info.nightscout.utils.BolusWizard;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
|
@ -189,8 +187,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
|
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
|
||||||
private static ScheduledFuture<?> scheduledUpdate = null;
|
private static ScheduledFuture<?> scheduledUpdate = null;
|
||||||
|
|
||||||
final Handler timeHandler = new Handler();
|
|
||||||
|
|
||||||
public OverviewFragment() {
|
public OverviewFragment() {
|
||||||
super();
|
super();
|
||||||
if (sHandlerThread == null) {
|
if (sHandlerThread == null) {
|
||||||
|
@ -337,7 +333,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
@Override
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
super.onCreateContextMenu(menu, v, menuInfo);
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
final LoopPlugin activeloop = MainApp.getConfigBuilder().getActiveLoop();
|
final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop();
|
||||||
if (activeloop == null)
|
if (activeloop == null)
|
||||||
return;
|
return;
|
||||||
menu.setHeaderTitle(MainApp.sResources.getString(R.string.loop));
|
menu.setHeaderTitle(MainApp.sResources.getString(R.string.loop));
|
||||||
|
@ -620,8 +616,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
NSUpload.uploadDeviceStatus();
|
NSUpload.uploadDeviceStatus();
|
||||||
ObjectivesPlugin objectivesPlugin = (ObjectivesPlugin) MainApp.getSpecificPlugin(ObjectivesPlugin.class);
|
ObjectivesPlugin objectivesPlugin = (ObjectivesPlugin) MainApp.getSpecificPlugin(ObjectivesPlugin.class);
|
||||||
if (objectivesPlugin != null) {
|
if (objectivesPlugin != null) {
|
||||||
objectivesPlugin.manualEnacts++;
|
ObjectivesPlugin.manualEnacts++;
|
||||||
objectivesPlugin.saveProgress();
|
ObjectivesPlugin.saveProgress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduleUpdateGUI("onClickAcceptTemp");
|
scheduleUpdateGUI("onClickAcceptTemp");
|
||||||
|
@ -643,7 +639,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
QuickWizard.QuickWizardEntry quickWizardEntry = getPlugin().quickWizard.getActive();
|
QuickWizard.QuickWizardEntry quickWizardEntry = getPlugin().quickWizard.getActive();
|
||||||
if (quickWizardEntry != null && actualBg != null) {
|
if (quickWizardEntry != null && actualBg != null) {
|
||||||
quickWizardButton.setVisibility(View.VISIBLE);
|
quickWizardButton.setVisibility(View.VISIBLE);
|
||||||
String text = MainApp.sResources.getString(R.string.bolus) + ": " + quickWizardEntry.buttonText();
|
|
||||||
BolusWizard wizard = new BolusWizard();
|
BolusWizard wizard = new BolusWizard();
|
||||||
wizard.doCalc(profile, quickWizardEntry.carbs(), 0d, actualBg.valueToUnits(profile.getUnits()), 0d, true, true, false, false);
|
wizard.doCalc(profile, quickWizardEntry.carbs(), 0d, actualBg.valueToUnits(profile.getUnits()), 0d, true, true, false, false);
|
||||||
|
|
||||||
|
@ -679,7 +674,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
confirmMessage += "\n" + getString(R.string.bolus) + ": " + formatNumber2decimalplaces.format(insulinAfterConstraints) + "U";
|
confirmMessage += "\n" + getString(R.string.bolus) + ": " + formatNumber2decimalplaces.format(insulinAfterConstraints) + "U";
|
||||||
confirmMessage += "\n" + getString(R.string.carbs) + ": " + carbsAfterConstraints + "g";
|
confirmMessage += "\n" + getString(R.string.carbs) + ": " + carbsAfterConstraints + "g";
|
||||||
|
|
||||||
if (insulinAfterConstraints - wizard.calculatedTotalInsulin != 0 || carbsAfterConstraints != quickWizardEntry.carbs()) {
|
if (!insulinAfterConstraints.equals(wizard.calculatedTotalInsulin) || !carbsAfterConstraints.equals(quickWizardEntry.carbs())) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||||
builder.setTitle(MainApp.sResources.getString(R.string.treatmentdeliveryerror));
|
builder.setTitle(MainApp.sResources.getString(R.string.treatmentdeliveryerror));
|
||||||
builder.setMessage(getString(R.string.constraints_violation) + "\n" + getString(R.string.changeyourinput));
|
builder.setMessage(getString(R.string.constraints_violation) + "\n" + getString(R.string.changeyourinput));
|
||||||
|
@ -921,7 +916,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
apsModeView.setVisibility(View.VISIBLE);
|
apsModeView.setVisibility(View.VISIBLE);
|
||||||
apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.loopenabled));
|
apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.loopenabled));
|
||||||
apsModeView.setTextColor(Color.BLACK);
|
apsModeView.setTextColor(Color.BLACK);
|
||||||
final LoopPlugin activeloop = MainApp.getConfigBuilder().getActiveLoop();
|
final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop();
|
||||||
if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isSuperBolus()) {
|
if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isSuperBolus()) {
|
||||||
apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended));
|
apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended));
|
||||||
apsModeView.setText(String.format(MainApp.sResources.getString(R.string.loopsuperbolusfor), activeloop.minutesToEndOfSuspend()));
|
apsModeView.setText(String.format(MainApp.sResources.getString(R.string.loopsuperbolusfor), activeloop.minutesToEndOfSuspend()));
|
||||||
|
@ -959,7 +954,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits()));
|
tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits()));
|
||||||
else
|
else
|
||||||
tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits()) + " - " + Profile.toUnitsString(tempTarget.high, Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits()), profile.getUnits()));
|
tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits()) + " - " + Profile.toUnitsString(tempTarget.high, Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits()), profile.getUnits()));
|
||||||
} if (Config.NSCLIENT) {
|
}
|
||||||
|
if (Config.NSCLIENT) {
|
||||||
tempTargetView.setVisibility(View.GONE);
|
tempTargetView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -1173,7 +1169,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
int predHours = (int) (Math.ceil(((DetermineBasalResultAMA) finalLastRun.constraintsProcessed).getLatestPredictionsTime() - new Date().getTime()) / (60 * 60 * 1000));
|
int predHours = (int) (Math.ceil(((DetermineBasalResultAMA) finalLastRun.constraintsProcessed).getLatestPredictionsTime() - new Date().getTime()) / (60 * 60 * 1000));
|
||||||
predHours = Math.min(2, predHours);
|
predHours = Math.min(2, predHours);
|
||||||
predHours = Math.max(0, predHours);
|
predHours = Math.max(0, predHours);
|
||||||
hoursToFetch = (int) (rangeToDisplay - predHours);
|
hoursToFetch = rangeToDisplay - predHours;
|
||||||
toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding
|
toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding
|
||||||
fromTime = toTime - hoursToFetch * 60 * 60 * 1000L;
|
fromTime = toTime - hoursToFetch * 60 * 60 * 1000L;
|
||||||
endTime = toTime + predHours * 60 * 60 * 1000L;
|
endTime = toTime + predHours * 60 * 60 * 1000L;
|
||||||
|
@ -1189,12 +1185,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
LineGraphSeries<DataPoint> tempBasalsSeries = null;
|
LineGraphSeries<DataPoint> tempBasalsSeries = null;
|
||||||
AreaGraphSeries<DoubleDataPoint> areaSeries;
|
AreaGraphSeries<DoubleDataPoint> areaSeries;
|
||||||
LineGraphSeries<DataPoint> seriesNow, seriesNow2;
|
LineGraphSeries<DataPoint> seriesNow, seriesNow2;
|
||||||
PointsGraphSeries<BgReading> seriesInRage;
|
|
||||||
PointsGraphSeries<BgReading> seriesLow;
|
|
||||||
PointsGraphSeries<BgReading> seriesHigh;
|
|
||||||
PointsGraphSeries<BgReading> predSeries;
|
|
||||||
PointsWithLabelGraphSeries<Treatment> seriesTreatments;
|
|
||||||
VerticalTextsGraphSeries<ProfileSwitch> seriesProfileSwitch;
|
|
||||||
|
|
||||||
// **** TEMP BASALS graph ****
|
// **** TEMP BASALS graph ****
|
||||||
Double maxBasalValueFound = 0d;
|
Double maxBasalValueFound = 0d;
|
||||||
|
@ -1406,9 +1396,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
|
|
||||||
// **** BG graph ****
|
// **** BG graph ****
|
||||||
List<BgReading> bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true);
|
List<BgReading> bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true);
|
||||||
List<BgReading> inRangeArray = new ArrayList<>();
|
List<DataPointWithLabelInterface> bgListArray = new ArrayList<>();
|
||||||
List<BgReading> lowArray = new ArrayList<>();
|
|
||||||
List<BgReading> highArray = new ArrayList<>();
|
|
||||||
|
|
||||||
if (bgReadingsArray.size() == 0)
|
if (bgReadingsArray.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -1418,59 +1406,28 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
BgReading bg = it.next();
|
BgReading bg = it.next();
|
||||||
if (bg.value > maxBgValue) maxBgValue = bg.value;
|
if (bg.value > maxBgValue) maxBgValue = bg.value;
|
||||||
if (bg.valueToUnits(units) < lowLine)
|
bgListArray.add(bg);
|
||||||
lowArray.add(bg);
|
|
||||||
else if (bg.valueToUnits(units) > highLine)
|
|
||||||
highArray.add(bg);
|
|
||||||
else
|
|
||||||
inRangeArray.add(bg);
|
|
||||||
}
|
}
|
||||||
|
if (showPrediction) {
|
||||||
|
DetermineBasalResultAMA amaResult = (DetermineBasalResultAMA) finalLastRun.constraintsProcessed;
|
||||||
|
List<BgReading> predArray = amaResult.getPredictions();
|
||||||
|
Iterator<BgReading> itPred = predArray.iterator();
|
||||||
|
while (itPred.hasNext()) {
|
||||||
|
BgReading bg = it.next();
|
||||||
|
bgListArray.add(bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units);
|
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units);
|
||||||
maxBgValue = units.equals(Constants.MGDL) ? Round.roundTo(maxBgValue, 40d) + 80 : Round.roundTo(maxBgValue, 2d) + 4;
|
maxBgValue = units.equals(Constants.MGDL) ? Round.roundTo(maxBgValue, 40d) + 80 : Round.roundTo(maxBgValue, 2d) + 4;
|
||||||
if (highLine > maxBgValue) maxBgValue = highLine;
|
if (highLine > maxBgValue) maxBgValue = highLine;
|
||||||
Integer numOfHorizLines = units.equals(Constants.MGDL) ? (int) (maxBgValue / 40 + 1) : (int) (maxBgValue / 2 + 1);
|
Integer numOfHorizLines = units.equals(Constants.MGDL) ? (int) (maxBgValue / 40 + 1) : (int) (maxBgValue / 2 + 1);
|
||||||
|
|
||||||
BgReading[] inRange = new BgReading[inRangeArray.size()];
|
DataPointWithLabelInterface[] bg = new DataPointWithLabelInterface[bgListArray.size()];
|
||||||
BgReading[] low = new BgReading[lowArray.size()];
|
bg = bgListArray.toArray(bg);
|
||||||
BgReading[] high = new BgReading[highArray.size()];
|
|
||||||
inRange = inRangeArray.toArray(inRange);
|
|
||||||
low = lowArray.toArray(low);
|
|
||||||
high = highArray.toArray(high);
|
|
||||||
|
|
||||||
boolean isTablet = MainApp.sResources.getBoolean(R.bool.isTablet);
|
if (bg.length > 0) {
|
||||||
|
bgGraph.addSeries(new PointsWithLabelGraphSeries<>(bg));
|
||||||
if (inRange.length > 0) {
|
|
||||||
bgGraph.addSeries(seriesInRage = new PointsGraphSeries<>(inRange));
|
|
||||||
seriesInRage.setShape(PointsGraphSeries.Shape.POINT);
|
|
||||||
seriesInRage.setSize(isTablet ? 8 : 5);
|
|
||||||
seriesInRage.setColor(MainApp.sResources.getColor(R.color.inrange));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (low.length > 0) {
|
|
||||||
bgGraph.addSeries(seriesLow = new PointsGraphSeries<>(low));
|
|
||||||
seriesLow.setShape(PointsGraphSeries.Shape.POINT);
|
|
||||||
seriesLow.setSize(isTablet ? 8 : 5);
|
|
||||||
seriesLow.setColor(MainApp.sResources.getColor(R.color.low));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (high.length > 0) {
|
|
||||||
bgGraph.addSeries(seriesHigh = new PointsGraphSeries<>(high));
|
|
||||||
seriesHigh.setShape(PointsGraphSeries.Shape.POINT);
|
|
||||||
seriesHigh.setSize(isTablet ? 8 : 5);
|
|
||||||
seriesHigh.setColor(MainApp.sResources.getColor(R.color.high));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showPrediction) {
|
|
||||||
DetermineBasalResultAMA amaResult = (DetermineBasalResultAMA) finalLastRun.constraintsProcessed;
|
|
||||||
List<BgReading> predArray = amaResult.getPredictions();
|
|
||||||
BgReading[] pred = new BgReading[predArray.size()];
|
|
||||||
pred = predArray.toArray(pred);
|
|
||||||
if (pred.length > 0) {
|
|
||||||
bgGraph.addSeries(predSeries = new PointsGraphSeries<BgReading>(pred));
|
|
||||||
predSeries.setShape(PointsGraphSeries.Shape.POINT);
|
|
||||||
predSeries.setSize(4);
|
|
||||||
predSeries.setColor(MainApp.sResources.getColor(R.color.prediction));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **** NOW line ****
|
// **** NOW line ****
|
||||||
|
@ -1498,40 +1455,30 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
|
|
||||||
|
|
||||||
// Treatments
|
// Treatments
|
||||||
|
List<DataPointWithLabelInterface> filteredTreatments = new ArrayList<>();
|
||||||
|
|
||||||
List<Treatment> treatments = MainApp.getConfigBuilder().getTreatmentsFromHistory();
|
List<Treatment> treatments = MainApp.getConfigBuilder().getTreatmentsFromHistory();
|
||||||
List<Treatment> filteredTreatments = new ArrayList<Treatment>();
|
|
||||||
|
|
||||||
for (int tx = 0; tx < treatments.size(); tx++) {
|
for (int tx = 0; tx < treatments.size(); tx++) {
|
||||||
Treatment t = treatments.get(tx);
|
DataPointWithLabelInterface t = treatments.get(tx);
|
||||||
if (t.date < fromTime || t.date > endTime) continue;
|
if (t.getX() < fromTime || t.getX() > endTime) continue;
|
||||||
t.setYValue(bgReadingsArray);
|
t.setY(getNearestBg((long) t.getX(), bgReadingsArray));
|
||||||
filteredTreatments.add(t);
|
filteredTreatments.add(t);
|
||||||
}
|
}
|
||||||
Treatment[] treatmentsArray = new Treatment[filteredTreatments.size()];
|
|
||||||
treatmentsArray = filteredTreatments.toArray(treatmentsArray);
|
|
||||||
if (treatmentsArray.length > 0) {
|
|
||||||
bgGraph.addSeries(seriesTreatments = new PointsWithLabelGraphSeries<Treatment>(treatmentsArray));
|
|
||||||
seriesTreatments.setShape(PointsWithLabelGraphSeries.Shape.TRIANGLE);
|
|
||||||
seriesTreatments.setSize(10);
|
|
||||||
seriesTreatments.setColor(Color.CYAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProfileSwitch
|
// ProfileSwitch
|
||||||
List<ProfileSwitch> profileSwitches = MainApp.getConfigBuilder().getProfileSwitchesFromHistory().getList();
|
List<ProfileSwitch> profileSwitches = MainApp.getConfigBuilder().getProfileSwitchesFromHistory().getList();
|
||||||
List<ProfileSwitch> filteredProfileSwitches = new ArrayList<ProfileSwitch>();
|
|
||||||
|
|
||||||
for (int tx = 0; tx < profileSwitches.size(); tx++) {
|
for (int tx = 0; tx < profileSwitches.size(); tx++) {
|
||||||
ProfileSwitch t = profileSwitches.get(tx);
|
DataPointWithLabelInterface t = profileSwitches.get(tx);
|
||||||
if (t.date < fromTime || t.date > endTime) continue;
|
if (t.getX() < fromTime || t.getX() > endTime) continue;
|
||||||
filteredProfileSwitches.add(t);
|
filteredTreatments.add(t);
|
||||||
}
|
}
|
||||||
ProfileSwitch[] profileSwitchArray = new ProfileSwitch[filteredProfileSwitches.size()];
|
|
||||||
profileSwitchArray = filteredProfileSwitches.toArray(profileSwitchArray);
|
DataPointWithLabelInterface[] treatmentsArray = new DataPointWithLabelInterface[filteredTreatments.size()];
|
||||||
if (profileSwitchArray.length > 0) {
|
treatmentsArray = filteredTreatments.toArray(treatmentsArray);
|
||||||
bgGraph.addSeries(seriesProfileSwitch = new VerticalTextsGraphSeries<ProfileSwitch>(profileSwitchArray));
|
if (treatmentsArray.length > 0) {
|
||||||
//seriesProfileSwitch.setShape(PointsWithLabelGraphSeries.Shape.TRIANGLE);
|
bgGraph.addSeries(new PointsWithLabelGraphSeries<>(treatmentsArray));
|
||||||
seriesProfileSwitch.setSize(10);
|
|
||||||
seriesProfileSwitch.setColor(Color.CYAN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set manual y bounds to have nice steps
|
// set manual y bounds to have nice steps
|
||||||
|
@ -1560,9 +1507,21 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getNearestBg(long date, List<BgReading> bgReadingsArray) {
|
||||||
|
double bg = 0;
|
||||||
|
Profile profile = MainApp.getConfigBuilder().getProfile();
|
||||||
|
for (int r = bgReadingsArray.size() - 1; r >= 0; r--) {
|
||||||
|
BgReading reading = bgReadingsArray.get(r);
|
||||||
|
if (reading.date > date) continue;
|
||||||
|
bg = Profile.fromMgdlToUnits(reading.value, profile.getUnits());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Notifications
|
//Notifications
|
||||||
public static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.NotificationsViewHolder> {
|
public static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.NotificationsViewHolder> {
|
||||||
|
|
||||||
|
|
|
@ -38,15 +38,21 @@ public interface DataPointWithLabelInterface extends DataPointInterface{
|
||||||
/**
|
/**
|
||||||
* @return the x value
|
* @return the x value
|
||||||
*/
|
*/
|
||||||
public double getX();
|
double getX();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the y value
|
* @return the y value
|
||||||
*/
|
*/
|
||||||
public double getY();
|
double getY();
|
||||||
|
void setY(double y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the label value
|
* @return the label value
|
||||||
*/
|
*/
|
||||||
public String getLabel();
|
String getLabel();
|
||||||
|
|
||||||
|
long getDuration();
|
||||||
|
PointsWithLabelGraphSeries.Shape getShape();
|
||||||
|
float getSize();
|
||||||
|
int getColor();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
import com.jjoe64.graphview.GraphView;
|
||||||
import com.jjoe64.graphview.series.BaseSeries;
|
import com.jjoe64.graphview.series.BaseSeries;
|
||||||
|
@ -42,26 +44,6 @@ import java.util.Iterator;
|
||||||
* @author jjoe64
|
* @author jjoe64
|
||||||
*/
|
*/
|
||||||
public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> extends BaseSeries<E> {
|
public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> extends BaseSeries<E> {
|
||||||
/**
|
|
||||||
* interface to implement a custom
|
|
||||||
* drawing for the data points.
|
|
||||||
*/
|
|
||||||
public static interface CustomShape {
|
|
||||||
/**
|
|
||||||
* called when drawing a single data point.
|
|
||||||
* use the x and y coordinates to render your
|
|
||||||
* drawing at this point.
|
|
||||||
*
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param paint internal paint object. this has the correct color.
|
|
||||||
* But you can use your own paint.
|
|
||||||
* @param x x-coordinate the point has to be drawn to
|
|
||||||
* @param y y-coordinate the point has to be drawn to
|
|
||||||
* @param dataPoint the related data point
|
|
||||||
*/
|
|
||||||
void draw(Canvas canvas, Paint paint, float x, float y, DataPointWithLabelInterface dataPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* choose a predefined shape to render for
|
* choose a predefined shape to render for
|
||||||
* each data point.
|
* each data point.
|
||||||
|
@ -77,45 +59,17 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
* draws a triangle
|
* draws a triangle
|
||||||
*/
|
*/
|
||||||
TRIANGLE,
|
TRIANGLE,
|
||||||
|
RECTANGLE,
|
||||||
/**
|
BOLUS,
|
||||||
* draws a rectangle
|
EXTENDEDBOLUS,
|
||||||
*/
|
PROFILE
|
||||||
RECTANGLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles for this series
|
|
||||||
*/
|
|
||||||
private final class Styles {
|
|
||||||
/**
|
|
||||||
* this is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*/
|
|
||||||
float size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the shape that will be drawn for each point.
|
|
||||||
*/
|
|
||||||
Shape shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* internal paint object
|
* internal paint object
|
||||||
*/
|
*/
|
||||||
private Paint mPaint;
|
private Paint mPaint;
|
||||||
|
|
||||||
/**
|
|
||||||
* handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
private CustomShape mCustomShape;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates the series without data
|
* creates the series without data
|
||||||
*/
|
*/
|
||||||
|
@ -138,11 +92,8 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
* set the defaults
|
* set the defaults
|
||||||
*/
|
*/
|
||||||
protected void init() {
|
protected void init() {
|
||||||
mStyles = new Styles();
|
|
||||||
mStyles.size = 20f;
|
|
||||||
mPaint = new Paint();
|
mPaint = new Paint();
|
||||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
setShape(Shape.POINT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,7 +128,6 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
double lastEndX = 0;
|
double lastEndX = 0;
|
||||||
|
|
||||||
// draw data
|
// draw data
|
||||||
mPaint.setColor(getColor());
|
|
||||||
|
|
||||||
double diffY = maxY - minY;
|
double diffY = maxY - minY;
|
||||||
double diffX = maxX - minX;
|
double diffX = maxX - minX;
|
||||||
|
@ -194,6 +144,8 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
E value = values.next();
|
E value = values.next();
|
||||||
|
|
||||||
|
mPaint.setColor(value.getColor());
|
||||||
|
|
||||||
double valY = value.getY() - minY;
|
double valY = value.getY() - minY;
|
||||||
double ratY = valY / diffY;
|
double ratY = valY / diffY;
|
||||||
double y = graphHeight * ratY;
|
double y = graphHeight * ratY;
|
||||||
|
@ -227,30 +179,57 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
|
|
||||||
// draw data point
|
// draw data point
|
||||||
if (!overdraw) {
|
if (!overdraw) {
|
||||||
if (mCustomShape != null) {
|
if (value.getShape() == Shape.POINT) {
|
||||||
mCustomShape.draw(canvas, mPaint, endX, endY, value);
|
canvas.drawCircle(endX, endY, value.getSize(), mPaint);
|
||||||
} else if (mStyles.shape == Shape.POINT) {
|
} else if (value.getShape() == Shape.RECTANGLE) {
|
||||||
canvas.drawCircle(endX, endY, mStyles.size, mPaint);
|
canvas.drawRect(endX-value.getSize(), endY-value.getSize(), endX+value.getSize(), endY+value.getSize(), mPaint);
|
||||||
} else if (mStyles.shape == Shape.RECTANGLE) {
|
} else if (value.getShape() == Shape.TRIANGLE) {
|
||||||
canvas.drawRect(endX-mStyles.size, endY-mStyles.size, endX+mStyles.size, endY+mStyles.size, mPaint);
|
|
||||||
} else if (mStyles.shape == Shape.TRIANGLE) {
|
|
||||||
Point[] points = new Point[3];
|
Point[] points = new Point[3];
|
||||||
points[0] = new Point((int)endX, (int)(endY-getSize()));
|
points[0] = new Point((int)endX, (int)(endY-value.getSize()));
|
||||||
points[1] = new Point((int)(endX+getSize()), (int)(endY+getSize()*0.67));
|
points[1] = new Point((int)(endX+value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
points[2] = new Point((int)(endX-getSize()), (int)(endY+getSize()*0.67));
|
points[2] = new Point((int)(endX-value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
drawArrows(points, canvas, mPaint);
|
drawArrows(points, canvas, mPaint);
|
||||||
|
} else if (value.getShape() == Shape.BOLUS) {
|
||||||
|
Point[] points = new Point[3];
|
||||||
|
points[0] = new Point((int)endX, (int)(endY-value.getSize()));
|
||||||
|
points[1] = new Point((int)(endX+value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
|
points[2] = new Point((int)(endX-value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
|
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||||
|
drawArrows(points, canvas, mPaint);
|
||||||
|
if (value.getLabel() != null) {
|
||||||
|
float px = endX;
|
||||||
|
float py = endY - (int) (value.getSize());
|
||||||
|
canvas.save();
|
||||||
|
canvas.rotate(-45, px, py);
|
||||||
|
mPaint.setTextSize((int) (value.getSize() * 2.5));
|
||||||
|
mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
|
||||||
|
mPaint.setFakeBoldText(true);
|
||||||
|
canvas.drawText(value.getLabel(), px + value.getSize(), py, mPaint);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
} else if (value.getShape() == Shape.EXTENDEDBOLUS) {
|
||||||
|
Point[] points = new Point[3];
|
||||||
|
points[0] = new Point((int)endX, (int)(endY-value.getSize()));
|
||||||
|
points[1] = new Point((int)(endX+value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
|
points[2] = new Point((int)(endX-value.getSize()), (int)(endY+value.getSize()*0.67));
|
||||||
|
drawArrows(points, canvas, mPaint);
|
||||||
|
} else if (value.getShape() == Shape.PROFILE) {
|
||||||
|
if (value.getLabel() != null) {
|
||||||
|
mPaint.setTextSize((int) (value.getSize() * 3));
|
||||||
|
mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds);
|
||||||
|
mPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
float px = endX + bounds.height() / 2;
|
||||||
|
float py = (float) (graphHeight * ratY + bounds.width() + 10);
|
||||||
|
canvas.save();
|
||||||
|
canvas.rotate(-90, px, py);
|
||||||
|
canvas.drawText(value.getLabel(), px, py, mPaint);
|
||||||
|
canvas.drawRect(px - 3, bounds.top + py - 3, bounds.right + px + 3, bounds.bottom + py + 3, mPaint);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// set values above point
|
// set values above point
|
||||||
if (value.getLabel() != null) {
|
|
||||||
float px = endX;
|
|
||||||
float py = endY - (int) (getSize());
|
|
||||||
canvas.save();
|
|
||||||
canvas.rotate(-45, px, py);
|
|
||||||
mPaint.setTextSize((int) (getSize() * 2.5));
|
|
||||||
mPaint.setFakeBoldText(true);
|
|
||||||
canvas.drawText(value.getLabel(), px + getSize(), py, mPaint);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
@ -276,57 +255,14 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
||||||
points[6] = point[0].x;
|
points[6] = point[0].x;
|
||||||
points[7] = point[0].y;
|
points[7] = point[0].y;
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint);
|
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint);
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
path.moveTo(point[0].x , point[0].y);
|
path.moveTo(point[0].x , point[0].y);
|
||||||
path.lineTo(point[1].x,point[1].y);
|
path.lineTo(point[1].x,point[1].y);
|
||||||
path.lineTo(point[2].x,point[2].y);
|
path.lineTo(point[2].x,point[2].y);
|
||||||
canvas.drawPath(path,paint);
|
canvas.drawPath(path,paint);
|
||||||
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @return the size of the shape
|
|
||||||
*/
|
|
||||||
public float getSize() {
|
|
||||||
return mStyles.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @param radius the size of the shape
|
|
||||||
*/
|
|
||||||
public void setSize(float radius) {
|
|
||||||
mStyles.size = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public Shape getShape() {
|
|
||||||
return mStyles.shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param s the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public void setShape(Shape s) {
|
|
||||||
mStyles.shape = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a custom handler to render your own
|
|
||||||
* drawing for each data point.
|
|
||||||
*
|
|
||||||
* @param shape handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
public void setCustomShape(CustomShape shape) {
|
|
||||||
mCustomShape = shape;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,326 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.Overview.graphExtensions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
* <p>
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
* <p>
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* <p>
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
* <p>
|
|
||||||
* Added by mike
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Added by mike
|
|
||||||
*/
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
import com.jjoe64.graphview.series.BaseSeries;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Series that plots the data as points.
|
|
||||||
* The points can be different shapes or a
|
|
||||||
* complete custom drawing.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class VerticalTextsGraphSeries<E extends DataPointWithLabelInterface> extends BaseSeries<E> {
|
|
||||||
/**
|
|
||||||
* interface to implement a custom
|
|
||||||
* drawing for the data points.
|
|
||||||
*/
|
|
||||||
public static interface CustomShape {
|
|
||||||
/**
|
|
||||||
* called when drawing a single data point.
|
|
||||||
* use the x and y coordinates to render your
|
|
||||||
* drawing at this point.
|
|
||||||
*
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param paint internal paint object. this has the correct color.
|
|
||||||
* But you can use your own paint.
|
|
||||||
* @param x x-coordinate the point has to be drawn to
|
|
||||||
* @param y y-coordinate the point has to be drawn to
|
|
||||||
* @param dataPoint the related data point
|
|
||||||
*/
|
|
||||||
void draw(Canvas canvas, Paint paint, float x, float y, DataPointWithLabelInterface dataPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* choose a predefined shape to render for
|
|
||||||
* each data point.
|
|
||||||
* You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape}
|
|
||||||
*/
|
|
||||||
public enum Shape {
|
|
||||||
/**
|
|
||||||
* draws a point / circle
|
|
||||||
*/
|
|
||||||
POINT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws a triangle
|
|
||||||
*/
|
|
||||||
TRIANGLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws a rectangle
|
|
||||||
*/
|
|
||||||
RECTANGLE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles for this series
|
|
||||||
*/
|
|
||||||
private final class Styles {
|
|
||||||
/**
|
|
||||||
* this is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*/
|
|
||||||
float size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the shape that will be drawn for each point.
|
|
||||||
*/
|
|
||||||
Shape shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* internal paint object
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
private CustomShape mCustomShape;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the series without data
|
|
||||||
*/
|
|
||||||
public VerticalTextsGraphSeries() {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the series with data
|
|
||||||
*
|
|
||||||
* @param data datapoints
|
|
||||||
*/
|
|
||||||
public VerticalTextsGraphSeries(E[] data) {
|
|
||||||
super(data);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* inits the internal objects
|
|
||||||
* set the defaults
|
|
||||||
*/
|
|
||||||
protected void init() {
|
|
||||||
mStyles = new Styles();
|
|
||||||
mStyles.size = 20f;
|
|
||||||
mPaint = new Paint();
|
|
||||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
setShape(Shape.POINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* plot the data to the viewport
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param isSecondScale whether it is the second scale
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
|
|
||||||
resetDataPoints();
|
|
||||||
|
|
||||||
// get data
|
|
||||||
double maxX = graphView.getViewport().getMaxX(false);
|
|
||||||
double minX = graphView.getViewport().getMinX(false);
|
|
||||||
|
|
||||||
double maxY;
|
|
||||||
double minY;
|
|
||||||
if (isSecondScale) {
|
|
||||||
maxY = graphView.getSecondScale().getMaxY();
|
|
||||||
minY = graphView.getSecondScale().getMinY();
|
|
||||||
} else {
|
|
||||||
maxY = graphView.getViewport().getMaxY(false);
|
|
||||||
minY = graphView.getViewport().getMinY(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<E> values = getValues(minX, maxX);
|
|
||||||
|
|
||||||
// draw background
|
|
||||||
double lastEndY = 0;
|
|
||||||
double lastEndX = 0;
|
|
||||||
|
|
||||||
// draw data
|
|
||||||
mPaint.setColor(getColor());
|
|
||||||
|
|
||||||
double diffY = maxY - minY;
|
|
||||||
double diffX = maxX - minX;
|
|
||||||
|
|
||||||
float graphHeight = graphView.getGraphContentHeight();
|
|
||||||
float graphWidth = graphView.getGraphContentWidth();
|
|
||||||
float graphLeft = graphView.getGraphContentLeft();
|
|
||||||
float graphTop = graphView.getGraphContentTop();
|
|
||||||
|
|
||||||
lastEndY = 0;
|
|
||||||
lastEndX = 0;
|
|
||||||
float firstX = 0;
|
|
||||||
int i = 0;
|
|
||||||
while (values.hasNext()) {
|
|
||||||
E value = values.next();
|
|
||||||
|
|
||||||
double valY = value.getY() - minY;
|
|
||||||
double ratY = valY / diffY;
|
|
||||||
double y = graphHeight * ratY;
|
|
||||||
|
|
||||||
double valX = value.getX() - minX;
|
|
||||||
double ratX = valX / diffX;
|
|
||||||
double x = graphWidth * ratX;
|
|
||||||
|
|
||||||
double orgX = x;
|
|
||||||
double orgY = y;
|
|
||||||
|
|
||||||
// overdraw
|
|
||||||
boolean overdraw = false;
|
|
||||||
if (x > graphWidth) { // end right
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
if (y < 0) { // end bottom
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
if (y > graphHeight) { // end top
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
/* Fix a bug that continue to show the DOT after Y axis */
|
|
||||||
if (x < 0) {
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float endX = (float) x + (graphLeft + 1);
|
|
||||||
float endY = (float) (graphTop - y) + graphHeight;
|
|
||||||
registerDataPoint(endX, endY, value);
|
|
||||||
|
|
||||||
// draw data point
|
|
||||||
if (!overdraw) {
|
|
||||||
if (value.getLabel() != null) {
|
|
||||||
mPaint.setTextSize((int) (getSize() * 3));
|
|
||||||
mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
|
||||||
Rect bounds = new Rect();
|
|
||||||
mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds);
|
|
||||||
mPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
float px = endX + bounds.height() / 2;
|
|
||||||
float py = (float) (graphHeight * ratY + bounds.width() + 10);
|
|
||||||
canvas.save();
|
|
||||||
canvas.rotate(-90, px, py);
|
|
||||||
canvas.drawText(value.getLabel(), px, py, mPaint);
|
|
||||||
canvas.drawRect(px - 3, bounds.top + py - 3, bounds.right + px + 3, bounds.bottom + py + 3, mPaint);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper to render triangle
|
|
||||||
*
|
|
||||||
* @param point array with 3 coordinates
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param paint paint object
|
|
||||||
*/
|
|
||||||
private void drawArrows(Point[] point, Canvas canvas, Paint paint) {
|
|
||||||
float[] points = new float[8];
|
|
||||||
points[0] = point[0].x;
|
|
||||||
points[1] = point[0].y;
|
|
||||||
points[2] = point[1].x;
|
|
||||||
points[3] = point[1].y;
|
|
||||||
points[4] = point[2].x;
|
|
||||||
points[5] = point[2].y;
|
|
||||||
points[6] = point[0].x;
|
|
||||||
points[7] = point[0].y;
|
|
||||||
|
|
||||||
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint);
|
|
||||||
Path path = new Path();
|
|
||||||
path.moveTo(point[0].x, point[0].y);
|
|
||||||
path.lineTo(point[1].x, point[1].y);
|
|
||||||
path.lineTo(point[2].x, point[2].y);
|
|
||||||
canvas.drawPath(path, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @return the size of the shape
|
|
||||||
*/
|
|
||||||
public float getSize() {
|
|
||||||
return mStyles.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @param radius the size of the shape
|
|
||||||
*/
|
|
||||||
public void setSize(float radius) {
|
|
||||||
mStyles.size = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public Shape getShape() {
|
|
||||||
return mStyles.shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param s the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public void setShape(Shape s) {
|
|
||||||
mStyles.shape = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a custom handler to render your own
|
|
||||||
* drawing for each data point.
|
|
||||||
*
|
|
||||||
* @param shape handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
public void setCustomShape(CustomShape shape) {
|
|
||||||
mCustomShape = shape;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue