Merge "Extract Mirror windowcontrol to a single class"
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 com.android.systemui.accessibility;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Contains a movable control UI to manipulate mirrored window's position, size and scale. The
|
||||
* window type of the UI is {@link LayoutParams#TYPE_APPLICATION_SUB_PANEL} and the window type
|
||||
* of the window token should be {@link LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY} to
|
||||
* ensure it is above all windows and won't be mirrored. It is not movable to the navigation bar.
|
||||
*/
|
||||
public abstract class MirrorWindowControl {
|
||||
private static final String TAG = "MirrorWindowControl";
|
||||
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG) | false;
|
||||
|
||||
/**
|
||||
* A delegate handling a mirrored window's offset.
|
||||
*/
|
||||
public interface MirrorWindowDelegate {
|
||||
/**
|
||||
* Moves the window with specified offset.
|
||||
*
|
||||
* @param xOffset the amount in pixels to offset the window in the X coordinate, in current
|
||||
* display pixels.
|
||||
* @param yOffset the amount in pixels to offset the window in the Y coordinate, in current
|
||||
* display pixels.
|
||||
*/
|
||||
void move(int xOffset, int yOffset);
|
||||
}
|
||||
|
||||
protected final Context mContext;
|
||||
private final Rect mDraggableBound = new Rect();
|
||||
final Point mTmpPoint = new Point();
|
||||
|
||||
@Nullable
|
||||
protected MirrorWindowDelegate mMirrorWindowDelegate;
|
||||
protected View mControlsView;
|
||||
/**
|
||||
* The left top position of the control UI. Initialized when the control UI is visible.
|
||||
*
|
||||
* @see #setDefaultPosition(LayoutParams)
|
||||
*/
|
||||
private final Point mControlPosition = new Point();
|
||||
private final WindowManager mWindowManager;
|
||||
|
||||
MirrorWindowControl(Context context) {
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
}
|
||||
|
||||
public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) {
|
||||
mMirrorWindowDelegate = windowDelegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the control UI.
|
||||
*
|
||||
* @param binder the window token of the
|
||||
* {@link LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY} window.
|
||||
*/
|
||||
public final void showControl(IBinder binder) {
|
||||
if (mControlsView != null) {
|
||||
Log.w(TAG, "control view is visible");
|
||||
return;
|
||||
}
|
||||
final Point viewSize = mTmpPoint;
|
||||
mControlsView = onCreateView(LayoutInflater.from(mContext), viewSize);
|
||||
|
||||
final LayoutParams lp = new LayoutParams();
|
||||
final int defaultSize = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.magnification_controls_size);
|
||||
lp.width = viewSize.x <= 0 ? defaultSize : viewSize.x;
|
||||
lp.height = viewSize.y <= 0 ? defaultSize : viewSize.y;
|
||||
lp.token = binder;
|
||||
setDefaultParams(lp);
|
||||
setDefaultPosition(lp);
|
||||
mWindowManager.addView(mControlsView, lp);
|
||||
updateDraggableBound(lp.width, lp.height);
|
||||
}
|
||||
|
||||
private void setDefaultParams(LayoutParams lp) {
|
||||
lp.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
lp.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
lp.type = LayoutParams.TYPE_APPLICATION_SUB_PANEL;
|
||||
lp.format = PixelFormat.RGBA_8888;
|
||||
lp.setTitle(getWindowTitle());
|
||||
}
|
||||
|
||||
private void setDefaultPosition(LayoutParams layoutParams) {
|
||||
final Point displaySize = mTmpPoint;
|
||||
mContext.getDisplay().getSize(displaySize);
|
||||
layoutParams.x = displaySize.x - layoutParams.width;
|
||||
layoutParams.y = displaySize.y - layoutParams.height;
|
||||
mControlPosition.set(layoutParams.x, layoutParams.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the UI from the scene.
|
||||
*/
|
||||
public final void destroyControl() {
|
||||
if (mControlsView != null) {
|
||||
mWindowManager.removeView(mControlsView);
|
||||
mControlsView = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the control view with specified offset.
|
||||
*
|
||||
* @param xOffset the amount in pixels to offset the UI in the X coordinate, in current
|
||||
* display pixels.
|
||||
* @param yOffset the amount in pixels to offset the UI in the Y coordinate, in current
|
||||
* display pixels.
|
||||
*/
|
||||
public void move(int xOffset, int yOffset) {
|
||||
if (mControlsView == null) {
|
||||
Log.w(TAG, "control view is not available yet or destroyed");
|
||||
return;
|
||||
}
|
||||
final Point nextPosition = mTmpPoint;
|
||||
nextPosition.set(mControlPosition.x, mControlPosition.y);
|
||||
mTmpPoint.offset(xOffset, yOffset);
|
||||
setPosition(mTmpPoint);
|
||||
}
|
||||
|
||||
private void setPosition(Point point) {
|
||||
constrainFrameToDraggableBound(point);
|
||||
if (point.equals(mControlPosition)) {
|
||||
return;
|
||||
}
|
||||
mControlPosition.set(point.x, point.y);
|
||||
LayoutParams lp = (LayoutParams) mControlsView.getLayoutParams();
|
||||
lp.x = mControlPosition.x;
|
||||
lp.y = mControlPosition.y;
|
||||
mWindowManager.updateViewLayout(mControlsView, lp);
|
||||
}
|
||||
|
||||
private void constrainFrameToDraggableBound(Point point) {
|
||||
point.x = MathUtils.constrain(point.x, mDraggableBound.left, mDraggableBound.right);
|
||||
point.y = MathUtils.constrain(point.y, mDraggableBound.top, mDraggableBound.bottom);
|
||||
}
|
||||
|
||||
private void updateDraggableBound(int viewWidth, int viewHeight) {
|
||||
final Point size = mTmpPoint;
|
||||
mContext.getDisplay().getSize(size);
|
||||
mDraggableBound.set(0, 0, size.x - viewWidth, size.y - viewHeight);
|
||||
if (DBG) {
|
||||
Log.d(TAG, "updateDraggableBound :" + mDraggableBound);
|
||||
}
|
||||
}
|
||||
|
||||
abstract String getWindowTitle();
|
||||
|
||||
/**
|
||||
* Called when the UI is going to show.
|
||||
*
|
||||
* @param inflater The LayoutInflater object used to inflate the view.
|
||||
* @param viewSize The {@link Point} to specify view's width with {@link Point#x)} and height
|
||||
* with {@link Point#y)} .The value should be greater than 0, otherwise will
|
||||
* fall back to the default size.
|
||||
* @return the View for the control's UI.
|
||||
*/
|
||||
@NonNull
|
||||
abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull Point viewSize);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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 com.android.systemui.accessibility;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* A basic control to move the mirror window.
|
||||
*/
|
||||
class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnClickListener,
|
||||
View.OnTouchListener, View.OnLongClickListener {
|
||||
|
||||
private static final String TAG = "SimpleMirrorWindowControl";
|
||||
private static final int MOVE_FRAME_DURATION_MS = 100;
|
||||
private final int mMoveFrameAmountShort;
|
||||
private final int mMoveFrameAmountLong;
|
||||
|
||||
private boolean mIsDragState;
|
||||
private boolean mShouldSetTouchStart;
|
||||
|
||||
@Nullable private MoveWindowTask mMoveWindowTask;
|
||||
private PointF mLastDrag = new PointF();
|
||||
private final Handler mHandler;
|
||||
|
||||
SimpleMirrorWindowControl(Context context, Handler handler) {
|
||||
super(context);
|
||||
mHandler = handler;
|
||||
final Resources resource = context.getResources();
|
||||
mMoveFrameAmountShort = resource.getDimensionPixelSize(
|
||||
R.dimen.magnification_frame_move_short);
|
||||
mMoveFrameAmountLong = resource.getDimensionPixelSize(
|
||||
R.dimen.magnification_frame_move_long);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getWindowTitle() {
|
||||
return mContext.getString(R.string.magnification_controls_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
View onCreateView(LayoutInflater layoutInflater, Point viewSize) {
|
||||
final View view = layoutInflater.inflate(R.layout.magnifier_controllers, null);
|
||||
final View leftControl = view.findViewById(R.id.left_control);
|
||||
final View upControl = view.findViewById(R.id.up_control);
|
||||
final View rightControl = view.findViewById(R.id.right_control);
|
||||
final View bottomControl = view.findViewById(R.id.down_control);
|
||||
|
||||
leftControl.setOnClickListener(this);
|
||||
upControl.setOnClickListener(this);
|
||||
rightControl.setOnClickListener(this);
|
||||
bottomControl.setOnClickListener(this);
|
||||
|
||||
leftControl.setOnLongClickListener(this);
|
||||
upControl.setOnLongClickListener(this);
|
||||
rightControl.setOnLongClickListener(this);
|
||||
bottomControl.setOnLongClickListener(this);
|
||||
|
||||
leftControl.setOnTouchListener(this);
|
||||
upControl.setOnTouchListener(this);
|
||||
rightControl.setOnTouchListener(this);
|
||||
bottomControl.setOnTouchListener(this);
|
||||
|
||||
view.setOnTouchListener(this);
|
||||
view.setOnLongClickListener(this::onViewRootLongClick);
|
||||
return view;
|
||||
}
|
||||
|
||||
private Point findOffset(View v, int moveFrameAmount) {
|
||||
final Point offset = mTmpPoint;
|
||||
offset.set(0, 0);
|
||||
if (v.getId() == R.id.left_control) {
|
||||
mTmpPoint.x = -moveFrameAmount;
|
||||
} else if (v.getId() == R.id.up_control) {
|
||||
mTmpPoint.y = -moveFrameAmount;
|
||||
} else if (v.getId() == R.id.right_control) {
|
||||
mTmpPoint.x = moveFrameAmount;
|
||||
} else if (v.getId() == R.id.down_control) {
|
||||
mTmpPoint.y = moveFrameAmount;
|
||||
} else {
|
||||
Log.w(TAG, "findOffset move is zero ");
|
||||
}
|
||||
return mTmpPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mMirrorWindowDelegate != null) {
|
||||
Point offset = findOffset(v, mMoveFrameAmountShort);
|
||||
mMirrorWindowDelegate.move(offset.x, offset.y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (handleDragState(event)) {
|
||||
return true;
|
||||
}
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (mMoveWindowTask != null) {
|
||||
mMoveWindowTask.cancel();
|
||||
mMoveWindowTask = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
Point offset = findOffset(v, mMoveFrameAmountLong);
|
||||
mMoveWindowTask = new MoveWindowTask(mMirrorWindowDelegate, mHandler, offset.x, offset.y,
|
||||
MOVE_FRAME_DURATION_MS);
|
||||
mMoveWindowTask.schedule();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onViewRootLongClick(View view) {
|
||||
mIsDragState = true;
|
||||
mShouldSetTouchStart = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleDragState(final MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mIsDragState) {
|
||||
if (mShouldSetTouchStart) {
|
||||
mLastDrag.set(event.getRawX(), event.getRawY());
|
||||
mShouldSetTouchStart = false;
|
||||
}
|
||||
int xDiff = (int) (event.getRawX() - mLastDrag.x);
|
||||
int yDiff = (int) (event.getRawY() - mLastDrag.y);
|
||||
move(xDiff, yDiff);
|
||||
mLastDrag.set(event.getRawX(), event.getRawY());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (mIsDragState) {
|
||||
mIsDragState = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A timer task to move the mirror window periodically.
|
||||
*/
|
||||
static class MoveWindowTask implements Runnable {
|
||||
private final MirrorWindowDelegate mMirrorWindowDelegate;
|
||||
private final int mXOffset;
|
||||
private final int mYOffset;
|
||||
private final Handler mHandler;
|
||||
/** Time in milliseconds between successive task executions.*/
|
||||
private long mPeriod;
|
||||
private boolean mCancel;
|
||||
|
||||
MoveWindowTask(@NonNull MirrorWindowDelegate windowDelegate, Handler handler, int xOffset,
|
||||
int yOffset, long period) {
|
||||
mMirrorWindowDelegate = windowDelegate;
|
||||
mXOffset = xOffset;
|
||||
mYOffset = yOffset;
|
||||
mHandler = handler;
|
||||
mPeriod = period;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mCancel) {
|
||||
return;
|
||||
}
|
||||
mMirrorWindowDelegate.move(mXOffset, mYOffset);
|
||||
schedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the specified task periodically and immediately.
|
||||
*/
|
||||
void schedule() {
|
||||
mHandler.postDelayed(this, mPeriod);
|
||||
mCancel = false;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
mHandler.removeCallbacks(this);
|
||||
mCancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class WindowMagnification extends SystemUI {
|
||||
|
||||
private void enableMagnification() {
|
||||
if (mWindowMagnificationController == null) {
|
||||
mWindowMagnificationController = new WindowMagnificationController(mContext, mHandler);
|
||||
mWindowMagnificationController = new WindowMagnificationController(mContext, null);
|
||||
}
|
||||
mWindowMagnificationController.createWindowMagnification();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.accessibility;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -25,7 +26,6 @@ import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -44,16 +44,13 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
/**
|
||||
* Class to handle adding and removing a window magnification.
|
||||
*/
|
||||
public class WindowMagnificationController implements View.OnClickListener,
|
||||
View.OnLongClickListener, View.OnTouchListener, SurfaceHolder.Callback {
|
||||
public class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback,
|
||||
MirrorWindowControl.MirrorWindowDelegate {
|
||||
private final int mBorderSize;
|
||||
private final int mMoveFrameAmountShort;
|
||||
private final int mMoveFrameAmountLong;
|
||||
|
||||
private final Context mContext;
|
||||
private final Point mDisplaySize = new Point();
|
||||
private final int mDisplayId;
|
||||
private final Handler mHandler;
|
||||
private final Rect mMagnificationFrame = new Rect();
|
||||
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
|
||||
@@ -66,13 +63,6 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
// The root of the mirrored content
|
||||
private SurfaceControl mMirrorSurface;
|
||||
|
||||
private boolean mIsPressedDown;
|
||||
|
||||
private View mLeftControl;
|
||||
private View mUpControl;
|
||||
private View mRightControl;
|
||||
private View mBottomControl;
|
||||
|
||||
private View mDragView;
|
||||
private View mLeftDrag;
|
||||
private View mTopDrag;
|
||||
@@ -80,20 +70,18 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
private View mBottomDrag;
|
||||
|
||||
private final PointF mLastDrag = new PointF();
|
||||
private final Point mMoveWindowOffset = new Point();
|
||||
|
||||
private View mMirrorView;
|
||||
private SurfaceView mMirrorSurfaceView;
|
||||
private View mControlsView;
|
||||
private View mOverlayView;
|
||||
// The boundary of magnification frame.
|
||||
private final Rect mMagnificationFrameBoundary = new Rect();
|
||||
|
||||
private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
|
||||
@Nullable
|
||||
private MirrorWindowControl mMirrorWindowControl;
|
||||
|
||||
WindowMagnificationController(Context context, Handler handler) {
|
||||
WindowMagnificationController(Context context, MirrorWindowControl mirrorWindowControl) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
Display display = mContext.getDisplay();
|
||||
display.getRealSize(mDisplaySize);
|
||||
mDisplayId = mContext.getDisplayId();
|
||||
@@ -102,10 +90,12 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
|
||||
Resources r = context.getResources();
|
||||
mBorderSize = (int) r.getDimension(R.dimen.magnification_border_size);
|
||||
mMoveFrameAmountShort = (int) r.getDimension(R.dimen.magnification_frame_move_short);
|
||||
mMoveFrameAmountLong = (int) r.getDimension(R.dimen.magnification_frame_move_long);
|
||||
|
||||
mScale = r.getInteger(R.integer.magnification_default_scale);
|
||||
mMirrorWindowControl = mirrorWindowControl;
|
||||
if (mMirrorWindowControl != null) {
|
||||
mMirrorWindowControl.setWindowDelegate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,9 +166,8 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
mMirrorView = null;
|
||||
}
|
||||
|
||||
if (mControlsView != null) {
|
||||
mWm.removeView(mControlsView);
|
||||
mControlsView = null;
|
||||
if (mMirrorWindowControl != null) {
|
||||
mMirrorWindowControl.destroyControl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,40 +227,9 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
}
|
||||
|
||||
private void createControls() {
|
||||
int controlsSize = (int) mContext.getResources().getDimension(
|
||||
R.dimen.magnification_controls_size);
|
||||
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(controlsSize, controlsSize,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.RGBA_8888);
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
|
||||
lp.token = mOverlayView.getWindowToken();
|
||||
lp.setTitle(mContext.getString(R.string.magnification_controls_title));
|
||||
|
||||
mControlsView = LayoutInflater.from(mContext).inflate(R.layout.magnifier_controllers, null);
|
||||
mWm.addView(mControlsView, lp);
|
||||
|
||||
mLeftControl = mControlsView.findViewById(R.id.left_control);
|
||||
mUpControl = mControlsView.findViewById(R.id.up_control);
|
||||
mRightControl = mControlsView.findViewById(R.id.right_control);
|
||||
mBottomControl = mControlsView.findViewById(R.id.down_control);
|
||||
|
||||
mLeftControl.setOnClickListener(this);
|
||||
mUpControl.setOnClickListener(this);
|
||||
mRightControl.setOnClickListener(this);
|
||||
mBottomControl.setOnClickListener(this);
|
||||
|
||||
mLeftControl.setOnLongClickListener(this);
|
||||
mUpControl.setOnLongClickListener(this);
|
||||
mRightControl.setOnLongClickListener(this);
|
||||
mBottomControl.setOnLongClickListener(this);
|
||||
|
||||
mLeftControl.setOnTouchListener(this);
|
||||
mUpControl.setOnTouchListener(this);
|
||||
mRightControl.setOnTouchListener(this);
|
||||
mBottomControl.setOnTouchListener(this);
|
||||
if (mMirrorWindowControl != null) {
|
||||
mMirrorWindowControl.showControl(mOverlayView.getWindowToken());
|
||||
}
|
||||
}
|
||||
|
||||
private void setInitialStartBounds() {
|
||||
@@ -330,41 +288,15 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
t.setGeometry(mMirrorSurface, sourceBounds, mTmpRect, Surface.ROTATION_0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setMoveOffset(v, mMoveFrameAmountShort);
|
||||
moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
mIsPressedDown = true;
|
||||
setMoveOffset(v, mMoveFrameAmountLong);
|
||||
mHandler.post(mMoveMirrorRunnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (v == mLeftControl || v == mUpControl || v == mRightControl || v == mBottomControl) {
|
||||
return handleControlTouchEvent(event);
|
||||
} else if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
|
||||
if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
|
||||
|| v == mBottomDrag) {
|
||||
return handleDragTouchEvent(event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleControlTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsPressedDown = false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleDragTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
@@ -380,20 +312,6 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setMoveOffset(View v, int moveFrameAmount) {
|
||||
mMoveWindowOffset.set(0, 0);
|
||||
|
||||
if (v == mLeftControl) {
|
||||
mMoveWindowOffset.x = -moveFrameAmount;
|
||||
} else if (v == mUpControl) {
|
||||
mMoveWindowOffset.y = -moveFrameAmount;
|
||||
} else if (v == mRightControl) {
|
||||
mMoveWindowOffset.x = moveFrameAmount;
|
||||
} else if (v == mBottomControl) {
|
||||
mMoveWindowOffset.y = moveFrameAmount;
|
||||
}
|
||||
}
|
||||
|
||||
private void moveMirrorWindow(int xOffset, int yOffset) {
|
||||
if (updateMagnificationFramePosition(xOffset, yOffset)) {
|
||||
modifyWindowMagnification(mTransaction);
|
||||
@@ -461,6 +379,7 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
createMirror();
|
||||
@@ -474,13 +393,13 @@ public class WindowMagnificationController implements View.OnClickListener,
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
}
|
||||
|
||||
class MoveMirrorRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsPressedDown) {
|
||||
moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
|
||||
mHandler.postDelayed(mMoveMirrorRunnable, 100);
|
||||
}
|
||||
@Override
|
||||
public void move(int xOffset, int yOffset) {
|
||||
if (mMirrorSurfaceView == null) {
|
||||
return;
|
||||
}
|
||||
mMagnificationFrame.offset(xOffset, yOffset);
|
||||
modifyWindowMagnification(mTransaction);
|
||||
mTransaction.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 com.android.systemui.accessibility;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.IBinder;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class MirrorWindowControlTest extends SysuiTestCase {
|
||||
|
||||
@Mock WindowManager mWindowManager;
|
||||
@Mock IBinder mIBinder;
|
||||
View mView;
|
||||
int mViewWidth;
|
||||
int mViewHeight;
|
||||
|
||||
StubMirrorWindowControl mStubMirrorWindowControl;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mView = new View(getContext());
|
||||
mViewWidth = 10;
|
||||
mViewHeight = 20;
|
||||
getContext().addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
doAnswer(invocation -> {
|
||||
View view = invocation.getArgument(0);
|
||||
LayoutParams lp = invocation.getArgument(1);
|
||||
view.setLayoutParams(lp);
|
||||
return null;
|
||||
}).when(mWindowManager).addView(any(View.class), any(LayoutParams.class));
|
||||
|
||||
mStubMirrorWindowControl = new StubMirrorWindowControl(getContext(), mView, mViewWidth,
|
||||
mViewHeight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showControl_createViewAndAddView() {
|
||||
mStubMirrorWindowControl.showControl(mIBinder);
|
||||
|
||||
assertTrue(mStubMirrorWindowControl.mInvokeOnCreateView);
|
||||
ArgumentCaptor<ViewGroup.LayoutParams> lpCaptor = ArgumentCaptor.forClass(
|
||||
ViewGroup.LayoutParams.class);
|
||||
verify(mWindowManager).addView(any(), lpCaptor.capture());
|
||||
assertTrue(lpCaptor.getValue().width == mViewWidth);
|
||||
assertTrue(lpCaptor.getValue().height == mViewHeight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destroyControl_removeView() {
|
||||
mStubMirrorWindowControl.showControl(mIBinder);
|
||||
ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
|
||||
verify(mWindowManager).addView(captor.capture(), any(LayoutParams.class));
|
||||
|
||||
mStubMirrorWindowControl.destroyControl();
|
||||
|
||||
verify(mWindowManager).removeView(eq(captor.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void move_offsetIsCorrect() {
|
||||
ArgumentCaptor<ViewGroup.LayoutParams> lpCaptor = ArgumentCaptor.forClass(
|
||||
ViewGroup.LayoutParams.class);
|
||||
mStubMirrorWindowControl.showControl(mIBinder);
|
||||
verify(mWindowManager).addView(any(), lpCaptor.capture());
|
||||
LayoutParams lp = (LayoutParams) lpCaptor.getValue();
|
||||
Point startPosition = new Point(lp.x, lp.y);
|
||||
|
||||
mStubMirrorWindowControl.move(-10, -20);
|
||||
|
||||
verify(mWindowManager).updateViewLayout(eq(mView), lpCaptor.capture());
|
||||
assertTrue(lpCaptor.getAllValues().size() == 2);
|
||||
lp = (LayoutParams) lpCaptor.getValue();
|
||||
Point currentPosition = new Point(lp.x, lp.y);
|
||||
assertEquals(-10, currentPosition.x - startPosition.x);
|
||||
assertEquals(-20, currentPosition.y - startPosition.y);
|
||||
}
|
||||
|
||||
private static class StubMirrorWindowControl extends MirrorWindowControl {
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final View mView;
|
||||
|
||||
boolean mInvokeOnCreateView = false;
|
||||
|
||||
StubMirrorWindowControl(Context context, View view, int width, int height) {
|
||||
super(context);
|
||||
mView = view;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWindowTitle() {
|
||||
return "StubMirrorWindowControl";
|
||||
}
|
||||
|
||||
@Override
|
||||
View onCreateView(LayoutInflater inflater, Point viewSize) {
|
||||
mInvokeOnCreateView = true;
|
||||
viewSize.x = mWidth;
|
||||
viewSize.y = mHeight;
|
||||
return mView;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.accessibility;
|
||||
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.os.IBinder;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class WindowMagnificationControllerTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
MirrorWindowControl mMirrorWindowControl;
|
||||
private WindowMagnificationController mWindowMagnificationController;
|
||||
private Instrumentation mInstrumentation;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mInstrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
mWindowMagnificationController = new WindowMagnificationController(getContext(),
|
||||
mMirrorWindowControl);
|
||||
verify(mMirrorWindowControl).setWindowDelegate(
|
||||
any(MirrorWindowControl.MirrorWindowDelegate.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
mWindowMagnificationController.deleteWindowMagnification();
|
||||
});
|
||||
mInstrumentation.waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWindowMagnification_showControl() {
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
mWindowMagnificationController.createWindowMagnification();
|
||||
});
|
||||
mInstrumentation.waitForIdleSync();
|
||||
verify(mMirrorWindowControl).showControl(any(IBinder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteWindowMagnification_destroyControl() {
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
mWindowMagnificationController.createWindowMagnification();
|
||||
});
|
||||
mInstrumentation.waitForIdleSync();
|
||||
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
mWindowMagnificationController.deleteWindowMagnification();
|
||||
});
|
||||
mInstrumentation.waitForIdleSync();
|
||||
|
||||
verify(mMirrorWindowControl).destroyControl();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user