1c587f7dba5a337169e854e235da59f595255d6ccAga Madurska/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
3c587f7dba5a337169e854e235da59f595255d6ccAga Madurska *
4c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Licensed under the Apache License, Version 2.0 (the "License");
5c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * you may not use this file except in compliance with the License.
6c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * You may obtain a copy of the License at
7c587f7dba5a337169e854e235da59f595255d6ccAga Madurska *
8c587f7dba5a337169e854e235da59f595255d6ccAga Madurska *      http://www.apache.org/licenses/LICENSE-2.0
9c587f7dba5a337169e854e235da59f595255d6ccAga Madurska *
10c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Unless required by applicable law or agreed to in writing, software
11c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * distributed under the License is distributed on an "AS IS" BASIS,
12c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * See the License for the specific language governing permissions and
14c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * limitations under the License.
15c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */
16c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget;
18c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
19fa39e2bca1b284ad7c931d9194287770b7b507faAurimas Liutikasimport static org.junit.Assert.assertEquals;
20fa39e2bca1b284ad7c931d9194287770b7b507faAurimas Liutikasimport static org.junit.Assert.assertNotSame;
21fa39e2bca1b284ad7c931d9194287770b7b507faAurimas Liutikasimport static org.junit.Assert.assertSame;
22fa39e2bca1b284ad7c931d9194287770b7b507faAurimas Liutikasimport static org.junit.Assert.assertTrue;
23c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
2435232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikasimport android.support.test.filters.LargeTest;
25c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.view.View;
26c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
27c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.Nullable;
28c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas
29e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport org.junit.Test;
30e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport org.junit.runner.RunWith;
31e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport org.junit.runners.Parameterized;
32e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikas
33c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport java.util.ArrayList;
34c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport java.util.List;
35c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport java.util.concurrent.atomic.AtomicBoolean;
36c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
3735232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas@LargeTest
38c587f7dba5a337169e854e235da59f595255d6ccAga Madurska@RunWith(Parameterized.class)
39c587f7dba5a337169e854e235da59f595255d6ccAga Madurskapublic class LinearLayoutManagerSnappingTest extends BaseLinearLayoutManagerTest {
40c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
41c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    final Config mConfig;
42c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    final boolean mReverseScroll;
43c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
44c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public LinearLayoutManagerSnappingTest(Config config, boolean reverseScroll) {
45c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mConfig = config;
46c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mReverseScroll = reverseScroll;
47c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
48c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
49e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikas    @Parameterized.Parameters(name = "config:{0},reverseScroll:{1}")
50c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public static List<Object[]> getParams() {
51c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        List<Object[]> result = new ArrayList<>();
52c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        List<Config> configs = createBaseVariations();
53c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        for (Config config : configs) {
54c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            for (boolean reverseScroll : new boolean[] {true, false}) {
55c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                result.add(new Object[]{config, reverseScroll});
56c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
57c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
58c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return result;
59c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
60c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
61c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Test
623911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska    public void snapOnScrollSameViewEdge() throws Throwable {
633911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        final Config config = (Config) mConfig.clone();
643911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        // Ensure that the views are big enough to reach the pathological case when the view closest
653911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        // to the center is an edge view, but it cannot scroll further in order to snap.
663911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        setupByConfig(config, true, new RecyclerView.LayoutParams(1000, 1000),
673911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska            new RecyclerView.LayoutParams(1500, 1500));
683911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        SnapHelper snapHelper = new LinearSnapHelper();
693911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.expectIdleState(1);
703911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        snapHelper.attachToRecyclerView(mRecyclerView);
713911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.waitForSnap(10);
723911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska
733911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        // Record the current center view.
743911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        View view = findCenterView(mLayoutManager);
753911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska
763911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        int scrollDistance = (getViewDimension(view) / 2) - 1;
773911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        int scrollDist = config.mStackFromEnd == config.mReverseLayout
783911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska            ? -scrollDistance : scrollDistance;
793911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.expectIdleState(1);
803911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        smoothScrollBy(scrollDist);
813911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.waitForSnap(10);
823911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.expectCallbacks(5);
833911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        mLayoutManager.assertNoCallbacks("There should be no callbacks after some time", 3);
843911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska    }
853911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska
863911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska    @Test
87c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public void snapOnScrollSameView() throws Throwable {
88c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final Config config = (Config) mConfig.clone();
89c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupByConfig(config, true);
90c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupSnapHelper();
91c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
92c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Record the current center view.
93c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View view = findCenterView(mLayoutManager);
94c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(view);
95c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
96c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int scrollDistance = (getViewDimension(view) / 2) - 1;
97c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
98c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(2);
99c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        smoothScrollBy(scrollDist);
100c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(10);
101c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
102c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Views have not changed
103c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View viewAfterFling = findCenterView(mLayoutManager);
1043911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        assertSame("The view should NOT have scrolled", view, viewAfterFling);
105c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(viewAfterFling);
106c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
107c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
108c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Test
109c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public void snapOnScrollNextView() throws Throwable {
110c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final Config config = (Config) mConfig.clone();
111c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupByConfig(config, true);
112c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupSnapHelper();
113c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
114c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Record the current center view.
115c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View view = findCenterView(mLayoutManager);
116c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(view);
117c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
118c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int scrollDistance = (getViewDimension(view) / 2) + 1;
119c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
120c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(2);
121c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        smoothScrollBy(scrollDist);
122c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(10);
123c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
124c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Views have not changed
125c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View viewAfterFling = findCenterView(mLayoutManager);
126c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertNotSame("The view should have scrolled", view, viewAfterFling);
127c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(viewAfterFling);
128c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
129c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
130c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Test
131c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public void snapOnFlingSameView() throws Throwable {
132c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final Config config = (Config) mConfig.clone();
133c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupByConfig(config, true);
134c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupSnapHelper();
135c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
136c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Record the current center view.
137c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View view = findCenterView(mLayoutManager);
138c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(view);
139c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
140c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Velocity small enough to not scroll to the next view.
141c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int velocity = (int) (1.000001 * mRecyclerView.getMinFlingVelocity());
142c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int velocityDir = mReverseScroll ? -velocity : velocity;
143c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(2);
144c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertTrue(fling(velocityDir, velocityDir));
145c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Wait for two settling scrolls: the initial one and the corrective one.
146c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        waitForIdleScroll(mRecyclerView);
147c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(100);
148c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
149c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View viewAfterFling = findCenterView(mLayoutManager);
150c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
151c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertSame("The view should NOT have scrolled", view, viewAfterFling);
152c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(viewAfterFling);
153c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
154c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
155c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Test
156c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    public void snapOnFlingNextView() throws Throwable {
157c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final Config config = (Config) mConfig.clone();
158c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupByConfig(config, true);
159c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        setupSnapHelper();
160c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
161c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Record the current center view.
162c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View view = findCenterView(mLayoutManager);
163c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(view);
164c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
165c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        // Velocity high enough to scroll beyond the current view.
166c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int velocity = (int) (0.2 * mRecyclerView.getMaxFlingVelocity());
167c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int velocityDir = mReverseScroll ? -velocity : velocity;
168c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(1);
169c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertTrue(fling(velocityDir, velocityDir));
170c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(100);
171c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        getInstrumentation().waitForIdleSync();
172c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
173c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View viewAfterFling = findCenterView(mLayoutManager);
174c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
175c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertNotSame("The view should have scrolled", view, viewAfterFling);
176c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        assertCenterAligned(viewAfterFling);
177c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
178c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
179c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private void setupSnapHelper() throws Throwable {
180c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        SnapHelper snapHelper = new LinearSnapHelper();
181c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(1);
182c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        snapHelper.attachToRecyclerView(mRecyclerView);
183c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(10);
184c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
185c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectLayouts(1);
186c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        scrollToPosition(mConfig.mItemCount / 2);
187c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForLayout(2);
188c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
189c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View view = findCenterView(mLayoutManager);
1903911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        int scrollDistance = distFromCenter(view) / 2;
1913911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        if (scrollDistance == 0) {
1923911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska            return;
1933911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        }
1943911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska
195c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
196c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
197c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.expectIdleState(2);
198c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        smoothScrollBy(scrollDist);
199c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        mLayoutManager.waitForSnap(10);
200c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
201c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
202c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Nullable private View findCenterView(RecyclerView.LayoutManager layoutManager) {
203c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (layoutManager.canScrollHorizontally()) {
204c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return mRecyclerView.findChildViewUnder(mRecyclerView.getWidth() / 2, 0);
205c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        } else {
206c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return mRecyclerView.findChildViewUnder(0, mRecyclerView.getHeight() / 2);
207c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
208c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
209c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
210c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private int getViewDimension(View view) {
211c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        OrientationHelper helper;
212c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (mLayoutManager.canScrollHorizontally()) {
213c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            helper = OrientationHelper.createHorizontalHelper(mLayoutManager);
214c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        } else {
215c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            helper = OrientationHelper.createVerticalHelper(mLayoutManager);
216c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
217c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return helper.getDecoratedMeasurement(view);
218c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
219c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
220c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private void assertCenterAligned(View view) {
221c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (mLayoutManager.canScrollHorizontally()) {
222c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            assertEquals(mRecyclerView.getWidth() / 2,
223c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    mLayoutManager.getViewBounds(view).centerX());
224c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        } else {
225c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            assertEquals(mRecyclerView.getHeight() / 2,
226c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    mLayoutManager.getViewBounds(view).centerY());
227c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
228c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
229c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
2303911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska    private int distFromCenter(View view) {
2313911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        if (mLayoutManager.canScrollHorizontally()) {
2323911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska            return Math.abs(mRecyclerView.getWidth() / 2 -
2333911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska                mLayoutManager.getViewBounds(view).centerX());
2343911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        } else {
2353911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska            return Math.abs(mRecyclerView.getHeight() / 2 -
2363911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska                mLayoutManager.getViewBounds(view).centerY());
2373911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska        }
2383911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska    }
2393911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska
240c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private boolean fling(final int velocityX, final int velocityY) throws Throwable {
241c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final AtomicBoolean didStart = new AtomicBoolean(false);
24242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
243c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            @Override
244c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            public void run() {
245c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                boolean result = mRecyclerView.fling(velocityX, velocityY);
246c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                didStart.set(result);
247c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
248c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        });
249c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (!didStart.get()) {
250c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return false;
251c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
252c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        waitForIdleScroll(mRecyclerView);
253c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return true;
254c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
255c587f7dba5a337169e854e235da59f595255d6ccAga Madurska}
256