/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.testing; import androidx.annotation.IntDef; import android.graphics.Point; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashSet; import java.util.Set; /** * Handy-dandy wrapper class to facilitate the creation of MotionEvents. */ public final class TestEvents { /** * Common mouse event types...for your convenience. */ public static final class Mouse { public static final MotionEvent CLICK = TestEvents.builder().mouse().primary().build(); public static final MotionEvent CTRL_CLICK = TestEvents.builder().mouse().primary().ctrl().build(); public static final MotionEvent ALT_CLICK = TestEvents.builder().mouse().primary().alt().build(); public static final MotionEvent SHIFT_CLICK = TestEvents.builder().mouse().primary().shift().build(); public static final MotionEvent SECONDARY_CLICK = TestEvents.builder().mouse().secondary().build(); public static final MotionEvent TERTIARY_CLICK = TestEvents.builder().mouse().tertiary().build(); } /** * Common touch event types...for your convenience. */ public static final class Touch { public static final MotionEvent TAP = TestEvents.builder().touch().build(); } static final int ACTION_UNSET = -1; // Add other actions from MotionEvent.ACTION_ as needed. @IntDef(flag = true, value = { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP }) @Retention(RetentionPolicy.SOURCE) public @interface Action {} // Add other types from MotionEvent.TOOL_TYPE_ as needed. @IntDef(flag = true, value = { MotionEvent.TOOL_TYPE_FINGER, MotionEvent.TOOL_TYPE_MOUSE, MotionEvent.TOOL_TYPE_STYLUS, MotionEvent.TOOL_TYPE_UNKNOWN }) @Retention(RetentionPolicy.SOURCE) public @interface ToolType {} @IntDef(flag = true, value = { MotionEvent.BUTTON_PRIMARY, MotionEvent.BUTTON_SECONDARY }) @Retention(RetentionPolicy.SOURCE) public @interface Button {} @IntDef(flag = true, value = { KeyEvent.META_SHIFT_ON, KeyEvent.META_CTRL_ON }) @Retention(RetentionPolicy.SOURCE) public @interface Key {} private static final class State { private @Action int mAction = ACTION_UNSET; private @ToolType int mToolType = MotionEvent.TOOL_TYPE_UNKNOWN; private int mPointerCount = 1; private Set mButtons = new HashSet<>(); private Set mKeys = new HashSet<>(); private Point mLocation = new Point(0, 0); private Point mRawLocation = new Point(0, 0); } public static final Builder builder() { return new Builder(); } /** * Test event builder with convenience methods for common event attrs. */ public static final class Builder { private State mState = new State(); /** * @param action Any action specified in {@link MotionEvent}. * @return */ public Builder action(int action) { mState.mAction = action; return this; } public Builder type(@ToolType int type) { mState.mToolType = type; return this; } public Builder location(int x, int y) { mState.mLocation = new Point(x, y); return this; } public Builder rawLocation(int x, int y) { mState.mRawLocation = new Point(x, y); return this; } public Builder pointerCount(int count) { mState.mPointerCount = count; return this; } /** * Adds one or more button press attributes. */ public Builder pressButton(@Button int... buttons) { for (int button : buttons) { mState.mButtons.add(button); } return this; } /** * Removes one or more button press attributes. */ public Builder releaseButton(@Button int... buttons) { for (int button : buttons) { mState.mButtons.remove(button); } return this; } /** * Adds one or more key press attributes. */ public Builder pressKey(@Key int... keys) { for (int key : keys) { mState.mKeys.add(key); } return this; } /** * Removes one or more key press attributes. */ public Builder releaseKey(@Button int... keys) { for (int key : keys) { mState.mKeys.remove(key); } return this; } public Builder touch() { type(MotionEvent.TOOL_TYPE_FINGER); return this; } public Builder mouse() { type(MotionEvent.TOOL_TYPE_MOUSE); return this; } public Builder shift() { pressKey(KeyEvent.META_SHIFT_ON); return this; } /** * Use {@link #remove(@Attribute int...)} */ public Builder unshift() { releaseKey(KeyEvent.META_SHIFT_ON); return this; } public Builder ctrl() { pressKey(KeyEvent.META_CTRL_ON); return this; } public Builder alt() { pressKey(KeyEvent.META_ALT_ON); return this; } public Builder primary() { pressButton(MotionEvent.BUTTON_PRIMARY); releaseButton(MotionEvent.BUTTON_SECONDARY); releaseButton(MotionEvent.BUTTON_TERTIARY); return this; } public Builder secondary() { pressButton(MotionEvent.BUTTON_SECONDARY); releaseButton(MotionEvent.BUTTON_PRIMARY); releaseButton(MotionEvent.BUTTON_TERTIARY); return this; } public Builder tertiary() { pressButton(MotionEvent.BUTTON_TERTIARY); releaseButton(MotionEvent.BUTTON_PRIMARY); releaseButton(MotionEvent.BUTTON_SECONDARY); return this; } public MotionEvent build() { PointerProperties[] pointers = new PointerProperties[1]; pointers[0] = new PointerProperties(); pointers[0].id = 0; pointers[0].toolType = mState.mToolType; PointerCoords[] coords = new PointerCoords[1]; coords[0] = new PointerCoords(); coords[0].x = mState.mLocation.x; coords[0].y = mState.mLocation.y; int buttons = 0; for (Integer button : mState.mButtons) { buttons |= button; } int keys = 0; for (Integer key : mState.mKeys) { keys |= key; } return MotionEvent.obtain( 0, // down time 1, // event time mState.mAction, 1, // pointerCount, pointers, coords, keys, buttons, 1.0f, // x precision 1.0f, // y precision 0, // device id 0, // edge flags 0, // int source, 0 // int flags ); } } }