From 5e21d877c5f41123357a4ad719679dc6d91284e7 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Thu, 25 Feb 2016 20:17:44 -0500 Subject: [PATCH 1/2] Fetch all the data with one call --- .../java/org/isoron/uhabits/models/Habit.java | 49 ++++++++++++++++--- .../uhabits/views/HabitHistoryView.java | 20 ++------ .../isoron/uhabits/views/HabitScoreView.java | 42 +++++++--------- .../isoron/uhabits/views/HabitStreakView.java | 3 +- .../uhabits/views/ScrollableDataView.java | 1 - 5 files changed, 66 insertions(+), 49 deletions(-) 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 ccd483458..2ddfadcf7 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -294,6 +294,16 @@ public class Habit extends Model return checks; } + public int[] getAllCheckmarks() + { + Repetition oldestRep = getOldestRep(); + if(oldestRep == null) return new int[0]; + + Long toTimestamp = DateHelper.getStartOfToday(); + Long fromTimestamp = oldestRep.timestamp; + return getCheckmarks(fromTimestamp, toTimestamp); + } + public void updateCheckmarks() { long beginning; @@ -512,13 +522,40 @@ public class Habit extends Model return lastScore; } - public List getScores(long fromTimestamp, long toTimestamp, int divisor, long offset) + public int[] getScores(Long fromTimestamp, Long toTimestamp, Integer divisor, Long offset) { - return new Select().from(Score.class) - .where("habit = ? and timestamp > ? and " + - "timestamp <= ? and (timestamp - ?) % ? = 0", getId(), fromTimestamp, - toTimestamp, offset, divisor) - .execute(); + String query = "select score from Score where habit = ? and timestamp > ? and " + + "timestamp <= ? and (timestamp - ?) % ? = 0 order by timestamp desc"; + + String params[] = {getId().toString(), fromTimestamp.toString(), toTimestamp.toString(), + offset.toString(), divisor.toString()}; + + SQLiteDatabase db = Cache.openDatabase(); + Cursor c = db.rawQuery(query, params); + + if(!c.moveToFirst()) return new int[0]; + + int k = 0; + int[] scores = new int[c.getCount()]; + + do + { + scores[k++] = c.getInt(0); + } + while (c.moveToNext()); + + return scores; + + } + + public int[] getAllScores(int divisor) + { + Repetition oldestRep = getOldestRep(); + if(oldestRep == null) return new int[0]; + + long fromTimestamp = oldestRep.timestamp; + long toTimestamp = DateHelper.getStartOfToday(); + return getScores(fromTimestamp, toTimestamp, divisor, toTimestamp); } public List getStreaks() diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java index 3d0a25396..8255f489f 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java @@ -126,22 +126,7 @@ public class HabitHistoryView extends ScrollableDataView protected void fetchData() { - Calendar currentDate = new GregorianCalendar(); - currentDate.add(Calendar.DAY_OF_YEAR, -dataOffset * 7); - int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7; - - long dateTo = DateHelper.getStartOfToday(); - for (int i = 0; i < 7 - dayOfWeek; i++) - dateTo += DateHelper.millisecondsInOneDay; - - for (int i = 0; i < dataOffset * 7; i++) - dateTo -= DateHelper.millisecondsInOneDay; - - long dateFrom = dateTo; - for (int i = 0; i < (nColumns - 1) * 7; i++) - dateFrom -= DateHelper.millisecondsInOneDay; - - checkmarks = habit.getCheckmarks(dateFrom, dateTo); + checkmarks = habit.getAllCheckmarks(); updateDate(); } @@ -160,6 +145,7 @@ public class HabitHistoryView extends ScrollableDataView previousYear = ""; justPrintedYear = false; + updateDate(); GregorianCalendar currentDate = (GregorianCalendar) baseDate.clone(); for (int column = 0; column < nColumns - 1; column++) @@ -180,7 +166,7 @@ public class HabitHistoryView extends ScrollableDataView { if (!(column == nColumns - 2 && dataOffset == 0 && j > todayWeekday)) { - int checkmarkOffset = nDays - 7 * column - j; + int checkmarkOffset = dataOffset * 7 + nDays - 7 * (column + 1) + todayWeekday - j; drawSquare(canvas, location, date, checkmarkOffset); } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java index 6b4294f80..054aca4a6 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java @@ -25,10 +25,8 @@ import android.graphics.RectF; import org.isoron.helpers.ColorHelper; import org.isoron.helpers.DateHelper; import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Score; import java.text.SimpleDateFormat; -import java.util.List; public class HabitScoreView extends ScrollableDataView { @@ -41,7 +39,7 @@ public class HabitScoreView extends ScrollableDataView private Paint pText, pGraph; private int[] colors; - private List scores; + private int[] scores; public HabitScoreView(Context context, Habit habit, int columnWidth) { @@ -82,17 +80,7 @@ public class HabitScoreView extends ScrollableDataView protected void fetchData() { - - long toTimestamp = DateHelper.getStartOfToday(); - for (int i = 0; i < dataOffset * BUCKET_SIZE; i++) - toTimestamp -= DateHelper.millisecondsInOneDay; - - long fromTimestamp = toTimestamp; - for (int i = 0; i < nColumns * BUCKET_SIZE; i++) - fromTimestamp -= DateHelper.millisecondsInOneDay; - - scores = habit.getScores(fromTimestamp, toTimestamp, - BUCKET_SIZE * DateHelper.millisecondsInOneDay, toTimestamp); + scores = habit.getAllScores(BUCKET_SIZE * DateHelper.millisecondsInOneDay); } @Override @@ -114,19 +102,25 @@ public class HabitScoreView extends ScrollableDataView pGraph.setColor(habit.color); RectF prevR = null; - for (int offset = nColumns - scores.size(); offset < nColumns; offset++) + long currentDate = DateHelper.getStartOfToday(); + + for(int k = 0; k < nColumns + dataOffset - 1; k++) + currentDate -= 7 * DateHelper.millisecondsInOneDay; + + for (int k = 0; k < nColumns; k++) { - Score score = scores.get(offset - nColumns + scores.size()); - String month = dfMonth.format(score.timestamp); - String day = dfDay.format(score.timestamp); + String month = dfMonth.format(currentDate); + String day = dfDay.format(currentDate); - long s = score.score; - double sRelative = ((double) s) / Habit.MAX_SCORE; + int score = 0; + int offset = nColumns - k - 1 + dataOffset; + if(offset < scores.length) score = scores[offset]; + double sRelative = ((double) score) / Habit.MAX_SCORE; int height = (int) (columnHeight * sRelative); RectF r = new RectF(0, 0, columnWidth, columnWidth); - r.offset(offset * columnWidth, + r.offset(k * columnWidth, headerHeight + columnHeight - height - columnWidth / 2); if (prevR != null) @@ -135,19 +129,19 @@ public class HabitScoreView extends ScrollableDataView drawMarker(canvas, prevR); } - if (offset == nColumns - 1) drawMarker(canvas, r); + if (k == nColumns - 1) drawMarker(canvas, r); prevR = r; r = new RectF(0, 0, columnWidth, columnHeight); - r.offset(offset * columnWidth, headerHeight); + r.offset(k * columnWidth, headerHeight); if (!month.equals(previousMonth)) canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText); else canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText); previousMonth = month; - + currentDate += 7 * DateHelper.millisecondsInOneDay; } } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java index cf3fb7569..a3a1eb31f 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java @@ -96,13 +96,14 @@ public class HabitStreakView extends ScrollableDataView float barHeaderOffset = lineHeight * 0.4f; int nStreaks = streaks.size(); - int start = Math.max(0, nStreaks - nColumns - dataOffset); + int start = nStreaks - nColumns - dataOffset; SimpleDateFormat dfMonth = new SimpleDateFormat("MMM"); String previousMonth = ""; for (int offset = 0; offset < nColumns && start + offset < nStreaks; offset++) { + if(start + offset < 0) continue; String month = dfMonth.format(streaks.get(start + offset).start); long l = streaks.get(offset + start).length; diff --git a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java index 5368595a4..fbad9d127 100644 --- a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java +++ b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java @@ -49,7 +49,6 @@ public abstract class ScrollableDataView extends View if (newDataOffset != dataOffset) { dataOffset = newDataOffset; - fetchData(); invalidate(); return true; } From f9a9339042b3b1acc06857993619dd65191ecee6 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Thu, 25 Feb 2016 21:30:03 -0500 Subject: [PATCH 2/2] Use GestureDetector for scrolling --- .../uhabits/views/ScrollableDataView.java | 117 ++++++++++++------ 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java index fbad9d127..cdd7bf99e 100644 --- a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java +++ b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java @@ -19,12 +19,15 @@ package org.isoron.uhabits.views; +import android.animation.ValueAnimator; import android.content.Context; -import android.support.v4.view.MotionEventCompat; +import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; +import android.widget.Scroller; -public abstract class ScrollableDataView extends View +public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener, + ValueAnimator.AnimatorUpdateListener { protected int dataOffset; @@ -32,73 +35,105 @@ public abstract class ScrollableDataView extends View protected int columnWidth, columnHeight; protected int headerHeight, footerHeight; - private float prevX, prevY; + private GestureDetector detector; + private Scroller scroller; + private ValueAnimator scrollAnimator; public ScrollableDataView(Context context) { super(context); + + detector = new GestureDetector(context, this); + scroller = new Scroller(context, null, true); + scrollAnimator = ValueAnimator.ofFloat(0, 1); + scrollAnimator.addUpdateListener(this); } protected abstract void fetchData(); - protected boolean move(float dx) + @Override + public boolean onTouchEvent(MotionEvent event) { - int newDataOffset = dataOffset + (int) (dx / columnWidth); - newDataOffset = Math.max(0, newDataOffset); + return detector.onTouchEvent(event); + } - if (newDataOffset != dataOffset) - { - dataOffset = newDataOffset; - invalidate(); - return true; - } - else return false; + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(getMeasuredWidth(), columnHeight + headerHeight + footerHeight); } @Override - public boolean onTouchEvent(MotionEvent event) + protected void onSizeChanged(int w, int h, int oldw, int oldh) { - int action = event.getAction(); + super.onSizeChanged(w, h, oldw, oldh); + nColumns = w / columnWidth; + fetchData(); + } - int pointerIndex = MotionEventCompat.getActionIndex(event); - final float x = MotionEventCompat.getX(event, pointerIndex); - final float y = MotionEventCompat.getY(event, pointerIndex); + @Override + public boolean onDown(MotionEvent e) + { + return true; + } - if (action == MotionEvent.ACTION_DOWN) - { - prevX = x; - prevY = y; - } + @Override + public void onShowPress(MotionEvent e) + { - if (action == MotionEvent.ACTION_MOVE) - { - float dx = x - prevX; - float dy = y - prevY; + } - if (Math.abs(dy) > Math.abs(dx)) return false; + @Override + public boolean onSingleTapUp(MotionEvent e) + { + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) + { + if(Math.abs(dx) > Math.abs(dy)) getParent().requestDisallowInterceptTouchEvent(true); - if (move(dx)) - { - prevX = x; - prevY = y; - } - } + scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) -dx, (int) dy, 0); + scroller.computeScrollOffset(); + dataOffset = Math.max(0, scroller.getCurrX() / columnWidth); + postInvalidate(); return true; } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) + public void onLongPress(MotionEvent e) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(getMeasuredWidth(), columnHeight + headerHeight + footerHeight); + } @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - super.onSizeChanged(w, h, oldw, oldh); - nColumns = w / columnWidth; - fetchData(); + scroller.fling(scroller.getCurrX(), scroller.getCurrY(), (int) velocityX / 2, 0, 0, 100000, + 0, 0); + invalidate(); + + scrollAnimator.setDuration(scroller.getDuration()); + scrollAnimator.start(); + + return false; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) + { + if (!scroller.isFinished()) + { + scroller.computeScrollOffset(); + dataOffset = Math.max(0, scroller.getCurrX() / columnWidth); + postInvalidate(); + } + else + { + scrollAnimator.cancel(); + } } }