From f0de29fbfe06fc1cd57d8abd0902562f0db535b2 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Sun, 19 Mar 2017 18:31:43 -0400 Subject: [PATCH 1/4] Make HeaderView scrollable --- .../habits/list/views/HeaderView.java | 76 ++++++++++++++----- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java index 8f9d7c4d0..9492b5857 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java @@ -20,22 +20,25 @@ package org.isoron.uhabits.activities.habits.list.views; import android.content.*; +import android.content.res.*; +import android.graphics.*; import android.support.annotation.*; +import android.text.*; import android.util.*; -import android.view.*; -import android.widget.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.activities.common.views.*; import org.isoron.uhabits.activities.habits.list.*; import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.utils.*; import java.util.*; -public class HeaderView extends LinearLayout +import static org.isoron.uhabits.utils.InterfaceUtils.*; + +public class HeaderView extends ScrollableChart implements Preferences.Listener, MidnightTimer.MidnightListener { - private final Context context; private int buttonCount; @@ -45,16 +48,31 @@ public class HeaderView extends LinearLayout @Nullable private MidnightTimer midnightTimer; + private final TextPaint paint; + + private RectF rect; + public HeaderView(Context context, AttributeSet attrs) { super(context, attrs); - this.context = context; if (isInEditMode()) { setButtonCount(5); } + StyledResources res = new StyledResources(context); + setScrollerBucketSize((int) dpToPixels(context, 42)); + paint = new TextPaint(); + paint.setColor(Color.BLACK); + paint.setAntiAlias(true); + paint.setTextSize(getResources().getDimension(R.dimen.tinyTextSize)); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTypeface(Typeface.DEFAULT_BOLD); + paint.setColor(res.getColor(R.attr.mediumContrastTextColor)); + + rect = new RectF(); + Context appContext = context.getApplicationContext(); if (appContext instanceof HabitsApplication) { @@ -72,19 +90,19 @@ public class HeaderView extends LinearLayout @Override public void atMidnight() { - post(() -> createButtons()); + post(() -> invalidate()); } @Override public void onCheckmarkOrderChanged() { - createButtons(); + postInvalidate(); } public void setButtonCount(int buttonCount) { this.buttonCount = buttonCount; - createButtons(); + postInvalidate(); } @Override @@ -103,23 +121,43 @@ public class HeaderView extends LinearLayout super.onDetachedFromWindow(); } - private void createButtons() + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - removeAllViews(); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = (int) getContext() + .getResources() + .getDimension(R.dimen.checkmarkHeight); + setMeasuredDimension(width, height); + } + + @Override + protected void onDraw(Canvas canvas) + { + super.onDraw(canvas); + GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); - for (int i = 0; i < buttonCount; i++) - addView( - inflate(context, R.layout.list_habits_header_checkmark, null)); + Resources res = getContext().getResources(); + float width = res.getDimension(R.dimen.checkmarkWidth); + float height = res.getDimension(R.dimen.checkmarkHeight); + rect.set(0, 0, width, height); + rect.offset(canvas.getWidth(), 0); - for (int i = 0; i < getChildCount(); i++) + day.add(GregorianCalendar.DAY_OF_MONTH, -getDataOffset()); + float em = paint.measureText("m"); + + for (int i = 0; i < buttonCount; i++) { - int position = i; - if (shouldReverseCheckmarks()) position = getChildCount() - i - 1; + rect.offset(-width, 0); + String text = DateUtils.formatHeaderDate(day).toUpperCase(); + String[] lines = text.split("\n"); + + int y1 = (int)(rect.centerY() - 0.5 * em); + int y2 = (int)(rect.centerY() + em); - View button = getChildAt(position); - TextView label = (TextView) button.findViewById(R.id.tvCheck); - label.setText(DateUtils.formatHeaderDate(day)); + canvas.drawText(lines[0], rect.centerX(), y1, paint); + canvas.drawText(lines[1], rect.centerX(), y2, paint); day.add(GregorianCalendar.DAY_OF_MONTH, -1); } } From 3fe09efe9b7624fda9b3d93ce0ef3928330e807c Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Sun, 19 Mar 2017 20:36:16 -0400 Subject: [PATCH 2/4] Scroll checkmarks --- .../common/views/ScrollableChart.java | 32 ++++++++++++++--- .../habits/list/ListHabitsRootView.java | 10 +++++- .../list/model/HabitCardListAdapter.java | 14 ++++++++ .../habits/list/views/CheckmarkPanelView.java | 26 ++++++++++---- .../habits/list/views/HabitCardListView.java | 35 ++++++++++++++++--- .../habits/list/views/HabitCardView.java | 15 +++++++- 6 files changed, 115 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java b/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java index 5cbda9bd4..0e77043b7 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java +++ b/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java @@ -41,6 +41,13 @@ public abstract class ScrollableChart extends View private ValueAnimator scrollAnimator; + private ScrollController scrollController; + + public void setScrollController(ScrollController scrollController) + { + this.scrollController = scrollController; + } + public ScrollableChart(Context context) { super(context); @@ -64,8 +71,7 @@ public abstract class ScrollableChart extends View if (!scroller.isFinished()) { scroller.computeScrollOffset(); - dataOffset = Math.max(0, scroller.getCurrX() / scrollerBucketSize); - postInvalidate(); + updateDataOffset(); } else { @@ -73,6 +79,19 @@ public abstract class ScrollableChart extends View } } + private void updateDataOffset() + { + int newDataOffset = + Math.max(0, scroller.getCurrX() / scrollerBucketSize); + + if(newDataOffset != dataOffset) + { + dataOffset = newDataOffset; + scrollController.onDataOffsetChanged(dataOffset); + postInvalidate(); + } + } + @Override public boolean onDown(MotionEvent e) { @@ -115,8 +134,7 @@ public abstract class ScrollableChart extends View scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) -dx, (int) dy, 0); scroller.computeScrollOffset(); - dataOffset = Math.max(0, scroller.getCurrX() / scrollerBucketSize); - postInvalidate(); + updateDataOffset(); return true; } @@ -173,5 +191,11 @@ public abstract class ScrollableChart extends View scroller = new Scroller(context, null, true); scrollAnimator = ValueAnimator.ofFloat(0, 1); scrollAnimator.addUpdateListener(this); + scrollController = new ScrollController(){}; + } + + public interface ScrollController + { + default void onDataOffsetChanged(int newDataOffset) {} } } diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java index d25d5d4fd..4a3c1ee73 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java @@ -28,6 +28,7 @@ import android.widget.*; import org.isoron.uhabits.R; import org.isoron.uhabits.activities.*; +import org.isoron.uhabits.activities.common.views.*; import org.isoron.uhabits.activities.habits.list.controllers.*; import org.isoron.uhabits.activities.habits.list.model.*; import org.isoron.uhabits.activities.habits.list.views.*; @@ -43,7 +44,7 @@ import butterknife.*; public class ListHabitsRootView extends BaseRootView implements ModelObservable.Listener, TaskRunner.Listener { - public static final int MAX_CHECKMARK_COUNT = 21; + public static final int MAX_CHECKMARK_COUNT = 60; @BindView(R.id.listView) HabitCardListView listView; @@ -132,6 +133,13 @@ public class ListHabitsRootView extends BaseRootView listController.setSelectionListener(menu); listView.setController(listController); menu.setListController(listController); + header.setScrollController(new ScrollableChart.ScrollController() { + @Override + public void onDataOffsetChanged(int newDataOffset) + { + listView.setDataOffset(newDataOffset); + } + }); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java index 55656b542..1245668a0 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java @@ -178,6 +178,20 @@ public class HabitCardListAdapter listView.bindCardView(holder, habit, score, checkmarks, selected); } + @Override + public void onViewAttachedToWindow(@Nullable HabitCardViewHolder holder) + { + if (listView == null) return; + listView.attachCardView(holder); + } + + @Override + public void onViewDetachedFromWindow(@Nullable HabitCardViewHolder holder) + { + if (listView == null) return; + listView.detachCardView(holder); + } + @Override public HabitCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.java index e5b47800c..b569f9208 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.java @@ -53,6 +53,8 @@ public class CheckmarkPanelView extends LinearLayout implements Preferences.List @NonNull private Habit habit; + private int dataOffset; + public CheckmarkPanelView(Context context) { super(context); @@ -75,19 +77,23 @@ public class CheckmarkPanelView extends LinearLayout implements Preferences.List return (CheckmarkButtonView) getChildAt(position); } - public void setCheckmarkValues(int[] checkmarkValues) + public void setButtonCount(int newButtonCount) { - this.checkmarkValues = checkmarkValues; - - if (this.nButtons != checkmarkValues.length) + if(nButtons != newButtonCount) { - this.nButtons = checkmarkValues.length; + nButtons = newButtonCount; addCheckmarkButtons(); } setupCheckmarkButtons(); } + public void setCheckmarkValues(int[] checkmarkValues) + { + this.checkmarkValues = checkmarkValues; + setupCheckmarkButtons(); + } + public void setColor(int color) { this.color = color; @@ -100,6 +106,12 @@ public class CheckmarkPanelView extends LinearLayout implements Preferences.List setupCheckmarkButtons(); } + public void setDataOffset(int dataOffset) + { + this.dataOffset = dataOffset; + setupCheckmarkButtons(); + } + public void setHabit(@NonNull Habit habit) { this.habit = habit; @@ -170,11 +182,13 @@ public class CheckmarkPanelView extends LinearLayout implements Preferences.List { long timestamp = DateUtils.getStartOfToday(); long day = DateUtils.millisecondsInOneDay; + timestamp -= day * dataOffset; for (int i = 0; i < nButtons; i++) { CheckmarkButtonView buttonView = indexToButton(i); - buttonView.setValue(checkmarkValues[i]); + if(i + dataOffset >= checkmarkValues.length) break; + buttonView.setValue(checkmarkValues[i + dataOffset]); buttonView.setColor(color); setupButtonControllers(timestamp, buttonView); timestamp -= day; diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java index fab2680eb..4034e2cbf 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java @@ -26,9 +26,9 @@ import android.support.v7.widget.helper.*; import android.util.*; import android.view.*; -import org.isoron.uhabits.models.*; import org.isoron.uhabits.activities.habits.list.controllers.*; import org.isoron.uhabits.activities.habits.list.model.*; +import org.isoron.uhabits.models.*; import java.util.*; @@ -44,6 +44,10 @@ public class HabitCardListView extends RecyclerView private int checkmarkCount; + private int dataOffset; + + private LinkedList attachedHolders; + public HabitCardListView(Context context, AttributeSet attrs) { super(context, attrs); @@ -54,6 +58,13 @@ public class HabitCardListView extends RecyclerView TouchHelperCallback callback = new TouchHelperCallback(); touchHelper = new ItemTouchHelper(callback); touchHelper.attachToRecyclerView(this); + + attachedHolders = new LinkedList<>(); + } + + public void attachCardView(HabitCardViewHolder holder) + { + attachedHolders.add(holder); } /** @@ -75,13 +86,12 @@ public class HabitCardListView extends RecyclerView int[] checkmarks, boolean selected) { - int visibleCheckmarks[] = - Arrays.copyOfRange(checkmarks, 0, checkmarkCount); - HabitCardView cardView = (HabitCardView) holder.itemView; cardView.setHabit(habit); cardView.setSelected(selected); - cardView.setCheckmarkValues(visibleCheckmarks); + cardView.setCheckmarkValues(checkmarks); + cardView.setCheckmarkCount(checkmarkCount); + cardView.setDataOffset(dataOffset); cardView.setScore(score); if (controller != null) setupCardViewController(holder); return cardView; @@ -92,6 +102,11 @@ public class HabitCardListView extends RecyclerView return new HabitCardView(getContext()); } + public void detachCardView(HabitCardViewHolder holder) + { + attachedHolders.remove(holder); + } + @Override public void setAdapter(RecyclerView.Adapter adapter) { @@ -109,6 +124,16 @@ public class HabitCardListView extends RecyclerView this.controller = controller; } + public void setDataOffset(int dataOffset) + { + this.dataOffset = dataOffset; + for(HabitCardViewHolder holder : attachedHolders) + { + HabitCardView cardView = (HabitCardView) holder.itemView; + cardView.setDataOffset(dataOffset); + } + } + @Override protected void onAttachedToWindow() { 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 bc2123c7c..d57e0a71c 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 @@ -72,6 +72,8 @@ public class HabitCardView extends FrameLayout @Nullable private Habit habit; + private int dataOffset; + public HabitCardView(Context context) { super(context); @@ -90,6 +92,11 @@ public class HabitCardView extends FrameLayout new Handler(Looper.getMainLooper()).post(() -> refresh()); } + public void setCheckmarkCount(int checkmarkCount) + { + checkmarkPanel.setButtonCount(checkmarkCount); + } + public void setCheckmarkValues(int checkmarks[]) { checkmarkPanel.setCheckmarkValues(checkmarks); @@ -103,6 +110,12 @@ public class HabitCardView extends FrameLayout checkmarkPanel.setController(controller); } + public void setDataOffset(int dataOffset) + { + this.dataOffset = dataOffset; + checkmarkPanel.setDataOffset(dataOffset); + } + public void setHabit(@NonNull Habit habit) { if (this.habit != null) detachFromHabit(); @@ -134,7 +147,7 @@ public class HabitCardView extends FrameLayout { long today = DateUtils.getStartOfToday(); long day = DateUtils.millisecondsInOneDay; - int offset = (int) ((today - timestamp) / day); + int offset = (int) ((today - timestamp) / day) - dataOffset; CheckmarkButtonView button = checkmarkPanel.indexToButton(offset); float y = button.getHeight() / 2.0f; From 56f2ae57fe4da53d0b078d55f5d3306475b15797 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Mon, 20 Mar 2017 18:53:48 -0400 Subject: [PATCH 3/4] Persist scrolling after configuration change --- .../common/views/BundleSavedState.java | 2 +- .../habits/list/views/HabitCardListView.java | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java b/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java index 16eb77d88..9f61d88f0 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java +++ b/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java @@ -40,7 +40,7 @@ public class BundleSavedState extends View.BaseSavedState } }; - final Bundle bundle; + public final Bundle bundle; public BundleSavedState(Parcelable superState, Bundle bundle) { diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java index 4034e2cbf..bc784465e 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.java @@ -20,12 +20,14 @@ package org.isoron.uhabits.activities.habits.list.views; import android.content.*; +import android.os.*; import android.support.annotation.*; import android.support.v7.widget.*; import android.support.v7.widget.helper.*; import android.util.*; import android.view.*; +import org.isoron.uhabits.activities.common.views.*; import org.isoron.uhabits.activities.habits.list.controllers.*; import org.isoron.uhabits.activities.habits.list.model.*; import org.isoron.uhabits.models.*; @@ -127,7 +129,7 @@ public class HabitCardListView extends RecyclerView public void setDataOffset(int dataOffset) { this.dataOffset = dataOffset; - for(HabitCardViewHolder holder : attachedHolders) + for (HabitCardViewHolder holder : attachedHolders) { HabitCardView cardView = (HabitCardView) holder.itemView; cardView.setDataOffset(dataOffset); @@ -148,6 +150,23 @@ public class HabitCardListView extends RecyclerView super.onDetachedFromWindow(); } + @Override + protected void onRestoreInstanceState(Parcelable state) + { + BundleSavedState bss = (BundleSavedState) state; + dataOffset = bss.bundle.getInt("dataOffset"); + super.onRestoreInstanceState(bss.getSuperState()); + } + + @Override + protected Parcelable onSaveInstanceState() + { + Parcelable superState = super.onSaveInstanceState(); + Bundle bundle = new Bundle(); + bundle.putInt("dataOffset", dataOffset); + return new BundleSavedState(superState, bundle); + } + protected void setupCardViewController(@NonNull HabitCardViewHolder holder) { HabitCardView cardView = (HabitCardView) holder.itemView; @@ -193,7 +212,7 @@ public class HabitCardListView extends RecyclerView { int position = holder.getAdapterPosition(); if (controller != null) controller.onItemLongClick(position); - if(adapter.isSortable()) touchHelper.startDrag(holder); + if (adapter.isSortable()) touchHelper.startDrag(holder); } @Override From f4f7faf3a4e62c86233af7eb7fb51591cebf2917 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Mon, 20 Mar 2017 20:00:10 -0400 Subject: [PATCH 4/4] Fix a few issues with header scrolling --- .../common/views/ScrollableChart.java | 117 +++++++++++------- .../habits/list/ListHabitsRootView.java | 1 + .../habits/list/views/HeaderView.java | 46 ++++--- 3 files changed, 101 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java b/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java index 0e77043b7..8b54b324f 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java +++ b/app/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java @@ -33,7 +33,9 @@ public abstract class ScrollableChart extends View private int dataOffset; - private int scrollerBucketSize; + private int scrollerBucketSize = 1; + + private int direction = 1; private GestureDetector detector; @@ -43,10 +45,7 @@ public abstract class ScrollableChart extends View private ScrollController scrollController; - public void setScrollController(ScrollController scrollController) - { - this.scrollController = scrollController; - } + private int maxDataOffset = 10000; public ScrollableChart(Context context) { @@ -79,19 +78,6 @@ public abstract class ScrollableChart extends View } } - private void updateDataOffset() - { - int newDataOffset = - Math.max(0, scroller.getCurrX() / scrollerBucketSize); - - if(newDataOffset != dataOffset) - { - dataOffset = newDataOffset; - scrollController.onDataOffsetChanged(dataOffset); - postInvalidate(); - } - } - @Override public boolean onDown(MotionEvent e) { @@ -105,19 +91,44 @@ public abstract class ScrollableChart extends View float velocityY) { scroller.fling(scroller.getCurrX(), scroller.getCurrY(), - (int) velocityX / 2, 0, 0, 100000, 0, 0); + direction * ((int) velocityX) / 2, 0, 0, getMaxX(), 0, 0); invalidate(); scrollAnimator.setDuration(scroller.getDuration()); scrollAnimator.start(); - return false; } + private int getMaxX() + { + return maxDataOffset * scrollerBucketSize; + } + @Override - public void onLongPress(MotionEvent e) + public void onRestoreInstanceState(Parcelable state) { + BundleSavedState bss = (BundleSavedState) state; + int x = bss.bundle.getInt("x"); + int y = bss.bundle.getInt("y"); + direction = bss.bundle.getInt("direction"); + dataOffset = bss.bundle.getInt("dataOffset"); + maxDataOffset = bss.bundle.getInt("maxDataOffset"); + scroller.startScroll(0, 0, x, y, 0); + scroller.computeScrollOffset(); + super.onRestoreInstanceState(bss.getSuperState()); + } + @Override + public Parcelable onSaveInstanceState() + { + Parcelable superState = super.onSaveInstanceState(); + Bundle bundle = new Bundle(); + bundle.putInt("x", scroller.getCurrX()); + bundle.putInt("y", scroller.getCurrY()); + bundle.putInt("dataOffset", dataOffset); + bundle.putInt("direction", direction); + bundle.putInt("maxDataOffset", maxDataOffset); + return new BundleSavedState(superState, bundle); } @Override @@ -131,11 +142,14 @@ public abstract class ScrollableChart extends View if (parent != null) parent.requestDisallowInterceptTouchEvent(true); } - scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), - (int) -dx, (int) dy, 0); + + dx = - direction * dx; + dx = Math.min(dx, getMaxX() - scroller.getCurrX()); + scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) dx, + (int) dy, 0); + scroller.computeScrollOffset(); updateDataOffset(); - return true; } @@ -157,32 +171,35 @@ public abstract class ScrollableChart extends View return detector.onTouchEvent(event); } - public void setScrollerBucketSize(int scrollerBucketSize) + public void setDirection(int direction) { - this.scrollerBucketSize = scrollerBucketSize; + if (direction != 1 && direction != -1) + throw new IllegalArgumentException(); + this.direction = direction; } @Override - public void onRestoreInstanceState(Parcelable state) + public void onLongPress(MotionEvent e) { - BundleSavedState bss = (BundleSavedState) state; - int x = bss.bundle.getInt("x"); - int y = bss.bundle.getInt("y"); - dataOffset = bss.bundle.getInt("dataOffset"); - scroller.startScroll(0, 0, x, y, 0); - scroller.computeScrollOffset(); - super.onRestoreInstanceState(bss.getSuperState()); + } - @Override - public Parcelable onSaveInstanceState() + public void setMaxDataOffset(int maxDataOffset) { - Parcelable superState = super.onSaveInstanceState(); - Bundle bundle = new Bundle(); - bundle.putInt("x", scroller.getCurrX()); - bundle.putInt("y", scroller.getCurrY()); - bundle.putInt("dataOffset", dataOffset); - return new BundleSavedState(superState, bundle); + this.maxDataOffset = maxDataOffset; + this.dataOffset = Math.min(dataOffset, maxDataOffset); + scrollController.onDataOffsetChanged(this.dataOffset); + postInvalidate(); + } + + public void setScrollController(ScrollController scrollController) + { + this.scrollController = scrollController; + } + + public void setScrollerBucketSize(int scrollerBucketSize) + { + this.scrollerBucketSize = scrollerBucketSize; } private void init(Context context) @@ -191,7 +208,21 @@ public abstract class ScrollableChart extends View scroller = new Scroller(context, null, true); scrollAnimator = ValueAnimator.ofFloat(0, 1); scrollAnimator.addUpdateListener(this); - scrollController = new ScrollController(){}; + scrollController = new ScrollController() {}; + } + + private void updateDataOffset() + { + int newDataOffset = scroller.getCurrX() / scrollerBucketSize; + newDataOffset = Math.max(0, newDataOffset); + newDataOffset = Math.min(maxDataOffset, newDataOffset); + + if (newDataOffset != dataOffset) + { + dataOffset = newDataOffset; + scrollController.onDataOffsetChanged(dataOffset); + postInvalidate(); + } } public interface ScrollController diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java index 4a3c1ee73..7a716c9cd 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.java @@ -164,6 +164,7 @@ public class ListHabitsRootView extends BaseRootView { int count = getCheckmarkCount(); header.setButtonCount(count); + header.setMaxDataOffset(Math.max(MAX_CHECKMARK_COUNT - count, 0)); listView.setCheckmarkCount(count); super.onSizeChanged(w, h, oldw, oldh); } diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java index 9492b5857..e4863cd54 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java @@ -34,8 +34,6 @@ import org.isoron.uhabits.utils.*; import java.util.*; -import static org.isoron.uhabits.utils.InterfaceUtils.*; - public class HeaderView extends ScrollableChart implements Preferences.Listener, MidnightTimer.MidnightListener { @@ -52,6 +50,8 @@ public class HeaderView extends ScrollableChart private RectF rect; + private int maxDataOffset; + public HeaderView(Context context, AttributeSet attrs) { super(context, attrs); @@ -61,18 +61,6 @@ public class HeaderView extends ScrollableChart setButtonCount(5); } - StyledResources res = new StyledResources(context); - setScrollerBucketSize((int) dpToPixels(context, 42)); - paint = new TextPaint(); - paint.setColor(Color.BLACK); - paint.setAntiAlias(true); - paint.setTextSize(getResources().getDimension(R.dimen.tinyTextSize)); - paint.setTextAlign(Paint.Align.CENTER); - paint.setTypeface(Typeface.DEFAULT_BOLD); - paint.setColor(res.getColor(R.attr.mediumContrastTextColor)); - - rect = new RectF(); - Context appContext = context.getApplicationContext(); if (appContext instanceof HabitsApplication) { @@ -85,6 +73,21 @@ public class HeaderView extends ScrollableChart ListHabitsActivity activity = (ListHabitsActivity) context; midnightTimer = activity.getListHabitsComponent().getMidnightTimer(); } + + Resources res = context.getResources(); + setScrollerBucketSize((int) res.getDimension(R.dimen.checkmarkWidth)); + setDirection(shouldReverseCheckmarks() ? 1 : -1); + + StyledResources sr = new StyledResources(context); + paint = new TextPaint(); + paint.setColor(Color.BLACK); + paint.setAntiAlias(true); + paint.setTextSize(getResources().getDimension(R.dimen.tinyTextSize)); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTypeface(Typeface.DEFAULT_BOLD); + paint.setColor(sr.getColor(R.attr.mediumContrastTextColor)); + + rect = new RectF(); } @Override @@ -96,6 +99,7 @@ public class HeaderView extends ScrollableChart @Override public void onCheckmarkOrderChanged() { + setDirection(shouldReverseCheckmarks() ? 1 : -1); postInvalidate(); } @@ -137,24 +141,26 @@ public class HeaderView extends ScrollableChart super.onDraw(canvas); GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); - Resources res = getContext().getResources(); float width = res.getDimension(R.dimen.checkmarkWidth); float height = res.getDimension(R.dimen.checkmarkHeight); - rect.set(0, 0, width, height); - rect.offset(canvas.getWidth(), 0); + boolean reverse = shouldReverseCheckmarks(); day.add(GregorianCalendar.DAY_OF_MONTH, -getDataOffset()); float em = paint.measureText("m"); for (int i = 0; i < buttonCount; i++) { - rect.offset(-width, 0); + rect.set(0, 0, width, height); + rect.offset(canvas.getWidth(), 0); + if(reverse) rect.offset(- (i + 1) * width, 0); + else rect.offset((i - buttonCount) * width, 0); + String text = DateUtils.formatHeaderDate(day).toUpperCase(); String[] lines = text.split("\n"); - int y1 = (int)(rect.centerY() - 0.5 * em); - int y2 = (int)(rect.centerY() + em); + int y1 = (int)(rect.centerY() - 0.25 * em); + int y2 = (int)(rect.centerY() + 1.25 * em); canvas.drawText(lines[0], rect.centerX(), y1, paint); canvas.drawText(lines[1], rect.centerX(), y2, paint);