Refactor and write docs for Score and ScoreList

pull/69/head
Alinson S. Xavier 10 years ago
parent eb017bf99b
commit 0921f9346e

@ -48,6 +48,8 @@ public class HabitFixtures
static Habit createEmptyHabit()
{
Habit habit = new Habit();
habit.freqNum = 1;
habit.freqDen = 1;
habit.save();
return habit;
}

@ -0,0 +1,146 @@
/*
* 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.unit.models;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.helpers.ActiveAndroidHelper;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScoreListTest
{
private Habit habit;
@Before
public void prepare()
{
HabitFixtures.purgeHabits();
DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME);
habit = HabitFixtures.createEmptyHabit();
}
@After
public void tearDown()
{
DateHelper.setFixedLocalTime(null);
}
@Test
public void invalidateNewerThan()
{
assertThat(habit.scores.getTodayValue(), equalTo(0));
toggleRepetitions(0, 2);
assertThat(habit.scores.getTodayValue(), equalTo(1948077));
habit.freqNum = 1;
habit.freqDen = 2;
habit.scores.invalidateNewerThan(0);
assertThat(habit.scores.getTodayValue(), equalTo(1974654));
}
@Test
public void getTodayStarValue()
{
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.EMPTY_STAR));
int k = 0;
while(habit.scores.getTodayValue() < Score.HALF_STAR_CUTOFF) toggleRepetitions(k, ++k);
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.HALF_STAR));
while(habit.scores.getTodayValue() < Score.FULL_STAR_CUTOFF) toggleRepetitions(k, ++k);
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.FULL_STAR));
}
@Test
public void getTodayValue()
{
toggleRepetitions(0, 20);
assertThat(habit.scores.getTodayValue(), equalTo(12629351));
}
@Test
public void getValue()
{
toggleRepetitions(0, 20);
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
4507040, 3699107, 2846927, 1948077, 1000000 };
long current = DateHelper.getStartOfToday();
for(int expectedValue : expectedValues)
{
assertThat(habit.scores.getValue(current), equalTo(expectedValue));
current -= DateHelper.millisecondsInOneDay;
}
}
@Test
public void getAllValues_withoutGroups()
{
toggleRepetitions(0, 20);
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
4507040, 3699107, 2846927, 1948077, 1000000 };
int actualValues[] = habit.scores.getAllValues(1);
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void getAllValues_withGroups()
{
toggleRepetitions(0, 20);
int expectedValues[] = { 12629351, 11006461, 7272612, 2800230 };
int actualValues[] = habit.scores.getAllValues(7);
assertThat(actualValues, equalTo(expectedValues));
}
private void toggleRepetitions(final int from, final int to)
{
ActiveAndroidHelper.executeAsTransaction(new ActiveAndroidHelper.Command()
{
@Override
public void execute()
{
long today = DateHelper.getStartOfToday();
for (int i = from; i < to; i++)
habit.repetitions.toggle(today - i * DateHelper.millisecondsInOneDay);
}
});
}
}

@ -0,0 +1,113 @@
/*
* 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.unit.models;
import android.graphics.Color;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.LinkedList;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import org.isoron.uhabits.models.Score;
import org.isoron.uhabits.models.Repetition;
import org.isoron.uhabits.models.Checkmark;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScoreTest
{
@Test
public void compute_withDailyHabit()
{
int checkmark = Checkmark.UNCHECKED;
assertThat(Score.compute(1, 0, checkmark), equalTo(0));
assertThat(Score.compute(1, 5000000, checkmark), equalTo(4740387));
assertThat(Score.compute(1, 10000000, checkmark), equalTo(9480775));
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(18259478));
checkmark = Checkmark.CHECKED_IMPLICITLY;
assertThat(Score.compute(1, 0, checkmark), equalTo(0));
assertThat(Score.compute(1, 5000000, checkmark), equalTo(4740387));
assertThat(Score.compute(1, 10000000, checkmark), equalTo(9480775));
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(18259478));
checkmark = Checkmark.CHECKED_EXPLICITLY;
assertThat(Score.compute(1, 0, checkmark), equalTo(1000000));
assertThat(Score.compute(1, 5000000, checkmark), equalTo(5740387));
assertThat(Score.compute(1, 10000000, checkmark), equalTo(10480775));
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
}
@Test
public void compute_withNonDailyHabit()
{
int checkmark = Checkmark.CHECKED_EXPLICITLY;
assertThat(Score.compute(1/3.0, 0, checkmark), equalTo(1000000));
assertThat(Score.compute(1/3.0, 5000000, checkmark), equalTo(5916180));
assertThat(Score.compute(1/3.0, 10000000, checkmark), equalTo(10832360));
assertThat(Score.compute(1/3.0, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
assertThat(Score.compute(1/7.0, 0, checkmark), equalTo(1000000));
assertThat(Score.compute(1/7.0, 5000000, checkmark), equalTo(5964398));
assertThat(Score.compute(1/7.0, 10000000, checkmark), equalTo(10928796));
assertThat(Score.compute(1/7.0, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
}
@Test
public void getStarStatus()
{
Score s = new Score();
s.score = Score.FULL_STAR_CUTOFF + 1;
assertThat(s.getStarStatus(), equalTo(Score.FULL_STAR));
s.score = Score.FULL_STAR_CUTOFF;
assertThat(s.getStarStatus(), equalTo(Score.FULL_STAR));
s.score = Score.FULL_STAR_CUTOFF - 1;
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
s.score = Score.HALF_STAR_CUTOFF + 1;
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
s.score = Score.HALF_STAR_CUTOFF;
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
s.score = Score.HALF_STAR_CUTOFF - 1;
assertThat(s.getStarStatus(), equalTo(Score.EMPTY_STAR));
s.score = 0;
assertThat(s.getStarStatus(), equalTo(Score.EMPTY_STAR));
}
}

@ -0,0 +1,29 @@
package org.isoron.helpers;
import com.activeandroid.ActiveAndroid;
public class ActiveAndroidHelper
{
public interface Command
{
void execute();
}
public static void executeAsTransaction(Command command)
{
ActiveAndroid.beginTransaction();
try
{
command.execute();
ActiveAndroid.setTransactionSuccessful();
}
catch (RuntimeException e)
{
throw e;
}
finally
{
ActiveAndroid.endTransaction();
}
}
}

@ -50,7 +50,7 @@ public class EditHabitCommand extends Command
{
habit.checkmarks.deleteNewerThan(0);
habit.streaks.deleteNewerThan(0);
habit.scores.deleteNewerThan(0);
habit.scores.invalidateNewerThan(0);
}
}
@ -65,7 +65,7 @@ public class EditHabitCommand extends Command
{
habit.checkmarks.deleteNewerThan(0);
habit.streaks.deleteNewerThan(0);
habit.scores.deleteNewerThan(0);
habit.scores.invalidateNewerThan(0);
}
}

@ -133,7 +133,7 @@ public class ShowHabitFragment extends Fragment
{
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
scoreRing.setColor(habit.color);
scoreRing.setPercentage((float) habit.scores.getNewestValue() / Score.MAX_SCORE);
scoreRing.setPercentage((float) habit.scores.getTodayValue() / Score.MAX_VALUE);
}
private void updateHeaders(View view)

@ -75,7 +75,7 @@ public class CSVExporter
public String formatScore(int score)
{
return String.format("%.2f", ((float) score) / Score.MAX_SCORE);
return String.format("%.2f", ((float) score) / Score.MAX_VALUE);
}
private void writeScores(String dirPath, Habit habit) throws IOException

@ -144,7 +144,7 @@ public class HabitListLoader
if (isCancelled()) return null;
Long id = h.getId();
newScores.put(id, h.scores.getNewestValue());
newScores.put(id, h.scores.getTodayValue());
newCheckmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
publishProgress(current++, newHabits.size());
@ -213,7 +213,7 @@ public class HabitListLoader
Habit h = Habit.get(id);
habits.put(id, h);
scores.put(id, h.scores.getNewestValue());
scores.put(id, h.scores.getTodayValue());
checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
return null;

@ -117,21 +117,25 @@ public class Habit extends Model
/**
* List of streaks belonging to this habit.
*/
@NonNull
public StreakList streaks;
/**
* List of scores belonging to this habit.
*/
@NonNull
public ScoreList scores;
/**
* List of repetitions belonging to this habit.
*/
@NonNull
public RepetitionList repetitions;
/**
* List of checkmarks belonging to this habit.
*/
@NonNull
public CheckmarkList checkmarks;
/**
@ -142,7 +146,11 @@ public class Habit extends Model
public Habit(Habit model)
{
copyAttributes(model);
initializeLists();
checkmarks = new CheckmarkList(this);
streaks = new StreakList(this);
scores = new ScoreList(this);
repetitions = new RepetitionList(this);
}
/**
@ -157,15 +165,11 @@ public class Habit extends Model
this.archived = 0;
this.freqDen = 7;
this.freqNum = 3;
initializeLists();
}
private void initializeLists()
{
checkmarks = new CheckmarkList(this);
streaks = new StreakList(this);
scores = new ScoreList(this);
repetitions = new RepetitionList(this);
checkmarks = new CheckmarkList(this);
}
/**

@ -107,7 +107,7 @@ public class RepetitionList
rep.save();
}
habit.scores.deleteNewerThan(timestamp);
habit.scores.invalidateNewerThan(timestamp);
habit.checkmarks.deleteNewerThan(timestamp);
habit.streaks.deleteNewerThan(timestamp);
}

@ -26,16 +26,94 @@ import com.activeandroid.annotation.Table;
@Table(name = "Score")
public class Score extends Model
{
/**
* Minimum score value required to earn half a star.
*/
public static final int HALF_STAR_CUTOFF = 9629750;
/**
* Minimum score value required to earn a full star.
*/
public static final int FULL_STAR_CUTOFF = 15407600;
public static final int MAX_SCORE = 19259500;
/**
* Maximum score value attainable by any habit.
*/
public static final int MAX_VALUE = 19259478;
/**
* Status indicating that the habit has not earned any star.
*/
public static final int EMPTY_STAR = 0;
/**
* Status indicating that the habit has earned half a star.
*/
public static final int HALF_STAR = 1;
/**
* Status indicating that the habit has earned a full star.
*/
public static final int FULL_STAR = 2;
/**
* Habit to which this score belongs to.
*/
@Column(name = "habit")
public Habit habit;
/**
* Timestamp of the day to which this score applies. Time of day should be midnight (UTC).
*/
@Column(name = "timestamp")
public Long timestamp;
/**
* Value of the score.
*/
@Column(name = "score")
public Integer score;
/**
* Given the frequency of the habit, the previous score, and the value of the current checkmark,
* computes the current score for the habit.
*
* The frequency of the habit is the number of repetitions divided by the length of the
* interval. For example, a habit that should be repeated 3 times in 8 days has frequency 3.0 /
* 8.0 = 0.375.
*
* The checkmarkValue should be UNCHECKED, CHECKED_IMPLICITLY or CHECK_EXPLICITLY.
*
* @param frequency the frequency of the habit
* @param previousScore the previous score of the habit
* @param checkmarkValue the value of the current checkmark
*
* @return the current score
*/
public static int compute(double frequency, int previousScore, int checkmarkValue)
{
double multiplier = Math.pow(0.5, 1.0 / (14.0 / frequency - 1));
int score = (int) (previousScore * multiplier);
if (checkmarkValue == Checkmark.CHECKED_EXPLICITLY)
{
score += 1000000;
score = Math.min(score, Score.MAX_VALUE);
}
return score;
}
/**
* Return the current star status for the habit, which can one of EMPTY_STAR, HALF_STAR or
* FULL_STAR.
*
* @return current star status
*/
public int getStarStatus()
{
if(score >= Score.FULL_STAR_CUTOFF) return Score.FULL_STAR;
if(score >= Score.HALF_STAR_CUTOFF) return Score.HALF_STAR;
return Score.EMPTY_STAR;
}
}

@ -21,42 +21,73 @@ package org.isoron.uhabits.models;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.activeandroid.ActiveAndroid;
import com.activeandroid.Cache;
import com.activeandroid.query.Delete;
import com.activeandroid.query.From;
import com.activeandroid.query.Select;
import org.isoron.helpers.ActiveAndroidHelper;
import org.isoron.helpers.DateHelper;
public class ScoreList
{
@NonNull
private Habit habit;
public ScoreList(Habit habit)
/**
* Constructs a new ScoreList associated with the given habit.
*
* @param habit the habit this list should be associated with
*/
public ScoreList(@NonNull Habit habit)
{
this.habit = habit;
}
public int getCurrentStarStatus()
protected From select()
{
int score = getNewestValue();
return new Select()
.from(Score.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc");
}
if(score >= Score.FULL_STAR_CUTOFF) return 2;
else if(score >= Score.HALF_STAR_CUTOFF) return 1;
else return 0;
/**
* Returns the most recent score already computed. If no score has been computed yet, returns
* null.
*
* @return newest score, or null if none exist
*/
@Nullable
protected Score findNewest()
{
return select().limit(1).executeSingle();
}
public Score getNewest()
/**
* Returns the value of the most recent score that was already computed. If no score has been
* computed yet, returns zero.
*
* @return value of newest score, or zero if none exist
*/
protected int findNewestValue()
{
return new Select().from(Score.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc")
.limit(1)
.executeSingle();
Score newest = findNewest();
if(newest == null) return 0;
else return newest.score;
}
public void deleteNewerThan(long timestamp)
/**
* Marks all scores that have timestamp equal to or newer than the given timestamp as invalid.
* Any following getValue calls will trigger the scores to be recomputed.
*
* @param timestamp the oldest timestamp that should be invalidated
*/
public void invalidateNewerThan(long timestamp)
{
new Delete().from(Score.class)
.where("habit = ?", habit.getId())
@ -64,79 +95,137 @@ public class ScoreList
.execute();
}
public Integer getNewestValue()
/**
* Computes and saves the scores that are missing inside a given time interval. Scores that
* have already been computed are skipped, therefore there is no harm in calling this function
* more times, or with larger intervals, than strictly needed. The endpoints of the interval are
* included.
*
* This function assumes that there are no gaps on the scores. That is, if the newest score has
* timestamp t, then every score with timestamp lower than t has already been computed.
*
* @param from timestamp of the beginning of the interval
* @param to timestamp of the end of the time interval
*/
protected void compute(long from, long to)
{
int beginningScore;
long beginningTime;
long today = DateHelper.getStartOfDay(DateHelper.getLocalTime());
long day = DateHelper.millisecondsInOneDay;
double freq = ((double) habit.freqNum) / habit.freqDen;
double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1));
Score newestScore = getNewest();
if (newestScore == null)
{
Repetition oldestRep = habit.repetitions.getOldest();
if (oldestRep == null) return 0;
beginningTime = oldestRep.timestamp;
beginningScore = 0;
}
else
{
beginningTime = newestScore.timestamp + day;
beginningScore = newestScore.score;
}
final long day = DateHelper.millisecondsInOneDay;
final double freq = ((double) habit.freqNum) / habit.freqDen;
long nDays = (today - beginningTime) / day;
if (nDays < 0) return newestScore.score;
int newestScoreValue = findNewestValue();
Score newestScore = findNewest();
int reps[] = habit.checkmarks.getValues(beginningTime, today);
if(newestScore != null)
from = newestScore.timestamp + day;
ActiveAndroid.beginTransaction();
int lastScore = beginningScore;
final int checkmarkValues[] = habit.checkmarks.getValues(from, to);
final int firstScore = newestScoreValue;
final long beginning = from;
try
ActiveAndroidHelper.executeAsTransaction(new ActiveAndroidHelper.Command()
{
for (int i = 0; i < reps.length; i++)
@Override
public void execute()
{
Score s = new Score();
s.habit = habit;
s.timestamp = beginningTime + day * i;
s.score = (int) (lastScore * multiplier);
if (reps[reps.length - i - 1] == 2)
int lastScore = firstScore;
for (int i = 0; i < checkmarkValues.length; i++)
{
s.score += 1000000;
s.score = Math.min(s.score, Score.MAX_SCORE);
}
s.save();
int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1];
lastScore = s.score;
Score s = new Score();
s.habit = habit;
s.timestamp = beginning + day * i;
s.score = lastScore = Score.compute(freq, lastScore, checkmarkValue);
s.save();
}
}
});
}
ActiveAndroid.setTransactionSuccessful();
} finally
{
ActiveAndroid.endTransaction();
}
/**
* Returns the score for a certain day.
*
* @param timestamp the timestamp for the day
* @return the score for the day
*/
@Nullable
protected Score get(long timestamp)
{
Repetition oldestRep = habit.repetitions.getOldest();
if(oldestRep == null) return null;
return lastScore;
compute(oldestRep.timestamp, timestamp);
return select().where("timestamp = ?", timestamp).executeSingle();
}
public int[] getAllValues(Long fromTimestamp, Long toTimestamp, Long divisor)
/**
* Returns the value of the score for a given day.
*
* @param timestamp the timestamp of a day
* @return score for that day
*/
public int getValue(long timestamp)
{
// Force rebuild of the score table
getNewestValue();
Score s = get(timestamp);
if(s == null) return 0;
else return s.score;
}
Long offset = toTimestamp - (divisor - 1) * DateHelper.millisecondsInOneDay;
/**
* Returns the values of all the scores, from day of the first repetition until today, grouped
* in chunks of specified size.
*
* If the group size is one, then the value of each score is returned individually. If the group
* is, for example, seven, then the days are grouped in groups of seven consecutive days.
*
* The values are returned in an array of integers, with one entry for each group of days in the
* interval. This value corresponds to the average of the scores for the days inside the group.
* The first entry corresponds to the ending of the interval (that is, the most recent group of
* days). The last entry corresponds to the beginning of the interval. As usual, the time of the
* day for the timestamps should be midnight (UTC). The endpoints of the interval are included.
*
* The values are returned in an integer array. There is one entry for each day inside the
* interval. The first entry corresponds to today, while the last entry corresponds to the
* day of the oldest repetition.
*
* @param divisor the size of the groups
* @return array of values, with one entry for each group of days
*/
@NonNull
public int[] getAllValues(long divisor)
{
Repetition oldestRep = habit.repetitions.getOldest();
if(oldestRep == null) return new int[0];
long fromTimestamp = oldestRep.timestamp;
long toTimestamp = DateHelper.getStartOfToday();
return getValues(fromTimestamp, toTimestamp, divisor);
}
/**
* Same as getAllValues(long), but using a specified interval.
*
* @param from beginning of the interval (included)
* @param to end of the interval (included)
* @param divisor size of the groups
* @return array of values, with one entry for each group of days
*/
@NonNull
protected int[] getValues(long from, long to, long divisor)
{
compute(from, to);
divisor *= DateHelper.millisecondsInOneDay;
Long offset = to + divisor - 1;
String query = "select ((timestamp - ?) / ?) as time, avg(score) from Score " +
"where habit = ? and timestamp > ? and timestamp <= ? " +
"where habit = ? and timestamp >= ? and timestamp <= ? " +
"group by time order by time desc";
String params[] = { offset.toString(), divisor.toString(), habit.getId().toString(),
fromTimestamp.toString(), toTimestamp.toString()};
String params[] = { offset.toString(), Long.toString(divisor), habit.getId().toString(),
Long.toString(from), Long.toString(to) };
SQLiteDatabase db = Cache.openDatabase();
Cursor cursor = db.rawQuery(query, params);
@ -148,22 +237,45 @@ public class ScoreList
do
{
scores[k++] = (int) cursor.getLong(1);
scores[k++] = (int) cursor.getFloat(1);
}
while (cursor.moveToNext());
cursor.close();
return scores;
}
/**
* Returns the score for today.
*
* @return score for today
*/
@Nullable
protected Score getToday()
{
return get(DateHelper.getStartOfToday());
}
public int[] getAllValues(long divisor)
/**
* Returns the value of the score for today.
*
* @return value of today's score
*/
public int getTodayValue()
{
Repetition oldestRep = habit.repetitions.getOldest();
if(oldestRep == null) return new int[0];
return getValue(DateHelper.getStartOfToday());
}
long fromTimestamp = oldestRep.timestamp;
long toTimestamp = DateHelper.getStartOfToday();
return getAllValues(fromTimestamp, toTimestamp, divisor);
/**
* Returns the star status for today. The returned value is either Score.EMPTY_STAR,
* Score.HALF_STAR or Score.FULL_STAR.
*
* @return star status for today
*/
public int getTodayStarStatus()
{
Score score = getToday();
if(score != null) return score.getStarStatus();
else return Score.EMPTY_STAR;
}
}

@ -115,7 +115,7 @@ public class CheckmarkView extends View
public void setHabit(Habit habit)
{
this.check_status = habit.checkmarks.getTodayValue();
this.star_status = habit.scores.getCurrentStarStatus();
this.star_status = habit.scores.getTodayStarStatus();
this.primaryColor = Color.argb(230, Color.red(habit.color), Color.green(habit.color), Color.blue(habit.color));
this.label = habit.name;
updateLabel();

@ -171,7 +171,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
else
{
if (habit == null) return;
scores = habit.scores.getAllValues(BUCKET_SIZE * DateHelper.millisecondsInOneDay);
scores = habit.scores.getAllValues(BUCKET_SIZE);
}
invalidate();
@ -181,13 +181,13 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
{
Random random = new Random();
scores = new int[100];
scores[0] = Score.MAX_SCORE / 2;
scores[0] = Score.MAX_VALUE / 2;
for(int i = 1; i < 100; i++)
{
int step = Score.MAX_SCORE / 10;
int step = Score.MAX_VALUE / 10;
scores[i] = scores[i - 1] + random.nextInt(step * 2) - step;
scores[i] = Math.max(0, Math.min(Score.MAX_SCORE, scores[i]));
scores[i] = Math.max(0, Math.min(Score.MAX_VALUE, scores[i]));
}
}
@ -224,7 +224,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
int offset = nColumns - k - 1 + getDataOffset();
if(offset < scores.length) score = scores[offset];
double sRelative = ((double) score) / Score.MAX_SCORE;
double sRelative = ((double) score) / Score.MAX_VALUE;
int height = (int) (columnHeight * sRelative);
rect.set(0, 0, baseSize, baseSize);

Loading…
Cancel
Save