blob: e53eb08d64c11fcc364b4a29dc98df9277ad0d19 [file] [log] [blame]
/*
* Copyright (C) 2017 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.compatibility.common.util;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
public final class CtsMouseUtil {
private CtsMouseUtil() {}
public static View.OnHoverListener installHoverListener(View view) {
return installHoverListener(view, true);
}
public static View.OnHoverListener installHoverListener(View view, boolean result) {
final View.OnHoverListener mockListener = mock(View.OnHoverListener.class);
view.setOnHoverListener((v, event) -> {
// Clone the event to work around event instance reuse in the framework.
mockListener.onHover(v, MotionEvent.obtain(event));
return result;
});
return mockListener;
}
public static void clearHoverListener(View view) {
view.setOnHoverListener(null);
}
public static MotionEvent obtainMouseEvent(int action, View anchor, int offsetX, int offsetY) {
final long eventTime = SystemClock.uptimeMillis();
final int[] screenPos = new int[2];
anchor.getLocationOnScreen(screenPos);
final int x = screenPos[0] + offsetX;
final int y = screenPos[1] + offsetY;
MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
event.setSource(InputDevice.SOURCE_MOUSE);
return event;
}
/**
* Emulates a hover move on a point relative to the top-left corner of the passed {@link View}.
* Offset parameters are used to compute the final screen coordinates of the tap point.
*
* @param instrumentation the instrumentation used to run the test
* @param anchor the anchor view to determine the tap location on the screen
* @param offsetX extra X offset for the move
* @param offsetY extra Y offset for the move
*/
public static void emulateHoverOnView(Instrumentation instrumentation, View anchor, int offsetX,
int offsetY) {
final long downTime = SystemClock.uptimeMillis();
final UiAutomation uiAutomation = instrumentation.getUiAutomation();
final int[] screenPos = new int[2];
anchor.getLocationOnScreen(screenPos);
final int x = screenPos[0] + offsetX;
final int y = screenPos[1] + offsetY;
injectHoverEvent(uiAutomation, downTime, x, y);
}
private static void injectHoverEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
int yOnScreen) {
MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_MOVE,
xOnScreen, yOnScreen, 0);
event.setSource(InputDevice.SOURCE_MOUSE);
uiAutomation.injectInputEvent(event, true);
event.recycle();
}
public static class ActionMatcher implements ArgumentMatcher<MotionEvent> {
private final int mAction;
public ActionMatcher(int action) {
mAction = action;
}
@Override
public boolean matches(MotionEvent actual) {
return actual.getAction() == mAction;
}
@Override
public String toString() {
return "action=" + MotionEvent.actionToString(mAction);
}
}
public static class PositionMatcher extends ActionMatcher {
private final int mX;
private final int mY;
public PositionMatcher(int action, int x, int y) {
super(action);
mX = x;
mY = y;
}
@Override
public boolean matches(MotionEvent actual) {
return super.matches(actual)
&& ((int) actual.getX()) == mX
&& ((int) actual.getY()) == mY;
}
@Override
public String toString() {
return super.toString() + "@(" + mX + "," + mY + ")";
}
}
public static void verifyEnterMove(View.OnHoverListener listener, View view, int moveCount) {
final InOrder inOrder = inOrder(listener);
verifyEnterMoveInternal(listener, view, moveCount, inOrder);
inOrder.verifyNoMoreInteractions();
}
public static void verifyEnterMoveExit(
View.OnHoverListener listener, View view, int moveCount) {
final InOrder inOrder = inOrder(listener);
verifyEnterMoveInternal(listener, view, moveCount, inOrder);
inOrder.verify(listener, times(1)).onHover(eq(view),
argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT)));
inOrder.verifyNoMoreInteractions();
}
private static void verifyEnterMoveInternal(
View.OnHoverListener listener, View view, int moveCount, InOrder inOrder) {
inOrder.verify(listener, times(1)).onHover(eq(view),
argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER)));
inOrder.verify(listener, times(moveCount)).onHover(eq(view),
argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_MOVE)));
}
}