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 17package androidx.wear.widget; 18 19import static android.support.test.espresso.Espresso.onView; 20import static android.support.test.espresso.matcher.ViewMatchers.withId; 21 22import static androidx.wear.widget.util.AsyncViewActions.waitForMatchingView; 23import static androidx.wear.widget.util.MoreViewAssertions.withNoVerticalScrollOffset; 24import static androidx.wear.widget.util.MoreViewAssertions.withPositiveVerticalScrollOffset; 25 26import static org.hamcrest.Matchers.allOf; 27import static org.junit.Assert.assertEquals; 28import static org.junit.Assert.assertFalse; 29import static org.junit.Assert.assertNotNull; 30import static org.junit.Assert.assertTrue; 31 32import android.app.Activity; 33import android.support.test.InstrumentationRegistry; 34import android.support.test.espresso.ViewAction; 35import android.support.test.espresso.action.GeneralLocation; 36import android.support.test.espresso.action.GeneralSwipeAction; 37import android.support.test.espresso.action.Press; 38import android.support.test.espresso.action.Swipe; 39import android.support.test.filters.MediumTest; 40import android.support.test.rule.ActivityTestRule; 41import android.support.test.runner.AndroidJUnit4; 42import android.view.View; 43 44import androidx.annotation.IdRes; 45import androidx.recyclerview.widget.RecyclerView; 46import androidx.wear.test.R; 47import androidx.wear.widget.util.WakeLockRule; 48 49import org.junit.Before; 50import org.junit.Rule; 51import org.junit.Test; 52import org.junit.runner.RunWith; 53import org.mockito.Mock; 54import org.mockito.MockitoAnnotations; 55 56@MediumTest 57@RunWith(AndroidJUnit4.class) 58public class WearableRecyclerViewTest { 59 60 private static final long MAX_WAIT_TIME = 10000; 61 @Mock 62 WearableRecyclerView.LayoutManager mMockChildLayoutManager; 63 64 @Rule 65 public final WakeLockRule wakeLock = new WakeLockRule(); 66 67 @Rule 68 public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule = 69 new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true); 70 71 @Before 72 public void setUp() { 73 MockitoAnnotations.initMocks(this); 74 } 75 76 @Test 77 public void testCaseInitState() { 78 WearableRecyclerView wrv = new WearableRecyclerView(mActivityRule.getActivity()); 79 wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext())); 80 81 assertFalse(wrv.isEdgeItemsCenteringEnabled()); 82 assertFalse(wrv.isCircularScrollingGestureEnabled()); 83 assertEquals(1.0f, wrv.getBezelFraction(), 0.01f); 84 assertEquals(180.0f, wrv.getScrollDegreesPerScreen(), 0.01f); 85 } 86 87 @Test 88 public void testEdgeItemsCenteringOnAndOff() throws Throwable { 89 mActivityRule.runOnUiThread(new Runnable() { 90 @Override 91 public void run() { 92 WearableRecyclerView wrv = 93 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 94 wrv.setEdgeItemsCenteringEnabled(true); 95 } 96 }); 97 98 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 99 100 mActivityRule.runOnUiThread(new Runnable() { 101 @Override 102 public void run() { 103 WearableRecyclerView wrv = 104 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 105 View child = wrv.getChildAt(0); 106 assertNotNull("child", child); 107 assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop()); 108 } 109 }); 110 111 mActivityRule.runOnUiThread(new Runnable() { 112 @Override 113 public void run() { 114 WearableRecyclerView wrv = 115 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 116 wrv.setEdgeItemsCenteringEnabled(false); 117 } 118 }); 119 120 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 121 122 mActivityRule.runOnUiThread(new Runnable() { 123 @Override 124 public void run() { 125 WearableRecyclerView wrv = 126 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 127 View child = wrv.getChildAt(0); 128 assertNotNull("child", child); 129 assertEquals(0, child.getTop()); 130 131 } 132 }); 133 } 134 135 @Test 136 public void testEdgeItemsCenteringBeforeChildrenDrawn() throws Throwable { 137 mActivityRule.runOnUiThread(new Runnable() { 138 @Override 139 public void run() { 140 Activity activity = mActivityRule.getActivity(); 141 WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv); 142 RecyclerView.Adapter<WearableRecyclerView.ViewHolder> adapter = wrv.getAdapter(); 143 wrv.setAdapter(null); 144 wrv.setEdgeItemsCenteringEnabled(true); 145 wrv.setAdapter(adapter); 146 } 147 }); 148 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 149 150 mActivityRule.runOnUiThread(new Runnable() { 151 @Override 152 public void run() { 153 WearableRecyclerView wrv = 154 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 155 // Verify the first child 156 View child = wrv.getChildAt(0); 157 assertNotNull("child", child); 158 assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop()); 159 } 160 }); 161 } 162 163 @Test 164 public void testCircularScrollingGesture() throws Throwable { 165 onView(withId(R.id.wrv)).perform(swipeDownFromTopRight()); 166 assertNotScrolledY(R.id.wrv); 167 168 mActivityRule.runOnUiThread(new Runnable() { 169 @Override 170 public void run() { 171 WearableRecyclerView wrv = 172 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 173 wrv.setCircularScrollingGestureEnabled(true); 174 } 175 }); 176 177 onView(withId(R.id.wrv)).perform(swipeDownFromTopRight()); 178 assertScrolledY(R.id.wrv); 179 } 180 181 @Test 182 public void testCurvedOffsettingHelper() throws Throwable { 183 mActivityRule.runOnUiThread(new Runnable() { 184 @Override 185 public void run() { 186 WearableRecyclerView wrv = 187 (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv); 188 wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext())); 189 } 190 }); 191 192 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 193 194 onView(withId(R.id.wrv)).perform(swipeDownFromTopRight()); 195 196 mActivityRule.runOnUiThread(new Runnable() { 197 @Override 198 public void run() { 199 Activity activity = mActivityRule.getActivity(); 200 WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv); 201 if (activity.getResources().getConfiguration().isScreenRound()) { 202 View child = wrv.getChildAt(0); 203 assertTrue(child.getLeft() > 0); 204 } else { 205 for (int i = 0; i < wrv.getChildCount(); i++) { 206 assertEquals(0, wrv.getChildAt(i).getLeft()); 207 } 208 } 209 } 210 }); 211 } 212 213 private static ViewAction swipeDownFromTopRight() { 214 return new GeneralSwipeAction( 215 Swipe.FAST, GeneralLocation.TOP_RIGHT, GeneralLocation.BOTTOM_RIGHT, 216 Press.FINGER); 217 } 218 219 private void assertScrolledY(@IdRes int layoutId) { 220 onView(withId(layoutId)).perform(waitForMatchingView( 221 allOf(withId(layoutId), withPositiveVerticalScrollOffset()), MAX_WAIT_TIME)); 222 } 223 224 private void assertNotScrolledY(@IdRes int layoutId) { 225 onView(withId(layoutId)).perform(waitForMatchingView( 226 allOf(withId(layoutId), withNoVerticalScrollOffset()), MAX_WAIT_TIME)); 227 } 228} 229