1111import androidx .core .view .GestureDetectorCompat ;
1212import androidx .core .view .ViewCompat ;
1313import androidx .interpolator .view .animation .FastOutLinearInInterpolator ;
14+
15+ import android .os .Handler ;
16+ import android .os .Looper ;
1417import android .text .*;
1518import android .text .format .DateFormat ;
1619import android .text .style .StyleSpan ;
@@ -38,6 +41,9 @@ private enum Direction {
3841 private enum NewEventScrollDirection {
3942 NONE , UP , DOWN
4043 }
44+ private enum AutoScrollDirection {
45+ LEFT , RIGHT
46+ }
4147
4248 @ Deprecated
4349 public static final int LENGTH_SHORT = 1 ;
@@ -94,6 +100,13 @@ private enum NewEventScrollDirection {
94100 private boolean movingNewEvent = false ;
95101 private NewEventScrollDirection mCurrentNewEventScrollDirection = NewEventScrollDirection .NONE ;
96102 private float mNewEventDragOffset = 0 ;
103+ private Handler autoScrollHandler = new Handler (Looper .getMainLooper ());
104+ private boolean isAutoScrolling = false ;
105+ private Runnable autoScrollRunnable ;
106+ private int mAutoScrollInterval = 1200 ;
107+ private int mAutoScrollDuration = 300 ;
108+ private int mAutoScrollLeftThreshold = 150 ;
109+ private int mAutoScrollRightThreshold = 150 ;
97110
98111 // Attributes and their default values.
99112 private int mHourHeight = 50 ;
@@ -299,7 +312,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
299312
300313 @ Override
301314 public boolean onFling (MotionEvent e1 , MotionEvent e2 , float velocityX , float velocityY ) {
302- if (mIsZooming )
315+ if (mIsZooming || movingNewEvent )
303316 return true ;
304317
305318 if ((mCurrentFlingDirection == Direction .LEFT && !mHorizontalFlingEnabled ) ||
@@ -2488,8 +2501,6 @@ public int getMinOverlappingMinutes() {
24882501 @ Override
24892502 public boolean onTouchEvent (MotionEvent event ) {
24902503 // TODO: fix single tap up
2491- // TODO: fix horizontal auto scroll when dragging
2492- // TODO: fix on drag verspringt bij start dragging -> lost op door verticale drag offset variabele toe te voegen
24932504 mScaleDetector .onTouchEvent (event );
24942505 boolean val = mGestureDetector .onTouchEvent (event );
24952506 if (event .getAction () == MotionEvent .ACTION_DOWN && creatingNewEvent && wasOnNewEventRect (event )) {
@@ -2500,7 +2511,11 @@ public boolean onTouchEvent(MotionEvent event) {
25002511 }
25012512
25022513 if (event .getAction () == MotionEvent .ACTION_UP && movingNewEvent ) {
2503- stopScrolling ();
2514+ if (isAutoScrolling ) {
2515+ stopAutoScroll ();
2516+ } else {
2517+ stopScrolling ();
2518+ }
25042519 movingNewEvent = false ;
25052520 }
25062521
@@ -2511,20 +2526,28 @@ public boolean onTouchEvent(MotionEvent event) {
25112526 float pixelsFromZero = event .getY () - mHeaderHeight
25122527 - mHeaderRowPadding * 2 - mTimeTextHeight / 2 - mHeaderMarginBottom ;
25132528 if (pixelsFromZero < mHourHeight ) {
2514- if (mCurrentNewEventScrollDirection == NewEventScrollDirection .NONE ) {
2529+ if (mCurrentNewEventScrollDirection == NewEventScrollDirection .NONE && ! isAutoScrolling ) {
25152530 mCurrentNewEventScrollDirection = NewEventScrollDirection .UP ;
25162531 int distance = (int ) getYMaxLimit () - (int ) mCurrentOrigin .y ;
25172532 mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y , 0 ,distance , Math .abs (distance / mHourHeight * mNewEventVerticalScrollDuration ));
2533+ ViewCompat .postInvalidateOnAnimation (WeekView .this );
25182534 }
25192535 } else if (event .getY () > getHeight () - mHourHeight ) {
2520- if (mCurrentNewEventScrollDirection == NewEventScrollDirection .NONE ) {
2536+ if (mCurrentNewEventScrollDirection == NewEventScrollDirection .NONE && ! isAutoScrolling ) {
25212537 mCurrentNewEventScrollDirection = NewEventScrollDirection .DOWN ;
25222538 int distance = (int ) getYMinLimit () - (int ) mCurrentOrigin .y ;
25232539 mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y , 0 , distance , Math .abs (distance / mHourHeight * mNewEventVerticalScrollDuration ));
2540+ ViewCompat .postInvalidateOnAnimation (WeekView .this );
25242541 }
2542+ } else if (event .getX () < mAutoScrollLeftThreshold ) {
2543+ if (!isAutoScrolling ) startAutoScroll (AutoScrollDirection .LEFT );
2544+ } else if (getWidth () - event .getX () < mAutoScrollRightThreshold ) {
2545+ if (!isAutoScrolling ) startAutoScroll (AutoScrollDirection .RIGHT );
25252546 } else if (mCurrentNewEventScrollDirection == NewEventScrollDirection .UP || mCurrentNewEventScrollDirection == NewEventScrollDirection .DOWN ) {
25262547 mCurrentNewEventScrollDirection = NewEventScrollDirection .NONE ;
25272548 mScroller .forceFinished (true );
2549+ } else if (isAutoScrolling ) {
2550+ stopAutoScroll ();
25282551 }
25292552 }
25302553
@@ -2539,6 +2562,42 @@ public boolean onTouchEvent(MotionEvent event) {
25392562 return val ;
25402563 }
25412564
2565+ // Call this when user enters the critical area (left or right edge)
2566+ private void startAutoScroll (AutoScrollDirection direction ) {
2567+ if (isAutoScrolling || !mScroller .isFinished ()) return ;
2568+
2569+ isAutoScrolling = true ;
2570+ autoScrollRunnable = new Runnable () {
2571+ @ Override
2572+ public void run () {
2573+ scrollOneWeek (direction );
2574+ autoScrollHandler .postDelayed (this , mAutoScrollInterval );
2575+ }
2576+ };
2577+ autoScrollHandler .post (autoScrollRunnable );
2578+ }
2579+
2580+ // Call this when user leaves the critical area
2581+ private void stopAutoScroll () {
2582+ isAutoScrolling = false ;
2583+ if (autoScrollRunnable != null ) {
2584+ autoScrollHandler .removeCallbacks (autoScrollRunnable );
2585+ autoScrollRunnable = null ;
2586+ }
2587+ }
2588+
2589+ // Replace with your actual scroll logic
2590+ private void scrollOneWeek (AutoScrollDirection direction ) {
2591+ int distance = (int ) -(mNumberOfVisibleDays * (mWidthPerDay + mColumnGap ));
2592+ if (direction == AutoScrollDirection .RIGHT ) {
2593+ mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y ,distance , 0 , mAutoScrollDuration );
2594+ ViewCompat .postInvalidateOnAnimation (WeekView .this );
2595+ } else if (direction == AutoScrollDirection .LEFT ) {
2596+ mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y , -distance , 0 , mAutoScrollDuration );
2597+ ViewCompat .postInvalidateOnAnimation (WeekView .this );
2598+ }
2599+ }
2600+
25422601 /**
25432602 * A lighter function to stop the current scroll animation
25442603 */
@@ -2617,12 +2676,7 @@ public void computeScroll() {
26172676 * @return true if scrolling should be stopped before reaching the end of animation.
26182677 */
26192678 private boolean forceFinishScroll () {
2620- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .ICE_CREAM_SANDWICH ) {
2621- // current velocity only available since api 14
2622- return mScroller .getCurrVelocity () <= mMinimumFlingVelocity ;
2623- } else {
2624- return false ;
2625- }
2679+ return mScroller .getCurrVelocity () <= mMinimumFlingVelocity ;
26262680 }
26272681
26282682
@@ -2726,9 +2780,15 @@ public boolean dateIsValid(Calendar day) {
27262780
27272781 private void updateNewEvent (MotionEvent e ) {
27282782 Log .d ("QuivrWeekView" , String .format ("updateNewEvent: %s%n" , e .toString ()));
2729- Log .d ("QuivrWeekView" , String .format (Float .valueOf (mNewEventDragOffset ).toString ()));
2730- Log .d ("QuivrWeekView" , String .format (Float .valueOf (e .getY ()).toString ()));
2731- Calendar selectedTime = getTimeFromPoint (e .getX (), e .getY () - mNewEventDragOffset );
2783+
2784+ float x = e .getX ();
2785+
2786+ // Ugly fix for when the user is dragging the event over the small left time column
2787+ if (x < mHeaderColumnWidth ) {
2788+ x = mHeaderColumnWidth + 1 ;
2789+ }
2790+
2791+ Calendar selectedTime = getTimeFromPoint (x , e .getY () - mNewEventDragOffset );
27322792 if (selectedTime != null ) {
27332793 List <WeekViewEvent > tempEvents = new ArrayList <>(mEvents );
27342794 if (mNewEventRect != null ) {
0 commit comments