15504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska/* 25504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * Copyright (C) 2017 The Android Open Source Project 35504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * 45504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * Licensed under the Apache License, Version 2.0 (the "License"); 55504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * you may not use this file except in compliance with the License. 65504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * You may obtain a copy of the License at 75504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * 85504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * http://www.apache.org/licenses/LICENSE-2.0 95504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * 105504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * Unless required by applicable law or agreed to in writing, software 115504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * distributed under the License is distributed on an "AS IS" BASIS, 125504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * See the License for the specific language governing permissions and 145504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska * limitations under the License. 155504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska */ 165504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 179b028c55b13889bf75b5dd43b5b0f4051834ae1dAga Madurskapackage android.support.wear.widget; 185504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 195504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport static org.mockito.Mockito.times; 205504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport static org.mockito.Mockito.verify; 215504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 225504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport static java.lang.Math.cos; 235504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport static java.lang.Math.sin; 245504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 255504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport android.os.SystemClock; 265504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport android.support.test.filters.MediumTest; 275504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport android.support.test.rule.ActivityTestRule; 285504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport android.support.test.runner.AndroidJUnit4; 299b028c55b13889bf75b5dd43b5b0f4051834ae1dAga Madurskaimport android.support.wear.widget.util.WakeLockRule; 305504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport android.view.MotionEvent; 315504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 325504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.junit.Before; 335504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.junit.Rule; 345504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.junit.Test; 355504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.junit.runner.RunWith; 365504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.mockito.Mock; 375504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskaimport org.mockito.MockitoAnnotations; 385504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 395504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska@MediumTest 405504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska@RunWith(AndroidJUnit4.class) 415504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurskapublic class ScrollManagerTest { 425504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static final int TEST_WIDTH = 400; 435504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static final int TEST_HEIGHT = 400; 445504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static final int STEP_COUNT = 300; 455504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 465504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static final int EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE = 36; 475504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static final int EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE = 199; 485504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 495504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Rule 505504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public final WakeLockRule wakeLock = new WakeLockRule(); 515504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 525504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Rule 535504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule = 545504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true); 555504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 565504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Mock 575504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska WearableRecyclerView mMockWearableRecyclerView; 585504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 595504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska ScrollManager mScrollManagerUnderTest; 605504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 615504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Before 625504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void setUp() throws Throwable { 635504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska MockitoAnnotations.initMocks(this); 645504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska mScrollManagerUnderTest = new ScrollManager(); 655504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska mScrollManagerUnderTest.setRecyclerView(mMockWearableRecyclerView, TEST_WIDTH, TEST_HEIGHT); 665504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 675504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 685504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 695504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testStraightUpScrollingGestureLeft() throws Throwable { 705504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in a straight line from center left to upper left 715504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scroll(mScrollManagerUnderTest, 30, 30, 200, 150); 725504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should require the recycler view to scroll up and only up 735504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE)) 745504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, 1); 755504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 765504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 775504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 785504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testStraightDownScrollingGestureLeft() throws Throwable { 795504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in a straight line upper left to center left 805504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scroll(mScrollManagerUnderTest, 30, 30, 150, 200); 815504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should require the recycler view to scroll down and only down 825504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE)) 835504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, -1); 845504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 855504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 865504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 875504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testStraightUpScrollingGestureRight() throws Throwable { 885504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in a straight line from center right to upper right 895504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scroll(mScrollManagerUnderTest, 370, 370, 200, 150); 905504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should require the recycler view to scroll down and only down 915504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE)) 925504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, -1); 935504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 945504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 955504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 965504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testStraightDownScrollingGestureRight() throws Throwable { 975504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in a straight line upper right to center right 985504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scroll(mScrollManagerUnderTest, 370, 370, 150, 200); 995504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should require the recycler view to scroll up and only up 1005504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE)) 1015504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, 1); 1025504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1035504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1045504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 1055504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testCircularScrollingGestureLeft() throws Throwable { 1065504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in an arch from center left to center right 1075504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollOnArch(mScrollManagerUnderTest, 30, 200, 180.0f); 1085504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should never reverse the scroll direction and scroll up 1095504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE)) 1105504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, 1); 1115504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1125504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1135504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska @Test 1145504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska public void testCircularScrollingGestureRight() throws Throwable { 1155504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // Pretend to scroll in an arch from center left to center right 1165504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollOnArch(mScrollManagerUnderTest, 370, 200, -180.0f); 1175504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska // The scroll manager should never reverse the scroll direction and scroll down. 1185504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE)) 1195504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska .scrollBy(0, -1); 1205504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1215504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1225504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static void scroll(ScrollManager scrollManager, float fromX, float toX, float fromY, 1235504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float toY) { 1245504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska long downTime = SystemClock.uptimeMillis(); 1255504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska long eventTime = SystemClock.uptimeMillis(); 1265504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1275504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float y = fromY; 1285504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float x = fromX; 1295504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1305504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float yStep = (toY - fromY) / STEP_COUNT; 1315504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float xStep = (toX - fromX) / STEP_COUNT; 1325504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1335504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska MotionEvent event = MotionEvent.obtain(downTime, eventTime, 1345504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska MotionEvent.ACTION_DOWN, x, y, 0); 1355504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1365504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska for (int i = 0; i < STEP_COUNT; ++i) { 1375504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska y += yStep; 1385504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska x += xStep; 1395504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska eventTime = SystemClock.uptimeMillis(); 1405504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0); 1415504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1425504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1435504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1445504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska eventTime = SystemClock.uptimeMillis(); 1455504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 1465504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1475504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1485504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1495504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static void scrollOnArch(ScrollManager scrollManager, float fromX, float fromY, 1505504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float deltaAngle) { 1515504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska long downTime = SystemClock.uptimeMillis(); 1525504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska long eventTime = SystemClock.uptimeMillis(); 1535504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1545504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float stepAngle = deltaAngle / STEP_COUNT; 1555504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeX = fromX - (TEST_WIDTH / 2); 1565504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeY = fromY - (TEST_HEIGHT / 2); 1575504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float radius = (float) Math.sqrt(relativeX * relativeX + relativeY * relativeY); 1585504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float angle = getAngle(fromX, fromY, TEST_WIDTH, TEST_HEIGHT); 1595504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1605504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float y = fromY; 1615504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska float x = fromX; 1625504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1635504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska MotionEvent event = MotionEvent.obtain(downTime, eventTime, 1645504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska MotionEvent.ACTION_DOWN, x, y, 0); 1655504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1665504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska for (int i = 0; i < STEP_COUNT; ++i) { 1675504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska angle += stepAngle; 1685504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska x = getX(angle, radius, TEST_WIDTH); 1695504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska y = getY(angle, radius, TEST_HEIGHT); 1705504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska eventTime = SystemClock.uptimeMillis(); 1715504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0); 1725504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1735504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1745504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1755504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska eventTime = SystemClock.uptimeMillis(); 1765504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 1775504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska scrollManager.onTouchEvent(event); 1785504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1795504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1805504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static float getX(double angle, double radius, double viewWidth) { 1815504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double radianAngle = Math.toRadians(angle - 90); 1825504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeX = cos(radianAngle) * radius; 1835504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska return (float) (relativeX + (viewWidth / 2)); 1845504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1855504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1865504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static float getY(double angle, double radius, double viewHeight) { 1875504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double radianAngle = Math.toRadians(angle - 90); 1885504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeY = sin(radianAngle) * radius; 1895504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska return (float) (relativeY + (viewHeight / 2)); 1905504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 1915504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska 1925504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska private static float getAngle(double x, double y, double viewWidth, double viewHeight) { 1935504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeX = x - (viewWidth / 2); 1945504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double relativeY = y - (viewHeight / 2); 1955504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double rowAngle = Math.atan2(relativeX, relativeY); 1965504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska double angle = -Math.toDegrees(rowAngle) - 180; 1975504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska if (angle < 0) { 1985504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska angle += 360; 1995504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 2005504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska return (float) angle; 2015504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska } 2025504220f38bb0552e1d8def09fb1b9a118264b45Aga Madurska} 203