1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.recyclerview.selection;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 
23 import android.graphics.Point;
24 
25 import androidx.recyclerview.selection.ViewAutoScroller.ScrollHost;
26 import androidx.test.ext.junit.runners.AndroidJUnit4;
27 import androidx.test.filters.SmallTest;
28 
29 import org.jspecify.annotations.Nullable;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 
34 @RunWith(AndroidJUnit4.class)
35 @SmallTest
36 public final class ViewAutoScrollerTest {
37 
38     private static final float SCROLL_THRESHOLD_RATIO = 0.125f;
39     private static final int VIEW_HEIGHT = 100;
40     private static final int TOP_Y_POINT = (int) (VIEW_HEIGHT * SCROLL_THRESHOLD_RATIO) - 1;
41     private static final int BOTTOM_Y_POINT =
42             VIEW_HEIGHT - (int) (VIEW_HEIGHT * SCROLL_THRESHOLD_RATIO) + 1;
43 
44     private ViewAutoScroller mScroller;
45     private TestHost mHost;
46 
47     @Before
setUp()48     public void setUp() {
49         mHost = new TestHost();
50         mScroller = new ViewAutoScroller(mHost, SCROLL_THRESHOLD_RATIO);
51     }
52 
53     @Test
testNoScrollWhenOutOfScrollZone()54     public void testNoScrollWhenOutOfScrollZone() {
55         mScroller.scroll(new Point(0, VIEW_HEIGHT / 2));
56         mHost.run();
57         mHost.assertNotScrolled();
58     }
59 
60 //    @Test
61 //    public void testNoScrollWhenDisabled() {
62 //        mScroller.reset();
63 //        mScroller.scroll(mEvent.location(0, TOP_Y_POINT).build());
64 //        mHost.assertNotScrolled();
65 //    }
66 
67     @Test
testMotionThreshold()68     public void testMotionThreshold() {
69         mScroller.scroll(new Point(0, TOP_Y_POINT));
70         mHost.run();
71 
72         mScroller.scroll(new Point(0, TOP_Y_POINT - 1));
73         mHost.run();
74 
75         mHost.assertNotScrolled();
76     }
77 
78     @Test
testMotionThreshold_Resets()79     public void testMotionThreshold_Resets() {
80         int expectedScrollDistance = mScroller.computeScrollDistance(-21);
81         mScroller.scroll(new Point(0, TOP_Y_POINT));
82         mHost.run();
83         // We need enough y motion to overcome motion threshold
84         mScroller.scroll(new Point(0, TOP_Y_POINT - 20));
85         mHost.run();
86 
87         mHost.reset();
88         // After resetting events should be required to cross the motion threshold
89         // before auto-scrolling again.
90         mScroller.reset();
91 
92         mScroller.scroll(new Point(0, TOP_Y_POINT));
93         mHost.run();
94 
95         mHost.assertNotScrolled();
96     }
97 
98     @Test
testAutoScrolls_Top()99     public void testAutoScrolls_Top() {
100         int expectedScrollDistance = mScroller.computeScrollDistance(-21);
101         mScroller.scroll(new Point(0, TOP_Y_POINT));
102         mHost.run();
103         // We need enough y motion to overcome motion threshold
104         mScroller.scroll(new Point(0, TOP_Y_POINT - 20));
105         mHost.run();
106 
107         mHost.assertScrolledBy(expectedScrollDistance);
108     }
109 
110     @Test
testAutoScrolls_Bottom()111     public void testAutoScrolls_Bottom() {
112         int expectedScrollDistance = mScroller.computeScrollDistance(21);
113         mScroller.scroll(new Point(0, BOTTOM_Y_POINT));
114         mHost.run();
115         // We need enough y motion to overcome motion threshold
116         mScroller.scroll(new Point(0, BOTTOM_Y_POINT + 20));
117         mHost.run();
118 
119         mHost.assertScrolledBy(expectedScrollDistance);
120     }
121 
122     private final class TestHost extends ScrollHost {
123 
124         private @Nullable Integer mScrollDistance;
125         private @Nullable Runnable mRunnable;
126 
127         @Override
getViewHeight()128         int getViewHeight() {
129             return VIEW_HEIGHT;
130         }
131 
132         @Override
scrollBy(int distance)133         void scrollBy(int distance) {
134             mScrollDistance = distance;
135         }
136 
137         @Override
runAtNextFrame(Runnable r)138         void runAtNextFrame(Runnable r) {
139             mRunnable = r;
140         }
141 
142         @Override
removeCallback(Runnable r)143         void removeCallback(Runnable r) {
144         }
145 
reset()146         private void reset() {
147             mScrollDistance = null;
148             mRunnable = null;
149         }
150 
run()151         private void run() {
152             mRunnable.run();
153         }
154 
assertNotScrolled()155         private void assertNotScrolled() {
156             assertNull(mScrollDistance);
157         }
158 
assertScrolledBy(int expectedDistance)159         private void assertScrolledBy(int expectedDistance) {
160             assertNotNull(mScrollDistance);
161             assertEquals(expectedDistance, mScrollDistance.intValue());
162         }
163     }
164 }
165