summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java197
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java221
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java129
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java148
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java89
6 files changed, 680 insertions, 106 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
new file mode 100644
index 000000000000..e76a2093f3f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
@@ -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);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
new file mode 100644
index 000000000000..2ba2bb6edc18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
@@ -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;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 898cd1363d2b..b3ce4a0fa6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 581cf7a2fbef..7176490d775d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -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() {
@@ -331,40 +289,14 @@ public class WindowMagnificationController implements View.OnClickListener,
}
@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();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
new file mode 100644
index 000000000000..fff7e4a04cb4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -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;
+ }
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
new file mode 100644
index 000000000000..08a61723aba2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -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();
+ }
+}