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