Merge branch 'develop' into 'mainline'

# Conflicts:
#   app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java
This commit is contained in:
Savek-CC 2017-08-06 22:35:07 +00:00
commit d520643d45
8 changed files with 447 additions and 5 deletions

View file

@ -0,0 +1,23 @@
package de.jotomo.ruffyscripter;
/**
* Created by adrian on 26/07/17.
*
* Contains the capabilities of the current pump model.
*/
public class PumpCapabilities {
public long maxTempPercent;
public PumpCapabilities maxTempPercent(long maxTempPercent) {
this.maxTempPercent = maxTempPercent;
return this;
}
@Override
public String toString() {
return "PumpCapabilities{" +
"maxTempPercent=" + maxTempPercent +
'}';
}
}

View file

@ -19,6 +19,8 @@ public class PumpState {
*/ */
public String errorMsg; public String errorMsg;
public boolean suspended; public boolean suspended;
public boolean lowBattery;
public int insulinState;
public PumpState tbrActive(boolean tbrActive) { public PumpState tbrActive(boolean tbrActive) {
this.tbrActive = tbrActive; this.tbrActive = tbrActive;
@ -59,6 +61,8 @@ public class PumpState {
", tbrRemainingDuration=" + tbrRemainingDuration + ", tbrRemainingDuration=" + tbrRemainingDuration +
", errorMsg=" + errorMsg + ", errorMsg=" + errorMsg +
", suspended=" + suspended + ", suspended=" + suspended +
", lowBattery=" + lowBattery +
", insulinState=" + insulinState +
", timestamp=" + timestamp + ", timestamp=" + timestamp +
'}'; '}';
} }

View file

@ -478,6 +478,8 @@ public class RuffyScripter {
state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute(); state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute();
state.tbrRate = ((double) menu.getAttribute(MenuAttribute.BASAL_RATE)); state.tbrRate = ((double) menu.getAttribute(MenuAttribute.BASAL_RATE));
} }
state.lowBattery = ((boolean) menu.getAttribute(MenuAttribute.LOW_BATTERY));
state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE));
// TODO v2, read current base basal rate, which is shown center when no TBR is active. // TODO v2, read current base basal rate, which is shown center when no TBR is active.
// Check if that holds true when an extended bolus is running. // Check if that holds true when an extended bolus is running.
// Add a field to PumpStatus, rather than renaming/overloading tbrRate to mean // Add a field to PumpStatus, rather than renaming/overloading tbrRate to mean
@ -486,6 +488,8 @@ public class RuffyScripter {
state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE); state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE);
} else if (menuType == MenuType.STOP) { } else if (menuType == MenuType.STOP) {
state.suspended = true; state.suspended = true;
state.lowBattery = ((boolean) menu.getAttribute(MenuAttribute.LOW_BATTERY));
state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE));
} else { } else {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (MenuAttribute menuAttribute : menu.attributes()) { for (MenuAttribute menuAttribute : menu.attributes()) {

View file

@ -3,6 +3,7 @@ package de.jotomo.ruffyscripter.commands;
import java.util.Date; import java.util.Date;
import de.jotomo.ruffyscripter.History; import de.jotomo.ruffyscripter.History;
import de.jotomo.ruffyscripter.PumpCapabilities;
import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.PumpState;
public class CommandResult { public class CommandResult {
@ -13,6 +14,7 @@ public class CommandResult {
public String message; public String message;
public PumpState state; public PumpState state;
public History history; public History history;
public PumpCapabilities capabilities;
public CommandResult() { public CommandResult() {
} }
@ -52,6 +54,11 @@ public class CommandResult {
return this; return this;
} }
public CommandResult capabilities(PumpCapabilities capabilities) {
this.capabilities = capabilities;
return this;
}
@Override @Override
public String toString() { public String toString() {
return "CommandResult{" + return "CommandResult{" +

View file

@ -0,0 +1,165 @@
package de.jotomo.ruffyscripter.commands;
import android.os.SystemClock;
import com.j256.ormlite.stmt.query.In;
import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute;
import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import de.jotomo.ruffyscripter.PumpCapabilities;
import de.jotomo.ruffyscripter.PumpState;
import de.jotomo.ruffyscripter.RuffyScripter;
public class DetermineCapabilitiesCommand implements Command {
private static final Logger log = LoggerFactory.getLogger(DetermineCapabilitiesCommand.class);
public static final int UP_STEPS = 75;
public static final int RETRIES = 5;
@Override
public List<String> validateArguments() {
return Collections.emptyList();
}
@Override
public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) {
try {
//read main menu 100% or TBR? Read remaining duration.
long durationBefore = readDisplayedTbrDurationMainMenu(scripter);
long percentageBefore = readDisplayedTbrPercentageMainMenu(scripter);
enterTbrMenu(scripter);
long maxTbrPercentage = findMaxTbrPercentage(scripter);
// TODO v2 this can probably be removed by now
SystemClock.sleep(750);
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU,
"Pump did not return to MAIN_MEU after finding max tbr. " +
"Check pump manually, the TBR might be wrong.");
//TODO: check if TBR is still the same or duration was less than 5 minutes
long durationAfter = readDisplayedTbrDurationMainMenu(scripter);
long percentageAfter = readDisplayedTbrPercentageMainMenu(scripter);
if(Math.abs(durationBefore-durationAfter) > 5){
throw new CommandException().message("Duration jump during DetermineCapabilities");
}
if(percentageAfter != percentageBefore){
if(durationBefore<5 && percentageAfter == 100){
log.debug("(percentageBefore != percentageAfter) - ignoring as tbr is now 100% and had a very short duration left");
}
throw new CommandException().message("TBR changed while determining maxTBR.");
}
//TODO return Result
return new CommandResult().success(true).enacted(false).message("Capablities: {maxTbrPercentage = " + maxTbrPercentage + ", success=" + "success }").capabilities((new PumpCapabilities()).maxTempPercent(maxTbrPercentage));
} catch (CommandException e) {
return e.toCommandResult();
}
}
private void enterTbrMenu(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
scripter.navigateToMenu(MenuType.TBR_MENU);
scripter.verifyMenuIsDisplayed(MenuType.TBR_MENU);
scripter.pressCheckKey();
scripter.waitForMenuUpdate();
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
}
private long findMaxTbrPercentage(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
long activeTempBasal = readDisplayedTbrPercentage(scripter);
// pretend to increase the TBR to more than 500%
log.debug("Pressing up " + UP_STEPS + " times to get to maximum");
for (int i = 0; i < UP_STEPS; i++) {
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
scripter.pressUpKey();
SystemClock.sleep(200);
log.debug("Push #" + (i + 1));
}
//read the displayed maximum value
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
long maximumTempBasal = readDisplayedTbrPercentage(scripter);
//reset the TBR in a controlled manner
long percentageChange = maximumTempBasal - activeTempBasal;
long percentageSteps = percentageChange / 10;
int retries= 0;
while (percentageSteps > 0 && retries < RETRIES) {
log.debug("Pressing down " + percentageSteps + " times to get to previous value. Retry " + retries);
for (int i = 0; i < percentageSteps; i++) {
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
scripter.pressDownKey();
SystemClock.sleep(200);
log.debug("Push #" + (i + 1));
}
//do the rest if button-presses failed.
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
long currentPercentage = readDisplayedTbrPercentage(scripter);
percentageChange = currentPercentage - activeTempBasal;
percentageSteps = percentageChange / 10;
retries++;
}
//exit menu
scripter.pressCheckKey();
scripter.waitForMenuToBeLeft(MenuType.TBR_SET);
return maximumTempBasal;
}
private long readDisplayedTbrPercentage(RuffyScripter scripter) {
SystemClock.sleep(1000);
// TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded
Object percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE);
// this as a bit hacky, the display value is blinking, so we might catch that, so
// keep trying till we get the Double we want
while (!(percentageObj instanceof Double)) {
scripter.waitForMenuUpdate();
percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE);
}
return ((Double) percentageObj).longValue();
}
private int readDisplayedTbrDurationMainMenu(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
if(scripter.currentMenu.attributes().contains(MenuAttribute.RUNTIME)){
// TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded
Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME);
MenuTime duration = (MenuTime) durationObj;
return duration.getHour() * 60 + duration.getMinute();
} else {
return 0;
}
}
private int readDisplayedTbrPercentageMainMenu(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
if(scripter.currentMenu.attributes().contains(MenuAttribute.TBR)){
return (int)((Double) scripter.currentMenu.getAttribute(MenuAttribute.TBR)).doubleValue();
} else {
return 100;
}
}
@Override
public String toString() {
return "DetermineCapabilitiesCommand{}";
}
}

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.PumpCombo;
import android.app.Activity; import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -33,6 +34,8 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
} }
private Button refresh; private Button refresh;
private TextView updateCapabilities;
private TextView statusText; private TextView statusText;
private TextView tbrPercentageText; private TextView tbrPercentageText;
@ -44,12 +47,19 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
private TextView lastCmdTimeText; private TextView lastCmdTimeText;
private TextView lastCmdResultText; private TextView lastCmdResultText;
private TextView tbrCapabilityText;
private TextView pumpstateBatteryText;
private TextView insulinstateText;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.combopump_fragment, container, false); View view = inflater.inflate(R.layout.combopump_fragment, container, false);
refresh = (Button) view.findViewById(R.id.combo_refresh); refresh = (Button) view.findViewById(R.id.combo_refresh);
updateCapabilities = (TextView) view.findViewById(R.id.combo_update_capabilities);
statusText = (TextView) view.findViewById(R.id.combo_status); statusText = (TextView) view.findViewById(R.id.combo_status);
tbrPercentageText = (TextView) view.findViewById(R.id.combo_tbr_percentage); tbrPercentageText = (TextView) view.findViewById(R.id.combo_tbr_percentage);
@ -60,8 +70,12 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
lastCmdText = (TextView) view.findViewById(R.id.combo_last_command); lastCmdText = (TextView) view.findViewById(R.id.combo_last_command);
lastCmdTimeText = (TextView) view.findViewById(R.id.combo_last_command_time); lastCmdTimeText = (TextView) view.findViewById(R.id.combo_last_command_time);
lastCmdResultText = (TextView) view.findViewById(R.id.combo_last_command_result); lastCmdResultText = (TextView) view.findViewById(R.id.combo_last_command_result);
tbrCapabilityText = (TextView) view.findViewById(R.id.combo_tbr_capability);
pumpstateBatteryText = (TextView) view.findViewById(R.id.combo_pumpstate_battery);
insulinstateText = (TextView) view.findViewById(R.id.combo_insulinstate);
refresh.setOnClickListener(this); refresh.setOnClickListener(this);
updateCapabilities.setOnClickListener(this);
updateGUI(); updateGUI();
return view; return view;
@ -97,6 +111,32 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
}); });
thread.start(); thread.start();
break; break;
case R.id.combo_update_capabilities:
(new Thread(new Runnable() {
@Override
public void run() {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCapabilities.setText("{fa-bluetooth spin}");
}
});
getPlugin().updateCapabilities();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCapabilities.setText("{fa-bluetooth-b}");
}
});
}
})).start();
break;
} }
} }
@ -121,6 +161,24 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
tbrRateText.setText(""); tbrRateText.setText("");
} }
pumpErrorText.setText(ps.errorMsg != null ? ps.errorMsg : ""); pumpErrorText.setText(ps.errorMsg != null ? ps.errorMsg : "");
if(ps.lowBattery){
pumpstateBatteryText.setText("{fa-battery-empty}");
pumpstateBatteryText.setTextColor(Color.RED);
} else {
pumpstateBatteryText.setText("{fa-battery-three-quarters}");
pumpstateBatteryText.setTextColor(Color.WHITE);
}
switch (ps.insulinState){
case 0: insulinstateText.setText("ok");
insulinstateText.setTextColor(Color.WHITE);
break;
case 1: insulinstateText.setText("low");
insulinstateText.setTextColor(Color.YELLOW);
break;
case 2: insulinstateText.setText("empty");
insulinstateText.setTextColor(Color.RED);
break;
}
} }
Command lastCmd = getPlugin().lastCmd; Command lastCmd = getPlugin().lastCmd;
@ -139,6 +197,7 @@ public class ComboFragment extends Fragment implements View.OnClickListener {
lastCmdResultText.setText(""); lastCmdResultText.setText("");
} }
} }
tbrCapabilityText.setText(getPlugin().getPumpDescription().maxTempPercent + "%");
} }
}); });
} }

View file

@ -5,11 +5,13 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Color; import android.graphics.Color;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.IBinder; import android.os.IBinder;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
@ -28,6 +30,7 @@ import de.jotomo.ruffyscripter.commands.BolusCommand;
import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand;
import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.Command;
import de.jotomo.ruffyscripter.commands.CommandResult; import de.jotomo.ruffyscripter.commands.CommandResult;
import de.jotomo.ruffyscripter.commands.DetermineCapabilitiesCommand;
import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand;
import de.jotomo.ruffyscripter.commands.SetTbrCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand;
import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.PumpState;
@ -46,11 +49,14 @@ import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI;
import info.nightscout.utils.DateUtil; import info.nightscout.utils.DateUtil;
import info.nightscout.utils.SP;
import info.nightscout.utils.ToastUtils;
/** /**
* Created by mike on 05.08.2016. * Created by mike on 05.08.2016.
*/ */
public class ComboPlugin implements PluginBase, PumpInterface { public class ComboPlugin implements PluginBase, PumpInterface {
public static final String COMBO_MAX_TEMP_PERCENT_SP = "combo_maxTempPercent";
private static Logger log = LoggerFactory.getLogger(ComboPlugin.class); private static Logger log = LoggerFactory.getLogger(ComboPlugin.class);
private boolean fragmentEnabled = false; private boolean fragmentEnabled = false;
@ -99,7 +105,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
pumpDescription.isTempBasalCapable = true; pumpDescription.isTempBasalCapable = true;
pumpDescription.tempBasalStyle = PumpDescription.PERCENT; pumpDescription.tempBasalStyle = PumpDescription.PERCENT;
pumpDescription.maxTempPercent = 500; pumpDescription.maxTempPercent = SP.getInt(COMBO_MAX_TEMP_PERCENT_SP, 500);
pumpDescription.tempPercentStep = 10; pumpDescription.tempPercentStep = 10;
pumpDescription.tempDurationStep = 15; pumpDescription.tempDurationStep = 15;
@ -319,9 +325,10 @@ public class ComboPlugin implements PluginBase, PumpInterface {
return; return;
} }
if (!reason.toLowerCase().contains("user") boolean notAUserRequest = !reason.toLowerCase().contains("user");
&& lastCmdTime.getTime() > 0 boolean wasRunAtLeastOnce = lastCmdTime.getTime() > 0;
&& System.currentTimeMillis() > lastCmdTime.getTime() + 60 * 1000) { boolean ranWithinTheLastMinute = System.currentTimeMillis() < lastCmdTime.getTime() + 60 * 1000;
if (notAUserRequest && wasRunAtLeastOnce && ranWithinTheLastMinute) {
log.debug("Not fetching state from pump, since we did already within the last 60 seconds"); log.debug("Not fetching state from pump, since we did already within the last 60 seconds");
} else { } else {
runCommand(new ReadPumpStateCommand()); runCommand(new ReadPumpStateCommand());
@ -623,6 +630,34 @@ public class ComboPlugin implements PluginBase, PumpInterface {
public void onStatusEvent(final EventAppExit e) { public void onStatusEvent(final EventAppExit e) {
unbindRuffyService(); unbindRuffyService();
} }
public void updateCapabilities() {
// if Android is sluggish this might get called before ruffy is bound
if (ruffyScripter == null) {
log.warn("Rejecting call to RefreshDataFromPump: ruffy service not bound (yet)");
ToastUtils.showToastInUiThread(MainApp.instance(), "Ruffy not initialized.");
return;
}
if (isBusy()){
ToastUtils.showToastInUiThread(MainApp.instance(), "Pump busy!");
return;
}
CommandResult result = runCommand(new DetermineCapabilitiesCommand());
if (result.success){
pumpDescription.maxTempPercent = (int) result.capabilities.maxTempPercent;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance());
SharedPreferences.Editor editor = preferences.edit();
editor.putInt(COMBO_MAX_TEMP_PERCENT_SP, pumpDescription.maxTempPercent);
editor.commit();
MainApp.bus().post(new EventComboPumpUpdateGUI());
} else {
ToastUtils.showToastInUiThread(MainApp.instance(), "No success.");
}
}
} }

View file

@ -20,7 +20,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Refresh" /> android:text="Refresh" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -145,6 +144,7 @@
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<View <View
@ -382,6 +382,151 @@
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:background="@color/listdelimiter" /> android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:paddingRight="5dp"
android:text="TBR Capability"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingEnd="2dp"
android:paddingStart="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/combo_tbr_capability"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/combo_update_capabilities"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="20sp"
android:gravity="center_horizontal"
android:text="{fa-bluetooth-b}" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:gravity="end"
android:paddingRight="5dp"
android:text="Battery"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingEnd="2dp"
android:paddingStart="2dp"
android:text=":"
android:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/combo_pumpstate_battery"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="20sp"
android:paddingLeft="5dp"
android:gravity="start"
android:text=" - " />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:gravity="end"
android:paddingRight="5dp"
android:text="Reservoir"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingEnd="2dp"
android:paddingStart="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/combo_insulinstate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/listdelimiter" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>