mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Improve performance of toggleCheckmark
This commit is contained in:
@@ -25,6 +25,7 @@ import android.test.suitebuilder.annotation.SmallTest;
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Repetition;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -37,6 +38,7 @@ import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@@ -174,4 +176,16 @@ public class RepetitionListTest extends BaseTest
|
||||
from = to - 5 * DateHelper.millisecondsInOneDay;
|
||||
assertThat(habit.repetitions.count(from, to), equalTo(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getOldest()
|
||||
{
|
||||
long expectedOldestTimestamp = DateHelper.getStartOfToday() - 9 * DateHelper.millisecondsInOneDay;
|
||||
|
||||
assertThat(habit.repetitions.getOldestTimestamp(), equalTo(expectedOldestTimestamp));
|
||||
|
||||
Repetition oldest = habit.repetitions.getOldest();
|
||||
assertFalse(oldest == null);
|
||||
assertThat(oldest.timestamp, equalTo(expectedOldestTimestamp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.Cache;
|
||||
import com.activeandroid.Configuration;
|
||||
|
||||
import org.isoron.uhabits.BuildConfig;
|
||||
@@ -163,4 +165,20 @@ public class DatabaseHelper
|
||||
|
||||
ActiveAndroid.initialize(dbConfig);
|
||||
}
|
||||
|
||||
public static long longQuery(String query, String args[])
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = Cache.openDatabase().rawQuery(query, args);
|
||||
if (!c.moveToFirst()) return 0;
|
||||
return c.getLong(0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(c != null) c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ package org.isoron.uhabits.models;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
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.Select;
|
||||
@@ -134,10 +134,9 @@ public class CheckmarkList
|
||||
*/
|
||||
protected void computeAll()
|
||||
{
|
||||
Repetition oldestRep = habit.repetitions.getOldest();
|
||||
if(oldestRep == null) return;
|
||||
long fromTimestamp = habit.repetitions.getOldestTimestamp();
|
||||
if(fromTimestamp == 0) return;
|
||||
|
||||
Long fromTimestamp = oldestRep.timestamp;
|
||||
Long toTimestamp = DateHelper.getStartOfToday();
|
||||
|
||||
compute(fromTimestamp, toTimestamp);
|
||||
@@ -150,9 +149,9 @@ public class CheckmarkList
|
||||
* @param from timestamp for the beginning of the interval
|
||||
* @param to timestamp for the end of the interval
|
||||
*/
|
||||
protected void compute(long from, long to)
|
||||
protected void compute(long from, final long to)
|
||||
{
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
final long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
Checkmark newestCheckmark = findNewest();
|
||||
if(newestCheckmark != null)
|
||||
@@ -165,9 +164,9 @@ public class CheckmarkList
|
||||
.selectFromTo(fromExtended, to)
|
||||
.execute();
|
||||
|
||||
int nDays = (int) ((to - from) / day) + 1;
|
||||
final int nDays = (int) ((to - from) / day) + 1;
|
||||
int nDaysExtended = (int) ((to - fromExtended) / day) + 1;
|
||||
int checks[] = new int[nDaysExtended];
|
||||
final int checks[] = new int[nDaysExtended];
|
||||
|
||||
for (Repetition rep : reps)
|
||||
{
|
||||
@@ -187,24 +186,38 @@ public class CheckmarkList
|
||||
checks[i] = Checkmark.CHECKED_IMPLICITLY;
|
||||
}
|
||||
|
||||
ActiveAndroid.beginTransaction();
|
||||
|
||||
long timestamps[] = new long[nDays];
|
||||
for (int i = 0; i < nDays; i++)
|
||||
timestamps[i] = to - i * day;
|
||||
|
||||
insert(timestamps, checks);
|
||||
}
|
||||
|
||||
private void insert(long timestamps[], int values[])
|
||||
{
|
||||
String query = "insert into Checkmarks(habit, timestamp, value) values (?,?,?)";
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
db.beginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < nDays; i++)
|
||||
SQLiteStatement statement = db.compileStatement(query);
|
||||
statement.bindString(1, habit.getId().toString());
|
||||
|
||||
for (int i = 0; i < timestamps.length; i++)
|
||||
{
|
||||
Checkmark c = new Checkmark();
|
||||
c.habit = habit;
|
||||
c.timestamp = to - i * day;
|
||||
c.value = checks[i];
|
||||
c.save();
|
||||
statement.bindLong(2, timestamps[i]);
|
||||
statement.bindLong(3, values[i]);
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ import com.activeandroid.Cache;
|
||||
import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.From;
|
||||
import com.activeandroid.query.Select;
|
||||
import com.activeandroid.util.SQLiteUtils;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -96,22 +98,21 @@ public class RepetitionList
|
||||
timestamp = DateHelper.getStartOfDay(timestamp);
|
||||
|
||||
if (contains(timestamp))
|
||||
{
|
||||
delete(timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Repetition rep = new Repetition();
|
||||
rep.habit = habit;
|
||||
rep.timestamp = timestamp;
|
||||
rep.save();
|
||||
}
|
||||
insert(timestamp);
|
||||
|
||||
habit.scores.invalidateNewerThan(timestamp);
|
||||
habit.checkmarks.deleteNewerThan(timestamp);
|
||||
habit.streaks.deleteNewerThan(timestamp);
|
||||
}
|
||||
|
||||
private void insert(long timestamp)
|
||||
{
|
||||
String[] args = { habit.getId().toString(), Long.toString(timestamp) };
|
||||
SQLiteUtils.execSql("insert into Repetitions(habit, timestamp) values (?,?)", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oldest repetition for the habit. If there is no repetition, returns null.
|
||||
* Repetitions in the future are discarded.
|
||||
@@ -124,6 +125,21 @@ public class RepetitionList
|
||||
return (Repetition) select().limit(1).executeSingle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the oldest repetition. If there are no repetitions, returns zero.
|
||||
* Repetitions in the future are discarded.
|
||||
*
|
||||
* @return timestamp of the oldest repetition
|
||||
*/
|
||||
public long getOldestTimestamp()
|
||||
{
|
||||
String[] args = { habit.getId().toString(), Long.toString(DateHelper.getStartOfToday()) };
|
||||
String query = "select timestamp from Repetitions where habit = ? and timestamp <= ? " +
|
||||
"order by timestamp limit 1";
|
||||
|
||||
return DatabaseHelper.longQuery(query, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of repetitions for each month, from the first repetition until
|
||||
* today, grouped by day of week. The repetitions are returned in a HashMap. The key is the
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.models;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -28,8 +29,8 @@ import com.activeandroid.Cache;
|
||||
import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.From;
|
||||
import com.activeandroid.query.Select;
|
||||
import com.activeandroid.util.SQLiteUtils;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -104,10 +105,9 @@ public class ScoreList
|
||||
*/
|
||||
private void computeAll()
|
||||
{
|
||||
Repetition oldestRep = habit.repetitions.getOldest();
|
||||
if(oldestRep == null) return;
|
||||
long fromTimestamp = habit.repetitions.getOldestTimestamp();
|
||||
if(fromTimestamp == 0) return;
|
||||
|
||||
long fromTimestamp = oldestRep.timestamp;
|
||||
long toTimestamp = DateHelper.getStartOfToday();
|
||||
compute(fromTimestamp, toTimestamp);
|
||||
}
|
||||
@@ -136,28 +136,51 @@ public class ScoreList
|
||||
from = newestScore.timestamp + day;
|
||||
|
||||
final int checkmarkValues[] = habit.checkmarks.getValues(from, to);
|
||||
final int firstScore = newestScoreValue;
|
||||
final long beginning = from;
|
||||
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
|
||||
int lastScore = newestScoreValue;
|
||||
int size = checkmarkValues.length;
|
||||
|
||||
long timestamps[] = new long[size];
|
||||
long values[] = new long[size];
|
||||
|
||||
for (int i = 0; i < checkmarkValues.length; i++)
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1];
|
||||
lastScore = Score.compute(freq, lastScore, checkmarkValue);
|
||||
timestamps[i] = beginning + day * i;
|
||||
values[i] = lastScore;
|
||||
}
|
||||
|
||||
insert(timestamps, values);
|
||||
}
|
||||
|
||||
private void insert(long timestamps[], long values[])
|
||||
{
|
||||
String query = "insert into Score(habit, timestamp, score) values (?,?,?)";
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
db.beginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
SQLiteStatement statement = db.compileStatement(query);
|
||||
statement.bindString(1, habit.getId().toString());
|
||||
|
||||
for (int i = 0; i < timestamps.length; i++)
|
||||
{
|
||||
int lastScore = firstScore;
|
||||
|
||||
for (int i = 0; i < checkmarkValues.length; i++)
|
||||
{
|
||||
int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1];
|
||||
|
||||
Score s = new Score();
|
||||
s.habit = habit;
|
||||
s.timestamp = beginning + day * i;
|
||||
s.score = lastScore = Score.compute(freq, lastScore, checkmarkValue);
|
||||
s.save();
|
||||
}
|
||||
statement.bindLong(2, timestamps[i]);
|
||||
statement.bindLong(3, values[i]);
|
||||
statement.execute();
|
||||
}
|
||||
});
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,9 +208,9 @@ public class ScoreList
|
||||
*/
|
||||
public int getValue(long timestamp)
|
||||
{
|
||||
Score s = get(timestamp);
|
||||
if(s == null) return 0;
|
||||
else return s.score;
|
||||
computeAll();
|
||||
String[] args = { habit.getId().toString(), Long.toString(timestamp) };
|
||||
return SQLiteUtils.intQuery("select score from Score where habit = ? and timestamp = ?", args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user