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 builder()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 */ action(int action)123 public Builder action(int action) { 124 mState.mAction = action; 125 return this; 126 } 127 type(@oolType int type)128 public Builder type(@ToolType int type) { 129 mState.mToolType = type; 130 return this; 131 } 132 location(int x, int y)133 public Builder location(int x, int y) { 134 mState.mLocation = new Point(x, y); 135 return this; 136 } 137 rawLocation(int x, int y)138 public Builder rawLocation(int x, int y) { 139 mState.mRawLocation = new Point(x, y); 140 return this; 141 } 142 pointerCount(int count)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 */ pressButton(@utton int... buttons)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 */ releaseButton(@utton int... buttons)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 */ pressKey(@ey int... keys)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 */ releaseKey(@utton int... keys)181 public Builder releaseKey(@Button int... keys) { 182 for (int key : keys) { 183 mState.mKeys.remove(key); 184 } 185 return this; 186 } 187 touch()188 public Builder touch() { 189 type(MotionEvent.TOOL_TYPE_FINGER); 190 return this; 191 } 192 mouse()193 public Builder mouse() { 194 type(MotionEvent.TOOL_TYPE_MOUSE); 195 return this; 196 } 197 shift()198 public Builder shift() { 199 pressKey(KeyEvent.META_SHIFT_ON); 200 return this; 201 } 202 unshift()203 public Builder unshift() { 204 releaseKey(KeyEvent.META_SHIFT_ON); 205 return this; 206 } 207 ctrl()208 public Builder ctrl() { 209 pressKey(KeyEvent.META_CTRL_ON); 210 return this; 211 } 212 alt()213 public Builder alt() { 214 pressKey(KeyEvent.META_ALT_ON); 215 return this; 216 } 217 primary()218 public Builder primary() { 219 pressButton(MotionEvent.BUTTON_PRIMARY); 220 releaseButton(MotionEvent.BUTTON_SECONDARY); 221 releaseButton(MotionEvent.BUTTON_TERTIARY); 222 return this; 223 } 224 secondary()225 public Builder secondary() { 226 pressButton(MotionEvent.BUTTON_SECONDARY); 227 releaseButton(MotionEvent.BUTTON_PRIMARY); 228 releaseButton(MotionEvent.BUTTON_TERTIARY); 229 return this; 230 } 231 tertiary()232 public Builder tertiary() { 233 pressButton(MotionEvent.BUTTON_TERTIARY); 234 releaseButton(MotionEvent.BUTTON_PRIMARY); 235 releaseButton(MotionEvent.BUTTON_SECONDARY); 236 return this; 237 } 238 build()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