1 package org.robolectric.shadows;
2 
3 import android.os.Looper;
4 import android.widget.OverScroller;
5 import org.robolectric.annotation.Implementation;
6 import org.robolectric.annotation.Implements;
7 import org.robolectric.shadow.api.Shadow;
8 import org.robolectric.util.Scheduler;
9 
10 @Implements(OverScroller.class)
11 public class ShadowOverScroller {
12   private int startX;
13   private int startY;
14   private int finalX;
15   private int finalY;
16   private long startTime;
17   private long duration;
18   private boolean started;
19 
20   @Implementation
getStartX()21   protected int getStartX() {
22     return startX;
23   }
24 
25   @Implementation
getStartY()26   protected int getStartY() {
27     return startY;
28   }
29 
30   @Implementation
getCurrX()31   protected int getCurrX() {
32     long dt = deltaTime();
33     return dt >= duration ? finalX : startX + (int) ((deltaX() * dt) / duration);
34   }
35 
36   @Implementation
getCurrY()37   protected int getCurrY() {
38     long dt = deltaTime();
39     return dt >= duration ? finalY : startY + (int) ((deltaY() * dt) / duration);
40   }
41 
42   @Implementation
getFinalX()43   protected int getFinalX() {
44     return finalX;
45   }
46 
47   @Implementation
getFinalY()48   protected int getFinalY() {
49     return finalY;
50   }
51 
52   @Implementation
getDuration()53   protected int getDuration() {
54     return (int) duration;
55   }
56 
57   @Implementation
startScroll(int startX, int startY, int dx, int dy, int duration)58   protected void startScroll(int startX, int startY, int dx, int dy, int duration) {
59     this.startX = startX;
60     this.startY = startY;
61     finalX = startX + dx;
62     finalY = startY + dy;
63     startTime = getScheduler().getCurrentTime();
64     this.duration = duration;
65     started = true;
66     // post a task so that the scheduler will actually run
67     getScheduler().postDelayed(new Runnable() {
68       @Override
69       public void run() {
70         // do nothing
71       }
72     }, duration);
73   }
74 
75   @Implementation
abortAnimation()76   protected void abortAnimation() {
77     duration = deltaTime() - 1;
78   }
79 
80   @Implementation
forceFinished(boolean finished)81   protected void forceFinished(boolean finished) {
82     if (!finished) {
83       throw new RuntimeException("Not implemented.");
84     }
85 
86     finalX = getCurrX();
87     finalY = getCurrY();
88     duration = deltaTime() - 1;
89   }
90 
91   @Implementation
computeScrollOffset()92   protected boolean computeScrollOffset() {
93     if (!started) {
94       return false;
95     }
96     started &= deltaTime() < duration;
97     return true;
98   }
99 
100   @Implementation
101   protected boolean isFinished() {
102     return deltaTime() > duration;
103   }
104 
105   @Implementation
timePassed()106   protected int timePassed() {
107     return (int) deltaTime();
108   }
109 
110   @Implementation
isScrollingInDirection(float xvel, float yvel)111   protected boolean isScrollingInDirection(float xvel, float yvel) {
112     final int dx = finalX - startX;
113     final int dy = finalY - startY;
114     return !isFinished()
115         && Math.signum(xvel) == Math.signum(dx)
116         && Math.signum(yvel) == Math.signum(dy);
117   }
118 
deltaTime()119   private long deltaTime() {
120     return getScheduler().getCurrentTime() - startTime;
121   }
122 
getScheduler()123   private Scheduler getScheduler() {
124     ShadowLooper shadowLooper = Shadow.extract(Looper.getMainLooper());
125     return shadowLooper.getScheduler();
126   }
127 
deltaX()128   private int deltaX() {
129     return (finalX - startX);
130   }
131 
deltaY()132   private int deltaY() {
133     return (finalY - startY);
134   }
135 }
136 
137