blob: c67f51453071cd8d400c068c9c1c6d1d0dacf3a9 [file] [log] [blame]
/*
* 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 android.annotation.IntDef;
import android.graphics.Point;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.MotionEvent;
import com.android.documentsui.base.Events.InputEvent;
import com.android.documentsui.dirlist.DocumentDetails;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
/**
* Events and DocDetails are closely related. For the pursposes of this test
* we coalesce the two in a single, handy-dandy test class.
*/
public class TestEvent implements InputEvent {
private 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 @Action int mAction;
private @ToolType int mToolType;
private Set<Integer> mButtons;
private Set<Integer> mKeys;
private Point mLocation;
private Point mRawLocation;
private Details mDetails;
private TestEvent() {
mAction = ACTION_UNSET; // somebody has to set this, else we'll barf later.
mToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
mButtons = new HashSet<>();
mKeys = new HashSet<>();
mLocation = new Point(0, 0);
mRawLocation = new Point(0, 0);
mDetails = new Details();
}
private TestEvent(TestEvent source) {
assert(source.mAction != ACTION_UNSET);
mAction = source.mAction;
mToolType = source.mToolType;
mButtons = source.mButtons;
mKeys = source.mKeys;
mLocation = source.mLocation;
mRawLocation = source.mRawLocation;
mDetails = new Details(source.mDetails);
}
@Override
public Point getOrigin() {
return mLocation;
}
@Override
public float getX() {
return mLocation.x;
}
@Override
public float getY() {
return mLocation.y;
}
@Override
public float getRawX() {
return mRawLocation.x;
}
@Override
public float getRawY() {
return mRawLocation.y;
}
@Override
public boolean isMouseEvent() {
return mToolType == MotionEvent.TOOL_TYPE_MOUSE;
}
@Override
public boolean isPrimaryButtonPressed() {
return mButtons.contains(MotionEvent.BUTTON_PRIMARY);
}
@Override
public boolean isSecondaryButtonPressed() {
return mButtons.contains(MotionEvent.BUTTON_SECONDARY);
}
@Override
public boolean isShiftKeyDown() {
return mKeys.contains(KeyEvent.META_SHIFT_ON);
}
@Override
public boolean isCtrlKeyDown() {
return mKeys.contains(KeyEvent.META_CTRL_ON);
}
@Override
public boolean isActionDown() {
return mAction == MotionEvent.ACTION_DOWN;
}
@Override
public boolean isActionUp() {
return mAction == MotionEvent.ACTION_UP;
}
@Override
public boolean isActionMove() {
return mAction == MotionEvent.ACTION_MOVE;
}
@Override
public boolean isActionCancel() {
return mAction == MotionEvent.ACTION_CANCEL;
}
@Override
public boolean isOverItem() {
return mDetails.isOverItem();
}
@Override
public boolean isOverDragHotspot() {
return mDetails.isOverInteractiveArea();
}
@Override
public boolean isOverModelItem() {
if (isOverItem()) {
DocumentDetails doc = getDocumentDetails();
return doc != null && doc.hasModelId();
}
return false;
}
@Override
public int getItemPosition() {
return mDetails.mPosition;
}
@Override
public DocumentDetails getDocumentDetails() {
return mDetails;
}
@Override
public void close() {}
@Override
public int hashCode() {
return mDetails.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TestEvent)) {
return false;
}
TestEvent other = (TestEvent) o;
return mAction == other.mAction
&& mToolType == other.mToolType
&& mButtons.equals(other.mButtons)
&& mKeys.equals(other.mKeys)
&& mLocation.equals(other.mLocation)
&& mRawLocation.equals(other.mRawLocation)
&& mDetails.equals(other.mDetails);
}
private static final class Details implements DocumentDetails {
private int mPosition;
private String mModelId;
private boolean mInSelectionHotspot;
private boolean mInDragHotspot;
public Details() {
mPosition = Integer.MIN_VALUE;
}
public Details(Details source) {
mPosition = source.mPosition;
mModelId = source.mModelId;
mInSelectionHotspot = source.mInSelectionHotspot;
mInDragHotspot = source.mInDragHotspot;
}
private boolean isOverItem() {
return mPosition != Integer.MIN_VALUE && mPosition != RecyclerView.NO_POSITION;
}
private boolean isOverInteractiveArea() {
return mPosition != Integer.MIN_VALUE && mPosition != RecyclerView.NO_POSITION;
}
@Override
public boolean hasModelId() {
return !TextUtils.isEmpty(mModelId);
}
@Override
public String getModelId() {
return mModelId;
}
@Override
public int getAdapterPosition() {
return mPosition;
}
@Override
public boolean isInSelectionHotspot(InputEvent event) {
return mInSelectionHotspot;
}
@Override
public boolean isInDragHotspot(InputEvent event) {
return mInDragHotspot;
}
@Override
public int hashCode() {
return mModelId != null ? mModelId.hashCode() : ACTION_UNSET;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Details)) {
return false;
}
Details other = (Details) o;
return mPosition == other.mPosition
&& mModelId == other.mModelId;
}
}
public static final Builder builder() {
return new Builder();
}
/**
* Test event builder with convenience methods for common event attrs.
*/
public static final class Builder {
private TestEvent mState = new TestEvent();
public Builder() {
}
public Builder(TestEvent state) {
mState = new TestEvent(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;
}
/**
* 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 at(int position) {
mState.mDetails.mPosition = position; // this is both "adapter position" and "item position".
mState.mDetails.mModelId = String.valueOf(position);
return this;
}
public Builder inSelectionHotspot() {
mState.mDetails.mInSelectionHotspot = true;
return this;
}
public Builder inDragHotspot() {
mState.mDetails.mInDragHotspot = true;
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...)}
*/
@Deprecated
public Builder unshift() {
releaseKey(KeyEvent.META_SHIFT_ON);
return this;
}
public Builder ctrl() {
pressKey(KeyEvent.META_CTRL_ON);
return this;
}
public Builder primary() {
pressButton(MotionEvent.BUTTON_PRIMARY);
releaseButton(MotionEvent.BUTTON_SECONDARY);
return this;
}
public Builder secondary() {
pressButton(MotionEvent.BUTTON_SECONDARY);
releaseButton(MotionEvent.BUTTON_PRIMARY);
return this;
}
public Builder reset() {
mState = new TestEvent();
return this;
}
@Override
public Builder clone() {
return new Builder(build());
}
public TestEvent build() {
// Return a copy, so nobody can mess w/ our internal state.
return new TestEvent(mState);
}
}
}