Home | History | Annotate | Download | only in helper
      1 /*
      2  * Copyright (C) 2015 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 
     17 package android.support.v7.widget.helper;
     18 
     19 import static android.support.v7.widget.helper.ItemTouchHelper.END;
     20 import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
     21 import static android.support.v7.widget.helper.ItemTouchHelper.RIGHT;
     22 import static android.support.v7.widget.helper.ItemTouchHelper.START;
     23 import static android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback;
     24 
     25 import static org.junit.Assert.assertEquals;
     26 import static org.junit.Assert.assertNotNull;
     27 import static org.junit.Assert.assertTrue;
     28 
     29 import android.os.Build;
     30 import android.support.test.filters.LargeTest;
     31 import android.support.test.filters.SdkSuppress;
     32 import android.support.test.filters.Suppress;
     33 import android.support.test.runner.AndroidJUnit4;
     34 import android.support.testutils.PollingCheck;
     35 import android.support.v7.util.TouchUtils;
     36 import android.support.v7.widget.BaseRecyclerViewInstrumentationTest;
     37 import android.support.v7.widget.RecyclerView;
     38 import android.support.v7.widget.WrappedRecyclerView;
     39 import android.view.Gravity;
     40 
     41 import org.junit.Test;
     42 import org.junit.runner.RunWith;
     43 
     44 import java.util.ArrayList;
     45 import java.util.List;
     46 
     47 @LargeTest
     48 @RunWith(AndroidJUnit4.class)
     49 public class ItemTouchHelperTest extends BaseRecyclerViewInstrumentationTest {
     50 
     51     TestAdapter mAdapter;
     52 
     53     TestLayoutManager mLayoutManager;
     54 
     55     private LoggingCalback mCalback;
     56 
     57     private LoggingItemTouchHelper mItemTouchHelper;
     58 
     59     private WrappedRecyclerView mWrappedRecyclerView;
     60 
     61     private Boolean mSetupRTL;
     62 
     63     public ItemTouchHelperTest() {
     64         super(false);
     65     }
     66 
     67     private RecyclerView setup(int dragDirs, int swipeDirs) throws Throwable {
     68         mWrappedRecyclerView = inflateWrappedRV();
     69         mAdapter = new TestAdapter(10);
     70         mLayoutManager = new TestLayoutManager() {
     71             @Override
     72             public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
     73                 detachAndScrapAttachedViews(recycler);
     74                 layoutRange(recycler, 0, Math.min(5, state.getItemCount()));
     75                 layoutLatch.countDown();
     76             }
     77 
     78             @Override
     79             public boolean canScrollHorizontally() {
     80                 return false;
     81             }
     82 
     83             @Override
     84             public boolean supportsPredictiveItemAnimations() {
     85                 return false;
     86             }
     87         };
     88         mWrappedRecyclerView.setFakeRTL(mSetupRTL);
     89         mWrappedRecyclerView.setAdapter(mAdapter);
     90         mWrappedRecyclerView.setLayoutManager(mLayoutManager);
     91         mCalback = new LoggingCalback(dragDirs, swipeDirs);
     92         mItemTouchHelper = new LoggingItemTouchHelper(mCalback);
     93         mActivityRule.runOnUiThread(new Runnable() {
     94             @Override
     95             public void run() {
     96                 mItemTouchHelper.attachToRecyclerView(mWrappedRecyclerView);
     97             }
     98         });
     99 
    100         return mWrappedRecyclerView;
    101     }
    102 
    103     @Test
    104     public void swipeLeft() throws Throwable {
    105         basicSwipeTest(LEFT, LEFT | RIGHT, -getActivity().getWindow().getDecorView().getWidth());
    106     }
    107 
    108     @Test
    109     public void swipeRight() throws Throwable {
    110         basicSwipeTest(RIGHT, LEFT | RIGHT, getActivity().getWindow().getDecorView().getWidth());
    111     }
    112 
    113     @Test
    114     public void swipeStart() throws Throwable {
    115         basicSwipeTest(START, START | END, -getActivity().getWindow().getDecorView().getWidth());
    116     }
    117 
    118     @Test
    119     public void swipeEnd() throws Throwable {
    120         basicSwipeTest(END, START | END, getActivity().getWindow().getDecorView().getWidth());
    121     }
    122 
    123     // Test is disabled as it is flaky.
    124     @Suppress
    125     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR1)
    126     @Test
    127     public void swipeStartInRTL() throws Throwable {
    128         mSetupRTL = true;
    129         basicSwipeTest(START, START | END, getActivity().getWindow().getDecorView().getWidth());
    130     }
    131 
    132     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR1)
    133     @Test
    134     public void swipeEndInRTL() throws Throwable {
    135         mSetupRTL = true;
    136         basicSwipeTest(END, START | END, -getActivity().getWindow().getDecorView().getWidth());
    137     }
    138 
    139     public void basicSwipeTest(int dir, int swipeDirs, int targetX) throws Throwable {
    140         final RecyclerView recyclerView = setup(0, swipeDirs);
    141         mLayoutManager.expectLayouts(1);
    142         setRecyclerView(recyclerView);
    143         mLayoutManager.waitForLayout(1);
    144 
    145         final RecyclerView.ViewHolder target = mRecyclerView
    146                 .findViewHolderForAdapterPosition(1);
    147         TouchUtils.dragViewToX(getInstrumentation(), target.itemView, Gravity.CENTER, targetX);
    148 
    149         PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() {
    150             @Override
    151             public boolean canProceed() {
    152                 return mCalback.getSwipe(target) != null;
    153             }
    154         });
    155         final SwipeRecord swipe = mCalback.getSwipe(target);
    156         assertNotNull(swipe);
    157         assertEquals(dir, swipe.dir);
    158         assertEquals(1, mItemTouchHelper.mRecoverAnimations.size());
    159         assertEquals(1, mItemTouchHelper.mPendingCleanup.size());
    160         // get rid of the view
    161         mLayoutManager.expectLayouts(1);
    162         mAdapter.deleteAndNotify(1, 1);
    163         mLayoutManager.waitForLayout(1);
    164         waitForAnimations();
    165         assertEquals(0, mItemTouchHelper.mRecoverAnimations.size());
    166         assertEquals(0, mItemTouchHelper.mPendingCleanup.size());
    167         assertTrue(mCalback.isCleared(target));
    168     }
    169 
    170     private void waitForAnimations() throws InterruptedException {
    171         while (mRecyclerView.getItemAnimator().isRunning()) {
    172             Thread.sleep(100);
    173         }
    174     }
    175 
    176     private static class LoggingCalback extends SimpleCallback {
    177 
    178         private List<MoveRecord> mMoveRecordList = new ArrayList<MoveRecord>();
    179 
    180         private List<SwipeRecord> mSwipeRecords = new ArrayList<SwipeRecord>();
    181 
    182         private List<RecyclerView.ViewHolder> mCleared = new ArrayList<RecyclerView.ViewHolder>();
    183 
    184         LoggingCalback(int dragDirs, int swipeDirs) {
    185             super(dragDirs, swipeDirs);
    186         }
    187 
    188         @Override
    189         public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
    190                 RecyclerView.ViewHolder target) {
    191             mMoveRecordList.add(new MoveRecord(viewHolder, target));
    192             return true;
    193         }
    194 
    195         @Override
    196         public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    197             mSwipeRecords.add(new SwipeRecord(viewHolder, direction));
    198         }
    199 
    200         public MoveRecord getMove(RecyclerView.ViewHolder vh) {
    201             for (MoveRecord move : mMoveRecordList) {
    202                 if (move.from == vh) {
    203                     return move;
    204                 }
    205             }
    206             return null;
    207         }
    208 
    209         @Override
    210         public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    211             super.clearView(recyclerView, viewHolder);
    212             mCleared.add(viewHolder);
    213         }
    214 
    215         public SwipeRecord getSwipe(RecyclerView.ViewHolder vh) {
    216             for (SwipeRecord swipe : mSwipeRecords) {
    217                 if (swipe.viewHolder == vh) {
    218                     return swipe;
    219                 }
    220             }
    221             return null;
    222         }
    223 
    224         public boolean isCleared(RecyclerView.ViewHolder vh) {
    225             return mCleared.contains(vh);
    226         }
    227     }
    228 
    229     private static class LoggingItemTouchHelper extends ItemTouchHelper {
    230 
    231         public LoggingItemTouchHelper(Callback callback) {
    232             super(callback);
    233         }
    234     }
    235 
    236     private static class SwipeRecord {
    237 
    238         RecyclerView.ViewHolder viewHolder;
    239 
    240         int dir;
    241 
    242         public SwipeRecord(RecyclerView.ViewHolder viewHolder, int dir) {
    243             this.viewHolder = viewHolder;
    244             this.dir = dir;
    245         }
    246     }
    247 
    248     private static class MoveRecord {
    249 
    250         final int fromPos, toPos;
    251 
    252         RecyclerView.ViewHolder from, to;
    253 
    254         MoveRecord(RecyclerView.ViewHolder from, RecyclerView.ViewHolder to) {
    255             this.from = from;
    256             this.to = to;
    257             fromPos = from.getAdapterPosition();
    258             toPos = to.getAdapterPosition();
    259         }
    260     }
    261 }
    262