diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 6c7850f6f2..c4e58a90fa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -201,7 +201,8 @@ object AutomationPlugin : PluginBase(PluginDescription() ActionStartTempTarget(), ActionStopTempTarget(), ActionNotification(), - ActionProfileSwitchPercent() + ActionProfileSwitchPercent(), + ActionProfileSwitch() ) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java new file mode 100644 index 0000000000..50fbb786db --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java @@ -0,0 +1,118 @@ +package info.nightscout.androidaps.plugins.general.automation.actions; + +import android.widget.LinearLayout; + +import com.google.common.base.Optional; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.interfaces.ProfileInterface; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.automation.elements.InputProfileName; +import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement; +import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder; +import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.utils.JsonHelper; + +public class ActionProfileSwitch extends Action { + private static Logger log = LoggerFactory.getLogger(L.AUTOMATION); + public InputProfileName inputProfileName = new InputProfileName(ProfileFunctions.getInstance().getProfileName()); + String profileName = ""; + + public ActionProfileSwitch() { + // Prevent action if active profile is already active + // but we don't have a trigger IS_NOT_EQUAL + // so check is in the doRun() + } + + @Override + public int friendlyName() { + return R.string.profilename; + } + + @Override + public String shortDescription() { + String returned = MainApp.gs(R.string.changengetoprofilename, inputProfileName.getValue()); + return returned; + } + + @Override + public void doAction(Callback callback) { + + String activeProfileName = ProfileFunctions.getInstance().getProfileName(); + //Check for uninitialized profileName + if ( profileName.equals("")){ profileName = activeProfileName; } + + if (profileName.equals(activeProfileName)) { + // Profile is already switched + return; + } + ProfileInterface activeProfile = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); + if (activeProfile == null) return; + ProfileStore profileStore = activeProfile.getProfile(); + if (profileStore == null) return; + if(profileStore.getSpecificProfile(profileName) == null) { + if (L.isEnabled(L.AUTOMATION)) + log.error("Selected profile does not exist! - "+ profileName); + return; + } + + ProfileFunctions.doProfileSwitch(profileStore, profileName, 0, 100, 0); + if (callback != null) + callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run(); + } + + @Override + public void generateDialog(LinearLayout root) { + new LayoutBuilder() + .add(new LabelWithElement(MainApp.gs(R.string.profilename), "", inputProfileName)) + .build(root); + } + + @Override + public boolean hasDialog() { + return true; + } + + @Override + public String toJSON() { + JSONObject o = new JSONObject(); + try { + o.put("type", ActionProfileSwitch.class.getName()); + JSONObject data = new JSONObject(); + data.put("profileToSwitchTo", inputProfileName.getValue()); + o.put("data", data); + } catch (JSONException e) { + e.printStackTrace(); + } + return o.toString(); + } + + @Override + public Action fromJSON(String data) { + + try { + JSONObject d = new JSONObject(data); + profileName = JsonHelper.safeGetString(d, "profileToSwitchTo"); + inputProfileName.setValue(profileName); + } catch (JSONException e) { + e.printStackTrace(); + } + return this; + } + + @Override + public Optional icon() { + return Optional.of(R.drawable.icon_actions_profileswitch); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileName.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileName.java new file mode 100644 index 0000000000..5431f47ede --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileName.java @@ -0,0 +1,99 @@ +package info.nightscout.androidaps.plugins.general.automation.elements; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.Spinner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; + +public class InputProfileName extends Element { + private static Logger log = LoggerFactory.getLogger(L.AUTOMATION); + ProfileStore profileStore; + + String profileName; + + public InputProfileName(String name) { + super(); + this.profileName = name; + } + + public InputProfileName(InputProfileName another) { + super(); + profileName = another.getValue(); + } + + + @Override + public void addToLayout(LinearLayout root) { + ArrayList profileList = new ArrayList<>(); + profileStore = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getProfile(); + if (profileStore == null) { + log.error("ProfileStore is empty"); + } else { + profileList = profileStore.getProfileList(); + } + ArrayAdapter adapter = new ArrayAdapter<>(root.getContext(), + R.layout.spinner_centered, profileList); + Spinner spinner = new Spinner(root.getContext()); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ); + spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4)); + spinner.setLayoutParams(spinnerParams); + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + setValue(listNames().get(position).toString()); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + spinner.setSelection(0); + LinearLayout l = new LinearLayout(root.getContext()); + l.setOrientation(LinearLayout.VERTICAL); + l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + l.addView(spinner); + root.addView(l); + } + + public InputProfileName setValue(String name) { + this.profileName = name; + return this; + } + + public String getValue() { + return profileName; + } + + public ArrayList listNames(){ + ArrayList profileList = new ArrayList<>(); + // profile + profileStore = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getProfile(); + if (profileStore == null) { + log.error("ProfileStore is empty"); + } else { + profileList = profileStore.getProfileList(); + } + return profileList; + } + + +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5f3d3bdc57..25dc61ff45 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1572,6 +1572,8 @@ Get Temporary Basal Set Temporary Basal Set Bolus + Change profile to + Change profile to %1$s %2$+.2fU]]> diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchTest.java new file mode 100644 index 0000000000..e62f573c2b --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchTest.java @@ -0,0 +1,120 @@ +package info.nightscout.androidaps.plugins.general.automation.actions; + +import com.google.common.base.Optional; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.AAPSMocker; +import info.SPMocker; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.ProfileSwitch; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.utils.SP; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, SP.class, TreatmentsPlugin.class, ProfileFunctions.class, ConfigBuilderPlugin.class, NSProfilePlugin.class}) +public class ActionProfileSwitchTest { + TreatmentsPlugin treatmentsPlugin; + ProfileSwitch profileAdded; + private ActionProfileSwitch actionProfileSwitch; + private String stringJson = "{\"data\":{\"profileToSwitchTo\":\"Test\"},\"type\":\"info.nightscout.androidaps.plugins.general.automation.actions.ActionProfileSwitch\"}"; + + @Before + public void setUp() { + AAPSMocker.mockMainApp(); + SPMocker.prepareMock(); + SP.putString("profile", AAPSMocker.getValidProfileStore().getData().toString()); + AAPSMocker.mockConfigBuilder(); + AAPSMocker.mockApplicationContext(); + AAPSMocker.mockStrings(); + AAPSMocker.mockTreatmentService(); + AAPSMocker.mockBus(); + AAPSMocker.mockDatabaseHelper(); + AAPSMocker.mockProfileFunctions(); + + treatmentsPlugin = AAPSMocker.mockTreatmentPlugin(); + NSProfilePlugin profilePlugin = NSProfilePlugin.getPlugin(); + when(ConfigBuilderPlugin.getPlugin().getActiveProfileInterface()) + .thenReturn(profilePlugin); + + Mockito.doAnswer(invocation -> { + profileAdded = invocation.getArgument(0); + return null; + }).when(treatmentsPlugin).addToHistoryProfileSwitch(any(ProfileSwitch.class)); + } + + @Test + public void friendlyName() { + actionProfileSwitch = new ActionProfileSwitch(); + Assert.assertEquals(R.string.profilename, actionProfileSwitch.friendlyName()); + } + + @Test + public void shortDescriptionTest() { + actionProfileSwitch = new ActionProfileSwitch(); + actionProfileSwitch.inputProfileName.setValue("Test"); + when(MainApp.gs(anyInt(), anyString())).thenReturn("Change profile to Test"); + Assert.assertEquals("Change profile to Test", actionProfileSwitch.shortDescription()); + } + + @Test + public void doAction() { + PowerMockito.when(ProfileFunctions.getInstance().getProfileName()).thenReturn("Test"); + + actionProfileSwitch = new ActionProfileSwitch(); + actionProfileSwitch.inputProfileName.setValue("someOtherProfile"); + actionProfileSwitch.profileName = "someProfile"; + + actionProfileSwitch.doAction(new Callback() { + @Override + public void run() { + Assert.assertTrue(result.success); + } + }); + + } + + @Test + public void hasDialogTest() { + actionProfileSwitch = new ActionProfileSwitch(); + Assert.assertTrue(actionProfileSwitch.hasDialog()); + } + + @Test + public void toJSONTest() { + actionProfileSwitch = new ActionProfileSwitch(); + actionProfileSwitch.inputProfileName.setValue("Test"); + Assert.assertEquals(stringJson, actionProfileSwitch.toJSON()); + } + + @Test + public void fromJSONTest() { + String data = "{\"profileToSwitchTo\":\"Test\"}"; + actionProfileSwitch = new ActionProfileSwitch(); + actionProfileSwitch.fromJSON(data); + Assert.assertEquals("Test", actionProfileSwitch.profileName); + } + + @Test + public void iconTest() { + actionProfileSwitch = new ActionProfileSwitch(); + Assert.assertEquals(Optional.of(R.drawable.icon_actions_profileswitch), actionProfileSwitch.icon()); + } +} \ No newline at end of file diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileNameTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileNameTest.java new file mode 100644 index 0000000000..7f257aa883 --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputProfileNameTest.java @@ -0,0 +1,29 @@ +package info.nightscout.androidaps.plugins.general.automation.elements; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.SP; + +import static org.junit.Assert.*; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, SP.class, TreatmentsPlugin.class, ProfileFunctions.class}) +public class InputProfileNameTest { + String profileName = "Test"; + InputProfileName inputProfileName = new InputProfileName(profileName); + + @Test + public void getSetValue() { + inputProfileName = new InputProfileName("Test"); + Assert.assertEquals("Test", inputProfileName.getValue()); + inputProfileName.setValue("Test2"); + Assert.assertEquals("Test2", inputProfileName.getValue()); + } +} \ No newline at end of file