Merge pull request #1095 from TebbeUbben/dev

Objectives styling
This commit is contained in:
Milos Kozak 2018-06-16 11:40:59 +02:00 committed by GitHub
commit 080bca6a8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 699 additions and 660 deletions

View file

@ -5,6 +5,7 @@ import android.content.IntentFilter;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.PluralsRes;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
@ -281,6 +282,10 @@ public class MainApp extends Application {
return sResources.getString(id, args); return sResources.getString(id, args);
} }
public static String gq(@PluralsRes int id, int quantity, Object... args) {
return sResources.getQuantityString(id, quantity, args);
}
public static int gc(int id) { public static int gc(int id) {
return sResources.getColor(id); return sResources.getColor(id);
} }

View file

@ -4,12 +4,10 @@ import java.util.ArrayList;
import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.interfaces.BgSourceInterface;
import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface;
import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
/** /**
* Created by mike on 19.03.2018. * Created by mike on 19.03.2018.
@ -25,7 +23,7 @@ public class ConstraintChecker implements ConstraintsInterface {
public Constraint<Boolean> isLoopInvokationAllowed() { public Constraint<Boolean> isLoopInvokationAllowed() {
return isLoopInvokationAllowed(new Constraint<>(true)); return isLoopInvocationAllowed(new Constraint<>(true));
} }
public Constraint<Boolean> isClosedLoopAllowed() { public Constraint<Boolean> isClosedLoopAllowed() {
@ -69,13 +67,13 @@ public class ConstraintChecker implements ConstraintsInterface {
} }
@Override @Override
public Constraint<Boolean> isLoopInvokationAllowed(Constraint<Boolean> value) { public Constraint<Boolean> isLoopInvocationAllowed(Constraint<Boolean> value) {
ArrayList<PluginBase> constraintsPlugins = mainApp.getSpecificPluginsListByInterface(ConstraintsInterface.class); ArrayList<PluginBase> constraintsPlugins = mainApp.getSpecificPluginsListByInterface(ConstraintsInterface.class);
for (PluginBase p : constraintsPlugins) { for (PluginBase p : constraintsPlugins) {
ConstraintsInterface constraint = (ConstraintsInterface) p; ConstraintsInterface constraint = (ConstraintsInterface) p;
if (!p.isEnabled(PluginType.CONSTRAINTS)) continue; if (!p.isEnabled(PluginType.CONSTRAINTS)) continue;
constraint.isLoopInvokationAllowed(value); constraint.isLoopInvocationAllowed(value);
} }
return value; return value;
} }

View file

@ -7,7 +7,7 @@ import info.nightscout.androidaps.data.Profile;
*/ */
public interface ConstraintsInterface { public interface ConstraintsInterface {
default Constraint<Boolean> isLoopInvokationAllowed(Constraint<Boolean> value) { default Constraint<Boolean> isLoopInvocationAllowed(Constraint<Boolean> value) {
return value; return value;
} }

View file

@ -1,12 +1,18 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives; package info.nightscout.androidaps.plugins.ConstraintsObjectives;
import android.animation.LayoutTransition;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView; import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.util.DisplayMetrics;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -16,199 +22,40 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.squareup.otto.Subscribe;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Date; import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventConfigBuilderChange;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventProfileSwitchChange;
import info.nightscout.androidaps.events.EventTreatmentChange;
import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective;
import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.FabricPrivacy;
import info.nightscout.utils.T;
public class ObjectivesFragment extends SubscriberFragment { public class ObjectivesFragment extends SubscriberFragment {
private static Logger log = LoggerFactory.getLogger(ObjectivesFragment.class); private static Logger log = LoggerFactory.getLogger(ObjectivesFragment.class);
RecyclerView recyclerView; RecyclerView recyclerView;
LinearLayoutManager llm;
CheckBox enableFake; CheckBox enableFake;
LinearLayout fake_layout;
TextView reset; TextView reset;
ObjectivesAdapter objectivesAdapter = new ObjectivesAdapter();
Handler handler = new Handler(Looper.getMainLooper());
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ObjectiveViewHolder> { private Runnable objectiveUpdater = new Runnable() {
List<ObjectivesPlugin.Objective> objectives;
RecyclerViewAdapter(List<ObjectivesPlugin.Objective> objectives) {
this.objectives = objectives;
}
@Override @Override
public ObjectiveViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { public void run() {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.objectives_item, viewGroup, false); handler.postDelayed(this, 60 * 1000);
return new ObjectiveViewHolder(v);
}
@Override
public void onBindViewHolder(ObjectiveViewHolder holder, int position) {
ObjectivesPlugin.Objective o = objectives.get(position);
ObjectivesPlugin.RequirementResult requirementsMet = ObjectivesPlugin.getPlugin().requirementsMet(position);
Context context = MainApp.instance().getApplicationContext();
holder.position.setText(String.valueOf(position + 1));
holder.objective.setText(o.objective);
holder.gate.setText(o.gate);
holder.duration.setText(MainApp.gs(R.string.objectives_minimalduration) + " " + o.durationInDays + " " + MainApp.gs(R.string.days));
holder.progress.setText(requirementsMet.comment);
holder.started.setText(o.started.toLocaleString());
holder.accomplished.setText(o.accomplished.toLocaleString());
holder.startButton.setTag(o);
holder.verifyButton.setTag(o);
holder.startButton.setOnClickListener(v -> {
ObjectivesPlugin.Objective o1 = (ObjectivesPlugin.Objective) v.getTag();
o1.started = new Date();
updateGUI(); updateGUI();
ObjectivesPlugin.saveProgress();
});
holder.verifyButton.setOnClickListener(v -> {
ObjectivesPlugin.Objective o12 = (ObjectivesPlugin.Objective) v.getTag();
if (ObjectivesPlugin.getPlugin().requirementsMet(o12.num).done || enableFake.isChecked()) {
o12.accomplished = new Date();
updateGUI();
ObjectivesPlugin.saveProgress();
}
});
long prevObjectiveAccomplishedTime = position > 0 ?
objectives.get(position - 1).accomplished.getTime() : -1;
int phase = modifyVisibility(position, prevObjectiveAccomplishedTime,
o.started.getTime(), o.durationInDays,
o.accomplished.getTime(), requirementsMet.done, enableFake.isChecked());
switch (phase) {
case 0:
// Phase 0: previous not completed
holder.startedLayout.setVisibility(View.GONE);
holder.durationLayout.setVisibility(View.GONE);
holder.progressLayout.setVisibility(View.GONE);
holder.verifyLayout.setVisibility(View.GONE);
break;
case 1:
// Phase 1: not started
holder.durationLayout.setVisibility(View.GONE);
holder.progressLayout.setVisibility(View.GONE);
holder.verifyLayout.setVisibility(View.GONE);
holder.started.setVisibility(View.GONE);
break;
case 2:
// Phase 2: started, waiting for duration and met requirements
holder.startButton.setEnabled(false);
holder.verifyLayout.setVisibility(View.GONE);
break;
case 3:
// Phase 3: started, after duration, requirements met
holder.startButton.setEnabled(false);
holder.accomplished.setVisibility(View.INVISIBLE);
break;
case 4:
// Phase 4: verified
holder.gateLayout.setVisibility(View.GONE);
holder.startedLayout.setVisibility(View.GONE);
holder.durationLayout.setVisibility(View.GONE);
holder.progressLayout.setVisibility(View.GONE);
holder.verifyButton.setVisibility(View.INVISIBLE);
break;
default:
// should not happen
}
}
@Override
public int getItemCount() {
return objectives.size();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public class ObjectiveViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView position;
TextView objective;
LinearLayout gateLayout;
TextView gate;
TextView duration;
LinearLayout durationLayout;
TextView progress;
LinearLayout progressLayout;
TextView started;
Button startButton;
LinearLayout startedLayout;
TextView accomplished;
Button verifyButton;
LinearLayout verifyLayout;
ObjectiveViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.objectives_cardview);
position = (TextView) itemView.findViewById(R.id.objectives_position);
objective = (TextView) itemView.findViewById(R.id.objectives_objective);
durationLayout = (LinearLayout) itemView.findViewById(R.id.objectives_duration_linearlayout);
duration = (TextView) itemView.findViewById(R.id.objectives_duration);
progressLayout = (LinearLayout) itemView.findViewById(R.id.objectives_progresslayout);
progress = (TextView) itemView.findViewById(R.id.objectives_progress);
gateLayout = (LinearLayout) itemView.findViewById(R.id.objectives_gate_linearlayout);
gate = (TextView) itemView.findViewById(R.id.objectives_gate);
startedLayout = (LinearLayout) itemView.findViewById(R.id.objectives_start_linearlayout);
started = (TextView) itemView.findViewById(R.id.objectives_started);
startButton = (Button) itemView.findViewById(R.id.objectives_start);
verifyLayout = (LinearLayout) itemView.findViewById(R.id.objectives_verify_linearlayout);
accomplished = (TextView) itemView.findViewById(R.id.objectives_accomplished);
verifyButton = (Button) itemView.findViewById(R.id.objectives_verify);
}
}
}
/**
* returns an int, which represents the phase the current objective is at.
*
* this is mainly used for unit-testing the conditions
*
* @param currentPosition
* @param prevObjectiveAccomplishedTime
* @param objectiveStartedTime
* @param durationInDays
* @param objectiveAccomplishedTime
* @param requirementsMet
* @return
*/
public int modifyVisibility(int currentPosition,
long prevObjectiveAccomplishedTime,
long objectiveStartedTime, int durationInDays,
long objectiveAccomplishedTime, boolean requirementsMet,
boolean enableFakeValue) {
Long now = System.currentTimeMillis();
if (currentPosition > 0 && prevObjectiveAccomplishedTime == 0) {
return 0;
} else if (objectiveStartedTime == 0) {
return 1;
} else if (objectiveStartedTime > 0 && !enableFakeValue
&& objectiveAccomplishedTime == 0
&& !(objectiveStartedTime + T.days(durationInDays).msecs() < now && requirementsMet)) {
return 2;
} else if (objectiveAccomplishedTime == 0) {
return 3;
} else {
return 4;
}
} }
};
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -216,45 +63,20 @@ public class ObjectivesFragment extends SubscriberFragment {
try { try {
View view = inflater.inflate(R.layout.objectives_fragment, container, false); View view = inflater.inflate(R.layout.objectives_fragment, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.objectives_recyclerview); recyclerView = view.findViewById(R.id.objectives_recyclerview);
recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
llm = new LinearLayoutManager(view.getContext()); recyclerView.setAdapter(objectivesAdapter);
recyclerView.setLayoutManager(llm); enableFake = view.findViewById(R.id.objectives_fake);
enableFake = (CheckBox) view.findViewById(R.id.objectives_fake); reset = view.findViewById(R.id.objectives_reset);
fake_layout = (LinearLayout) view.findViewById(R.id.objectives_fake_layout); enableFake.setOnClickListener(v -> updateGUI());
reset = (TextView) view.findViewById(R.id.objectives_reset); reset.setOnClickListener(v -> {
enableFake.setOnClickListener(new View.OnClickListener() { ObjectivesPlugin.getPlugin().reset();
public void onClick(View v) {
updateGUI();
}
});
reset.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ObjectivesPlugin.getPlugin().initializeData();
ObjectivesPlugin.saveProgress(); ObjectivesPlugin.saveProgress();
updateGUI(); recyclerView.getAdapter().notifyDataSetChanged();
} scrollToCurrentObjective();
}); });
scrollToCurrentObjective();
// Add correct translations to array after app is initialized startUpdateTimer();
ObjectivesPlugin.objectives.get(0).objective = MainApp.gs(R.string.objectives_0_objective);
ObjectivesPlugin.objectives.get(1).objective = MainApp.gs(R.string.objectives_1_objective);
ObjectivesPlugin.objectives.get(2).objective = MainApp.gs(R.string.objectives_2_objective);
ObjectivesPlugin.objectives.get(3).objective = MainApp.gs(R.string.objectives_3_objective);
ObjectivesPlugin.objectives.get(4).objective = MainApp.gs(R.string.objectives_4_objective);
ObjectivesPlugin.objectives.get(5).objective = MainApp.gs(R.string.objectives_5_objective);
ObjectivesPlugin.objectives.get(6).objective = MainApp.gs(R.string.objectives_6_objective);
ObjectivesPlugin.objectives.get(7).objective = MainApp.gs(R.string.objectives_7_objective);
ObjectivesPlugin.objectives.get(0).gate = MainApp.gs(R.string.objectives_0_gate);
ObjectivesPlugin.objectives.get(1).gate = MainApp.gs(R.string.objectives_1_gate);
ObjectivesPlugin.objectives.get(2).gate = MainApp.gs(R.string.objectives_2_gate);
ObjectivesPlugin.objectives.get(3).gate = MainApp.gs(R.string.objectives_3_gate);
ObjectivesPlugin.objectives.get(4).gate = MainApp.gs(R.string.objectives_4_gate);
ObjectivesPlugin.objectives.get(5).gate = MainApp.gs(R.string.objectives_5_gate);
ObjectivesPlugin.objectives.get(7).gate = MainApp.gs(R.string.objectives_7_gate);
updateGUI();
return view; return view;
} catch (Exception e) { } catch (Exception e) {
FabricPrivacy.logException(e); FabricPrivacy.logException(e);
@ -263,13 +85,142 @@ public class ObjectivesFragment extends SubscriberFragment {
return null; return null;
} }
@Override
public synchronized void onDestroyView() {
super.onDestroyView();
handler.removeCallbacks(objectiveUpdater);
}
private void startUpdateTimer() {
handler.removeCallbacks(objectiveUpdater);
for (Objective objective : ObjectivesPlugin.getObjectives()) {
if (objective.isStarted() && !objective.isAccomplished()) {
long timeTillNextMinute = (System.currentTimeMillis() - objective.getStartedOn().getTime()) % (60 * 1000);
handler.postDelayed(objectiveUpdater, timeTillNextMinute);
break;
}
}
}
private void scrollToCurrentObjective() {
for (int i = 0; i < ObjectivesPlugin.getObjectives().size(); i++) {
Objective objective = ObjectivesPlugin.getObjectives().get(i);
if (!objective.isStarted() || !objective.isAccomplished()) {
RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
@Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
@Override
protected int calculateTimeForScrolling(int dx) {
return super.calculateTimeForScrolling(dx) * 4;
}
};
smoothScroller.setTargetPosition(i);
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
break;
}
}
}
private class ObjectivesAdapter extends RecyclerView.Adapter<ObjectivesAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.objectives_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Objective objective = ObjectivesPlugin.getObjectives().get(position);
holder.title.setText(MainApp.gs(R.string.nth_objective, position + 1));
if (objective.getObjective() != 0) {
holder.objective.setVisibility(View.VISIBLE);
holder.objective.setText(MainApp.gs(objective.getObjective()));
} else holder.objective.setVisibility(View.GONE);
if (objective.getGate() != 0) {
holder.gate.setVisibility(View.VISIBLE);
holder.gate.setText(MainApp.gs(objective.getGate()));
} else holder.gate.setVisibility(View.GONE);
if (!objective.isStarted()) {
holder.gate.setTextColor(0xFFFFFFFF);
holder.verify.setVisibility(View.GONE);
holder.progress.setVisibility(View.GONE);
if (position == 0 || ObjectivesPlugin.getObjectives().get(position - 1).isAccomplished())
holder.start.setVisibility(View.VISIBLE);
else holder.start.setVisibility(View.GONE);
} else if (objective.isAccomplished()) {
holder.gate.setTextColor(0xFF4CAF50);
holder.verify.setVisibility(View.GONE);
holder.progress.setVisibility(View.GONE);
holder.start.setVisibility(View.GONE);
} else if (objective.isStarted()) {
holder.gate.setTextColor(0xFFFFFFFF);
holder.verify.setVisibility(View.VISIBLE);
holder.verify.setEnabled(objective.isCompleted() || enableFake.isChecked());
holder.start.setVisibility(View.GONE);
holder.progress.setVisibility(View.VISIBLE);
holder.progress.removeAllViews();
for (Objective.Task task : objective.getTasks()) {
if (task.shouldBeIgnored()) continue;
TextView textView = new TextView(holder.progress.getContext());
textView.setTextColor(0xFFFFFFFF);
String basicHTML = "%2$s: <font color=\"%1$s\"><b>%3$s</b></font>";
String formattedHTML = String.format(basicHTML, task.isCompleted() ? "#4CAF50" : "#FF9800", MainApp.gs(task.getTask()), task.getProgress());
textView.setText(Html.fromHtml(formattedHTML));
holder.progress.addView(textView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
}
}
holder.verify.setOnClickListener((view) -> {
objective.setAccomplishedOn(new Date());
notifyDataSetChanged();
scrollToCurrentObjective();
startUpdateTimer();
});
holder.start.setOnClickListener((view) -> {
objective.setStartedOn(new Date());
notifyDataSetChanged();
scrollToCurrentObjective();
startUpdateTimer();
});
}
@Override
public int getItemCount() {
return ObjectivesPlugin.getObjectives().size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public CardView cardView;
public TextView title;
public TextView objective;
public TextView gate;
public LinearLayout progress;
public Button verify;
public Button start;
public ViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView;
title = itemView.findViewById(R.id.objective_title);
objective = itemView.findViewById(R.id.objective_objective);
gate = itemView.findViewById(R.id.objective_gate);
progress = itemView.findViewById(R.id.objective_progress);
verify = itemView.findViewById(R.id.objective_verify);
start = itemView.findViewById(R.id.objective_start);
}
}
}
@Override @Override
public void updateGUI() { public void updateGUI() {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity != null) if (activity != null)
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
RecyclerViewAdapter adapter = new RecyclerViewAdapter(ObjectivesPlugin.objectives); objectivesAdapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
}); });
} }

View file

@ -1,20 +1,14 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives; package info.nightscout.androidaps.plugins.ConstraintsObjectives;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface;
import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginBase;
@ -23,12 +17,15 @@ import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpInterface; 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.ConstraintsObjectives.events.EventObjectivesSaved; import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved;
import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective;
import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective1;
import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective2;
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective3;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective4;
import info.nightscout.utils.DateUtil; import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective5;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective6;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective7;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective8;
import info.nightscout.utils.SP; import info.nightscout.utils.SP;
/** /**
@ -39,6 +36,11 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
private static ObjectivesPlugin objectivesPlugin; private static ObjectivesPlugin objectivesPlugin;
public static List<Objective> objectives = new ArrayList<>();
public static boolean bgIsAvailableInNS = false;
public static boolean pumpStatusIsAvailableInNS = false;
public static Integer manualEnacts = 0;
public static ObjectivesPlugin getPlugin() { public static ObjectivesPlugin getPlugin() {
if (objectivesPlugin == null) { if (objectivesPlugin == null) {
objectivesPlugin = new ObjectivesPlugin(); objectivesPlugin = new ObjectivesPlugin();
@ -46,8 +48,6 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
return objectivesPlugin; return objectivesPlugin;
} }
public static List<Objective> objectives;
private ObjectivesPlugin() { private ObjectivesPlugin() {
super(new PluginDescription() super(new PluginDescription()
.mainType(PluginType.CONSTRAINTS) .mainType(PluginType.CONSTRAINTS)
@ -58,7 +58,7 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
.shortName(R.string.objectives_shortname) .shortName(R.string.objectives_shortname)
.description(R.string.description_objectives) .description(R.string.description_objectives)
); );
initializeData(); setupObjectives();
loadProgress(); loadProgress();
} }
@ -68,187 +68,38 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
return pump == null || pump.getPumpDescription().isTempBasalCapable; return pump == null || pump.getPumpDescription().isTempBasalCapable;
} }
public class Objective { private void setupObjectives() {
Integer num; objectives.add(new Objective1());
String objective; objectives.add(new Objective2());
String gate; objectives.add(new Objective3());
Date started; objectives.add(new Objective4());
Integer durationInDays; objectives.add(new Objective5());
Date accomplished; objectives.add(new Objective6());
objectives.add(new Objective7());
Objective(Integer num, String objective, String gate, Date started, Integer durationInDays, Date accomplished) { objectives.add(new Objective8());
this.num = num;
this.objective = objective;
this.gate = gate;
this.started = started;
this.durationInDays = durationInDays;
this.accomplished = accomplished;
} }
public void setStarted(Date started) { public void reset() {
this.started = started; for (Objective objective : objectives) {
objective.setStartedOn(null);
objective.setAccomplishedOn(null);
} }
public boolean isStarted() {
return started.getTime() > 0;
}
boolean isFinished() {
return accomplished.getTime() != 0;
}
}
// Objective 0
public static boolean bgIsAvailableInNS = false;
public static boolean pumpStatusIsAvailableInNS = false;
// Objective 1
public static Integer manualEnacts = 0;
private static final Integer manualEnactsNeeded = 20;
class RequirementResult {
boolean done = false;
String comment = "";
RequirementResult(boolean done, String comment) {
this.done = done;
this.comment = comment;
}
}
private String yesOrNo(boolean yes) {
if (yes) return "";
else return "---";
}
RequirementResult requirementsMet(Integer objNum) {
switch (objNum) {
case 0:
boolean isVirtualPump = VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP);
boolean vpUploadEnabled = SP.getBoolean("virtualpump_uploadstatus", false);
boolean vpUploadNeeded = !isVirtualPump || vpUploadEnabled;
boolean hasBGData = DatabaseHelper.lastBg() != null;
boolean apsEnabled = false;
APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS();
if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS))
apsEnabled = true;
boolean profileSwitchExists = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null;
return new RequirementResult(hasBGData && bgIsAvailableInNS && pumpStatusIsAvailableInNS && NSClientPlugin.getPlugin().hasWritePermission() && LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && apsEnabled && vpUploadNeeded && profileSwitchExists,
MainApp.gs(R.string.objectives_bgavailableinns) + ": " + yesOrNo(bgIsAvailableInNS)
+ "\n" + MainApp.gs(R.string.nsclienthaswritepermission) + ": " + yesOrNo(NSClientPlugin.getPlugin().hasWritePermission())
+ (isVirtualPump ? "\n" + MainApp.gs(R.string.virtualpump_uploadstatus_title) + ": " + yesOrNo(vpUploadEnabled) : "")
+ "\n" + MainApp.gs(R.string.objectives_pumpstatusavailableinns) + ": " + yesOrNo(pumpStatusIsAvailableInNS)
+ "\n" + MainApp.gs(R.string.hasbgdata) + ": " + yesOrNo(hasBGData)
+ "\n" + MainApp.gs(R.string.loopenabled) + ": " + yesOrNo(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))
+ "\n" + MainApp.gs(R.string.apsselected) + ": " + yesOrNo(apsEnabled)
+ "\n" + MainApp.gs(R.string.activate_profile) + ": " + yesOrNo(profileSwitchExists)
);
case 1:
return new RequirementResult(manualEnacts >= manualEnactsNeeded,
MainApp.gs(R.string.objectives_manualenacts) + ": " + manualEnacts + "/" + manualEnactsNeeded);
case 2:
return new RequirementResult(true, "");
case 3:
Constraint<Boolean> closedLoopEnabled = new Constraint<>(true);
SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled);
return new RequirementResult(closedLoopEnabled.value(), MainApp.gs(R.string.closedmodeenabled) + ": " + yesOrNo(closedLoopEnabled.value()));
case 4:
double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value();
boolean maxIobSet = maxIOB > 0;
return new RequirementResult(maxIobSet, MainApp.gs(R.string.maxiobset) + ": " + yesOrNo(maxIobSet));
default:
return new RequirementResult(true, "");
}
}
void initializeData() {
bgIsAvailableInNS = false; bgIsAvailableInNS = false;
pumpStatusIsAvailableInNS = false; pumpStatusIsAvailableInNS = false;
manualEnacts = 0; manualEnacts = 0;
saveProgress();
objectives = new ArrayList<>();
objectives.add(new Objective(0,
MainApp.gs(R.string.objectives_0_objective),
MainApp.gs(R.string.objectives_0_gate),
new Date(0),
0, // 0 day
new Date(0)));
objectives.add(new Objective(1,
MainApp.gs(R.string.objectives_1_objective),
MainApp.gs(R.string.objectives_1_gate),
new Date(0),
7, // 7 days
new Date(0)));
objectives.add(new Objective(2,
MainApp.gs(R.string.objectives_2_objective),
MainApp.gs(R.string.objectives_2_gate),
new Date(0),
0, // 0 days
new Date(0)));
objectives.add(new Objective(3,
MainApp.gs(R.string.objectives_3_objective),
MainApp.gs(R.string.objectives_3_gate),
new Date(0),
5, // 5 days
new Date(0)));
objectives.add(new Objective(4,
MainApp.gs(R.string.objectives_4_objective),
MainApp.gs(R.string.objectives_4_gate),
new Date(0),
1,
new Date(0)));
objectives.add(new Objective(5,
MainApp.gs(R.string.objectives_5_objective),
MainApp.gs(R.string.objectives_5_gate),
new Date(0),
7,
new Date(0)));
objectives.add(new Objective(6,
MainApp.gs(R.string.objectives_6_objective),
"",
new Date(0),
28,
new Date(0)));
objectives.add(new Objective(7,
MainApp.gs(R.string.objectives_7_objective),
"",
new Date(0),
28,
new Date(0)));
} }
public static void saveProgress() { public static void saveProgress() {
if (objectives != null) { SP.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); SP.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS);
SharedPreferences.Editor editor = settings.edit(); SP.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts));
for (int num = 0; num < objectives.size(); num++) {
Objective o = objectives.get(num);
editor.putString("Objectives" + num + "started", Long.toString(o.started.getTime()));
editor.putString("Objectives" + num + "accomplished", Long.toString(o.accomplished.getTime()));
}
editor.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS);
editor.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS);
editor.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts));
editor.apply();
if (Config.logPrefsChange) if (Config.logPrefsChange)
log.debug("Objectives stored"); log.debug("Objectives stored");
MainApp.bus().post(new EventObjectivesSaved()); MainApp.bus().post(new EventObjectivesSaved());
} }
}
private void loadProgress() { private void loadProgress() {
for (int num = 0; num < objectives.size(); num++) {
Objective o = objectives.get(num);
try {
o.started = new Date(SP.getLong("Objectives" + num + "started", 0L));
o.accomplished = new Date(SP.getLong("Objectives" + num + "accomplished", 0L));
} catch (Exception e) {
log.error("Unhandled exception", e);
}
}
bgIsAvailableInNS = SP.getBoolean("Objectives" + "bgIsAvailableInNS", false); bgIsAvailableInNS = SP.getBoolean("Objectives" + "bgIsAvailableInNS", false);
pumpStatusIsAvailableInNS = SP.getBoolean("Objectives" + "pumpStatusIsAvailableInNS", false); pumpStatusIsAvailableInNS = SP.getBoolean("Objectives" + "pumpStatusIsAvailableInNS", false);
try { try {
@ -260,11 +111,15 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
log.debug("Objectives loaded"); log.debug("Objectives loaded");
} }
public static List<Objective> getObjectives() {
return objectives;
}
/** /**
* Constraints interface * Constraints interface
**/ **/
@Override @Override
public Constraint<Boolean> isLoopInvokationAllowed(Constraint<Boolean> value) { public Constraint<Boolean> isLoopInvocationAllowed(Constraint<Boolean> value) {
if (!objectives.get(0).isStarted()) if (!objectives.get(0).isStarted())
value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 1), this); value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 1), this);
return value; return value;
@ -300,7 +155,7 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface
@Override @Override
public Constraint<Double> applyMaxIOBConstraints(Constraint<Double> maxIob) { public Constraint<Double> applyMaxIOBConstraints(Constraint<Double> maxIob) {
if (objectives.get(3).isStarted() && !objectives.get(3).isFinished()) if (objectives.get(3).isStarted() && !objectives.get(3).isAccomplished())
maxIob.set(0d, String.format(MainApp.gs(R.string.objectivenotfinished), 4), this); maxIob.set(0d, String.format(MainApp.gs(R.string.objectivenotfinished), 4), this);
return maxIob; return maxIob;
} }

View file

@ -0,0 +1,140 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import android.support.annotation.StringRes;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.utils.SP;
public abstract class Objective {
private int number;
@StringRes
private int objective;
@StringRes
private int gate;
private Date startedOn;
private Date accomplishedOn;
private List<Task> tasks = new ArrayList<>();
public Objective(int number, @StringRes int objective, @StringRes int gate) {
this.number = number;
this.objective = objective;
this.gate = gate;
startedOn = new Date(SP.getLong("Objectives" + number + "started", 0L));
if (startedOn.getTime() == 0L) startedOn = null;
accomplishedOn = new Date(SP.getLong("Objectives" + number + "accomplished", 0L));
if (accomplishedOn.getTime() == 0L) accomplishedOn = null;
setupTasks(tasks);
for (Task task : tasks) task.objective = this;
}
public boolean isCompleted() {
for (Task task : tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted())
return false;
}
return true;
}
public boolean isAccomplished() {
return accomplishedOn != null;
}
public boolean isStarted() {
return startedOn != null;
}
public Date getStartedOn() {
return startedOn;
}
public int getObjective() {
return objective;
}
public int getGate() {
return gate;
}
public void setStartedOn(Date startedOn) {
this.startedOn = startedOn;
SP.putLong("Objectives" + number + "started", startedOn == null ? 0 : startedOn.getTime());
}
public void setAccomplishedOn(Date accomplishedOn) {
this.accomplishedOn = accomplishedOn;
SP.putLong("Objectives" + number + "accomplished", accomplishedOn == null ? 0 : accomplishedOn.getTime());
}
protected void setupTasks(List<Task> tasks) {
}
public List<Task> getTasks() {
return tasks;
}
public abstract class Task {
@StringRes
private int task;
private Objective objective;
public Task(@StringRes int task) {
this.task = task;
}
public int getTask() {
return task;
}
protected Objective getObjective() {
return objective;
}
public abstract boolean isCompleted();
public String getProgress() {
return MainApp.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet);
}
public boolean shouldBeIgnored() {
return false;
}
}
public class MinimumDurationTask extends Task {
private long minimumDuration;
public MinimumDurationTask(long minimumDuration) {
super(R.string.time_elapsed);
this.minimumDuration = minimumDuration;
}
@Override
public boolean isCompleted() {
return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn().getTime() >= minimumDuration;
}
@Override
public String getProgress() {
return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn().getTime())
+ " / " + getDurationText(minimumDuration);
}
private String getDurationText(long duration) {
int days = (int) Math.floor((double) duration / (24D * 60D * 60D * 1000D));
int hours = (int) Math.floor((double) duration / (60D * 60D * 1000D));
int minutes = (int) Math.floor((double) duration / (60D * 1000D));
if (days > 0) return MainApp.gq(R.plurals.objective_days, days, days);
else if (hours > 0) return MainApp.gq(R.plurals.objective_hours, hours, hours);
else return MainApp.gq(R.plurals.objective_minutes, minutes, minutes);
}
}
}

View file

@ -0,0 +1,84 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.Loop.LoopPlugin;
import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin;
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
import info.nightscout.utils.DateUtil;
import info.nightscout.utils.SP;
public class Objective1 extends Objective {
public Objective1() {
super(0, R.string.objectives_0_objective, R.string.objectives_0_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_bgavailableinns) {
@Override
public boolean isCompleted() {
return ObjectivesPlugin.bgIsAvailableInNS;
}
});
tasks.add(new Task(R.string.nsclienthaswritepermission) {
@Override
public boolean isCompleted() {
return NSClientPlugin.getPlugin().hasWritePermission();
}
});
tasks.add(new Task(R.string.virtualpump_uploadstatus_title) {
@Override
public boolean isCompleted() {
return SP.getBoolean("virtualpump_uploadstatus", false);
}
@Override
public boolean shouldBeIgnored() {
return !VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP);
}
});
tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) {
@Override
public boolean isCompleted() {
return ObjectivesPlugin.pumpStatusIsAvailableInNS;
}
});
tasks.add(new Task(R.string.hasbgdata) {
@Override
public boolean isCompleted() {
return DatabaseHelper.lastBg() != null;
}
});
tasks.add(new Task(R.string.loopenabled) {
@Override
public boolean isCompleted() {
return LoopPlugin.getPlugin().isEnabled(PluginType.LOOP);
}
});
tasks.add(new Task(R.string.apsselected) {
@Override
public boolean isCompleted() {
APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS();
if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS))
return true;
return false;
}
});
tasks.add(new Task(R.string.activate_profile) {
@Override
public boolean isCompleted() {
return TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null;
}
});
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin;
public class Objective2 extends Objective {
public static final int MANUAL_ENACTS_NEEDED = 20;
public Objective2() {
super(1, R.string.objectives_1_objective, R.string.objectives_1_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L));
tasks.add(new Task(R.string.objectives_manualenacts) {
@Override
public boolean isCompleted() {
return ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED;
}
@Override
public String getProgress() {
if (ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED) return MainApp.gs(R.string.completed_well_done);
else return ObjectivesPlugin.manualEnacts + " / " + MANUAL_ENACTS_NEEDED;
}
});
}
}

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import info.nightscout.androidaps.R;
public class Objective3 extends Objective {
public Objective3() {
super(2, R.string.objectives_2_objective, R.string.objectives_2_gate);
}
}

View file

@ -0,0 +1,27 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin;
public class Objective4 extends Objective {
public Objective4() {
super(3, R.string.objectives_3_objective, R.string.objectives_4_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(5L * 24L * 60L * 60L * 1000L));
tasks.add(new Task(R.string.closedmodeenabled) {
@Override
public boolean isCompleted() {
Constraint<Boolean> closedLoopEnabled = new Constraint<>(true);
SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled);
return closedLoopEnabled.value();
}
});
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Objective5 extends Objective {
public Objective5() {
super(4, R.string.objectives_4_objective, R.string.objectives_4_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(24L * 60L * 60L * 1000L));
tasks.add(new Task(R.string.maxiobset) {
@Override
public boolean isCompleted() {
double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value();
return maxIOB > 0;
}
});
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.R;
public class Objective6 extends Objective {
public Objective6() {
super(5, R.string.objectives_5_objective, R.string.objectives_5_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L));
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.R;
public class Objective7 extends Objective {
public Objective7() {
super(6, R.string.objectives_6_objective, 0);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L));
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives;
import java.util.List;
import info.nightscout.androidaps.R;
public class Objective8 extends Objective {
public Objective8() {
super(7, R.string.objectives_7_objective, 0);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L));
}
}

View file

@ -49,7 +49,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
* Constraints interface * Constraints interface
**/ **/
@Override @Override
public Constraint<Boolean> isLoopInvokationAllowed(Constraint<Boolean> value) { public Constraint<Boolean> isLoopInvocationAllowed(Constraint<Boolean> value) {
if (!ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable) if (!ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable)
value.set(false, MainApp.gs(R.string.pumpisnottempbasalcapable), this); value.set(false, MainApp.gs(R.string.pumpisnottempbasalcapable), this);
return value; return value;

View file

@ -1357,7 +1357,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
private boolean validBasalRateProfileSelectedOnPump = true; private boolean validBasalRateProfileSelectedOnPump = true;
@Override @Override
public Constraint<Boolean> isLoopInvokationAllowed(Constraint<Boolean> value) { public Constraint<Boolean> isLoopInvocationAllowed(Constraint<Boolean> value) {
if (!validBasalRateProfileSelectedOnPump) if (!validBasalRateProfileSelectedOnPump)
value.set(false, MainApp.gs(R.string.novalidbasalrate), this); value.set(false, MainApp.gs(R.string.novalidbasalrate), this);
return value; return value;

View file

@ -1,46 +1,41 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="2dp" android:orientation="vertical"
tools:context=".plugins.ConstraintsObjectives.ObjectivesFragment"> tools:context=".plugins.ConstraintsObjectives.ObjectivesFragment">
<LinearLayout <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/objectives_fake_layout" android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:visibility="gone"> android:visibility="gone">
<CheckBox <CheckBox
android:layout_width="wrap_content" android:id="@+id/objectives_fake"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Enable fake time and progress" android:layout_marginRight="16dp"
android:id="@+id/objectives_fake" /> android:layout_weight="1"
android:text="Enable fake time and progress" />
<TextView <Button
android:id="@+id/objectives_reset"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Reset" android:text="Reset" />
android:id="@+id/objectives_reset"
android:textColor="#fef900"
android:gravity="right"
android:layout_weight="0.5"
android:layout_marginRight="10dp" />
</LinearLayout> </LinearLayout>
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@+id/objectives_recyclerview" android:id="@+id/objectives_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="0dp"
android:layout_weight="1"
</android.support.v7.widget.RecyclerView> android:clipToPadding="false"
</LinearLayout> android:paddingBottom="16dp" />
</LinearLayout>
</FrameLayout>

View file

@ -1,162 +1,70 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/objectives_cardview" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/objective_cardview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
card_view:cardBackgroundColor="@color/cardColorBackground" android:layout_marginLeft="16dp"
card_view:cardCornerRadius="6dp" android:layout_marginRight="16dp"
card_view:cardUseCompatPadding="true" android:layout_marginTop="16dp"
card_view:contentPadding="6dp"> app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="2dp"
app:cardUseCompatPadding="true"
app:contentPadding="16dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/objectives_position" android:id="@+id/objective_title"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/circle"
android:gravity="center"
android:shadowRadius="10.0"
android:text="1"
android:textColor="@color/cardObjectiveText"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="true"
android:orientation="horizontal">
<TextView
android:id="@+id/objectives_objective_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top" android:fontFamily="sans-serif-medium"
android:paddingRight="10dp" android:textColor="#FFFFFF"
android:text="@string/objectives_objective_label_string" android:textSize="20sp"
android:textAppearance="?android:attr/textAppearanceLarge" tools:text="1. Title" />
android:textColor="@color/cardObjectiveText"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/objectives_objective" android:id="@+id/objective_objective"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_marginTop="8dp"
android:maxLines="4" android:textColor="#FFFFFF"
android:minLines="1" tools:text="Objective" />
android:text=""
android:textColor="@color/cardObjectiveText"
android:textStyle="bold" />
<TextView
</LinearLayout> android:id="@+id/objective_gate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="#FFFFFF"
android:textStyle="bold"
tools:text="Gate" />
<LinearLayout <LinearLayout
android:id="@+id/objective_progress"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="@+id/objectives_gate_linearlayout">
<TextView
android:id="@+id/objectives_gate_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top" android:layout_marginTop="8dp"
android:paddingRight="10dp" android:orientation="vertical" />
android:text="@string/objectives_gate_label_string"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/objectives_gate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:maxLines="4"
android:minLines="1"
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="@+id/objectives_start_linearlayout">
<Button <Button
android:id="@+id/objectives_start" android:id="@+id/objective_verify"
style="?android:attr/buttonStyle" style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/objectives_button_start" />
<TextView
android:id="@+id/objectives_started"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/objectives_duration_linearlayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/objectives_duration" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/objectives_progresslayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/objectives_progress" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="@+id/objectives_verify_linearlayout">
<Button
android:id="@+id/objectives_verify"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/objectives_button_verify" /> android:text="@string/objectives_button_verify" />
<TextView <Button
android:id="@+id/objectives_accomplished" android:id="@+id/objective_start"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:text="@string/objectives_button_start" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </android.support.v7.widget.CardView>

View file

@ -1156,6 +1156,23 @@
<string name="open_navigation">Open navigation</string> <string name="open_navigation">Open navigation</string>
<string name="close_navigation">Close navigation</string> <string name="close_navigation">Close navigation</string>
<string name="nav_plugin_preferences">Plugin preferences</string> <string name="nav_plugin_preferences">Plugin preferences</string>
<string name="completed_well_done">Completed, well done!</string>
<string name="not_completed_yet">Not completed yet</string>
<string name="time_elapsed">Time elapsed</string>
<string name="nth_objective">%1$d. Objective</string>
<string name="poctech">Poctech</string> <string name="poctech">Poctech</string>
<string name="description_source_poctech">Receive BG values from Poctech app</string> <string name="description_source_poctech">Receive BG values from Poctech app</string>
<plurals name="objective_days">
<item quantity="one">%d day</item>
<item quantity="other">%d days</item>
</plurals>
<plurals name="objective_hours">
<item quantity="one">%d hour</item>
<item quantity="other">%d hours</item>
</plurals>
<plurals name="objective_minutes">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
</plurals>
</resources> </resources>

View file

@ -27,6 +27,4 @@
<style name="ButtonSmallFontStyle"> <style name="ButtonSmallFontStyle">
<item name="android:textSize">10sp</item> <item name="android:textSize">10sp</item>
</style> </style>
<!-- Preferences -->
</resources> </resources>

View file

@ -76,7 +76,7 @@ public class ConstraintsCheckerTest {
@Test @Test
public void isClosedLoopAllowedTest() throws Exception { public void isClosedLoopAllowedTest() throws Exception {
when(SP.getString("aps_mode", "open")).thenReturn("closed"); when(SP.getString("aps_mode", "open")).thenReturn("closed");
objectivesPlugin.objectives.get(3).setStarted(new Date(0)); objectivesPlugin.objectives.get(3).setStartedOn(null);
Constraint<Boolean> c = constraintChecker.isClosedLoopAllowed(); Constraint<Boolean> c = constraintChecker.isClosedLoopAllowed();
Assert.assertEquals(true, c.getReasonList().size() == 2); // Safety & Objectives Assert.assertEquals(true, c.getReasonList().size() == 2); // Safety & Objectives
@ -92,7 +92,7 @@ public class ConstraintsCheckerTest {
@Test @Test
public void isAutosensModeEnabledTest() throws Exception { public void isAutosensModeEnabledTest() throws Exception {
objectivesPlugin.objectives.get(5).setStarted(new Date(0)); objectivesPlugin.objectives.get(5).setStartedOn(null);
when(SP.getBoolean(R.string.key_openapsama_useautosens, false)).thenReturn(false); when(SP.getBoolean(R.string.key_openapsama_useautosens, false)).thenReturn(false);
Constraint<Boolean> c = constraintChecker.isAutosensModeEnabled(); Constraint<Boolean> c = constraintChecker.isAutosensModeEnabled();
@ -103,7 +103,7 @@ public class ConstraintsCheckerTest {
@Test @Test
public void isAMAModeEnabledTest() throws Exception { public void isAMAModeEnabledTest() throws Exception {
objectivesPlugin.objectives.get(6).setStarted(new Date(0)); objectivesPlugin.objectives.get(6).setStartedOn(null);
Constraint<Boolean> c = constraintChecker.isAMAModeEnabled(); Constraint<Boolean> c = constraintChecker.isAMAModeEnabled();
Assert.assertEquals(true, c.getReasonList().size() == 1); // Objectives Assert.assertEquals(true, c.getReasonList().size() == 1); // Objectives
@ -123,7 +123,7 @@ public class ConstraintsCheckerTest {
@Test @Test
public void isSMBModeEnabledTest() throws Exception { public void isSMBModeEnabledTest() throws Exception {
objectivesPlugin.objectives.get(7).setStarted(new Date(0)); objectivesPlugin.objectives.get(7).setStartedOn(null);
when(SP.getBoolean(R.string.key_use_smb, false)).thenReturn(false); when(SP.getBoolean(R.string.key_use_smb, false)).thenReturn(false);
when(MainApp.getConstraintChecker().isClosedLoopAllowed()).thenReturn(new Constraint<>(true)); when(MainApp.getConstraintChecker().isClosedLoopAllowed()).thenReturn(new Constraint<>(true));

View file

@ -1,56 +0,0 @@
package info.nightscout.androidaps.plugins.ConstraintsObjectives;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ObjectivesFragmentTest {
@Test
public void testModifyVisibility() {
ObjectivesFragment fragment = new ObjectivesFragment();
int currentPosition = 1;
long prevObjectiveAccomplishedTime = 0;
long objectiveStartedTime = 0;
int durationInDays = 0;
long objectiveAccomplishedTime = 0;
boolean requirementsMet = false;
boolean enableFakeValue = false;
// previous objective is not accomplished yet
assertEquals(0, fragment.modifyVisibility(currentPosition, prevObjectiveAccomplishedTime,
objectiveStartedTime, durationInDays, objectiveAccomplishedTime, requirementsMet, enableFakeValue));
// not started yet
prevObjectiveAccomplishedTime = 4711;
assertEquals(1, fragment.modifyVisibility(currentPosition, prevObjectiveAccomplishedTime,
objectiveStartedTime, durationInDays, objectiveAccomplishedTime, requirementsMet, enableFakeValue));
// started
// time calculation is true, requirements met is false
objectiveStartedTime = Long.MAX_VALUE;
durationInDays = 0;
assertEquals(2, fragment.modifyVisibility(currentPosition, prevObjectiveAccomplishedTime,
objectiveStartedTime, durationInDays, objectiveAccomplishedTime, requirementsMet, enableFakeValue));
// started
// time calculation is true, requirements met is true
objectiveStartedTime = 10;
durationInDays = 0;
requirementsMet = true;
assertEquals(3, fragment.modifyVisibility(currentPosition, prevObjectiveAccomplishedTime,
objectiveStartedTime, durationInDays, objectiveAccomplishedTime, requirementsMet, enableFakeValue));
// finished
objectiveStartedTime = Long.MAX_VALUE;
durationInDays = 0;
requirementsMet = true;
objectiveAccomplishedTime = Long.MAX_VALUE;
assertEquals(4, fragment.modifyVisibility(currentPosition, prevObjectiveAccomplishedTime,
objectiveStartedTime, durationInDays, objectiveAccomplishedTime, requirementsMet, enableFakeValue));
}
}

View file

@ -13,7 +13,6 @@ import java.util.Date;
import info.AAPSMocker; import info.AAPSMocker;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.utils.SP; import info.nightscout.utils.SP;
@ -28,19 +27,19 @@ public class ObjectivesPluginTest {
ObjectivesPlugin objectivesPlugin; ObjectivesPlugin objectivesPlugin;
@Test @Test
public void notStartedObjectivesShouldLimitLoopInvokation() throws Exception { public void notStartedObjectivesShouldLimitLoopInvocation() throws Exception {
objectivesPlugin.objectives.get(0).setStarted(new Date(0)); objectivesPlugin.objectives.get(0).setStartedOn(null);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = objectivesPlugin.isLoopInvokationAllowed(c); c = objectivesPlugin.isLoopInvocationAllowed(c);
Assert.assertEquals("Objectives: Objective 1 not started", c.getReasons()); Assert.assertEquals("Objectives: Objective 1 not started", c.getReasons());
Assert.assertEquals(Boolean.FALSE, c.value()); Assert.assertEquals(Boolean.FALSE, c.value());
objectivesPlugin.objectives.get(0).setStarted(new Date()); objectivesPlugin.objectives.get(0).setStartedOn(new Date());
} }
@Test @Test
public void notStartedObjective4ShouldLimitClosedLoop() throws Exception { public void notStartedObjective4ShouldLimitClosedLoop() throws Exception {
objectivesPlugin.objectives.get(3).setStarted(new Date(0)); objectivesPlugin.objectives.get(3).setStartedOn(null);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = objectivesPlugin.isClosedLoopAllowed(c); c = objectivesPlugin.isClosedLoopAllowed(c);
@ -50,7 +49,7 @@ public class ObjectivesPluginTest {
@Test @Test
public void notStartedObjective6ShouldLimitAutosensMode() throws Exception { public void notStartedObjective6ShouldLimitAutosensMode() throws Exception {
objectivesPlugin.objectives.get(5).setStarted(new Date(0)); objectivesPlugin.objectives.get(5).setStartedOn(null);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = objectivesPlugin.isAutosensModeEnabled(c); c = objectivesPlugin.isAutosensModeEnabled(c);
@ -60,7 +59,7 @@ public class ObjectivesPluginTest {
@Test @Test
public void notStartedObjective7ShouldLimitAMAMode() throws Exception { public void notStartedObjective7ShouldLimitAMAMode() throws Exception {
objectivesPlugin.objectives.get(6).setStarted(new Date(0)); objectivesPlugin.objectives.get(6).setStartedOn(null);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = objectivesPlugin.isAMAModeEnabled(c); c = objectivesPlugin.isAMAModeEnabled(c);
@ -70,7 +69,7 @@ public class ObjectivesPluginTest {
@Test @Test
public void notStartedObjective8ShouldLimitSMBMode() throws Exception { public void notStartedObjective8ShouldLimitSMBMode() throws Exception {
objectivesPlugin.objectives.get(7).setStarted(new Date(0)); objectivesPlugin.objectives.get(7).setStartedOn(null);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = objectivesPlugin.isSMBModeEnabled(c); c = objectivesPlugin.isSMBModeEnabled(c);

View file

@ -19,7 +19,6 @@ import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin;
import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAPlugin; import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAPlugin;
import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.Source.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.Source.SourceGlimpPlugin;
import info.nightscout.utils.SP; import info.nightscout.utils.SP;
@ -42,7 +41,7 @@ public class SafetyPluginTest {
pump.getPumpDescription().isTempBasalCapable = false; pump.getPumpDescription().isTempBasalCapable = false;
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = safetyPlugin.isLoopInvokationAllowed(c); c = safetyPlugin.isLoopInvocationAllowed(c);
Assert.assertEquals("Safety: Pump is not temp basal capable", c.getReasons()); Assert.assertEquals("Safety: Pump is not temp basal capable", c.getReasons());
Assert.assertEquals(Boolean.FALSE, c.value()); Assert.assertEquals(Boolean.FALSE, c.value());
} }

View file

@ -32,7 +32,7 @@ public class ComboPluginTest {
comboPlugin.setValidBasalRateProfileSelectedOnPump(false); comboPlugin.setValidBasalRateProfileSelectedOnPump(false);
Constraint<Boolean> c = new Constraint<>(true); Constraint<Boolean> c = new Constraint<>(true);
c = comboPlugin.isLoopInvokationAllowed(c); c = comboPlugin.isLoopInvocationAllowed(c);
Assert.assertEquals("Combo: No valid basal rate read from pump", c.getReasons()); Assert.assertEquals("Combo: No valid basal rate read from pump", c.getReasons());
Assert.assertEquals(Boolean.FALSE, c.value()); Assert.assertEquals(Boolean.FALSE, c.value());
comboPlugin.setPluginEnabled(PluginType.PUMP, false); comboPlugin.setPluginEnabled(PluginType.PUMP, false);