Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright 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 
     17 package androidx.recyclerview.selection.testing;
     18 
     19 import android.graphics.Point;
     20 import android.view.KeyEvent;
     21 import android.view.MotionEvent;
     22 import android.view.MotionEvent.PointerCoords;
     23 import android.view.MotionEvent.PointerProperties;
     24 
     25 import androidx.annotation.IntDef;
     26 
     27 import java.lang.annotation.Retention;
     28 import java.lang.annotation.RetentionPolicy;
     29 import java.util.HashSet;
     30 import java.util.Set;
     31 
     32 /**
     33  * Handy-dandy wrapper class to facilitate the creation of MotionEvents.
     34  */
     35 public final class TestEvents {
     36 
     37     /**
     38      * Common mouse event types...for your convenience.
     39      */
     40     public static final class Mouse {
     41         public static final MotionEvent CLICK =
     42                 TestEvents.builder().mouse().primary().build();
     43         public static final MotionEvent CTRL_CLICK =
     44                 TestEvents.builder().mouse().primary().ctrl().build();
     45         public static final MotionEvent ALT_CLICK =
     46                 TestEvents.builder().mouse().primary().alt().build();
     47         public static final MotionEvent SHIFT_CLICK =
     48                 TestEvents.builder().mouse().primary().shift().build();
     49         public static final MotionEvent SECONDARY_CLICK =
     50                 TestEvents.builder().mouse().secondary().build();
     51         public static final MotionEvent TERTIARY_CLICK =
     52                 TestEvents.builder().mouse().tertiary().build();
     53     }
     54 
     55     /**
     56      * Common touch event types...for your convenience.
     57      */
     58     public static final class Touch {
     59         public static final MotionEvent TAP =
     60                 TestEvents.builder().touch().build();
     61     }
     62 
     63     static final int ACTION_UNSET = -1;
     64 
     65     // Add other actions from MotionEvent.ACTION_ as needed.
     66     @IntDef(flag = true, value = {
     67             MotionEvent.ACTION_DOWN,
     68             MotionEvent.ACTION_MOVE,
     69             MotionEvent.ACTION_UP
     70     })
     71     @Retention(RetentionPolicy.SOURCE)
     72     public @interface Action {}
     73 
     74     // Add other types from MotionEvent.TOOL_TYPE_ as needed.
     75     @IntDef(flag = true, value = {
     76             MotionEvent.TOOL_TYPE_FINGER,
     77             MotionEvent.TOOL_TYPE_MOUSE,
     78             MotionEvent.TOOL_TYPE_STYLUS,
     79             MotionEvent.TOOL_TYPE_UNKNOWN
     80     })
     81     @Retention(RetentionPolicy.SOURCE)
     82     public @interface ToolType {}
     83 
     84     @IntDef(flag = true, value = {
     85             MotionEvent.BUTTON_PRIMARY,
     86             MotionEvent.BUTTON_SECONDARY
     87     })
     88     @Retention(RetentionPolicy.SOURCE)
     89     public @interface Button {}
     90 
     91     @IntDef(flag = true, value = {
     92             KeyEvent.META_SHIFT_ON,
     93             KeyEvent.META_CTRL_ON
     94     })
     95     @Retention(RetentionPolicy.SOURCE)
     96     public @interface Key {}
     97 
     98     private static final class State {
     99         private @Action int mAction = ACTION_UNSET;
    100         private @ToolType int mToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
    101         private int mPointerCount = 1;
    102         private Set<Integer> mButtons = new HashSet<>();
    103         private Set<Integer> mKeys = new HashSet<>();
    104         private Point mLocation = new Point(0, 0);
    105         private Point mRawLocation = new Point(0, 0);
    106     }
    107 
    108     public static Builder builder() {
    109         return new Builder();
    110     }
    111 
    112     /**
    113      * Test event builder with convenience methods for common event attrs.
    114      */
    115     public static final class Builder {
    116 
    117         private State mState = new State();
    118 
    119         /**
    120          * @param action Any action specified in {@link MotionEvent}.
    121          * @return
    122          */
    123         public Builder action(int action) {
    124             mState.mAction = action;
    125             return this;
    126         }
    127 
    128         public Builder type(@ToolType int type) {
    129             mState.mToolType = type;
    130             return this;
    131         }
    132 
    133         public Builder location(int x, int y) {
    134             mState.mLocation = new Point(x, y);
    135             return this;
    136         }
    137 
    138         public Builder rawLocation(int x, int y) {
    139             mState.mRawLocation = new Point(x, y);
    140             return this;
    141         }
    142 
    143         public Builder pointerCount(int count) {
    144             mState.mPointerCount = count;
    145             return this;
    146         }
    147 
    148         /**
    149          * Adds one or more button press attributes.
    150          */
    151         public Builder pressButton(@Button int... buttons) {
    152             for (int button : buttons) {
    153                 mState.mButtons.add(button);
    154             }
    155             return this;
    156         }
    157 
    158         /**
    159          * Removes one or more button press attributes.
    160          */
    161         public Builder releaseButton(@Button int... buttons) {
    162             for (int button : buttons) {
    163                 mState.mButtons.remove(button);
    164             }
    165             return this;
    166         }
    167 
    168         /**
    169          * Adds one or more key press attributes.
    170          */
    171         public Builder pressKey(@Key int... keys) {
    172             for (int key : keys) {
    173                 mState.mKeys.add(key);
    174             }
    175             return this;
    176         }
    177 
    178         /**
    179          * Removes one or more key press attributes.
    180          */
    181         public Builder releaseKey(@Button int... keys) {
    182             for (int key : keys) {
    183                 mState.mKeys.remove(key);
    184             }
    185             return this;
    186         }
    187 
    188         public Builder touch() {
    189             type(MotionEvent.TOOL_TYPE_FINGER);
    190             return this;
    191         }
    192 
    193         public Builder mouse() {
    194             type(MotionEvent.TOOL_TYPE_MOUSE);
    195             return this;
    196         }
    197 
    198         public Builder shift() {
    199             pressKey(KeyEvent.META_SHIFT_ON);
    200             return this;
    201         }
    202 
    203         public Builder unshift() {
    204             releaseKey(KeyEvent.META_SHIFT_ON);
    205             return this;
    206         }
    207 
    208         public Builder ctrl() {
    209             pressKey(KeyEvent.META_CTRL_ON);
    210             return this;
    211         }
    212 
    213         public Builder alt() {
    214             pressKey(KeyEvent.META_ALT_ON);
    215             return this;
    216         }
    217 
    218         public Builder primary() {
    219             pressButton(MotionEvent.BUTTON_PRIMARY);
    220             releaseButton(MotionEvent.BUTTON_SECONDARY);
    221             releaseButton(MotionEvent.BUTTON_TERTIARY);
    222             return this;
    223         }
    224 
    225         public Builder secondary() {
    226             pressButton(MotionEvent.BUTTON_SECONDARY);
    227             releaseButton(MotionEvent.BUTTON_PRIMARY);
    228             releaseButton(MotionEvent.BUTTON_TERTIARY);
    229             return this;
    230         }
    231 
    232         public Builder tertiary() {
    233             pressButton(MotionEvent.BUTTON_TERTIARY);
    234             releaseButton(MotionEvent.BUTTON_PRIMARY);
    235             releaseButton(MotionEvent.BUTTON_SECONDARY);
    236             return this;
    237         }
    238 
    239         public MotionEvent build() {
    240 
    241             PointerProperties[] pointers = new PointerProperties[1];
    242             pointers[0] = new PointerProperties();
    243             pointers[0].id = 0;
    244             pointers[0].toolType = mState.mToolType;
    245 
    246             PointerCoords[] coords = new PointerCoords[1];
    247             coords[0] = new PointerCoords();
    248             coords[0].x = mState.mLocation.x;
    249             coords[0].y = mState.mLocation.y;
    250 
    251             int buttons = 0;
    252             for (Integer button : mState.mButtons) {
    253                 buttons |= button;
    254             }
    255 
    256             int keys = 0;
    257             for (Integer key : mState.mKeys) {
    258                 keys |= key;
    259             }
    260 
    261             return MotionEvent.obtain(
    262                     0,     // down time
    263                     1,     // event time
    264                     mState.mAction,
    265                     1,  // pointerCount,
    266                     pointers,
    267                     coords,
    268                     keys,
    269                     buttons,
    270                     1.0f,  // x precision
    271                     1.0f,  // y precision
    272                     0,     // device id
    273                     0,     // edge flags
    274                     0,     // int source,
    275                     0      // int flags
    276                     );
    277         }
    278     }
    279 }
    280