blob: 98183d43e77c3b47d15797ce5d427261c829f029 [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.server.wm;
import static android.server.wm.StateLogger.log;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
public class TouchHelper {
public final Context mContext;
public final Instrumentation mInstrumentation;
public final WindowManagerStateHelper mWmState;
public TouchHelper(Instrumentation instrumentation, WindowManagerStateHelper wmState) {
mInstrumentation = instrumentation;
mContext = mInstrumentation.getContext();
mWmState = wmState;
}
/**
* Insert an input event (ACTION_DOWN -> ACTION_CANCEL) to ensures the display to be focused
* without triggering potential clicked to impact the test environment.
* (e.g: Keyguard credential activated unexpectedly.)
*
* @param displayId the display ID to gain focused by inject swipe action
*/
public void touchAndCancelOnDisplayCenterSync(int displayId) {
WindowManagerState.DisplayContent dc = mWmState.getDisplay(displayId);
if (dc == null) {
// never get wm state before?
mWmState.computeState();
dc = mWmState.getDisplay(displayId);
}
if (dc == null) {
log("Cannot tap on display: " + displayId);
return;
}
final Rect bounds = dc.getDisplayRect();
final int x = bounds.left + bounds.width() / 2;
final int y = bounds.top + bounds.height() / 2;
final long downTime = SystemClock.uptimeMillis();
injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, displayId, true /* sync */);
final long eventTime = SystemClock.uptimeMillis();
final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
final int tapX = x + Math.round(touchSlop / 2.0f);
final int tapY = y + Math.round(touchSlop / 2.0f);
injectMotion(downTime, eventTime, MotionEvent.ACTION_CANCEL, tapX, tapY, displayId,
true /* sync */);
}
public void tapOnDisplaySync(int x, int y, int displayId) {
tapOnDisplay(x, y, displayId, true /* sync*/);
}
public void tapOnDisplay(int x, int y, int displayId, boolean sync) {
tapOnDisplay(x, y, displayId, sync, /* waitAnimations */ true);
}
public void tapOnDisplay(int x, int y, int displayId, boolean sync, boolean waitAnimations) {
final long downTime = SystemClock.uptimeMillis();
injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, displayId, sync,
waitAnimations);
final long upTime = SystemClock.uptimeMillis();
injectMotion(downTime, upTime, MotionEvent.ACTION_UP, x, y, displayId, sync,
waitAnimations);
if (waitAnimations) {
mWmState.waitForWithAmState(state -> state.getFocusedDisplayId() == displayId,
"top focused displayId: " + displayId);
}
// This is needed after a tap in multi-display to ensure that the display focus has really
// changed, if needed. The call to syncInputTransaction will wait until focus change has
// propagated from WMS to native input before returning.
mInstrumentation.getUiAutomation().syncInputTransactions(waitAnimations);
}
public void tapOnCenter(Rect bounds, int displayId) {
final int tapX = bounds.left + bounds.width() / 2;
final int tapY = bounds.top + bounds.height() / 2;
tapOnDisplaySync(tapX, tapY, displayId);
}
public void tapOnViewCenter(View view) {
tapOnViewCenter(view, true /* waitAnimations */);
}
public void tapOnViewCenter(View view, boolean waitAnimations) {
final int[] topleft = new int[2];
view.getLocationOnScreen(topleft);
int x = topleft[0] + view.getWidth() / 2;
int y = topleft[1] + view.getHeight() / 2;
tapOnDisplay(x, y, view.getDisplay().getDisplayId(), true /* sync */, waitAnimations);
}
public void tapOnStackCenter(WindowManagerState.ActivityTask stack) {
tapOnCenter(stack.getBounds(), stack.mDisplayId);
}
public void tapOnDisplayCenter(int displayId) {
final Rect bounds = mWmState.getDisplay(displayId).getDisplayRect();
tapOnDisplaySync(bounds.centerX(), bounds.centerY(), displayId);
}
public void tapOnDisplayCenterAsync(int displayId) {
final Rect bounds = mWmState.getDisplay(displayId).getDisplayRect();
tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId, false /* sync */);
}
public static void injectMotion(long downTime, long eventTime, int action,
int x, int y, int displayId, boolean sync) {
injectMotion(downTime, eventTime, action, x, y, displayId, sync,
true /* waitForAnimations */);
}
public static void injectMotion(long downTime, long eventTime, int action,
int x, int y, int displayId, boolean sync, boolean waitAnimations) {
final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
x, y, 0 /* metaState */);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
event.setDisplayId(displayId);
getInstrumentation().getUiAutomation().injectInputEvent(event, sync, waitAnimations);
}
public static void injectKey(int keyCode, boolean longPress, boolean sync) {
final long downTime = SystemClock.uptimeMillis();
int repeatCount = 0;
KeyEvent downEvent =
new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, repeatCount);
getInstrumentation().getUiAutomation().injectInputEvent(downEvent, sync);
if (longPress) {
repeatCount += 1;
KeyEvent repeatEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN, keyCode, repeatCount);
getInstrumentation().getUiAutomation().injectInputEvent(repeatEvent, sync);
}
KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
getInstrumentation().getUiAutomation().injectInputEvent(upEvent, sync);
}
}