diff --git a/app/src/main/java/edu/ucsd/cse110/successorator/MainActivity.java b/app/src/main/java/edu/ucsd/cse110/successorator/MainActivity.java index ac971ce..ec6b542 100644 --- a/app/src/main/java/edu/ucsd/cse110/successorator/MainActivity.java +++ b/app/src/main/java/edu/ucsd/cse110/successorator/MainActivity.java @@ -1,6 +1,7 @@ package edu.ucsd.cse110.successorator; +import android.app.AlertDialog; import android.os.Bundle; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -70,10 +71,23 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { //Pending logic so that click does nothing - if (mainViewModel.getCurrentMode().getValue() == AppMode.PENDING) { + var appMode = mainViewModel.getCurrentMode().getValue(); + if (appMode == AppMode.PENDING || appMode == appMode.RECURRING) { + // Do nothing } else { - mainViewModel.pressGoal(Math.toIntExact(id)); + boolean success = mainViewModel.pressGoal(Math.toIntExact(id)); + if (!success) { + // Pasted from piazza + String flavorText = "This goal is still active for Today. If you've finished this goal for Today, mark it finished in that view."; + if (appMode == AppMode.TODAY) { + flavorText = "This goal is active for Tomorrow. If you're not done for this goal Tomorrow, unfinish it in that view."; + } + new AlertDialog.Builder(MainActivity.this) + .setTitle("Goal is still active!") + .setMessage(flavorText) + .show(); + } } } }); diff --git a/app/src/main/java/edu/ucsd/cse110/successorator/MainViewModel.java b/app/src/main/java/edu/ucsd/cse110/successorator/MainViewModel.java index e608ea6..be86740 100644 --- a/app/src/main/java/edu/ucsd/cse110/successorator/MainViewModel.java +++ b/app/src/main/java/edu/ucsd/cse110/successorator/MainViewModel.java @@ -50,8 +50,8 @@ public class MainViewModel extends ViewModel { private final MutableSubject currentDateLocalized; private final MutableSubject currentTitleString; private final MutableSubject currentWeekday; - private final MutableSubject currentNumberedWeekday; - private final MutableSubject currentDateString; + private final MutableSubject monthlyButtonString; + private final MutableSubject yearlyButtonString; private final MutableSubject currentContext; private final MutableSubject currentMode; private final Calendar dateConverter; @@ -86,8 +86,8 @@ public MainViewModel(GoalRepository goalRepository, MutableSubject offset, this.currentDateLocalized = new SimpleSubject<>(); this.currentTitleString = new SimpleSubject<>(); this.currentWeekday = new SimpleSubject<>(); - this.currentNumberedWeekday = new SimpleSubject<>(); - this.currentDateString = new SimpleSubject<>(); + this.monthlyButtonString = new SimpleSubject<>(); + this.yearlyButtonString = new SimpleSubject<>(); this.currentMode = new SimpleSubject<>(); this.currentMode.setValue(AppMode.TODAY); @@ -290,11 +290,7 @@ public boolean pressGoal(int goalId) { return false; } } - } else if (appMode.equals(AppMode.RECURRING)) { - this.deleteRecurringGoal(goalId); - return true; } - if (goal.completed()) { var newGoal = goal.markIncomplete(); goalRepository.update(newGoal); @@ -312,7 +308,6 @@ public boolean pressGoal(int goalId) { } // Open question whether this method should take a Context or a String - // TODO: test these methods in the PRs where they actually start being used public void activateFocusMode(Context context) { this.currentContext.setValue(context); } @@ -350,16 +345,16 @@ public Subject> getGoalsToDisplay() { } // Exporting these strings for UI - public Subject getCurrentWeekday() { + public Subject getWeeklyButtonString() { return currentWeekday; } - public Subject getCurrentNumberedWeekday() { - return currentNumberedWeekday; + public Subject getMonthlyButtonString() { + return monthlyButtonString; } - public Subject getCurrentDateString() { - return currentDateString; + public Subject getYearlyButtonString() { + return yearlyButtonString; } public Subject getCurrentMode() { @@ -390,7 +385,7 @@ private void updateCalendarStrings(AppMode appMode, Calendar localized) { formatter.applyPattern("EEE"); var weekday = formatter.format(displayDate.getTime()); - this.currentWeekday.setValue(weekday); + this.currentWeekday.setValue("Weekly on " + weekday); var weekday_num = displayDate.get(Calendar.DAY_OF_WEEK_IN_MONTH); var weekday_string = Integer.toString(weekday_num); @@ -403,11 +398,11 @@ private void updateCalendarStrings(AppMode appMode, Calendar localized) { } else { weekday_string += "th"; } - this.currentNumberedWeekday.setValue(weekday_string + " " + weekday); + this.monthlyButtonString.setValue("Monthly on " + weekday_string + " " + weekday); formatter.applyPattern("M/d"); dateString = formatter.format(displayDate.getTime()); - this.currentDateString.setValue(dateString); + this.yearlyButtonString.setValue("Yearly on " + dateString); } @@ -416,6 +411,11 @@ public void addGoal(String contents, Context context) { // At the same time, I don't want to deal with nulls, so I'll just use the current time var currentTime = this.currentDate.getValue(); if (currentTime == null) return; + var appMode = this.currentMode.getValue(); + if (appMode == null) return; + if (appMode.equals(TOMORROW)) { + currentTime += 24 * 60 * 60 * 1000; + } var newGoal = new Goal(null, contents, 0, false, currentTime, false, false, RecurrenceType.NONE, context, currentTime, null, null, null, false); goalRepository.append(newGoal); } @@ -429,21 +429,23 @@ public void addPendingGoal(String contents, Context context) { // Returns true if the goal was added, or false if it wasn't (due to the selected date being in the past) public boolean addRecurringGoal(String contents, int year, int month, int day, RecurrenceType recurrenceType, Context context) { + if (context == null) return false; + if (recurrenceType == null) return false; var currentTime = this.currentDateLocalized.getValue(); if (currentTime == null) return false; var selectedDate = (Calendar) currentTime.clone(); // Set at 12:00 PM to avoid 2am edge cases selectedDate.set(year, month, day, 12, 0, 0); // Is this a bug? It seems like we are checking if selected date is equal to itself - // if (selectedDate.get(Calendar.DAY_OF_MONTH) == day && selectedDate.get(Calendar.YEAR) == year && selectedDate.get(Calendar.MONTH) == month) { - // return false; - // } + if (selectedDate.get(Calendar.DAY_OF_MONTH) != day || selectedDate.get(Calendar.YEAR) != year && selectedDate.get(Calendar.MONTH) != month) { + return false; + } currentTime = TimeUtils.twoAMNormalized(currentTime); if (selectedDate.before(currentTime)) return false; var selectedMoment = selectedDate.getTimeInMillis(); - var recurringGoal = new Goal(null, contents, 0, false, selectedMoment, false, true, recurrenceType, context, selectedDate.getTimeInMillis(), null, null, null, false); + var recurringGoal = new Goal(null, contents, 0, false, selectedMoment, false, true, recurrenceType, context, selectedMoment, null, null, null, false); var goalId = goalRepository.append(recurringGoal); - recurringGoal = goalRepository.findGoal(goalId); + recurringGoal = goalRepository.findGoal(goalId); // Populate object with db id handleRecurringGoalGeneration(recurringGoal, currentTime); return true; } @@ -559,10 +561,6 @@ public void deleteRecurringGoal(int goalId) { } } - } -} -// public AppMode getCurrentMode() { -// return currentMode.getValue(); -// } -//} \ No newline at end of file + } +} \ No newline at end of file diff --git a/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateGoalDialogFragment.java b/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateGoalDialogFragment.java index 40ec144..7e4767c 100644 --- a/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateGoalDialogFragment.java +++ b/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateGoalDialogFragment.java @@ -9,9 +9,7 @@ import androidx.fragment.app.DialogFragment; import androidx.lifecycle.ViewModelProvider; -import android.view.View; import android.view.inputmethod.EditorInfo; -import android.widget.Button; import android.widget.TextView; import edu.ucsd.cse110.successorator.MainViewModel; @@ -54,6 +52,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState){ this.view = FragmentDialogCreateGoalBinding.inflate(getLayoutInflater()); + // Hook up button labels to the view model + mainViewModel.getWeeklyButtonString().observe(text -> view.WeeklyRecurringGoalButton.setText(text)); + mainViewModel.getMonthlyButtonString().observe(text -> view.MonthlyRecurringGoalButton.setText(text)); + mainViewModel.getYearlyButtonString().observe(text -> view.YearlyRecurringGoalButton.setText(text)); + // Create listener for context buttons this.view.contextRadio.setOnCheckedChangeListener((group, checkedId) -> { diff --git a/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateRecurringGoalDialogFragment.java b/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateRecurringGoalDialogFragment.java index 0545057..0ddeb18 100644 --- a/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateRecurringGoalDialogFragment.java +++ b/app/src/main/java/edu/ucsd/cse110/successorator/ui/CreateRecurringGoalDialogFragment.java @@ -11,6 +11,7 @@ import android.view.inputmethod.EditorInfo; import android.widget.TextView; +import android.widget.Toast; import edu.ucsd.cse110.successorator.MainViewModel; import edu.ucsd.cse110.successorator.databinding.FragmentDialogCreateRecurringGoalBinding; @@ -53,6 +54,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState){ this.view = FragmentDialogCreateRecurringGoalBinding.inflate(getLayoutInflater()); + // Hook up prompt text to the view model // Create listener for context buttons @@ -93,8 +95,18 @@ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState){ if(actionId == EditorInfo.IME_ACTION_DONE){ String content = view.goalInput.getText().toString(); //Context is defaulted to HOME, Needs US3 to be implemented. - mainViewModel.addRecurringGoal(content,Integer.parseInt(parts[2]), Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), + + boolean success = mainViewModel.addRecurringGoal(content,Integer.parseInt(parts[2]), Integer.parseInt(parts[0]) - 1, Integer.parseInt(parts[1]), recurrenceType, context); + if (!success) { + // Display a popup telling the user to correct their date or select a context + // https://stackoverflow.com/questions/2115758/how-do-i-display-an-alert-dialog-on-android + new AlertDialog.Builder(getActivity()) + .setTitle("Invalid input!") + .setMessage("Please make sure you've selected a context, selected a recurring interval, and made sure you've selected a valid date (today or later, mm/dd/yyyy, and not the 32nd day of a month)") + .show(); + return false; + } //Lambda functions allow for usage of this. in interface declaration. //Interestingly, without it dismiss() appears to call the correct function regardless. diff --git a/app/src/test/java/edu/ucsd/cse110/successorator/MainViewModelTest.java b/app/src/test/java/edu/ucsd/cse110/successorator/MainViewModelTest.java index 20da789..ad8f307 100644 --- a/app/src/test/java/edu/ucsd/cse110/successorator/MainViewModelTest.java +++ b/app/src/test/java/edu/ucsd/cse110/successorator/MainViewModelTest.java @@ -163,38 +163,47 @@ public void advance24HoursGoalVisibility() { @Test public void testCalendarStreings() { // Verify that the current date string is correct - // Test the weekday and m/d strings as well, but the nth weekday gets its own test + // Test the weekday and m/d strings as well var currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); assertEquals("Today, Wed 2/7", currentDateString); - var currentWeekdayString = mainViewModel.getCurrentWeekday().getValue(); + var currentWeekdayString = mainViewModel.getWeeklyButtonString().getValue(); assertNotNull(currentWeekdayString); - assertEquals("Wed", currentWeekdayString); - var currentDateString2 = mainViewModel.getCurrentDateString().getValue(); + assertEquals("Weekly on Wed", currentWeekdayString); + var currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 1st Wed", currentMonthDayString); + var currentDateString2 = mainViewModel.getYearlyButtonString().getValue(); assertNotNull(currentDateString2); - assertEquals("2/7", currentDateString2); + assertEquals("Yearly on 2/7", currentDateString2); // advance 9 hours and verify no change dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.HOUR_LENGTH * 9); currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); assertEquals("Today, Wed 2/7", currentDateString); - currentWeekdayString = mainViewModel.getCurrentWeekday().getValue(); + currentWeekdayString = mainViewModel.getWeeklyButtonString().getValue(); assertNotNull(currentWeekdayString); - assertEquals("Wed", currentWeekdayString); - currentDateString2 = mainViewModel.getCurrentDateString().getValue(); + assertEquals("Weekly on Wed", currentWeekdayString); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 1st Wed", currentMonthDayString); + currentDateString2 = mainViewModel.getYearlyButtonString().getValue(); assertNotNull(currentDateString2); - assertEquals("2/7", currentDateString2); + assertEquals("Yearly on 2/7", currentDateString2); // Advance another 2 and verify change dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.HOUR_LENGTH * 11); currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); assertEquals("Today, Thu 2/8", currentDateString); - currentWeekdayString = mainViewModel.getCurrentWeekday().getValue(); + currentWeekdayString = mainViewModel.getWeeklyButtonString().getValue(); assertNotNull(currentWeekdayString); - assertEquals("Thu", currentWeekdayString); - currentDateString2 = mainViewModel.getCurrentDateString().getValue(); + assertEquals("Weekly on Thu", currentWeekdayString); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 2nd Thu", currentMonthDayString); + currentDateString2 = mainViewModel.getYearlyButtonString().getValue(); assertNotNull(currentDateString2); - assertEquals("2/8", currentDateString2); + assertEquals("Yearly on 2/8", currentDateString2); // Now test different app modes mainViewModel.activatePendingView(); currentDateString = mainViewModel.getCurrentTitleString().getValue(); @@ -204,12 +213,15 @@ public void testCalendarStreings() { currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); assertEquals("Tomorrow, Fri 2/9", currentDateString); - currentWeekdayString = mainViewModel.getCurrentWeekday().getValue(); + currentWeekdayString = mainViewModel.getWeeklyButtonString().getValue(); assertNotNull(currentWeekdayString); - assertEquals("Fri", currentWeekdayString); - currentDateString2 = mainViewModel.getCurrentDateString().getValue(); + assertEquals("Weekly on Fri", currentWeekdayString); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 2nd Fri", currentMonthDayString); + currentDateString2 = mainViewModel.getYearlyButtonString().getValue(); assertNotNull(currentDateString2); - assertEquals("2/9", currentDateString2); + assertEquals("Yearly on 2/9", currentDateString2); mainViewModel.activateRecurringView(); currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); @@ -218,13 +230,29 @@ public void testCalendarStreings() { currentDateString = mainViewModel.getCurrentTitleString().getValue(); assertNotNull(currentDateString); assertEquals("Today, Thu 2/8", currentDateString); - currentWeekdayString = mainViewModel.getCurrentWeekday().getValue(); + currentWeekdayString = mainViewModel.getWeeklyButtonString().getValue(); assertNotNull(currentWeekdayString); - assertEquals("Thu", currentWeekdayString); - currentDateString2 = mainViewModel.getCurrentDateString().getValue(); + assertEquals("Weekly on Thu", currentWeekdayString); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 2nd Thu", currentMonthDayString); + currentDateString2 = mainViewModel.getYearlyButtonString().getValue(); assertNotNull(currentDateString2); - assertEquals("2/8", currentDateString2); - + assertEquals("Yearly on 2/8", currentDateString2); + // Special case for 3rd, 4th, 5th + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH * 8); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 3rd Thu", currentMonthDayString); + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH * 15); + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 4th Thu", currentMonthDayString); + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH * 22); + // Take advantage of leap year to make this test easy + currentMonthDayString = mainViewModel.getMonthlyButtonString().getValue(); + assertNotNull(currentMonthDayString); + assertEquals("Monthly on 5th Thu", currentMonthDayString); } @Test @@ -366,7 +394,7 @@ public void ScenarioBasedSystemTests1() { mainViewModel.addGoal("Install game update", Context.HOME); mainViewModel.addRecurringGoalDateless("Pay bills", RecurrenceType.MONTHLY, Context.HOME); assertCompleteCount(0); - assertIncompleteCount(1); + assertIncompleteCount(2); //Steps 15 to 24 mainViewModel.activatePendingView(); @@ -641,7 +669,46 @@ public void deleteRecurringGoal() { goal = goals.get(1).getValue(); assertNotNull(goal); assertEquals((Integer) 3, goal.id()); + } + + @Test + public void deleteRecurringGoalGeneratedGoals() { + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + + // Tests the specific cases where: first goal of recurring goal is visible tomorrow + // so make sure that first goal still exists + // first goal of recurring goal is NOT visible today or tomorrow, so it should go away + // recurring goal has an instance visible today, and one not visible tomorrow + // Start date is feb 7 2024, so spawn a daily recurring on feb 8 2024 + mainViewModel.addRecurringGoal("Recurring Goal", 2024, 1, 8, RecurrenceType.DAILY, Context.HOME); + var goals = goalRepository.goals; + assertEquals(2, goals.size()); + + // Delete the goal + mainViewModel.deleteRecurringGoal(1); + goals = goalRepository.goals; + assertEquals(1, goals.size()); + // Reset, then do daily recurring goal on feb 9 + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + mainViewModel.addRecurringGoal("Recurring Goal", 2024, 1, 9, RecurrenceType.DAILY, Context.HOME); + goals = goalRepository.goals; + assertEquals(2, goals.size()); + mainViewModel.deleteRecurringGoal(1); + goals = goalRepository.goals; + assertEquals(0, goals.size()); + + // Reset, then do weekly recurring goal today + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + mainViewModel.addRecurringGoalDateless("Recurring Goal", RecurrenceType.WEEKLY, Context.HOME); + goals = goalRepository.goals; + assertEquals(3, goals.size()); // next instance should be generated in this case + mainViewModel.deleteRecurringGoal(1); + goals = goalRepository.goals; + assertEquals(1, goals.size()); } @Test @@ -714,6 +781,96 @@ public void MS2_US3() { assertEquals(output.get(3).context(), Context.ERRANDS); } + @Test + public void MS2_US4Scenario1() { + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + + mainViewModel.activateTomorrowView(); + + mainViewModel.addGoal("ASDF", Context.HOME); + + assertIncompleteCount(1); + + mainViewModel.activateTodayView(); + + assertIncompleteCount(0); + + mainViewModel.activateRecurringView(); + + assertIncompleteCount(0); + + mainViewModel.activatePendingView(); + + assertIncompleteCount(0); + } + + @Test + public void MS2_US4Scenario2() { + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + + mainViewModel.activateTomorrowView(); + + mainViewModel.addGoal("ASDF", Context.HOME); + mainViewModel.pressGoal(1); + + assertIncompleteCount(0); + assertCompleteCount(1); + + mainViewModel.activateTodayView(); + + assertIncompleteCount(0); + assertCompleteCount(0); + + // Advance real time by 24 hours + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH); + + mainViewModel.activateTomorrowView(); + + assertIncompleteCount(0); + assertCompleteCount(0); + + mainViewModel.activateTodayView(); + + assertIncompleteCount(0); + assertCompleteCount(1); + } + + @Test + public void MS2_US4Scenario3() { + goalRepository = MockGoalRepository.createWithEmptyGoals(); + mainViewModel = new MainViewModel(goalRepository, dateOffset, dateTicker, localizedCalendar); + + mainViewModel.addRecurringGoalDateless("ASDF", RecurrenceType.WEEKLY, Context.HOME); + + // Complete recurring goal instance today + // Generated goal has ID 2; we can hardcode here because of the way we generate goals + mainViewModel.pressGoal(2); + + // Advance the real time by 5 days; next recurring goal instance should be 2 days after + // So not visible in today or tomorrow view + + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH * 5); + + assertCompleteCount(0); + assertIncompleteCount(0); + + mainViewModel.activateTomorrowView(); + + assertCompleteCount(0); + assertIncompleteCount(0); + + // Advance the real time by another day + dateTicker.setValue(TimeUtils.START_TIME + TimeUtils.DAY_LENGTH * 6); + + mainViewModel.activateTomorrowView(); + + assertCompleteCount(0); + assertIncompleteCount(1); + } + + @Test public void MS2_US6Scenario1() { goalRepository = MockGoalRepository.createWithEmptyGoals(); diff --git a/lib/src/main/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilter.java b/lib/src/main/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilter.java index c97d47d..9c7412d 100644 --- a/lib/src/main/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilter.java +++ b/lib/src/main/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilter.java @@ -5,7 +5,6 @@ import edu.ucsd.cse110.successorator.lib.domain.Goal; import edu.ucsd.cse110.successorator.lib.util.TimeUtils; -// TODO: actually implement this and unit test it when you write task 4.1/6.5 public class TomorrowGoalFilter implements IGoalFilter { @Override public boolean shouldShow(Goal goal, Calendar now) { diff --git a/lib/src/main/java/edu/ucsd/cse110/successorator/lib/util/TimeUtils.java b/lib/src/main/java/edu/ucsd/cse110/successorator/lib/util/TimeUtils.java index f04a0ef..35bf582 100644 --- a/lib/src/main/java/edu/ucsd/cse110/successorator/lib/util/TimeUtils.java +++ b/lib/src/main/java/edu/ucsd/cse110/successorator/lib/util/TimeUtils.java @@ -44,8 +44,8 @@ public static boolean shouldShowIncompleteGoal(Calendar startTime, Calendar nowL } } - // TODO: implement this and test when you write task 4.1 public static boolean shouldShowIncompleteGoalTomorrow(Calendar nowLocalized, Calendar startTime) { + // Remember nowLocalized is "tomorrow" not now var today2am = (Calendar) nowLocalized.clone(); today2am.set(Calendar.HOUR_OF_DAY, 2); var yesterday2am = (Calendar) today2am.clone(); diff --git a/lib/src/test/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilterTest.java b/lib/src/test/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilterTest.java new file mode 100644 index 0000000..83b45db --- /dev/null +++ b/lib/src/test/java/edu/ucsd/cse110/successorator/lib/domain/filtering/TomorrowGoalFilterTest.java @@ -0,0 +1,46 @@ +package edu.ucsd.cse110.successorator.lib.domain.filtering; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import java.util.Calendar; + +import edu.ucsd.cse110.successorator.lib.domain.Context; +import edu.ucsd.cse110.successorator.lib.domain.Goal; +import edu.ucsd.cse110.successorator.lib.domain.RecurrenceType; +import edu.ucsd.cse110.successorator.lib.util.TimeUtils; + +public class TomorrowGoalFilterTest { + + @Test + public void shouldShow() { + // Verify returns false for pending and recurring + Goal testGoal = new Goal(1, "Goal 1", 1, false, 0, true, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME, null, null, null, false); + Calendar localizedCalendar = Calendar.getInstance(TimeUtils.GMT); + Calendar startTime = TimeUtils.localize(TimeUtils.START_TIME, localizedCalendar); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + testGoal = new Goal(1, "Goal 1", 1, false, 0, false, true, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME, null, null, null, false); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + + // Verify returns false for completed and yesterday + testGoal = new Goal(1, "Goal 1", 1, true, TimeUtils.START_TIME - TimeUtils.DAY_LENGTH, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME - TimeUtils.DAY_LENGTH, null, null, null, false); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + + // Verify returns false for completed and today + testGoal = new Goal(1, "Goal 1", 1, true, TimeUtils.START_TIME, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME, null, null, null, false); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + + // Verify returns false for incomplete, today or yesterday + testGoal = new Goal(1, "Goal 1", 1, false, 0, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME, null, null, null, false); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + testGoal = new Goal(1, "Goal 1", 1, false, 0, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME - TimeUtils.DAY_LENGTH, null, null, null, false); + assertFalse(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + + // Verify returns true for incomplete or complete and tomorrow + testGoal = new Goal(1, "Goal 1", 1, false, 0, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME + TimeUtils.DAY_LENGTH, null, null, null, false); + assertTrue(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + testGoal = new Goal(1, "Goal 1", 1, true, TimeUtils.START_TIME + TimeUtils.DAY_LENGTH, false, false, RecurrenceType.NONE, Context.HOME, TimeUtils.START_TIME + TimeUtils.DAY_LENGTH, null, null, null, false); + assertTrue(new TomorrowGoalFilter().shouldShow(testGoal, startTime)); + } +} \ No newline at end of file diff --git a/lib/src/test/java/edu/ucsd/cse110/successorator/lib/util/TimeUtilsTest.java b/lib/src/test/java/edu/ucsd/cse110/successorator/lib/util/TimeUtilsTest.java index 4daced6..22abc7e 100644 --- a/lib/src/test/java/edu/ucsd/cse110/successorator/lib/util/TimeUtilsTest.java +++ b/lib/src/test/java/edu/ucsd/cse110/successorator/lib/util/TimeUtilsTest.java @@ -293,4 +293,44 @@ public void twoAMNormalized() { assertEquals(normalized.get(Calendar.DAY_OF_YEAR), test.get(Calendar.DAY_OF_YEAR)); } + + @Test + public void shouldShowIncompleteGoalTomorrow() { + + // Test 1: Goal Created After 2AM Today and Current Time is After 2AM + var now = Calendar.getInstance(); + now.set(Calendar.HOUR_OF_DAY, 4); + var today = (Calendar) now.clone(); + var tomorrow = (Calendar) now.clone(); + tomorrow.add(Calendar.DAY_OF_YEAR, 1); + var yesterday = (Calendar) now.clone(); + yesterday.add(Calendar.DAY_OF_YEAR, -1); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(yesterday, now)); + assertTrue(TimeUtils.shouldShowIncompleteGoalTomorrow(today, now)); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(tomorrow, now)); + + // Test 2: Goal Created Before 2AM Today and Current Time is After 2AM + yesterday.set(Calendar.HOUR_OF_DAY, 1); + today.set(Calendar.HOUR_OF_DAY, 1); + tomorrow.set(Calendar.HOUR_OF_DAY, 1); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(yesterday, now)); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(today, now)); + assertTrue(TimeUtils.shouldShowIncompleteGoalTomorrow(tomorrow, now)); + + // Adjust now to represent a time before 2AM - For tests 3, 4 + now.set(Calendar.HOUR_OF_DAY, 1); + + // Test 3: Goal Created before 2AM and Current Time is Before 2AM + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(yesterday, now)); + assertTrue(TimeUtils.shouldShowIncompleteGoalTomorrow(today, now)); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(tomorrow, now)); + + // Test 4: Goal Created after 2AM and Current Time is Before 2AM + yesterday.set(Calendar.HOUR_OF_DAY, 3); + today.set(Calendar.HOUR_OF_DAY, 3); + tomorrow.set(Calendar.HOUR_OF_DAY, 3); + assertTrue(TimeUtils.shouldShowIncompleteGoalTomorrow(yesterday, now)); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(today, now)); + assertFalse(TimeUtils.shouldShowIncompleteGoalTomorrow(tomorrow, now)); + } } \ No newline at end of file