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