Skip to content

Commit

Permalink
Manual dose (#115)
Browse files Browse the repository at this point in the history
* Also ask for time when adding manual dose

* Propose last entered custom medicine entry

* Fix tests
  • Loading branch information
Futsch1 committed May 6, 2024
1 parent ece7e8a commit 6ca7f45
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 98 deletions.
Expand Up @@ -126,7 +126,7 @@ public void basicUITest() {
0),
1),
isDisplayed()));
onView(isRoot()).perform(AndroidTestHelper.waitFor(500));
onView(isRoot()).perform(AndroidTestHelper.waitFor(1000));
materialButton4.perform(click());

ViewInteraction checkableImageButton = onView(
Expand Down
Expand Up @@ -16,6 +16,7 @@
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.futsch1.medtimer.AndroidTestHelper.childAtPosition;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -49,8 +50,8 @@ public class TestDataAndDeleteAndManualDoseTest {
public void testDataAndDeleteAndManualDoseTest() {
ViewInteraction overflowMenuButton = onView(
allOf(withContentDescription("More options"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.action_bar),
1),
0),
Expand All @@ -59,8 +60,8 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction materialTextView = onView(
allOf(withId(androidx.recyclerview.R.id.title), withText("Settings"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.content),
1),
0),
Expand All @@ -69,24 +70,24 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction recyclerView = onView(
allOf(withId(com.takisoft.preferencex.ringtone.R.id.recycler_view),
AndroidTestHelper.childAtPosition(
childAtPosition(
withId(android.R.id.list_container),
0)));
recyclerView.perform(actionOnItemAtPosition(7, click()));

DataInteraction appCompatCheckedTextView = onData(anything())
.inAdapterView(allOf(withId(androidx.appcompat.R.id.select_dialog_listview),
AndroidTestHelper.childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.contentPanel),
0)))
.atPosition(1);
appCompatCheckedTextView.perform(click());

ViewInteraction appCompatImageButton = onView(
allOf(withContentDescription("Navigate up"),
AndroidTestHelper.childAtPosition(
childAtPosition(
allOf(withId(androidx.appcompat.R.id.action_bar),
AndroidTestHelper.childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.action_bar_container),
0)),
2),
Expand All @@ -95,8 +96,8 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction overflowMenuButton2 = onView(
allOf(withContentDescription("More options"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.action_bar),
1),
0),
Expand All @@ -105,8 +106,8 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction materialTextView2 = onView(
allOf(withId(androidx.recyclerview.R.id.title), withText("Generate test data"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.content),
1),
0),
Expand All @@ -118,15 +119,15 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction recyclerView2 = onView(
allOf(withId(R.id.medicineList),
AndroidTestHelper.childAtPosition(
childAtPosition(
withClassName(is("android.widget.FrameLayout")),
0)));
recyclerView2.perform(actionOnItemAtPosition(0, longClick()));

ViewInteraction materialTextView3 = onView(
allOf(withId(android.R.id.title), withText("Delete"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(android.R.id.content),
0),
0),
Expand All @@ -135,31 +136,31 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction materialButton = onView(
allOf(withId(android.R.id.button1), withText("Yes"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withClassName(is("android.widget.ScrollView")),
0),
3)));
materialButton.perform(scrollTo(), click());

ViewInteraction recyclerView3 = onView(
allOf(withId(R.id.medicineList),
AndroidTestHelper.childAtPosition(
childAtPosition(
withClassName(is("android.widget.FrameLayout")),
0)));
recyclerView3.perform(actionOnItemAtPosition(2, click()));

ViewInteraction recyclerView4 = onView(
allOf(withId(R.id.reminderList),
AndroidTestHelper.childAtPosition(
childAtPosition(
withClassName(is("android.widget.LinearLayout")),
2)));
recyclerView4.perform(actionOnItemAtPosition(1, longClick()));

ViewInteraction materialTextView4 = onView(
allOf(withId(android.R.id.title), withText("Delete"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withId(android.R.id.content),
0),
0),
Expand All @@ -168,18 +169,18 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction materialButton2 = onView(
allOf(withId(android.R.id.button1), withText("Yes"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withClassName(is("android.widget.ScrollView")),
0),
3)));
materialButton2.perform(scrollTo(), click());

ViewInteraction appCompatImageButton2 = onView(
allOf(withContentDescription("Navigate up"),
AndroidTestHelper.childAtPosition(
childAtPosition(
allOf(withId(androidx.appcompat.R.id.action_bar),
AndroidTestHelper.childAtPosition(
childAtPosition(
withId(androidx.appcompat.R.id.action_bar_container),
0)),
2),
Expand All @@ -195,15 +196,15 @@ public void testDataAndDeleteAndManualDoseTest() {

DataInteraction materialTextView5 = onData(anything())
.inAdapterView(allOf(withClassName(is("com.android.internal.app.AlertController$RecycleListView")),
AndroidTestHelper.childAtPosition(
childAtPosition(
withClassName(is("android.widget.FrameLayout")),
0)))
.atPosition(2);
materialTextView5.perform(click());

ViewInteraction textInputEditText = onView(
allOf(AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
allOf(childAtPosition(
childAtPosition(
withClassName(is("com.google.android.material.textfield.TextInputLayout")),
0),
0),
Expand All @@ -212,13 +213,23 @@ public void testDataAndDeleteAndManualDoseTest() {

ViewInteraction materialButton4 = onView(
allOf(withId(android.R.id.button1), withText("OK"),
AndroidTestHelper.childAtPosition(
AndroidTestHelper.childAtPosition(
childAtPosition(
childAtPosition(
withClassName(is("android.widget.ScrollView")),
0),
3)));
materialButton4.perform(scrollTo(), click());

ViewInteraction materialButton5 = onView(
allOf(withId(com.google.android.material.R.id.material_timepicker_ok_button), withText("OK"),
childAtPosition(
childAtPosition(
withId(android.R.id.content),
0),
5),
isDisplayed()));
materialButton5.perform(click());

onView(new RecyclerViewMatcher(R.id.latestReminders).atPositionOnView(0, R.id.reminderEventText))
.check(matches(withText(startsWith("1 of Ginseng (200mg)"))));
}
Expand Down
28 changes: 18 additions & 10 deletions app/src/main/java/com/futsch1/medtimer/helpers/TimeHelper.java
Expand Up @@ -9,8 +9,9 @@

import org.jetbrains.annotations.Nullable;

import java.time.LocalDate;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
Expand Down Expand Up @@ -48,6 +49,22 @@ public static String daysSinceEpochToDateString(long daysSinceEpoch) {
}
}

public static Instant instantFromTodayMinutes(int minutes) {
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.of(date, LocalTime.of((minutes / 60), (minutes % 60)));
return dateTime.toInstant(ZoneId.systemDefault().getRules().getOffset(dateTime));

}

public static String toLocalizedTimeString(long timeStamp, ZoneId zoneId) {
Instant remindedTime = Instant.ofEpochSecond(timeStamp);
ZonedDateTime zonedDateTime = remindedTime.atZone(zoneId);

return String.format("%s %s",
zonedDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)),
zonedDateTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)));
}

public interface TimePickerResult {
void onTimeSelected(int minutes);
}
Expand All @@ -72,13 +89,4 @@ public void show(int hourOfDay, int minute, TimePickerResult timePickerResult) {
timePickerDialog.show(activity.getSupportFragmentManager(), "time_picker");
}
}

public static String toLocalizedTimeString(long timeStamp, ZoneId zoneId) {
Instant remindedTime = Instant.ofEpochSecond(timeStamp);
ZonedDateTime zonedDateTime = remindedTime.atZone(zoneId);

return String.format("%s %s",
zonedDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)),
zonedDateTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)));
}
}
126 changes: 126 additions & 0 deletions app/src/main/java/com/futsch1/medtimer/overview/ManualDose.java
@@ -0,0 +1,126 @@
package com.futsch1.medtimer.overview;

import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;

import com.futsch1.medtimer.R;
import com.futsch1.medtimer.database.Medicine;
import com.futsch1.medtimer.database.MedicineRepository;
import com.futsch1.medtimer.database.MedicineWithReminders;
import com.futsch1.medtimer.database.ReminderEvent;
import com.futsch1.medtimer.helpers.DialogHelper;
import com.futsch1.medtimer.helpers.TimeHelper;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class ManualDose {

private final Context context;
private final MedicineRepository medicineRepository;
private final FragmentActivity activity;
private final SharedPreferences sharedPreferences;

public ManualDose(Context context, MedicineRepository medicineRepository, FragmentActivity activity) {
this.context = context;
this.medicineRepository = medicineRepository;
this.activity = activity;
this.sharedPreferences = context.getSharedPreferences("medtimer.data", Context.MODE_PRIVATE);
}

public void logManualDose() {
List<MedicineWithReminders> medicines = medicineRepository.getMedicines();
List<ManualDoseEntry> entries = getManualDoseEntries(medicines);

// But run the actual dialog on the UI thread again
activity.runOnUiThread(() ->
new AlertDialog.Builder(context)
.setItems(entries.stream().map(e -> e.name).toArray(String[]::new), (dialog, which) ->
startLogProcess(entries.get(which)))
.setTitle(R.string.tab_medicine)
.show());
}

@NonNull
private List<ManualDoseEntry> getManualDoseEntries(List<MedicineWithReminders> medicines) {
String lastCustomDose = getLastCustomDose();
List<ManualDoseEntry> entries = new ArrayList<>();
entries.add(new ManualDoseEntry(context.getString(R.string.custom)));
if (!lastCustomDose.isBlank()) {
entries.add(new ManualDoseEntry(lastCustomDose));
}
for (MedicineWithReminders medicine : medicines) {
entries.add(new ManualDoseEntry(medicine.medicine));
}
return entries;
}

private void startLogProcess(ManualDoseEntry entry) {
ReminderEvent reminderEvent = new ReminderEvent();
// Manual dose is not assigned to an existing reminder
reminderEvent.reminderId = -1;
reminderEvent.status = ReminderEvent.ReminderStatus.TAKEN;
reminderEvent.medicineName = entry.name;
reminderEvent.color = entry.color;
reminderEvent.useColor = entry.useColor;
if (reminderEvent.medicineName.equals(context.getString(R.string.custom))) {
DialogHelper.showTextInputDialog(context, R.string.log_additional_dose, R.string.medicine_name, name -> {
setLastCustomDose(name);
reminderEvent.medicineName = name;
getAmountAndContinue(reminderEvent);
});
} else {
getAmountAndContinue(reminderEvent);
}
}

private String getLastCustomDose() {
return sharedPreferences.getString("lastCustomDose", "");
}

private void getAmountAndContinue(ReminderEvent reminderEvent) {
DialogHelper.showTextInputDialog(context, R.string.log_additional_dose, R.string.dosage, amount -> {
reminderEvent.amount = amount;
getTimeAndLog(reminderEvent);
});
}

private void getTimeAndLog(ReminderEvent reminderEvent) {
LocalDateTime localDateTime = LocalDateTime.now();
TimeHelper.TimePickerWrapper timePicker = new TimeHelper.TimePickerWrapper(activity);
timePicker.show(localDateTime.getHour(), localDateTime.getMinute(), minutes -> {
reminderEvent.remindedTimestamp = TimeHelper.instantFromTodayMinutes(minutes).toEpochMilli() / 1000;
reminderEvent.processedTimestamp = Instant.now().toEpochMilli() / 1000;

medicineRepository.insertReminderEvent(reminderEvent);
});
}

private void setLastCustomDose(String lastCustomDose) {
sharedPreferences.edit().putString("lastCustomDose", lastCustomDose).apply();
}

private static class ManualDoseEntry {
public final String name;
public final int color;
public final boolean useColor;

public ManualDoseEntry(String name) {
this.name = name;
this.color = 0;
this.useColor = false;
}

public ManualDoseEntry(Medicine medicine) {
this.name = medicine.name;
this.color = medicine.color;
this.useColor = medicine.useColor;
}
}
}

0 comments on commit 6ca7f45

Please sign in to comment.