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