1 /* 2 * Copyright (C) 2020 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.server.accessibility.utils; 18 19 import static android.view.MotionEvent.ACTION_DOWN; 20 import static android.view.MotionEvent.ACTION_MOVE; 21 import static android.view.MotionEvent.ACTION_POINTER_DOWN; 22 import static android.view.MotionEvent.ACTION_POINTER_UP; 23 import static android.view.MotionEvent.ACTION_UP; 24 25 import android.graphics.PointF; 26 import android.os.SystemClock; 27 import android.view.InputDevice; 28 import android.view.MotionEvent; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * generates {@link MotionEvent} with source {@link InputDevice#SOURCE_TOUCHSCREEN} 35 */ 36 public class TouchEventGenerator { 37 downEvent(int displayId, float x, float y)38 public static MotionEvent downEvent(int displayId, float x, float y) { 39 return generateSingleTouchEvent(displayId, ACTION_DOWN, x, y); 40 } 41 moveEvent(int displayId, float x, float y)42 public static MotionEvent moveEvent(int displayId, float x, float y) { 43 return generateSingleTouchEvent(displayId, ACTION_MOVE, x, y); 44 } 45 upEvent(int displayId, float x, float y)46 public static MotionEvent upEvent(int displayId, float x, float y) { 47 return generateSingleTouchEvent(displayId, ACTION_UP, x, y); 48 } 49 generateSingleTouchEvent(int displayId, int action, float x, float y)50 private static MotionEvent generateSingleTouchEvent(int displayId, int action, float x, 51 float y) { 52 return generateMultiplePointersEvent(displayId, action, new PointF(x, y)); 53 } 54 55 /** 56 * Creates a list of {@link MotionEvent} with given pointers location. 57 * 58 * @param displayId the display id 59 * @param pointF1 location on the screen of the second pointer. 60 * @param pointF2 location on the screen of the second pointer. 61 * @return a list of {@link MotionEvent} with {@link MotionEvent#ACTION_DOWN} and {@link 62 * MotionEvent#ACTION_POINTER_DOWN}. 63 */ twoPointersDownEvents(int displayId, PointF pointF1, PointF pointF2)64 public static List<MotionEvent> twoPointersDownEvents(int displayId, PointF pointF1, 65 PointF pointF2) { 66 final List<MotionEvent> downEvents = new ArrayList<>(); 67 final MotionEvent downEvent = generateMultiplePointersEvent(displayId, 68 MotionEvent.ACTION_DOWN, pointF1); 69 downEvents.add(downEvent); 70 71 final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT; 72 final int action = ACTION_POINTER_DOWN | actionIndex; 73 74 final MotionEvent twoPointersDownEvent = generateMultiplePointersEvent(displayId, action, 75 pointF1, pointF2); 76 downEvents.add(twoPointersDownEvent); 77 return downEvents; 78 } 79 generateMultiplePointersEvent(int displayId, int action, PointF... pointFs)80 private static MotionEvent generateMultiplePointersEvent(int displayId, int action, 81 PointF... pointFs) { 82 final int length = pointFs.length; 83 final MotionEvent.PointerCoords[] pointerCoordsArray = 84 new MotionEvent.PointerCoords[length]; 85 final MotionEvent.PointerProperties[] pointerPropertiesArray = 86 new MotionEvent.PointerProperties[length]; 87 for (int i = 0; i < length; i++) { 88 MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords(); 89 pointerCoords.x = pointFs[i].x; 90 pointerCoords.y = pointFs[i].y; 91 pointerCoordsArray[i] = pointerCoords; 92 93 MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties(); 94 pointerProperties.id = i; 95 pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER; 96 pointerPropertiesArray[i] = pointerProperties; 97 } 98 99 final long downTime = SystemClock.uptimeMillis(); 100 final MotionEvent ev = MotionEvent.obtain( 101 /* downTime */ downTime, 102 /* eventTime */ downTime, 103 /* action */ action, 104 /* pointerCount */ length, 105 /* pointerProperties */ pointerPropertiesArray, 106 /* pointerCoords */ pointerCoordsArray, 107 /* metaState */ 0, 108 /* buttonState */ 0, 109 /* xPrecision */ 1.0f, 110 /* yPrecision */ 1.0f, 111 /* deviceId */ 0, 112 /* edgeFlags */ 0, 113 /* source */ InputDevice.SOURCE_TOUCHSCREEN, 114 /* flags */ 0); 115 ev.setDisplayId(displayId); 116 return ev; 117 } 118 119 /** 120 * Generates a move event that moves the pointer of the original event with given index. 121 * The original event should not be up event and we don't support 122 * {@link MotionEvent#ACTION_POINTER_UP} now. 123 * 124 * @param originalEvent the move or down event 125 * @param pointerIndex the index of the pointer we want to move. 126 * @param offsetX the offset in X coordinate. 127 * @param offsetY the offset in Y coordinate. 128 * @return a motion event with move action. 129 */ movePointer(MotionEvent originalEvent, int pointerIndex, float offsetX, float offsetY)130 public static MotionEvent movePointer(MotionEvent originalEvent, int pointerIndex, 131 float offsetX, float offsetY) { 132 if (originalEvent.getActionMasked() == ACTION_UP) { 133 throw new IllegalArgumentException("No pointer is on the screen"); 134 } 135 136 if (originalEvent.getActionMasked() == ACTION_POINTER_UP) { 137 throw new IllegalArgumentException("unsupported yet,please implement it first"); 138 } 139 140 final int pointerCount = originalEvent.getPointerCount(); 141 if (pointerIndex >= pointerCount) { 142 throw new IllegalArgumentException( 143 pointerIndex + "is not available with pointer count" + pointerCount); 144 } 145 final int action = MotionEvent.ACTION_MOVE; 146 final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[pointerCount]; 147 for (int i = 0; i < pointerCount; i++) { 148 MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties(); 149 originalEvent.getPointerProperties(i, pointerProperty); 150 pp[i] = pointerProperty; 151 } 152 153 final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[pointerCount]; 154 for (int i = 0; i < pointerCount; i++) { 155 MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords(); 156 originalEvent.getPointerCoords(i, pointerCoord); 157 pc[i] = pointerCoord; 158 } 159 pc[pointerIndex].x += offsetX; 160 pc[pointerIndex].y += offsetY; 161 final MotionEvent ev = MotionEvent.obtain( 162 /* downTime */ originalEvent.getDownTime(), 163 /* eventTime */ SystemClock.uptimeMillis(), 164 /* action */ action, 165 /* pointerCount */ 2, 166 /* pointerProperties */ pp, 167 /* pointerCoords */ pc, 168 /* metaState */ 0, 169 /* buttonState */ 0, 170 /* xPrecision */ 1.0f, 171 /* yPrecision */ 1.0f, 172 /* deviceId */ 0, 173 /* edgeFlags */ 0, 174 /* source */ originalEvent.getSource(), 175 /* flags */ originalEvent.getFlags()); 176 ev.setDisplayId(originalEvent.getDisplayId()); 177 return ev; 178 } 179 } 180