Allow user to skip days without breaking streak

Co-authored-by: Alinson S. Xavier <git@axavier.org>
pull/629/head
KristianTashkov 5 years ago committed by Alinson S. Xavier
parent d9ff429c28
commit 1a05f7d85d

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

@ -93,8 +93,8 @@ class CheckmarkPanelViewTest : BaseViewTest() {
@Test @Test
fun testToggle() { fun testToggle() {
var timestamps = mutableListOf<Timestamp>() val timestamps = mutableListOf<Timestamp>()
view.onToggle = { timestamps.add(it) } view.onToggle = { t, _ -> timestamps.add(t) }
view.buttons[0].performLongClick() view.buttons[0].performLongClick()
view.buttons[2].performLongClick() view.buttons[2].performLongClick()
view.buttons[3].performLongClick() view.buttons[3].performLongClick()
@ -105,7 +105,7 @@ class CheckmarkPanelViewTest : BaseViewTest() {
fun testToggle_withOffset() { fun testToggle_withOffset() {
val timestamps = mutableListOf<Timestamp>() val timestamps = mutableListOf<Timestamp>()
view.dataOffset = 3 view.dataOffset = 3
view.onToggle = { timestamps += it } view.onToggle = { t, _ -> timestamps += t }
view.buttons[0].performLongClick() view.buttons[0].performLongClick()
view.buttons[2].performLongClick() view.buttons[2].performLongClick()
view.buttons[3].performLongClick() view.buttons[3].performLongClick()

@ -70,7 +70,10 @@ public class CheckmarkWidgetTest extends BaseViewTest
// possible to capture intents sent to BroadcastReceivers. // possible to capture intents sent to BroadcastReceivers.
button.performClick(); button.performClick();
sleep(1000); sleep(1000);
assertThat(checkmarks.getTodayValue(), equalTo(SKIPPED));
button.performClick();
sleep(1000);
assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED)); assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED));
} }

@ -147,11 +147,10 @@ public class HistoryChart extends ScrollableChart
int offset = timestamp.daysUntil(today); int offset = timestamp.daysUntil(today);
if (offset < checkmarks.length) if (offset < checkmarks.length)
{ {
boolean isChecked = checkmarks[offset] == CHECKED_EXPLICITLY; checkmarks[offset] = Repetition.nextToggleValue(checkmarks[offset]);
checkmarks[offset] = (isChecked ? UNCHECKED : CHECKED_EXPLICITLY);
} }
controller.onToggleCheckmark(timestamp); controller.onToggleCheckmark(timestamp, checkmarks[offset]);
postInvalidate(); postInvalidate();
return true; return true;
} }
@ -362,21 +361,42 @@ public class HistoryChart extends ScrollableChart
GregorianCalendar date, GregorianCalendar date,
int checkmarkOffset) int checkmarkOffset)
{ {
int checkmark = 0;
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]); if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
else else
{ {
int checkmark = checkmarks[checkmarkOffset]; checkmark = checkmarks[checkmarkOffset];
if(checkmark == 0) pSquareBg.setColor(colors[0]); if(checkmark == 0)
pSquareBg.setColor(colors[0]);
else if(checkmark < target) else if(checkmark < target)
{ pSquareBg.setColor(colors[1]);
pSquareBg.setColor(isNumerical ? textColor : colors[1]); else
} pSquareBg.setColor(colors[2]);
else pSquareBg.setColor(colors[2]);
} }
pSquareFg.setColor(reverseTextColor); pSquareFg.setColor(reverseTextColor);
pSquareFg.setStrokeWidth(columnWidth * 0.025f);
float round = dpToPixels(getContext(), 2); float round = dpToPixels(getContext(), 2);
canvas.drawRoundRect(location, round, round, pSquareBg); canvas.drawRoundRect(location, round, round, pSquareBg);
if (!isNumerical && checkmark == SKIPPED)
{
canvas.save();
canvas.clipRect(location);
float offset = - columnWidth;
for (int k = 0; k < 10; k++)
{
offset += columnWidth / 5;
canvas.drawLine(location.left + offset,
location.bottom,
location.right + offset,
location.top,
pSquareFg);
}
canvas.restore();
}
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);
@ -492,6 +512,6 @@ public class HistoryChart extends ScrollableChart
public interface Controller public interface Controller
{ {
default void onToggleCheckmark(Timestamp timestamp) {} default void onToggleCheckmark(Timestamp timestamp, int value) {}
} }
} }

@ -27,6 +27,7 @@ import android.view.View.MeasureSpec.*
import com.google.auto.factory.* import com.google.auto.factory.*
import org.isoron.androidbase.activities.* import org.isoron.androidbase.activities.*
import org.isoron.uhabits.* import org.isoron.uhabits.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.models.Checkmark.* import org.isoron.uhabits.core.models.Checkmark.*
import org.isoron.uhabits.core.preferences.* import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.utils.* import org.isoron.uhabits.utils.*
@ -51,7 +52,7 @@ class CheckmarkButtonView(
invalidate() invalidate()
} }
var onToggle: () -> Unit = {} var onToggle: (Int) -> Unit = {}
private var drawer = Drawer() private var drawer = Drawer()
init { init {
@ -61,11 +62,8 @@ class CheckmarkButtonView(
} }
fun performToggle() { fun performToggle() {
onToggle() value = Repetition.nextToggleValue(value)
value = when (value) { onToggle(value)
CHECKED_EXPLICITLY -> UNCHECKED
else -> CHECKED_EXPLICITLY
}
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
invalidate() invalidate()
} }
@ -106,9 +104,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 -> color
else -> lowContrastColor else -> lowContrastColor
} }
val id = when (value) { val id = when (value) {
SKIPPED -> R.string.fa_skipped
UNCHECKED -> R.string.fa_times UNCHECKED -> R.string.fa_times
else -> R.string.fa_check else -> R.string.fa_check
} }

@ -46,7 +46,7 @@ class CheckmarkPanelView(
setupButtons() setupButtons()
} }
var onToggle: (Timestamp) -> Unit = {} var onToggle: (Timestamp, Int) -> Unit = {_, _ ->}
set(value) { set(value) {
field = value field = value
setupButtons() setupButtons()
@ -65,7 +65,7 @@ class CheckmarkPanelView(
else -> UNCHECKED else -> UNCHECKED
} }
button.color = color button.color = color
button.onToggle = { onToggle(timestamp) } button.onToggle = { value -> onToggle(timestamp, value) }
} }
} }
} }

@ -121,9 +121,9 @@ class HabitCardView(
} }
checkmarkPanel = checkmarkPanelFactory.create().apply { checkmarkPanel = checkmarkPanelFactory.create().apply {
onToggle = { timestamp -> onToggle = { timestamp, value ->
triggerRipple(timestamp) triggerRipple(timestamp)
habit?.let { behavior.onToggle(it, timestamp) } habit?.let { behavior.onToggle(it, timestamp, value) }
} }
} }

@ -81,9 +81,9 @@ public class ShowHabitScreen extends BaseScreen
} }
@Override @Override
public void onToggleCheckmark(Timestamp timestamp) public void onToggleCheckmark(Timestamp timestamp, int value)
{ {
behavior.get().onToggleCheckmark(timestamp); behavior.get().onToggleCheckmark(timestamp, value);
} }
@Override @Override

@ -46,7 +46,7 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
val component = app.component val component = app.component
val parser = app.component.intentParser val parser = app.component.intentParser
data = parser.parseCheckmarkIntent(intent) data = parser.parseCheckmarkIntent(intent)
behavior = WidgetBehavior(component.habitList, component.commandRunner, component.notificationTray) behavior = WidgetBehavior(component.commandRunner, component.notificationTray)
widgetUpdater = component.widgetUpdater widgetUpdater = component.widgetUpdater
showNumberSelector(this) showNumberSelector(this)
} }

@ -76,6 +76,7 @@ public class CheckmarkWidgetView extends HabitWidgetView {
switch (checkmarkState) { switch (checkmarkState) {
case Checkmark.CHECKED_EXPLICITLY: case Checkmark.CHECKED_EXPLICITLY:
case Checkmark.SKIPPED:
bgColor = activeColor; bgColor = activeColor;
fgColor = res.getColor(R.attr.highContrastReverseTextColor); fgColor = res.getColor(R.attr.highContrastReverseTextColor);
setShadowAlpha(0x4f); setShadowAlpha(0x4f);
@ -117,7 +118,8 @@ public class CheckmarkWidgetView extends HabitWidgetView {
case Checkmark.CHECKED_EXPLICITLY: case Checkmark.CHECKED_EXPLICITLY:
case Checkmark.CHECKED_IMPLICITLY: case Checkmark.CHECKED_IMPLICITLY:
return getResources().getString(R.string.fa_check); return getResources().getString(R.string.fa_check);
case Checkmark.SKIPPED:
return getResources().getString(R.string.fa_skipped);
case Checkmark.UNCHECKED: case Checkmark.UNCHECKED:
default: default:
return getResources().getString(R.string.fa_times); return getResources().getString(R.string.fa_times);

@ -23,6 +23,7 @@
<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_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_skipped">&#xf068;</string>
<string translatable="false" name="fa_bell_o">&#xf0a2;</string> <string translatable="false" name="fa_bell_o">&#xf0a2;</string>
<string translatable="false" name="fa_calendar">&#xf073;</string> <string translatable="false" name="fa_calendar">&#xf073;</string>

@ -54,7 +54,7 @@ public class WidgetControllerTest extends BaseAndroidJVMTest
commandRunner = mock(CommandRunner.class); commandRunner = mock(CommandRunner.class);
notificationTray = mock(NotificationTray.class); notificationTray = mock(NotificationTray.class);
controller = controller =
new WidgetBehavior(habitList, commandRunner, notificationTray); new WidgetBehavior(commandRunner, notificationTray);
} }
@Test @Test

@ -72,10 +72,6 @@ public class CommandParser
.fromJson(json, EditHabitCommand.Record.class) .fromJson(json, EditHabitCommand.Record.class)
.toCommand(modelFactory, habitList); .toCommand(modelFactory, habitList);
if (event.equals("Toggle")) return gson
.fromJson(json, ToggleRepetitionCommand.Record.class)
.toCommand(habitList);
if (event.equals("Unarchive")) return gson if (event.equals("Unarchive")) return gson
.fromJson(json, UnarchiveHabitsCommand.Record.class) .fromJson(json, UnarchiveHabitsCommand.Record.class)
.toCommand(habitList); .toCommand(habitList);

@ -58,8 +58,11 @@ public class CreateRepetitionCommand extends Command
previousRep = reps.getByTimestamp(timestamp); previousRep = reps.getByTimestamp(timestamp);
if (previousRep != null) reps.remove(previousRep); if (previousRep != null) reps.remove(previousRep);
newRep = new Repetition(timestamp, value); if (value > 0)
reps.add(newRep); {
newRep = new Repetition(timestamp, value);
reps.add(newRep);
}
habit.invalidateNewerThan(timestamp); habit.invalidateNewerThan(timestamp);
} }
@ -80,9 +83,7 @@ public class CreateRepetitionCommand extends Command
@Override @Override
public void undo() public void undo()
{ {
if(newRep == null) throw new IllegalStateException(); if(newRep != null) habit.getRepetitions().remove(newRep);
habit.getRepetitions().remove(newRep);
if (previousRep != null) habit.getRepetitions().add(previousRep); if (previousRep != null) habit.getRepetitions().add(previousRep);
habit.invalidateNewerThan(timestamp); habit.invalidateNewerThan(timestamp);
} }

@ -1,109 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.commands;
import androidx.annotation.*;
import org.isoron.uhabits.core.models.*;
/**
* Command to toggle a repetition.
*/
public class ToggleRepetitionCommand extends Command
{
@NonNull
private HabitList list;
final Timestamp timestamp;
@NonNull
final Habit habit;
public ToggleRepetitionCommand(@NonNull HabitList list,
@NonNull Habit habit,
Timestamp timestamp)
{
super();
this.list = list;
this.timestamp = timestamp;
this.habit = habit;
}
@Override
public void execute()
{
habit.getRepetitions().toggle(timestamp);
list.update(habit);
}
@NonNull
public Habit getHabit()
{
return habit;
}
@Override
@NonNull
public Record toRecord()
{
return new Record(this);
}
@Override
public void undo()
{
execute();
}
public static class Record
{
@NonNull
public String id;
@NonNull
public String event = "Toggle";
public long habit;
public long repTimestamp;
public Record(@NonNull ToggleRepetitionCommand command)
{
id = command.getId();
Long habitId = command.habit.getId();
if (habitId == null) throw new RuntimeException("Habit not saved");
this.repTimestamp = command.timestamp.getUnixTime();
this.habit = habitId;
}
public ToggleRepetitionCommand toCommand(@NonNull HabitList habitList)
{
Habit h = habitList.getById(habit);
if (h == null) throw new HabitNotFoundException();
ToggleRepetitionCommand command;
command = new ToggleRepetitionCommand(
habitList, h, new Timestamp(repTimestamp));
command.setId(id);
return command;
}
}
}

@ -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 = 3;
/** /**
* Indicates that there was a repetition at the timestamp. * Indicates that there was a repetition at the timestamp.
*/ */
@ -59,8 +64,8 @@ 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, CHECKED_EXPLICITLY, CHECKED_IMPLICITLY
* or CHECKED_IMPLICITLY. * or SKIPPED.
* <p> * <p>
* 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 stored as 1500. * is, if the user enters value 1.50 on the app, it is stored as 1500.

@ -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;

@ -27,10 +27,11 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import static org.isoron.uhabits.core.models.Checkmark.*;
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,9 +42,9 @@ 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 CHECKED_EXPLICITLY if performed or SKIPPED 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
* is, if the user enters value 1.50 on the app, it is here stored as 1500. * value 1.50 on the app, it is here stored as 1500.
*/ */
private final int value; private final int value;
@ -61,6 +62,21 @@ public final class Repetition
this.value = value; this.value = value;
} }
public static int nextToggleValue(int value)
{
switch(value) {
case UNCHECKED:
case CHECKED_IMPLICITLY:
return CHECKED_EXPLICITLY;
case CHECKED_EXPLICITLY:
return SKIPPED;
default:
case SKIPPED:
return UNCHECKED;
}
}
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {

@ -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,9 @@ public abstract class RepetitionList
for (Repetition r : reps) for (Repetition r : reps)
{ {
if (!habit.isNumerical() && r.getValue() != Checkmark.CHECKED_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);
@ -202,12 +205,6 @@ public abstract class RepetitionList
return rep; return rep;
} }
/**
* Returns the number of all repetitions
*
* @return number of all repetitions
*/
@NonNull
public abstract long getTotalCount(); public abstract long getTotalCount();
public void toggle(Timestamp timestamp, int value) public void toggle(Timestamp timestamp, int value)

@ -275,17 +275,16 @@ public abstract class ScoreList implements Iterable<Score>
for (int i = 0; i < checkmarkValues.length; i++) for (int i = 0; i < checkmarkValues.length; i++)
{ {
double value = checkmarkValues[checkmarkValues.length - i - 1]; double value = checkmarkValues[checkmarkValues.length - i - 1];
if (!habit.isNumerical() || value != Checkmark.SKIPPED)
if (habit.isNumerical())
{ {
value /= 1000; if (habit.isNumerical())
value /= habit.getTargetValue(); {
value /= 1000;
value /= habit.getTargetValue();
}
value = Math.min(1, value); value = Math.min(1, value);
previousValue = Score.compute(freq, previousValue, value);
} }
if (!habit.isNumerical() && value > 0) value = 1;
previousValue = Score.compute(freq, previousValue, value);
scores.add(new Score(from.plus(i), previousValue)); scores.add(new Score(from.plus(i), previousValue));
} }

@ -121,7 +121,11 @@ public class MemoryRepetitionList extends RepetitionList
@Override @Override
public long getTotalCount() public long getTotalCount()
{ {
return list.size(); int count = 0;
for (Repetition rep : list)
if (rep.getValue() == Checkmark.CHECKED_EXPLICITLY)
count++;
return count;
} }
@Override @Override

@ -59,7 +59,7 @@ public class ReminderScheduler implements CommandRunner.Listener
public synchronized void onCommandExecuted(@NonNull Command command, public synchronized void onCommandExecuted(@NonNull Command command,
@Nullable Long refreshKey) @Nullable Long refreshKey)
{ {
if (command instanceof ToggleRepetitionCommand) return; if (command instanceof CreateRepetitionCommand) return;
if (command instanceof ChangeHabitColorCommand) return; if (command instanceof ChangeHabitColorCommand) return;
scheduleAll(); scheduleAll();
} }

@ -76,17 +76,11 @@ public class NotificationTray
public void onCommandExecuted(@NonNull Command command, public void onCommandExecuted(@NonNull Command command,
@Nullable Long refreshKey) @Nullable Long refreshKey)
{ {
if (command instanceof ToggleRepetitionCommand) if (command instanceof CreateRepetitionCommand)
{ {
ToggleRepetitionCommand toggleCmd = CreateRepetitionCommand createCmd = (CreateRepetitionCommand) command;
(ToggleRepetitionCommand) command; Habit habit = createCmd.getHabit();
cancel(habit);
Habit habit = toggleCmd.getHabit();
taskRunner.execute(() ->
{
if (habit.getCheckmarks().getTodayValue() !=
Checkmark.UNCHECKED) cancel(habit);
});
} }
if (command instanceof DeleteHabitsCommand) if (command instanceof DeleteHabitsCommand)

@ -149,11 +149,11 @@ public class ListHabitsBehavior
if (prefs.isFirstRun()) onFirstRun(); if (prefs.isFirstRun()) onFirstRun();
} }
public void onToggle(@NonNull Habit habit, Timestamp timestamp) public void onToggle(@NonNull Habit habit, Timestamp timestamp, int value)
{ {
commandRunner.execute( commandRunner.execute(
new ToggleRepetitionCommand(habitList, habit, timestamp), new CreateRepetitionCommand(habit, timestamp, value),
habit.getId()); habit.getId());
} }
public enum Message public enum Message

@ -56,10 +56,10 @@ public class ShowHabitBehavior
screen.showEditHistoryScreen(); screen.showEditHistoryScreen();
} }
public void onToggleCheckmark(Timestamp timestamp) public void onToggleCheckmark(Timestamp timestamp, int value)
{ {
commandRunner.execute( commandRunner.execute(
new ToggleRepetitionCommand(habitList, habit, timestamp), null); new CreateRepetitionCommand(habit, timestamp, value), null);
} }
public interface Screen public interface Screen

@ -110,7 +110,7 @@ public class ShowHabitMenuBehavior
if (i % 7 == 0) strength = max(0, min(100, strength + 10 * random.nextGaussian())); if (i % 7 == 0) strength = max(0, min(100, strength + 10 * random.nextGaussian()));
if (random.nextInt(100) > strength) continue; if (random.nextInt(100) > strength) continue;
int value = 1; int value = Checkmark.CHECKED_EXPLICITLY;
if (habit.isNumerical()) if (habit.isNumerical())
value = (int) (1000 + 250 * random.nextGaussian() * strength / 100) * 1000; value = (int) (1000 + 250 * random.nextGaussian() * strength / 100) * 1000;

@ -29,19 +29,15 @@ import javax.inject.*;
public class WidgetBehavior public class WidgetBehavior
{ {
private HabitList habitList;
@NonNull @NonNull
private final CommandRunner commandRunner; private final CommandRunner commandRunner;
private NotificationTray notificationTray; private NotificationTray notificationTray;
@Inject @Inject
public WidgetBehavior(@NonNull HabitList habitList, public WidgetBehavior(@NonNull CommandRunner commandRunner,
@NonNull CommandRunner commandRunner,
@NonNull NotificationTray notificationTray) @NonNull NotificationTray notificationTray)
{ {
this.habitList = habitList;
this.commandRunner = commandRunner; this.commandRunner = commandRunner;
this.notificationTray = notificationTray; this.notificationTray = notificationTray;
} }
@ -51,7 +47,7 @@ public class WidgetBehavior
notificationTray.cancel(habit); notificationTray.cancel(habit);
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
if (rep != null) return; if (rep != null) return;
performToggle(habit, timestamp); performToggle(habit, timestamp, Checkmark.CHECKED_EXPLICITLY);
} }
public void onRemoveRepetition(@NonNull Habit habit, Timestamp timestamp) public void onRemoveRepetition(@NonNull Habit habit, Timestamp timestamp)
@ -59,18 +55,20 @@ public class WidgetBehavior
notificationTray.cancel(habit); notificationTray.cancel(habit);
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
if (rep == null) return; if (rep == null) return;
performToggle(habit, timestamp); performToggle(habit, timestamp, Checkmark.UNCHECKED);
} }
public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp) public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp)
{ {
performToggle(habit, timestamp); Repetition previous = habit.getRepetitions().getByTimestamp(timestamp);
if(previous == null) performToggle(habit, timestamp, Checkmark.CHECKED_EXPLICITLY);
else performToggle(habit, timestamp, Repetition.nextToggleValue(previous.getValue()));
} }
private void performToggle(@NonNull Habit habit, Timestamp timestamp) private void performToggle(@NonNull Habit habit, Timestamp timestamp, int value)
{ {
commandRunner.execute( commandRunner.execute(
new ToggleRepetitionCommand(habitList, habit, timestamp), new CreateRepetitionCommand(habit, timestamp, value),
habit.getId()); habit.getId());
} }

@ -136,20 +136,6 @@ public class CommandParserTest extends BaseUnitTest
.getData())); .getData()));
} }
@Test
public void testDecodeToggleCommand() throws JSONException
{
ToggleRepetitionCommand original, decoded;
original = new ToggleRepetitionCommand(habitList, habit,
Timestamp.ZERO.plus(100));
decoded = (ToggleRepetitionCommand) parser.parse(original.toJson());
MatcherAssert.assertThat(decoded.getId(), equalTo(original.getId()));
MatcherAssert.assertThat(decoded.timestamp, equalTo(original
.timestamp));
MatcherAssert.assertThat(decoded.habit, equalTo(original.habit));
}
@Test @Test
public void testDecodeUnarchiveCommand() throws JSONException public void testDecodeUnarchiveCommand() throws JSONException
{ {

@ -1,76 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.commands;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class ToggleRepetitionCommandTest extends BaseUnitTest
{
private ToggleRepetitionCommand command;
private Habit habit;
private Timestamp today;
@Override
@Before
public void setUp() throws Exception
{
super.setUp();
habit = fixtures.createShortHabit();
habitList.add(habit);
today = DateUtils.getToday();
command = new ToggleRepetitionCommand(habitList, habit, today);
}
@Test
public void testExecuteUndoRedo()
{
assertTrue(habit.getRepetitions().containsTimestamp(today));
command.execute();
assertFalse(habit.getRepetitions().containsTimestamp(today));
command.undo();
assertTrue(habit.getRepetitions().containsTimestamp(today));
command.execute();
assertFalse(habit.getRepetitions().containsTimestamp(today));
}
@Test
public void testRecord()
{
ToggleRepetitionCommand.Record rec = command.toRecord();
ToggleRepetitionCommand other = rec.toCommand(habitList);
assertThat(command.getId(), equalTo(other.getId()));
assertThat(command.timestamp, equalTo(other.timestamp));
assertThat(command.habit, equalTo(other.habit));
}
}

@ -84,9 +84,7 @@ public class HabitCardListCacheTest extends BaseUnitTest
{ {
Habit h2 = habitList.getByPosition(2); Habit h2 = habitList.getByPosition(2);
Timestamp today = DateUtils.getToday(); Timestamp today = DateUtils.getToday();
commandRunner.execute(new ToggleRepetitionCommand(habitList, h2, today), commandRunner.execute(new CreateRepetitionCommand(h2, today, Checkmark.UNCHECKED), h2.getId());
h2.getId());
verify(listener).onItemChanged(2); verify(listener).onItemChanged(2);
verify(listener).onRefreshFinished(); verify(listener).onRefreshFinished();
verifyNoMoreInteractions(listener); verifyNoMoreInteractions(listener);

@ -173,7 +173,7 @@ public class ListHabitsBehaviorTest extends BaseUnitTest
public void testOnToggle() public void testOnToggle()
{ {
assertTrue(habit1.isCompletedToday()); assertTrue(habit1.isCompletedToday());
behavior.onToggle(habit1, DateUtils.getToday()); behavior.onToggle(habit1, DateUtils.getToday(), Checkmark.UNCHECKED);
assertFalse(habit1.isCompletedToday()); assertFalse(habit1.isCompletedToday());
} }

Loading…
Cancel
Save