diff --git a/app/build.gradle b/app/build.gradle index 93d60fe16..649ab0f05 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { minSdkVersion 15 targetSdkVersion 25 - buildConfigField "Integer", "databaseVersion", "15" + buildConfigField "Integer", "databaseVersion", "16" buildConfigField "String", "databaseFilename", "\"uhabits.db\"" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java index 992c1e673..766271ada 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java @@ -37,6 +37,7 @@ import java.util.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsNot.not; +import static org.isoron.uhabits.models.Checkmark.*; @RunWith(AndroidJUnit4.class) @MediumTest @@ -67,7 +68,7 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest RepetitionRecord record = getByTimestamp(today + day); assertThat(record, is(nullValue())); - Repetition rep = new Repetition(today + day); + Repetition rep = new Repetition(today + day, CHECKED_EXPLICITLY); habit.getRepetitions().add(rep); record = getByTimestamp(today + day); diff --git a/app/src/main/assets/migrations/16.sql b/app/src/main/assets/migrations/16.sql new file mode 100644 index 000000000..7a7746f63 --- /dev/null +++ b/app/src/main/assets/migrations/16.sql @@ -0,0 +1,2 @@ +alter table Habits add column type integer not null default 0; +alter table Repetitions add column value integer not null default 2; \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java b/app/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java index 620a2d59f..798ecdb88 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java +++ b/app/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java @@ -323,7 +323,11 @@ public class HistoryChart extends ScrollableChart int checkmarkOffset) { if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]); - else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]); + else + { + int checkmark = checkmarks[checkmarkOffset]; + pSquareBg.setColor(colors[Integer.min(2, checkmark)]); + } pSquareFg.setColor(reverseTextColor); canvas.drawRect(location, pSquareBg); diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java index 08be54b39..7d02974e4 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java @@ -20,7 +20,6 @@ package org.isoron.uhabits.activities.habits.list; import android.support.annotation.*; -import android.util.*; import org.isoron.uhabits.*; import org.isoron.uhabits.activities.*; @@ -169,9 +168,9 @@ public class ListHabitsController { int oldValue = habit.getCheckmarks().getTodayValue(); screen.showNumberPicker(oldValue, newValue -> { - Log.d("ListHabitsController", - String.format("%s %d %d", habit.getName(), timestamp, - newValue)); + commandRunner.execute( + new CreateRepetitionCommand(habit, timestamp, newValue), + habit.getId()); }); } diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.java index 028da530c..febbfde84 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.java @@ -104,11 +104,13 @@ public class HabitCardView extends FrameLayout public void setValues(int values[]) { checkmarkPanel.setValues(values); + numberPanel.setValues(values); + numberPanel.setThreshold(10); - int[] magnitudes = new int[]{10, 100, 1000, 10000}; - int threshold = magnitudes[new Random().nextInt(4)]; - numberPanel.setThreshold(threshold); - numberPanel.initEditMode(); +// int[] magnitudes = new int[]{10, 100, 1000, 10000}; +// int threshold = magnitudes[new Random().nextInt(4)]; +// numberPanel.setThreshold(threshold); +// numberPanel.initEditMode(); postInvalidate(); } @@ -237,7 +239,7 @@ public class HabitCardView extends FrameLayout checkmarkPanel.setColor(color); numberPanel.setColor(color); - boolean isNumberHabit = false; //(new Random().nextInt(3) == 0); + boolean isNumberHabit = true; //(new Random().nextInt(3) == 0); checkmarkPanel.setVisibility(isNumberHabit ? GONE : VISIBLE); numberPanel.setVisibility(isNumberHabit ? VISIBLE : GONE); diff --git a/app/src/main/java/org/isoron/uhabits/commands/CreateRepetitionCommand.java b/app/src/main/java/org/isoron/uhabits/commands/CreateRepetitionCommand.java new file mode 100644 index 000000000..5422d7f1b --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/commands/CreateRepetitionCommand.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.commands; + +import android.support.annotation.*; + +import org.isoron.uhabits.models.*; + +/** + * Command to toggle a repetition. + */ +public class CreateRepetitionCommand extends Command +{ + @NonNull + private final Habit habit; + + private final long timestamp; + + private final int value; + + private Repetition previousRep; + + private Repetition newRep; + + public CreateRepetitionCommand(@NonNull Habit habit, + long timestamp, + int value) + { + this.timestamp = timestamp; + this.habit = habit; + this.value = value; + } + + @Override + public void execute() + { + RepetitionList reps = habit.getRepetitions(); + + previousRep = reps.getByTimestamp(timestamp); + if (previousRep != null) reps.remove(previousRep); + + newRep = new Repetition(timestamp, value); + reps.add(newRep); + + habit.invalidateNewerThan(timestamp); + } + + @NonNull + public Habit getHabit() + { + return habit; + } + + @Override + public void undo() + { + habit.getRepetitions().remove(newRep); + if (previousRep != null) habit.getRepetitions().add(previousRep); + habit.invalidateNewerThan(timestamp); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java index 86488a8d1..5e71eb9e5 100644 --- a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java @@ -27,6 +27,9 @@ import java.io.*; import java.text.*; import java.util.*; +import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY; +import static org.isoron.uhabits.models.Checkmark.CHECKED_IMPLICITLY; + /** * The collection of {@link Checkmark}s belonging to a habit. */ @@ -239,7 +242,7 @@ public abstract class CheckmarkList for (Repetition rep : reps) { int offset = (int) ((rep.getTimestamp() - fromExtended) / day); - checks[nDaysExtended - offset - 1] = Checkmark.CHECKED_EXPLICITLY; + checks[nDaysExtended - offset - 1] = rep.getValue(); } for (int i = 0; i < nDays; i++) @@ -247,11 +250,11 @@ public abstract class CheckmarkList int counter = 0; for (int j = 0; j < freq.getDenominator(); j++) - if (checks[i + j] == 2) counter++; + if (checks[i + j] == CHECKED_EXPLICITLY) counter++; if (counter >= freq.getNumerator()) - if (checks[i] != Checkmark.CHECKED_EXPLICITLY) - checks[i] = Checkmark.CHECKED_IMPLICITLY; + if (checks[i] != CHECKED_EXPLICITLY) + checks[i] = CHECKED_IMPLICITLY; } List checkmarks = new LinkedList<>(); diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java index 5ba711407..5d81ddd64 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -274,4 +274,11 @@ public class Habit .append("archived", archived) .toString(); } + + public void invalidateNewerThan(long timestamp) + { + getScores().invalidateNewerThan(timestamp); + getCheckmarks().invalidateNewerThan(timestamp); + getStreaks().invalidateNewerThan(timestamp); + } } diff --git a/app/src/main/java/org/isoron/uhabits/models/Repetition.java b/app/src/main/java/org/isoron/uhabits/models/Repetition.java index 72e378205..3104fda9c 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Repetition.java +++ b/app/src/main/java/org/isoron/uhabits/models/Repetition.java @@ -30,6 +30,8 @@ public final class Repetition private final long timestamp; + private final int value; + /** * Creates a new repetition with given parameters. *

@@ -38,9 +40,24 @@ public final class Repetition * * @param timestamp the time this repetition occurred. */ - public Repetition(long timestamp) + public Repetition(long timestamp, int value) { this.timestamp = timestamp; + this.value = value; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Repetition that = (Repetition) o; + + return new EqualsBuilder() + .append(timestamp, that.timestamp) + .append(value, that.value) + .isEquals(); } public long getTimestamp() @@ -48,11 +65,26 @@ public final class Repetition return timestamp; } + public int getValue() + { + return value; + } + + @Override + public int hashCode() + { + return new HashCodeBuilder(17, 37) + .append(timestamp) + .append(value) + .toHashCode(); + } + @Override public String toString() { return new ToStringBuilder(this) .append("timestamp", timestamp) + .append("value", value) .toString(); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java index 07fa7b681..ec355267c 100644 --- a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java @@ -192,13 +192,11 @@ public abstract class RepetitionList if (rep != null) remove(rep); else { - rep = new Repetition(timestamp); + rep = new Repetition(timestamp, Checkmark.CHECKED_EXPLICITLY); add(rep); } - habit.getScores().invalidateNewerThan(timestamp); - habit.getCheckmarks().invalidateNewerThan(timestamp); - habit.getStreaks().invalidateNewerThan(timestamp); + habit.invalidateNewerThan(timestamp); return rep; } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java index dbac82b40..cf1772e0b 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java @@ -86,7 +86,6 @@ public class MemoryRepetitionList extends RepetitionList oldestRep = rep; oldestTime = rep.getTimestamp(); } - } return oldestRep; @@ -106,7 +105,6 @@ public class MemoryRepetitionList extends RepetitionList newestRep = rep; newestTime = rep.getTimestamp(); } - } return newestRep; @@ -119,7 +117,6 @@ public class MemoryRepetitionList extends RepetitionList observable.notifyListeners(); } - @NonNull @Override public long getTotalCount() { diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java index 6278863e9..2794950f5 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java @@ -73,7 +73,7 @@ public class SQLiteRepetitionList extends RepetitionList public List getByInterval(long timeFrom, long timeTo) { check(habit.getId()); - String query = "select habit, timestamp " + + String query = "select habit, timestamp, value " + "from Repetitions " + "where habit = ? and timestamp >= ? and timestamp <= ? " + "order by timestamp"; @@ -93,7 +93,7 @@ public class SQLiteRepetitionList extends RepetitionList public Repetition getByTimestamp(long timestamp) { check(habit.getId()); - String query = "select habit, timestamp " + + String query = "select habit, timestamp, value " + "from Repetitions " + "where habit = ? and timestamp = ? " + "limit 1"; @@ -111,7 +111,7 @@ public class SQLiteRepetitionList extends RepetitionList public Repetition getOldest() { check(habit.getId()); - String query = "select habit, timestamp " + + String query = "select habit, timestamp, value " + "from Repetitions " + "where habit = ? " + "order by timestamp asc " + @@ -129,7 +129,7 @@ public class SQLiteRepetitionList extends RepetitionList public Repetition getNewest() { check(habit.getId()); - String query = "select habit, timestamp " + + String query = "select habit, timestamp, value " + "from Repetitions " + "where habit = ? " + "order by timestamp desc " + @@ -182,7 +182,6 @@ public class SQLiteRepetitionList extends RepetitionList return reps; } - @NonNull @Override public long getTotalCount() { diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java index 5f831495f..874fd0954 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java @@ -38,6 +38,9 @@ public class RepetitionRecord extends Model implements SQLiteRecord @Column(name = "timestamp") public Long timestamp; + @Column(name = "value") + public int value; + public static RepetitionRecord get(Long id) { return RepetitionRecord.load(RepetitionRecord.class, id); @@ -46,16 +49,18 @@ public class RepetitionRecord extends Model implements SQLiteRecord public void copyFrom(Repetition repetition) { timestamp = repetition.getTimestamp(); + value = repetition.getValue(); } @Override public void copyFrom(Cursor c) { timestamp = c.getLong(1); + value = c.getInt(2); } public Repetition toRepetition() { - return new Repetition(timestamp); + return new Repetition(timestamp, value); } } diff --git a/app/src/test/java/org/isoron/uhabits/commands/CreateRepetitionCommandTest.java b/app/src/test/java/org/isoron/uhabits/commands/CreateRepetitionCommandTest.java new file mode 100644 index 000000000..45816c559 --- /dev/null +++ b/app/src/test/java/org/isoron/uhabits/commands/CreateRepetitionCommandTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.commands; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; + +import static junit.framework.Assert.*; +import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY; + +public class CreateRepetitionCommandTest extends BaseUnitTest +{ + + private CreateRepetitionCommand command; + + private Habit habit; + + private long today; + + @Override + @Before + public void setUp() + { + super.setUp(); + + habit = fixtures.createShortHabit(); + + today = DateUtils.getStartOfToday(); + command = new CreateRepetitionCommand(habit, today, 100); + } + + @Test + public void testExecuteUndoRedo() + { + RepetitionList reps = habit.getRepetitions(); + + Repetition rep = reps.getByTimestamp(today); + assertNotNull(rep); + assertEquals(CHECKED_EXPLICITLY, rep.getValue()); + + command.execute(); + rep = reps.getByTimestamp(today); + assertNotNull(rep); + assertEquals(100, rep.getValue()); + + command.undo(); + rep = reps.getByTimestamp(today); + assertNotNull(rep); + assertEquals(CHECKED_EXPLICITLY, rep.getValue()); + } +}