V1 of skip days

pull/610/head
KristianTashkov 5 years ago
parent 34c73e89db
commit 3cff51ea42

@ -362,6 +362,7 @@ public class HistoryChart extends ScrollableChart
GregorianCalendar date, GregorianCalendar date,
int checkmarkOffset) int checkmarkOffset)
{ {
pSquareFg.setStrikeThruText(false);
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]); if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
else else
{ {
@ -371,13 +372,18 @@ public class HistoryChart extends ScrollableChart
{ {
pSquareBg.setColor(isNumerical ? textColor : colors[1]); pSquareBg.setColor(isNumerical ? textColor : colors[1]);
} }
else if (!isNumerical && checkmark == 3) {
pSquareFg.setStrikeThruText(true);
pSquareBg.setColor(colors[1]);
}
else pSquareBg.setColor(colors[2]); else pSquareBg.setColor(colors[2]);
} }
pSquareFg.setColor(reverseTextColor); pSquareFg.setColor(reverseTextColor);
float round = dpToPixels(getContext(), 2); float round = dpToPixels(getContext(), 2);
canvas.drawRoundRect(location, round, round, pSquareBg); canvas.drawRoundRect(location, round, round, pSquareBg);
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH)); String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH)) ;
canvas.drawText(text, location.centerX(), canvas.drawText(text, location.centerX(),
location.centerY() + squareTextOffset, pSquareFg); location.centerY() + squareTextOffset, pSquareFg);
} }

@ -63,7 +63,8 @@ class CheckmarkButtonView(
fun performToggle() { fun performToggle() {
onToggle() onToggle()
value = when (value) { value = when (value) {
CHECKED_EXPLICITLY -> UNCHECKED CHECKED_EXPLICITLY -> SKIPPED_EXPLICITLY
SKIPPED_EXPLICITLY -> UNCHECKED
else -> CHECKED_EXPLICITLY else -> CHECKED_EXPLICITLY
} }
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
@ -95,6 +96,9 @@ class CheckmarkButtonView(
private inner class Drawer { private inner class Drawer {
private val rect = RectF() private val rect = RectF()
private val lowContrastColor = sres.getColor(R.attr.lowContrastTextColor) private val lowContrastColor = sres.getColor(R.attr.lowContrastTextColor)
private val mediumContrastTextColor = sres.getColor(R.attr.mediumContrastTextColor)
private val paint = TextPaint().apply { private val paint = TextPaint().apply {
typeface = getFontAwesome() typeface = getFontAwesome()
@ -106,9 +110,11 @@ class CheckmarkButtonView(
fun draw(canvas: Canvas) { fun draw(canvas: Canvas) {
paint.color = when (value) { paint.color = when (value) {
CHECKED_EXPLICITLY -> color CHECKED_EXPLICITLY -> color
SKIPPED_EXPLICITLY -> mediumContrastTextColor
else -> lowContrastColor else -> lowContrastColor
} }
val id = when (value) { val id = when (value) {
SKIPPED_EXPLICITLY -> R.string.fa_skipped
UNCHECKED -> R.string.fa_times UNCHECKED -> R.string.fa_times
else -> R.string.fa_check else -> R.string.fa_check
} }

@ -160,7 +160,7 @@ public class OverviewCard extends HabitCard
cache.todayScore = (float) scores.getTodayValue(); cache.todayScore = (float) scores.getTodayValue();
cache.lastMonthScore = (float) scores.getValue(lastMonth); cache.lastMonthScore = (float) scores.getValue(lastMonth);
cache.lastYearScore = (float) scores.getValue(lastYear); cache.lastYearScore = (float) scores.getValue(lastYear);
cache.totalCount = habit.getRepetitions().getTotalCount(); cache.totalCount = habit.getRepetitions().getTotalSuccessfulCount();
} }
@Override @Override

@ -21,6 +21,7 @@
<resources> <resources>
<string translatable="false" name="fa_star_half_o">&#xf123;</string> <string translatable="false" name="fa_star_half_o">&#xf123;</string>
<string translatable="false" name="fa_arrow_circle_up">&#xf0aa;</string> <string translatable="false" name="fa_arrow_circle_up">&#xf0aa;</string>
<string translatable="false" name="fa_skipped">&#xf101;</string>
<string translatable="false" name="fa_check">&#xf00c;</string> <string translatable="false" name="fa_check">&#xf00c;</string>
<string translatable="false" name="fa_times">&#xf00d;</string> <string translatable="false" name="fa_times">&#xf00d;</string>
<string translatable="false" name="fa_bell_o">&#xf0a2;</string> <string translatable="false" name="fa_bell_o">&#xf0a2;</string>

@ -70,6 +70,7 @@ public class ToggleRepetitionCommand extends Command
public void undo() public void undo()
{ {
execute(); execute();
execute();
} }
public static class Record public static class Record

@ -37,6 +37,11 @@ import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle;
@ThreadSafe @ThreadSafe
public final class Checkmark public final class Checkmark
{ {
/**
* Indicates that there was an explicit skip at the timestamp.
*/
public static final int SKIPPED_EXPLICITLY = 3;
/** /**
* Indicates that there was a repetition at the timestamp. * Indicates that there was a repetition at the timestamp.
*/ */
@ -59,7 +64,7 @@ public final class Checkmark
/** /**
* The value of the checkmark. * The value of the checkmark.
* <p> * <p>
* For boolean habits, this equals either UNCHECKED, CHECKED_EXPLICITLY, * For boolean habits, this equals either UNCHECKED, SKIPPED_EXPLICITLY, CHECKED_EXPLICITLY,
* or CHECKED_IMPLICITLY. * or CHECKED_IMPLICITLY.
* <p> * <p>
* For numerical habits, this number is stored in thousandths. That * For numerical habits, this number is stored in thousandths. That

@ -79,7 +79,7 @@ public abstract class CheckmarkList
{ {
Timestamp date = rep.getTimestamp(); Timestamp date = rep.getTimestamp();
int offset = date.daysUntil(today); int offset = date.daysUntil(today);
checkmarks.set(offset, new Checkmark(date, CHECKED_EXPLICITLY)); checkmarks.set(offset, new Checkmark(date, rep.getValue()));
} }
return checkmarks; return checkmarks;
@ -379,7 +379,14 @@ public abstract class CheckmarkList
private void computeYesNo(Repetition[] reps) private void computeYesNo(Repetition[] reps)
{ {
ArrayList<Interval> intervals; ArrayList<Interval> intervals;
intervals = buildIntervals(habit.getFrequency(), reps); List<Repetition> successful_repetitions = new ArrayList<>();
for (Repetition rep : reps) {
if (rep.getValue() != SKIPPED_EXPLICITLY) {
successful_repetitions.add(rep);
}
}
intervals = buildIntervals(
habit.getFrequency(), successful_repetitions.toArray(new Repetition[0]));
snapIntervalsTogether(intervals); snapIntervalsTogether(intervals);
add(buildCheckmarksFromIntervals(reps, intervals)); add(buildCheckmarksFromIntervals(reps, intervals));
} }

@ -30,7 +30,7 @@ import java.util.GregorianCalendar;
import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle; import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle;
/** /**
* Represents a record that the user has performed a certain habit at a certain * Represents a record that the user has performed or skipped a certain habit at a certain
* date. * date.
*/ */
public final class Repetition public final class Repetition
@ -41,7 +41,8 @@ public final class Repetition
/** /**
* The value of the repetition. * The value of the repetition.
* *
* For boolean habits, this always equals Checkmark.CHECKED_EXPLICITLY. * For boolean habits, this equals Checkmark.CHECKED_EXPLICITLY if performed
* or Checkmark.SKIPPED_EXPLICITLY if skipped.
* For numerical habits, this number is stored in thousandths. That * For numerical habits, this number is stored in thousandths. That
* is, if the user enters value 1.50 on the app, it is here stored as 1500. * is, if the user enters value 1.50 on the app, it is here stored as 1500.
*/ */

@ -119,15 +119,15 @@ public abstract class RepetitionList
public abstract Repetition getNewest(); public abstract Repetition getNewest();
/** /**
* Returns the total number of repetitions for each month, from the first * Returns the total number of successful repetitions for each month, from the first
* repetition until today, grouped by day of week. * repetition until today, grouped by day of week.
* <p> * <p>
* The repetitions are returned in a HashMap. The key is the timestamp for * The repetitions are returned in a HashMap. The key is the timestamp for
* the first day of the month, at midnight (00:00). The value is an integer * the first day of the month, at midnight (00:00). The value is an integer
* array with 7 entries. The first entry contains the total number of * array with 7 entries. The first entry contains the total number of
* repetitions during the specified month that occurred on a Saturday. The * successful repetitions during the specified month that occurred on a Saturday. The
* second entry corresponds to Sunday, and so on. If there are no * second entry corresponds to Sunday, and so on. If there are no
* repetitions during a certain month, the value is null. * successful repetitions during a certain month, the value is null.
* *
* @return total number of repetitions by month versus day of week * @return total number of repetitions by month versus day of week
*/ */
@ -140,6 +140,11 @@ public abstract class RepetitionList
for (Repetition r : reps) for (Repetition r : reps)
{ {
if ((habit.getData().type == Habit.YES_NO_HABIT)
&& (r.getValue() == Checkmark.SKIPPED_EXPLICITLY)) {
continue;
}
Calendar date = r.getTimestamp().toCalendar(); Calendar date = r.getTimestamp().toCalendar();
int weekday = r.getTimestamp().getWeekday(); int weekday = r.getTimestamp().getWeekday();
date.set(Calendar.DAY_OF_MONTH, 1); date.set(Calendar.DAY_OF_MONTH, 1);
@ -191,7 +196,13 @@ public abstract class RepetitionList
throw new IllegalStateException("habit must NOT be numerical"); throw new IllegalStateException("habit must NOT be numerical");
Repetition rep = getByTimestamp(timestamp); Repetition rep = getByTimestamp(timestamp);
if (rep != null) remove(rep); if (rep != null) {
remove(rep);
if (rep.getValue() == Checkmark.CHECKED_EXPLICITLY) {
rep = new Repetition(timestamp, Checkmark.SKIPPED_EXPLICITLY);
add(rep);
}
}
else else
{ {
rep = new Repetition(timestamp, Checkmark.CHECKED_EXPLICITLY); rep = new Repetition(timestamp, Checkmark.CHECKED_EXPLICITLY);
@ -203,12 +214,12 @@ public abstract class RepetitionList
} }
/** /**
* Returns the number of all repetitions * Returns the number of all successful repetitions
* *
* @return number of all repetitions * @return number of all successful repetitions
*/ */
@NonNull @NonNull
public abstract long getTotalCount(); public abstract long getTotalSuccessfulCount();
public void toggle(Timestamp timestamp, int value) public void toggle(Timestamp timestamp, int value)
{ {

@ -283,9 +283,13 @@ public abstract class ScoreList implements Iterable<Score>
value = Math.min(1, value); value = Math.min(1, value);
} }
if (!habit.isNumerical() && value > 0) value = 1; if (!habit.isNumerical() & value > 0) {
value = value != Checkmark.SKIPPED_EXPLICITLY ? 1 : -1;
}
previousValue = Score.compute(freq, previousValue, value); if (value > -1) {
previousValue = Score.compute(freq, previousValue, value);
}
scores.add(new Score(from.plus(i), previousValue)); scores.add(new Score(from.plus(i), previousValue));
} }

@ -119,9 +119,15 @@ public class MemoryRepetitionList extends RepetitionList
} }
@Override @Override
public long getTotalCount() public long getTotalSuccessfulCount()
{ {
return list.size(); int count = 0;
for (Repetition rep : list) {
if (rep.getValue() != Checkmark.SKIPPED_EXPLICITLY) {
++count;
}
}
return count;
} }
@Override @Override

@ -129,10 +129,10 @@ public class SQLiteRepetitionList extends RepetitionList
} }
@Override @Override
public long getTotalCount() public long getTotalSuccessfulCount()
{ {
loadRecords(); loadRecords();
return list.getTotalCount(); return list.getTotalSuccessfulCount();
} }
public void reload() public void reload()

@ -53,11 +53,19 @@ public class ToggleRepetitionCommandTest extends BaseUnitTest
{ {
assertTrue(habit.getRepetitions().containsTimestamp(today)); assertTrue(habit.getRepetitions().containsTimestamp(today));
command.execute();
assertEquals(
habit.getRepetitions().getByTimestamp(today).getValue(),
Checkmark.SKIPPED_EXPLICITLY
);
command.execute(); command.execute();
assertFalse(habit.getRepetitions().containsTimestamp(today)); assertFalse(habit.getRepetitions().containsTimestamp(today));
command.undo(); command.undo();
assertTrue(habit.getRepetitions().containsTimestamp(today)); assertEquals(
habit.getRepetitions().getByTimestamp(today).getValue(),
Checkmark.SKIPPED_EXPLICITLY
);
command.execute(); command.execute();
assertFalse(habit.getRepetitions().containsTimestamp(today)); assertFalse(habit.getRepetitions().containsTimestamp(today));

@ -31,6 +31,7 @@ import static java.util.Calendar.*;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.*; import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -157,16 +158,20 @@ public class RepetitionListTest extends BaseUnitTest
@Test @Test
public void test_toggle() public void test_toggle()
{ {
assertTrue(reps.containsTimestamp(today)); assertEquals(reps.getByTimestamp(today).getValue(), Checkmark.CHECKED_EXPLICITLY);
reps.toggle(today);
assertEquals(reps.getByTimestamp(today).getValue(), Checkmark.SKIPPED_EXPLICITLY);
reps.toggle(today); reps.toggle(today);
assertFalse(reps.containsTimestamp(today)); assertFalse(reps.containsTimestamp(today));
verify(listener).onModelChange(); verify(listener, times(3)).onModelChange();
reset(listener); reset(listener);
assertFalse(reps.containsTimestamp(today.minus(1))); assertFalse(reps.containsTimestamp(today.minus(1)));
reps.toggle(today.minus(1)); reps.toggle(today.minus(1));
assertTrue(reps.containsTimestamp(today.minus(1))); assertEquals(reps.getByTimestamp(today.minus(1)).getValue(), Checkmark.CHECKED_EXPLICITLY);
verify(listener).onModelChange(); reps.toggle(today.minus(1));
assertEquals(reps.getByTimestamp(today.minus(1)).getValue(), Checkmark.SKIPPED_EXPLICITLY);
verify(listener, times(3)).onModelChange();
reset(listener); reset(listener);
habit.setType(Habit.NUMBER_HABIT); habit.setType(Habit.NUMBER_HABIT);

@ -172,6 +172,8 @@ public class ListHabitsBehaviorTest extends BaseUnitTest
@Test @Test
public void testOnToggle() public void testOnToggle()
{ {
assertTrue(habit1.isCompletedToday());
behavior.onToggle(habit1, DateUtils.getToday());
assertTrue(habit1.isCompletedToday()); assertTrue(habit1.isCompletedToday());
behavior.onToggle(habit1, DateUtils.getToday()); behavior.onToggle(habit1, DateUtils.getToday());
assertFalse(habit1.isCompletedToday()); assertFalse(habit1.isCompletedToday());

Loading…
Cancel
Save