1 /*
2  * Copyright (C) 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 package com.android.launcher3.touch;
17 
18 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
19 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
20 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
21 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
22 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
23 
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.Matchers.any;
26 import static org.mockito.Matchers.anyBoolean;
27 import static org.mockito.Matchers.anyFloat;
28 import static org.mockito.Mockito.doAnswer;
29 import static org.mockito.Mockito.doReturn;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.verify;
32 
33 import android.content.Context;
34 import android.util.Log;
35 import android.view.MotionEvent;
36 import android.view.ViewConfiguration;
37 
38 import androidx.test.InstrumentationRegistry;
39 import androidx.test.filters.SmallTest;
40 import androidx.test.runner.AndroidJUnit4;
41 
42 import com.android.launcher3.testcomponent.TouchEventGenerator;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4.class)
52 public class SingleAxisSwipeDetectorTest {
53 
54     private static final String TAG = SingleAxisSwipeDetectorTest.class.getSimpleName();
L(String s, Object... parts)55     public static void L(String s, Object... parts) {
56         Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
57     }
58 
59     private TouchEventGenerator mGenerator;
60     private SingleAxisSwipeDetector mDetector;
61     private int mTouchSlop;
62     Context mContext;
63 
64     @Mock
65     private SingleAxisSwipeDetector.Listener mMockListener;
66 
67     @Mock
68     private ViewConfiguration mMockConfig;
69 
70     @Before
setup()71     public void setup() {
72         MockitoAnnotations.initMocks(this);
73         mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
74         mContext = InstrumentationRegistry.getTargetContext();
75         ViewConfiguration orgConfig = ViewConfiguration.get(mContext);
76         doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
77                 .getScaledMaximumFlingVelocity();
78 
79         mDetector = new SingleAxisSwipeDetector(mContext,
80                 mMockConfig, mMockListener, VERTICAL, false);
81         mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
82         mTouchSlop = orgConfig.getScaledTouchSlop();
83         doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
84 
85         L("mTouchSlop=", mTouchSlop);
86     }
87 
88     @Test
testDragStart_verticalPositive()89     public void testDragStart_verticalPositive() {
90         mDetector = new SingleAxisSwipeDetector(mContext,
91                 mMockConfig, mMockListener, VERTICAL, false);
92         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
93         mGenerator.put(0, 100, 100);
94         mGenerator.move(0, 100, 100 - mTouchSlop);
95         // TODO: actually calculate the following parameters and do exact value checks.
96         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
97     }
98 
99     @Test
testDragStart_verticalNegative()100     public void testDragStart_verticalNegative() {
101         mDetector = new SingleAxisSwipeDetector(mContext,
102                 mMockConfig, mMockListener, VERTICAL, false);
103         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
104         mGenerator.put(0, 100, 100);
105         mGenerator.move(0, 100, 100 + mTouchSlop);
106         // TODO: actually calculate the following parameters and do exact value checks.
107         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
108     }
109 
110     @Test
testDragStart_failed()111     public void testDragStart_failed() {
112         mGenerator.put(0, 100, 100);
113         mGenerator.move(0, 100 + mTouchSlop, 100);
114         // TODO: actually calculate the following parameters and do exact value checks.
115         verify(mMockListener, never()).onDragStart(anyBoolean(), anyFloat());
116     }
117 
118     @Test
testDragStart_horizontalPositive()119     public void testDragStart_horizontalPositive() {
120         mDetector = new SingleAxisSwipeDetector(mContext,
121                 mMockConfig, mMockListener, HORIZONTAL, false);
122         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
123 
124         mGenerator.put(0, 100, 100);
125         mGenerator.move(0, 100 + mTouchSlop, 100);
126         // TODO: actually calculate the following parameters and do exact value checks.
127         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
128     }
129 
130     @Test
testDragStart_horizontalNegative()131     public void testDragStart_horizontalNegative() {
132         mDetector = new SingleAxisSwipeDetector(mContext,
133                 mMockConfig, mMockListener, HORIZONTAL, false);
134         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
135 
136         mGenerator.put(0, 100, 100);
137         mGenerator.move(0, 100 - mTouchSlop, 100);
138         // TODO: actually calculate the following parameters and do exact value checks.
139         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
140     }
141 
142     @Test
testDragStart_horizontalRtlPositive()143     public void testDragStart_horizontalRtlPositive() {
144         mDetector = new SingleAxisSwipeDetector(mContext,
145                 mMockConfig, mMockListener, HORIZONTAL, true);
146         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
147 
148         mGenerator.put(0, 100, 100);
149         mGenerator.move(0, 100 - mTouchSlop, 100);
150         // TODO: actually calculate the following parameters and do exact value checks.
151         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
152     }
153 
154     @Test
testDragStart_horizontalRtlNegative()155     public void testDragStart_horizontalRtlNegative() {
156         mDetector = new SingleAxisSwipeDetector(mContext,
157                 mMockConfig, mMockListener, HORIZONTAL, true);
158         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
159 
160         mGenerator.put(0, 100, 100);
161         mGenerator.move(0, 100 + mTouchSlop, 100);
162         // TODO: actually calculate the following parameters and do exact value checks.
163         verify(mMockListener).onDragStart(anyBoolean(), anyFloat());
164     }
165 
166     @Test
testDrag()167     public void testDrag() {
168         mGenerator.put(0, 100, 100);
169         mGenerator.move(0, 100, 100 + mTouchSlop);
170         // TODO: actually calculate the following parameters and do exact value checks.
171         verify(mMockListener).onDrag(anyFloat(), anyFloat(), any(MotionEvent.class));
172     }
173 
174     @Test
testDragEnd()175     public void testDragEnd() {
176         mGenerator.put(0, 100, 100);
177         mGenerator.move(0, 100, 100 + mTouchSlop);
178         mGenerator.move(0, 100, 100 + mTouchSlop * 2);
179         mGenerator.lift(0);
180         // TODO: actually calculate the following parameters and do exact value checks.
181         verify(mMockListener).onDragEnd(anyFloat());
182     }
183 
184     @Test
testInterleavedSetState()185     public void testInterleavedSetState() {
186         doAnswer(invocationOnMock -> {
187             // Sets state to IDLE. (Normally onDragEnd() will have state SETTLING.)
188             mDetector.finishedScrolling();
189             return null;
190         }).when(mMockListener).onDragEnd(anyFloat());
191 
192         mGenerator.put(0, 100, 100);
193         mGenerator.move(0, 100, 100 + mTouchSlop);
194         mGenerator.move(0, 100, 100 + mTouchSlop * 2);
195         mGenerator.lift(0);
196         verify(mMockListener).onDragEnd(anyFloat());
197         assertTrue("SwipeDetector should be IDLE but was " + mDetector.mState,
198                 mDetector.isIdleState());
199     }
200 }
201