Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2018 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 androidx.recyclerview.widget;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertNotEquals;
     21 
     22 import android.content.Context;
     23 import android.support.test.InstrumentationRegistry;
     24 import android.support.test.filters.SmallTest;
     25 import android.support.test.runner.AndroidJUnit4;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 import android.view.ViewConfiguration;
     29 import android.view.ViewGroup;
     30 import android.widget.TextView;
     31 
     32 import androidx.annotation.NonNull;
     33 import androidx.core.view.InputDeviceCompat;
     34 import androidx.core.view.ViewConfigurationCompat;
     35 
     36 import org.junit.Before;
     37 import org.junit.Test;
     38 import org.junit.runner.RunWith;
     39 
     40 @SmallTest
     41 @RunWith(AndroidJUnit4.class)
     42 public class RecyclerViewOnGenericMotionEventTest {
     43 
     44     TestRecyclerView mRecyclerView;
     45 
     46     @Before
     47     public void setUp() throws Exception {
     48         mRecyclerView = new TestRecyclerView(getContext());
     49     }
     50 
     51     private Context getContext() {
     52         return InstrumentationRegistry.getContext();
     53     }
     54 
     55     private void layout() {
     56         mRecyclerView.layout(0, 0, 320, 320);
     57     }
     58 
     59     @Test
     60     public void rotaryEncoderVerticalScroll() {
     61         MockLayoutManager layoutManager = new MockLayoutManager(true, true);
     62         mRecyclerView.setLayoutManager(layoutManager);
     63         layout();
     64         TouchUtils.scrollView(
     65                 MotionEvent.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER, mRecyclerView);
     66         assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()));
     67     }
     68 
     69     @Test
     70     public void rotaryEncoderHorizontalScroll() {
     71         // The encoder is one-dimensional, and can only scroll horizontally if vertical scrolling
     72         // is not enabled.
     73         MockLayoutManager layoutManager = new MockLayoutManager(true, false);
     74         mRecyclerView.setLayoutManager(layoutManager);
     75         layout();
     76         TouchUtils.scrollView(
     77                 MotionEvent.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER, mRecyclerView);
     78         assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0);
     79     }
     80 
     81     @Test
     82     public void pointerVerticalScroll() {
     83         MockLayoutManager layoutManager = new MockLayoutManager(true, true);
     84         mRecyclerView.setLayoutManager(layoutManager);
     85         layout();
     86         TouchUtils.scrollView(
     87                 MotionEvent.AXIS_VSCROLL, 2, InputDeviceCompat.SOURCE_CLASS_POINTER, mRecyclerView);
     88         assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()));
     89     }
     90 
     91     @Test
     92     public void pointerHorizontalScroll() {
     93         MockLayoutManager layoutManager = new MockLayoutManager(true, true);
     94         mRecyclerView.setLayoutManager(layoutManager);
     95         layout();
     96         TouchUtils.scrollView(
     97                 MotionEvent.AXIS_HSCROLL, 2, InputDeviceCompat.SOURCE_CLASS_POINTER, mRecyclerView);
     98         assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0);
     99     }
    100 
    101     @Test
    102     public void nonZeroScaledVerticalScrollFactor() {
    103         assertNotEquals(0, getScaledVerticalScrollFactor());
    104     }
    105 
    106     @Test
    107     public void nonZeroScaledHorizontalScrollFactor() {
    108         assertNotEquals(0, getScaledHorizontalScrollFactor());
    109     }
    110 
    111     private void assertTotalScroll(int x, int y) {
    112         assertEquals("x total scroll", x, mRecyclerView.mTotalX);
    113         assertEquals("y total scroll", y, mRecyclerView.mTotalY);
    114     }
    115 
    116     private static MotionEvent obtainScrollMotionEvent(int axis, int axisValue, int inputDevice) {
    117         MotionEvent.PointerProperties[] pointerProperties = { new MotionEvent.PointerProperties() };
    118         MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
    119         coords.setAxisValue(axis, axisValue);
    120         MotionEvent.PointerCoords[] pointerCoords = { coords };
    121         float xPrecision = 1;
    122         float yPrecision = 1;
    123         int deviceId = 0;
    124         int edgeFlags = 0;
    125         int flags = 0;
    126         return MotionEvent.obtain(0, System.currentTimeMillis(), MotionEvent.ACTION_SCROLL,
    127                 1, pointerProperties, pointerCoords, 0, 0, xPrecision, yPrecision, deviceId,
    128                 edgeFlags, inputDevice, flags);
    129     }
    130 
    131     private float getScaledVerticalScrollFactor() {
    132         return ViewConfigurationCompat.getScaledVerticalScrollFactor(
    133                 ViewConfiguration.get(getContext()), getContext());
    134     }
    135 
    136     private float getScaledHorizontalScrollFactor() {
    137         return ViewConfigurationCompat.getScaledHorizontalScrollFactor(
    138                 ViewConfiguration.get(getContext()), getContext());
    139     }
    140 
    141     static class MockLayoutManager extends RecyclerView.LayoutManager {
    142 
    143         private final boolean mCanScrollHorizontally;
    144 
    145         private final boolean mCanScrollVertically;
    146 
    147         MockLayoutManager(boolean canScrollHorizontally, boolean canScrollVertically) {
    148             mCanScrollHorizontally = canScrollHorizontally;
    149             mCanScrollVertically = canScrollVertically;
    150         }
    151 
    152         @Override
    153         public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    154             return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
    155                     ViewGroup.LayoutParams.WRAP_CONTENT);
    156         }
    157 
    158         @Override
    159         public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
    160                 RecyclerView.State state) {
    161             return dx;
    162         }
    163 
    164         @Override
    165         public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
    166                 RecyclerView.State state) {
    167             return dy;
    168         }
    169 
    170         @Override
    171         public boolean canScrollHorizontally() {
    172             return mCanScrollHorizontally;
    173         }
    174 
    175         @Override
    176         public boolean canScrollVertically() {
    177             return mCanScrollVertically;
    178         }
    179     }
    180 
    181     static class MockAdapter extends RecyclerView.Adapter {
    182 
    183         private int mCount = 0;
    184 
    185         MockAdapter(int count) {
    186             this.mCount = count;
    187         }
    188 
    189         @Override
    190         public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    191             return new MockViewHolder(new TextView(parent.getContext()));
    192         }
    193 
    194         @Override
    195         public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    196 
    197         }
    198 
    199         @Override
    200         public int getItemCount() {
    201             return mCount;
    202         }
    203     }
    204 
    205     static class MockViewHolder extends RecyclerView.ViewHolder {
    206         MockViewHolder(View itemView) {
    207             super(itemView);
    208         }
    209     }
    210 
    211     private static class TestRecyclerView extends RecyclerView {
    212         int mTotalX = 0;
    213         int mTotalY = 0;
    214 
    215         TestRecyclerView(Context context) {
    216             super(context);
    217         }
    218 
    219         boolean scrollByInternal(int x, int y, MotionEvent ev) {
    220             mTotalX += x;
    221             mTotalY += y;
    222             return super.scrollByInternal(x, y, ev);
    223         }
    224     }
    225 }
    226