Extract Mirror windowcontrol to a single class

To change control UI and make if more flexible, we extract to a single
class to adapt new UI requirement.

Bug: 143745827
Test: MirrorWindowControlTest
Test: WindowMagnificationControllerTest
Change-Id: I0dc141bed293d485357c5347d04d9abd46a010ff
This commit is contained in:
ryanlwlin
2019-12-18 21:24:30 +08:00
parent e0757c5827
commit 7bc9d86c2f
6 changed files with 680 additions and 106 deletions

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}