Home | History | Annotate | Download | only in widget
      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 
     17 package androidx.wear.widget;
     18 
     19 import static org.mockito.Mockito.times;
     20 import static org.mockito.Mockito.verify;
     21 
     22 import static java.lang.Math.cos;
     23 import static java.lang.Math.sin;
     24 
     25 import android.os.SystemClock;
     26 import android.support.test.filters.MediumTest;
     27 import android.support.test.rule.ActivityTestRule;
     28 import android.support.test.runner.AndroidJUnit4;
     29 import android.view.MotionEvent;
     30 
     31 import androidx.wear.widget.util.WakeLockRule;
     32 
     33 import org.junit.Before;
     34 import org.junit.Rule;
     35 import org.junit.Test;
     36 import org.junit.runner.RunWith;
     37 import org.mockito.Mock;
     38 import org.mockito.MockitoAnnotations;
     39 
     40 @MediumTest
     41 @RunWith(AndroidJUnit4.class)
     42 public class ScrollManagerTest {
     43     private static final int TEST_WIDTH = 400;
     44     private static final int TEST_HEIGHT = 400;
     45     private static final int STEP_COUNT = 300;
     46 
     47     private static final int EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE = 36;
     48     private static final int EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE = 199;
     49 
     50     @Rule
     51     public final WakeLockRule wakeLock = new WakeLockRule();
     52 
     53     @Rule
     54     public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
     55             new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
     56 
     57     @Mock
     58     WearableRecyclerView mMockWearableRecyclerView;
     59 
     60     ScrollManager mScrollManagerUnderTest;
     61 
     62     @Before
     63     public void setUp() throws Throwable {
     64         MockitoAnnotations.initMocks(this);
     65         mScrollManagerUnderTest = new ScrollManager();
     66         mScrollManagerUnderTest.setRecyclerView(mMockWearableRecyclerView, TEST_WIDTH, TEST_HEIGHT);
     67     }
     68 
     69     @Test
     70     public void testStraightUpScrollingGestureLeft() throws Throwable {
     71         // Pretend to scroll in a straight line from center left to upper left
     72         scroll(mScrollManagerUnderTest, 30, 30, 200, 150);
     73         // The scroll manager should require the recycler view to scroll up and only up
     74         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
     75                 .scrollBy(0, 1);
     76     }
     77 
     78     @Test
     79     public void testStraightDownScrollingGestureLeft() throws Throwable {
     80         // Pretend to scroll in a straight line upper left to center left
     81         scroll(mScrollManagerUnderTest, 30, 30, 150, 200);
     82         // The scroll manager should require the recycler view to scroll down and only down
     83         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
     84                 .scrollBy(0, -1);
     85     }
     86 
     87     @Test
     88     public void testStraightUpScrollingGestureRight() throws Throwable {
     89         // Pretend to scroll in a straight line from center right to upper right
     90         scroll(mScrollManagerUnderTest, 370, 370, 200, 150);
     91         // The scroll manager should require the recycler view to scroll down and only down
     92         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
     93                 .scrollBy(0, -1);
     94     }
     95 
     96     @Test
     97     public void testStraightDownScrollingGestureRight() throws Throwable {
     98         // Pretend to scroll in a straight line upper right to center right
     99         scroll(mScrollManagerUnderTest, 370, 370, 150, 200);
    100         // The scroll manager should require the recycler view to scroll up and only up
    101         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
    102                 .scrollBy(0, 1);
    103     }
    104 
    105     @Test
    106     public void testCircularScrollingGestureLeft() throws Throwable {
    107         // Pretend to scroll in an arch from center left to center right
    108         scrollOnArch(mScrollManagerUnderTest, 30, 200, 180.0f);
    109         // The scroll manager should never reverse the scroll direction and scroll up
    110         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE))
    111                 .scrollBy(0, 1);
    112     }
    113 
    114     @Test
    115     public void testCircularScrollingGestureRight() throws Throwable {
    116         // Pretend to scroll in an arch from center left to center right
    117         scrollOnArch(mScrollManagerUnderTest, 370, 200, -180.0f);
    118         // The scroll manager should never reverse the scroll direction and scroll down.
    119         verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE))
    120                 .scrollBy(0, -1);
    121     }
    122 
    123     private static void scroll(ScrollManager scrollManager, float fromX, float toX, float fromY,
    124             float toY) {
    125         long downTime = SystemClock.uptimeMillis();
    126         long eventTime = SystemClock.uptimeMillis();
    127 
    128         float y = fromY;
    129         float x = fromX;
    130 
    131         float yStep = (toY - fromY) / STEP_COUNT;
    132         float xStep = (toX - fromX) / STEP_COUNT;
    133 
    134         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
    135                 MotionEvent.ACTION_DOWN, x, y, 0);
    136         scrollManager.onTouchEvent(event);
    137         for (int i = 0; i < STEP_COUNT; ++i) {
    138             y += yStep;
    139             x += xStep;
    140             eventTime = SystemClock.uptimeMillis();
    141             event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
    142             scrollManager.onTouchEvent(event);
    143         }
    144 
    145         eventTime = SystemClock.uptimeMillis();
    146         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
    147         scrollManager.onTouchEvent(event);
    148     }
    149 
    150     private static void scrollOnArch(ScrollManager scrollManager, float fromX, float fromY,
    151             float deltaAngle) {
    152         long downTime = SystemClock.uptimeMillis();
    153         long eventTime = SystemClock.uptimeMillis();
    154 
    155         float stepAngle = deltaAngle / STEP_COUNT;
    156         double relativeX = fromX - (TEST_WIDTH / 2);
    157         double relativeY = fromY - (TEST_HEIGHT / 2);
    158         float radius = (float) Math.sqrt(relativeX * relativeX + relativeY * relativeY);
    159         float angle = getAngle(fromX, fromY, TEST_WIDTH, TEST_HEIGHT);
    160 
    161         float y = fromY;
    162         float x = fromX;
    163 
    164         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
    165                 MotionEvent.ACTION_DOWN, x, y, 0);
    166         scrollManager.onTouchEvent(event);
    167         for (int i = 0; i < STEP_COUNT; ++i) {
    168             angle += stepAngle;
    169             x = getX(angle, radius, TEST_WIDTH);
    170             y = getY(angle, radius, TEST_HEIGHT);
    171             eventTime = SystemClock.uptimeMillis();
    172             event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
    173             scrollManager.onTouchEvent(event);
    174         }
    175 
    176         eventTime = SystemClock.uptimeMillis();
    177         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
    178         scrollManager.onTouchEvent(event);
    179     }
    180 
    181     private static float getX(double angle, double radius, double viewWidth) {
    182         double radianAngle = Math.toRadians(angle - 90);
    183         double relativeX = cos(radianAngle) * radius;
    184         return (float) (relativeX + (viewWidth / 2));
    185     }
    186 
    187     private static float getY(double angle, double radius, double viewHeight) {
    188         double radianAngle = Math.toRadians(angle - 90);
    189         double relativeY = sin(radianAngle) * radius;
    190         return (float) (relativeY + (viewHeight / 2));
    191     }
    192 
    193     private static float getAngle(double x, double y, double viewWidth, double viewHeight) {
    194         double relativeX = x - (viewWidth / 2);
    195         double relativeY = y - (viewHeight / 2);
    196         double rowAngle = Math.atan2(relativeX, relativeY);
    197         double angle = -Math.toDegrees(rowAngle) - 180;
    198         if (angle < 0) {
    199             angle += 360;
    200         }
    201         return (float) angle;
    202     }
    203 }
    204