147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * 447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License"); 547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * you may not use this file except in compliance with the License. 647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * You may obtain a copy of the License at 747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * 847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * http://www.apache.org/licenses/LICENSE-2.0 947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * 1047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * Unless required by applicable law or agreed to in writing, software 1147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS, 1247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * See the License for the specific language governing permissions and 1447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * limitations under the License. 1547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal */ 1647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 1847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 1947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory; 2147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 2247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.hamcrest.CoreMatchers.is; 2347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.hamcrest.MatcherAssert.assertThat; 2447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.junit.Assert.assertEquals; 2547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.junit.Assert.assertNotNull; 2647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.junit.Assert.assertNull; 2747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport static org.junit.Assert.assertTrue; 2847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 2947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.content.Context; 3047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.support.test.filters.MediumTest; 3147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.support.test.runner.AndroidJUnit4; 3247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.view.MotionEvent; 3347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.view.ViewGroup; 3447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport android.widget.EdgeEffect; 3547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 36c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull; 37c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.view.InputDeviceCompat; 38c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas 3947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport org.junit.Before; 4047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport org.junit.Test; 4147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalimport org.junit.runner.RunWith; 4247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 4347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal/** 4447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal * Tests custom edge effect are properly applied when scrolling. 4547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal */ 4647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal@MediumTest 4747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal@RunWith(AndroidJUnit4.class) 4847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyalpublic class CustomEdgeEffectTest extends BaseRecyclerViewInstrumentationTest { 4947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 5047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private static final int NUM_ITEMS = 10; 5147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 5247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private LinearLayoutManager mLayoutManager; 5347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private RecyclerView mRecyclerView; 5447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 5547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Before 5647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal public void setup() throws Throwable { 5747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mLayoutManager = new LinearLayoutManager(getActivity()); 5847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mLayoutManager.ensureLayoutState(); 5947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 6047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView = new RecyclerView(getActivity()); 6147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView.setLayoutManager(mLayoutManager); 6247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView.setAdapter(new TestAdapter(NUM_ITEMS) { 6347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 6447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Override 658a11e6829c522aa1efcc903afa4c01d337082eabChris Craik public TestViewHolder onCreateViewHolder(@NonNull ViewGroup parent, 6647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal int viewType) { 6747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestViewHolder holder = super.onCreateViewHolder(parent, viewType); 6847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal holder.itemView.setMinimumHeight(mRecyclerView.getMeasuredHeight() * 2 / NUM_ITEMS); 6947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal return holder; 7047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 7147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal }); 7247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal setRecyclerView(mRecyclerView); 7347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal getInstrumentation().waitForIdleSync(); 7447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertThat("Test sanity", mRecyclerView.getChildCount() > 0, is(true)); 7547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 7647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 7747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Test 7847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal public void testEdgeEffectDirections() throws Throwable { 7947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffectFactory factory = new TestEdgeEffectFactory(); 8047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView.setEdgeEffectFactory(factory); 8147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollToPosition(0); 8247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal waitForIdleScroll(mRecyclerView); 8347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollViewBy(3); 8447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertNull(factory.mBottom); 8547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertNotNull(factory.mTop); 8647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertTrue(factory.mTop.mPullDistance > 0); 8747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 8847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollToPosition(NUM_ITEMS - 1); 8947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal waitForIdleScroll(mRecyclerView); 9047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollViewBy(-3); 9147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 9247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertNotNull(factory.mBottom); 9347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertTrue(factory.mBottom.mPullDistance > 0); 9447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 9547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 9647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Test 9747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal public void testEdgeEffectReplaced() throws Throwable { 9847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffectFactory factory1 = new TestEdgeEffectFactory(); 9947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView.setEdgeEffectFactory(factory1); 10047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollToPosition(0); 10147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal waitForIdleScroll(mRecyclerView); 10247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 10347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollViewBy(3); 10447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertNotNull(factory1.mTop); 10547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal float oldPullDistance = factory1.mTop.mPullDistance; 10647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 10747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal waitForIdleScroll(mRecyclerView); 10847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffectFactory factory2 = new TestEdgeEffectFactory(); 10947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mRecyclerView.setEdgeEffectFactory(factory2); 11047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal scrollViewBy(30); 11147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertNotNull(factory2.mTop); 11247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 11347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertTrue(factory2.mTop.mPullDistance > oldPullDistance); 11447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal assertEquals(oldPullDistance, factory1.mTop.mPullDistance, 0.1f); 11547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 11647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 11747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private void scrollViewBy(final int value) throws Throwable { 11847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mActivityRule.runOnUiThread(new Runnable() { 11947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Override 12047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal public void run() { 12147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TouchUtils.scrollView(MotionEvent.AXIS_VSCROLL, value, 12247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal InputDeviceCompat.SOURCE_CLASS_POINTER, mRecyclerView); 12347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 12447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal }); 12547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 12647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 12747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private class TestEdgeEffectFactory extends EdgeEffectFactory { 12847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 12947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffect mTop, mBottom; 13047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 13147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @NonNull 13247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Override 13347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) { 13447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffect effect = new TestEdgeEffect(view.getContext()); 13547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal if (direction == EdgeEffectFactory.DIRECTION_TOP) { 13647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mTop = effect; 13747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } else if (direction == EdgeEffectFactory.DIRECTION_BOTTOM) { 13847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mBottom = effect; 13947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 14047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal return effect; 14147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 14247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 14347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 14447ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private class TestEdgeEffect extends EdgeEffect { 14547ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 14647ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal private float mPullDistance; 14747ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 14847ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal TestEdgeEffect(Context context) { 14947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal super(context); 15047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 15147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal 15247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal @Override 15347ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal public void onPull(float deltaDistance, float displacement) { 154d0697af515e2e6c949b52b4ad1805058c22fb9b9Sunny Goyal onPull(deltaDistance); 155d0697af515e2e6c949b52b4ad1805058c22fb9b9Sunny Goyal } 156d0697af515e2e6c949b52b4ad1805058c22fb9b9Sunny Goyal 157d0697af515e2e6c949b52b4ad1805058c22fb9b9Sunny Goyal @Override 158d0697af515e2e6c949b52b4ad1805058c22fb9b9Sunny Goyal public void onPull(float deltaDistance) { 15947ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal mPullDistance = deltaDistance; 16047ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 16147ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal } 16247ab6d82dd22a73520a9b22766765a47cb28e9afSunny Goyal} 163