mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Allow user to skip days without breaking streak
Co-authored-by: Alinson S. Xavier <git@axavier.org>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
@@ -93,8 +93,8 @@ class CheckmarkPanelViewTest : BaseViewTest() {
|
||||
|
||||
@Test
|
||||
fun testToggle() {
|
||||
var timestamps = mutableListOf<Timestamp>()
|
||||
view.onToggle = { timestamps.add(it) }
|
||||
val timestamps = mutableListOf<Timestamp>()
|
||||
view.onToggle = { t, _ -> timestamps.add(t) }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
@@ -105,7 +105,7 @@ class CheckmarkPanelViewTest : BaseViewTest() {
|
||||
fun testToggle_withOffset() {
|
||||
val timestamps = mutableListOf<Timestamp>()
|
||||
view.dataOffset = 3
|
||||
view.onToggle = { timestamps += it }
|
||||
view.onToggle = { t, _ -> timestamps += t }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
|
||||
@@ -70,7 +70,10 @@ public class CheckmarkWidgetTest extends BaseViewTest
|
||||
// possible to capture intents sent to BroadcastReceivers.
|
||||
button.performClick();
|
||||
sleep(1000);
|
||||
assertThat(checkmarks.getTodayValue(), equalTo(SKIPPED));
|
||||
|
||||
button.performClick();
|
||||
sleep(1000);
|
||||
assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||
}
|
||||
|
||||
|
||||
@@ -147,11 +147,10 @@ public class HistoryChart extends ScrollableChart
|
||||
int offset = timestamp.daysUntil(today);
|
||||
if (offset < checkmarks.length)
|
||||
{
|
||||
boolean isChecked = checkmarks[offset] == CHECKED_EXPLICITLY;
|
||||
checkmarks[offset] = (isChecked ? UNCHECKED : CHECKED_EXPLICITLY);
|
||||
checkmarks[offset] = Repetition.nextToggleValue(checkmarks[offset]);
|
||||
}
|
||||
|
||||
controller.onToggleCheckmark(timestamp);
|
||||
controller.onToggleCheckmark(timestamp, checkmarks[offset]);
|
||||
postInvalidate();
|
||||
return true;
|
||||
}
|
||||
@@ -362,21 +361,42 @@ public class HistoryChart extends ScrollableChart
|
||||
GregorianCalendar date,
|
||||
int checkmarkOffset)
|
||||
{
|
||||
int checkmark = 0;
|
||||
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
|
||||
else
|
||||
{
|
||||
int checkmark = checkmarks[checkmarkOffset];
|
||||
if(checkmark == 0) pSquareBg.setColor(colors[0]);
|
||||
checkmark = checkmarks[checkmarkOffset];
|
||||
if(checkmark == 0)
|
||||
pSquareBg.setColor(colors[0]);
|
||||
else if(checkmark < target)
|
||||
{
|
||||
pSquareBg.setColor(isNumerical ? textColor : colors[1]);
|
||||
}
|
||||
else pSquareBg.setColor(colors[2]);
|
||||
pSquareBg.setColor(colors[1]);
|
||||
else
|
||||
pSquareBg.setColor(colors[2]);
|
||||
}
|
||||
|
||||
pSquareFg.setColor(reverseTextColor);
|
||||
pSquareFg.setStrokeWidth(columnWidth * 0.025f);
|
||||
|
||||
float round = dpToPixels(getContext(), 2);
|
||||
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));
|
||||
canvas.drawText(text, location.centerX(),
|
||||
location.centerY() + squareTextOffset, pSquareFg);
|
||||
@@ -492,6 +512,6 @@ public class HistoryChart extends ScrollableChart
|
||||
|
||||
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 org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Checkmark.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
@@ -51,7 +52,7 @@ class CheckmarkButtonView(
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var onToggle: () -> Unit = {}
|
||||
var onToggle: (Int) -> Unit = {}
|
||||
private var drawer = Drawer()
|
||||
|
||||
init {
|
||||
@@ -61,11 +62,8 @@ class CheckmarkButtonView(
|
||||
}
|
||||
|
||||
fun performToggle() {
|
||||
onToggle()
|
||||
value = when (value) {
|
||||
CHECKED_EXPLICITLY -> UNCHECKED
|
||||
else -> CHECKED_EXPLICITLY
|
||||
}
|
||||
value = Repetition.nextToggleValue(value)
|
||||
onToggle(value)
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
invalidate()
|
||||
}
|
||||
@@ -106,9 +104,11 @@ class CheckmarkButtonView(
|
||||
fun draw(canvas: Canvas) {
|
||||
paint.color = when (value) {
|
||||
CHECKED_EXPLICITLY -> color
|
||||
SKIPPED -> color
|
||||
else -> lowContrastColor
|
||||
}
|
||||
val id = when (value) {
|
||||
SKIPPED -> R.string.fa_skipped
|
||||
UNCHECKED -> R.string.fa_times
|
||||
else -> R.string.fa_check
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class CheckmarkPanelView(
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
var onToggle: (Timestamp) -> Unit = {}
|
||||
var onToggle: (Timestamp, Int) -> Unit = {_, _ ->}
|
||||
set(value) {
|
||||
field = value
|
||||
setupButtons()
|
||||
@@ -65,7 +65,7 @@ class CheckmarkPanelView(
|
||||
else -> UNCHECKED
|
||||
}
|
||||
button.color = color
|
||||
button.onToggle = { onToggle(timestamp) }
|
||||
button.onToggle = { value -> onToggle(timestamp, value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +121,9 @@ class HabitCardView(
|
||||
}
|
||||
|
||||
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
||||
onToggle = { timestamp ->
|
||||
onToggle = { timestamp, value ->
|
||||
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
|
||||
public void onToggleCheckmark(Timestamp timestamp)
|
||||
public void onToggleCheckmark(Timestamp timestamp, int value)
|
||||
{
|
||||
behavior.get().onToggleCheckmark(timestamp);
|
||||
behavior.get().onToggleCheckmark(timestamp, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,7 +46,7 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
|
||||
val component = app.component
|
||||
val parser = app.component.intentParser
|
||||
data = parser.parseCheckmarkIntent(intent)
|
||||
behavior = WidgetBehavior(component.habitList, component.commandRunner, component.notificationTray)
|
||||
behavior = WidgetBehavior(component.commandRunner, component.notificationTray)
|
||||
widgetUpdater = component.widgetUpdater
|
||||
showNumberSelector(this)
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ public class CheckmarkWidgetView extends HabitWidgetView {
|
||||
|
||||
switch (checkmarkState) {
|
||||
case Checkmark.CHECKED_EXPLICITLY:
|
||||
case Checkmark.SKIPPED:
|
||||
bgColor = activeColor;
|
||||
fgColor = res.getColor(R.attr.highContrastReverseTextColor);
|
||||
setShadowAlpha(0x4f);
|
||||
@@ -117,7 +118,8 @@ public class CheckmarkWidgetView extends HabitWidgetView {
|
||||
case Checkmark.CHECKED_EXPLICITLY:
|
||||
case Checkmark.CHECKED_IMPLICITLY:
|
||||
return getResources().getString(R.string.fa_check);
|
||||
|
||||
case Checkmark.SKIPPED:
|
||||
return getResources().getString(R.string.fa_skipped);
|
||||
case Checkmark.UNCHECKED:
|
||||
default:
|
||||
return getResources().getString(R.string.fa_times);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<string translatable="false" name="fa_arrow_circle_up"></string>
|
||||
<string translatable="false" name="fa_check"></string>
|
||||
<string translatable="false" name="fa_times"></string>
|
||||
<string translatable="false" name="fa_skipped"></string>
|
||||
<string translatable="false" name="fa_bell_o"></string>
|
||||
<string translatable="false" name="fa_calendar"></string>
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class WidgetControllerTest extends BaseAndroidJVMTest
|
||||
commandRunner = mock(CommandRunner.class);
|
||||
notificationTray = mock(NotificationTray.class);
|
||||
controller =
|
||||
new WidgetBehavior(habitList, commandRunner, notificationTray);
|
||||
new WidgetBehavior(commandRunner, notificationTray);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -72,10 +72,6 @@ public class CommandParser
|
||||
.fromJson(json, EditHabitCommand.Record.class)
|
||||
.toCommand(modelFactory, habitList);
|
||||
|
||||
if (event.equals("Toggle")) return gson
|
||||
.fromJson(json, ToggleRepetitionCommand.Record.class)
|
||||
.toCommand(habitList);
|
||||
|
||||
if (event.equals("Unarchive")) return gson
|
||||
.fromJson(json, UnarchiveHabitsCommand.Record.class)
|
||||
.toCommand(habitList);
|
||||
|
||||
@@ -58,8 +58,11 @@ public class CreateRepetitionCommand extends Command
|
||||
previousRep = reps.getByTimestamp(timestamp);
|
||||
if (previousRep != null) reps.remove(previousRep);
|
||||
|
||||
newRep = new Repetition(timestamp, value);
|
||||
reps.add(newRep);
|
||||
if (value > 0)
|
||||
{
|
||||
newRep = new Repetition(timestamp, value);
|
||||
reps.add(newRep);
|
||||
}
|
||||
|
||||
habit.invalidateNewerThan(timestamp);
|
||||
}
|
||||
@@ -80,9 +83,7 @@ public class CreateRepetitionCommand extends Command
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
if(newRep == null) throw new IllegalStateException();
|
||||
habit.getRepetitions().remove(newRep);
|
||||
|
||||
if(newRep != null) habit.getRepetitions().remove(newRep);
|
||||
if (previousRep != null) habit.getRepetitions().add(previousRep);
|
||||
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
|
||||
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.
|
||||
*/
|
||||
@@ -59,8 +64,8 @@ public final class Checkmark
|
||||
/**
|
||||
* The value of the checkmark.
|
||||
* <p>
|
||||
* For boolean habits, this equals either UNCHECKED, CHECKED_EXPLICITLY,
|
||||
* or CHECKED_IMPLICITLY.
|
||||
* For boolean habits, this equals either UNCHECKED, CHECKED_EXPLICITLY, CHECKED_IMPLICITLY
|
||||
* or SKIPPED.
|
||||
* <p>
|
||||
* 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.
|
||||
|
||||
@@ -79,7 +79,7 @@ public abstract class CheckmarkList
|
||||
{
|
||||
Timestamp date = rep.getTimestamp();
|
||||
int offset = date.daysUntil(today);
|
||||
checkmarks.set(offset, new Checkmark(date, CHECKED_EXPLICITLY));
|
||||
checkmarks.set(offset, new Checkmark(date, rep.getValue()));
|
||||
}
|
||||
|
||||
return checkmarks;
|
||||
|
||||
@@ -27,10 +27,11 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static org.isoron.uhabits.core.models.Checkmark.*;
|
||||
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.
|
||||
*/
|
||||
public final class Repetition
|
||||
@@ -41,9 +42,9 @@ public final class Repetition
|
||||
/**
|
||||
* The value of the repetition.
|
||||
*
|
||||
* For boolean habits, this always equals Checkmark.CHECKED_EXPLICITLY.
|
||||
* 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.
|
||||
* For boolean habits, this equals CHECKED_EXPLICITLY if performed or SKIPPED if skipped.
|
||||
* 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.
|
||||
*/
|
||||
private final int value;
|
||||
|
||||
@@ -61,6 +62,21 @@ public final class Repetition
|
||||
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
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
|
||||
@@ -119,15 +119,15 @@ public abstract class RepetitionList
|
||||
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.
|
||||
* <p>
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
@@ -140,6 +140,9 @@ public abstract class RepetitionList
|
||||
|
||||
for (Repetition r : reps)
|
||||
{
|
||||
if (!habit.isNumerical() && r.getValue() != Checkmark.CHECKED_EXPLICITLY)
|
||||
continue;
|
||||
|
||||
Calendar date = r.getTimestamp().toCalendar();
|
||||
int weekday = r.getTimestamp().getWeekday();
|
||||
date.set(Calendar.DAY_OF_MONTH, 1);
|
||||
@@ -202,12 +205,6 @@ public abstract class RepetitionList
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of all repetitions
|
||||
*
|
||||
* @return number of all repetitions
|
||||
*/
|
||||
@NonNull
|
||||
public abstract long getTotalCount();
|
||||
|
||||
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++)
|
||||
{
|
||||
double value = checkmarkValues[checkmarkValues.length - i - 1];
|
||||
|
||||
if (habit.isNumerical())
|
||||
if (!habit.isNumerical() || value != Checkmark.SKIPPED)
|
||||
{
|
||||
value /= 1000;
|
||||
value /= habit.getTargetValue();
|
||||
if (habit.isNumerical())
|
||||
{
|
||||
value /= 1000;
|
||||
value /= habit.getTargetValue();
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,11 @@ public class MemoryRepetitionList extends RepetitionList
|
||||
@Override
|
||||
public long getTotalCount()
|
||||
{
|
||||
return list.size();
|
||||
int count = 0;
|
||||
for (Repetition rep : list)
|
||||
if (rep.getValue() == Checkmark.CHECKED_EXPLICITLY)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ReminderScheduler implements CommandRunner.Listener
|
||||
public synchronized void onCommandExecuted(@NonNull Command command,
|
||||
@Nullable Long refreshKey)
|
||||
{
|
||||
if (command instanceof ToggleRepetitionCommand) return;
|
||||
if (command instanceof CreateRepetitionCommand) return;
|
||||
if (command instanceof ChangeHabitColorCommand) return;
|
||||
scheduleAll();
|
||||
}
|
||||
|
||||
@@ -76,17 +76,11 @@ public class NotificationTray
|
||||
public void onCommandExecuted(@NonNull Command command,
|
||||
@Nullable Long refreshKey)
|
||||
{
|
||||
if (command instanceof ToggleRepetitionCommand)
|
||||
if (command instanceof CreateRepetitionCommand)
|
||||
{
|
||||
ToggleRepetitionCommand toggleCmd =
|
||||
(ToggleRepetitionCommand) command;
|
||||
|
||||
Habit habit = toggleCmd.getHabit();
|
||||
taskRunner.execute(() ->
|
||||
{
|
||||
if (habit.getCheckmarks().getTodayValue() !=
|
||||
Checkmark.UNCHECKED) cancel(habit);
|
||||
});
|
||||
CreateRepetitionCommand createCmd = (CreateRepetitionCommand) command;
|
||||
Habit habit = createCmd.getHabit();
|
||||
cancel(habit);
|
||||
}
|
||||
|
||||
if (command instanceof DeleteHabitsCommand)
|
||||
|
||||
@@ -149,11 +149,11 @@ public class ListHabitsBehavior
|
||||
if (prefs.isFirstRun()) onFirstRun();
|
||||
}
|
||||
|
||||
public void onToggle(@NonNull Habit habit, Timestamp timestamp)
|
||||
public void onToggle(@NonNull Habit habit, Timestamp timestamp, int value)
|
||||
{
|
||||
commandRunner.execute(
|
||||
new ToggleRepetitionCommand(habitList, habit, timestamp),
|
||||
habit.getId());
|
||||
new CreateRepetitionCommand(habit, timestamp, value),
|
||||
habit.getId());
|
||||
}
|
||||
|
||||
public enum Message
|
||||
|
||||
@@ -56,10 +56,10 @@ public class ShowHabitBehavior
|
||||
screen.showEditHistoryScreen();
|
||||
}
|
||||
|
||||
public void onToggleCheckmark(Timestamp timestamp)
|
||||
public void onToggleCheckmark(Timestamp timestamp, int value)
|
||||
{
|
||||
commandRunner.execute(
|
||||
new ToggleRepetitionCommand(habitList, habit, timestamp), null);
|
||||
new CreateRepetitionCommand(habit, timestamp, value), null);
|
||||
}
|
||||
|
||||
public interface Screen
|
||||
|
||||
@@ -110,7 +110,7 @@ public class ShowHabitMenuBehavior
|
||||
if (i % 7 == 0) strength = max(0, min(100, strength + 10 * random.nextGaussian()));
|
||||
if (random.nextInt(100) > strength) continue;
|
||||
|
||||
int value = 1;
|
||||
int value = Checkmark.CHECKED_EXPLICITLY;
|
||||
if (habit.isNumerical())
|
||||
value = (int) (1000 + 250 * random.nextGaussian() * strength / 100) * 1000;
|
||||
|
||||
|
||||
@@ -29,19 +29,15 @@ import javax.inject.*;
|
||||
|
||||
public class WidgetBehavior
|
||||
{
|
||||
private HabitList habitList;
|
||||
|
||||
@NonNull
|
||||
private final CommandRunner commandRunner;
|
||||
|
||||
private NotificationTray notificationTray;
|
||||
|
||||
@Inject
|
||||
public WidgetBehavior(@NonNull HabitList habitList,
|
||||
@NonNull CommandRunner commandRunner,
|
||||
public WidgetBehavior(@NonNull CommandRunner commandRunner,
|
||||
@NonNull NotificationTray notificationTray)
|
||||
{
|
||||
this.habitList = habitList;
|
||||
this.commandRunner = commandRunner;
|
||||
this.notificationTray = notificationTray;
|
||||
}
|
||||
@@ -51,7 +47,7 @@ public class WidgetBehavior
|
||||
notificationTray.cancel(habit);
|
||||
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
|
||||
if (rep != null) return;
|
||||
performToggle(habit, timestamp);
|
||||
performToggle(habit, timestamp, Checkmark.CHECKED_EXPLICITLY);
|
||||
}
|
||||
|
||||
public void onRemoveRepetition(@NonNull Habit habit, Timestamp timestamp)
|
||||
@@ -59,18 +55,20 @@ public class WidgetBehavior
|
||||
notificationTray.cancel(habit);
|
||||
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
|
||||
if (rep == null) return;
|
||||
performToggle(habit, timestamp);
|
||||
performToggle(habit, timestamp, Checkmark.UNCHECKED);
|
||||
}
|
||||
|
||||
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(
|
||||
new ToggleRepetitionCommand(habitList, habit, timestamp),
|
||||
new CreateRepetitionCommand(habit, timestamp, value),
|
||||
habit.getId());
|
||||
}
|
||||
|
||||
|
||||
@@ -136,20 +136,6 @@ public class CommandParserTest extends BaseUnitTest
|
||||
.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
|
||||
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);
|
||||
Timestamp today = DateUtils.getToday();
|
||||
commandRunner.execute(new ToggleRepetitionCommand(habitList, h2, today),
|
||||
h2.getId());
|
||||
|
||||
commandRunner.execute(new CreateRepetitionCommand(h2, today, Checkmark.UNCHECKED), h2.getId());
|
||||
verify(listener).onItemChanged(2);
|
||||
verify(listener).onRefreshFinished();
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
@@ -173,7 +173,7 @@ public class ListHabitsBehaviorTest extends BaseUnitTest
|
||||
public void testOnToggle()
|
||||
{
|
||||
assertTrue(habit1.isCompletedToday());
|
||||
behavior.onToggle(habit1, DateUtils.getToday());
|
||||
behavior.onToggle(habit1, DateUtils.getToday(), Checkmark.UNCHECKED);
|
||||
assertFalse(habit1.isCompletedToday());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user