diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 6a86189db57bc..826b4b3daef2a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -16,13 +16,22 @@ package com.android.systemui.accessibility; +import android.annotation.MainThread; +import android.annotation.NonNull; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Rect; import android.os.Handler; +import android.os.RemoteException; import android.provider.Settings; +import android.util.Log; +import android.view.Display; +import android.view.accessibility.IWindowMagnificationConnection; +import android.view.accessibility.IWindowMagnificationConnectionCallback; +import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.dagger.qualifiers.Main; @@ -33,12 +42,15 @@ import javax.inject.Singleton; * Class to handle changes to setting window_magnification value. */ @Singleton -public class WindowMagnification extends SystemUI { +public class WindowMagnification extends SystemUI implements WindowMagnifierCallback { + private static final String TAG = "WindowMagnification"; private static final int CONFIG_MASK = ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION; private WindowMagnificationController mWindowMagnificationController; private final Handler mHandler; + //TODO:Set it by the request from AccessibilityManager. + private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl; private Configuration mLastConfiguration; @@ -78,7 +90,8 @@ public class WindowMagnification extends SystemUI { boolean enable = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.WINDOW_MAGNIFICATION) != 0; if (enable) { - enableMagnification(); + enableMagnification( + mContext.getResources().getInteger(R.integer.magnification_default_scale)); } else { disableMagnification(); } @@ -87,17 +100,108 @@ public class WindowMagnification extends SystemUI { } } - private void enableMagnification() { - if (mWindowMagnificationController == null) { - mWindowMagnificationController = new WindowMagnificationController(mContext, null); - } - mWindowMagnificationController.createWindowMagnification(); + private void enableMagnification(float scale) { + enableWindowMagnification(Display.DEFAULT_DISPLAY, scale, Float.NaN, Float.NaN); } private void disableMagnification() { + disableWindowMagnification(Display.DEFAULT_DISPLAY); + } + + @MainThread + void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { + //TODO: b/144080869 support multi-display. + if (mWindowMagnificationController == null) { + mWindowMagnificationController = new WindowMagnificationController(mContext, null, + this); + } + mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY); + } + + @MainThread + void setScale(int displayId, float scale) { + //TODO: b/144080869 support multi-display. + if (mWindowMagnificationController != null) { + mWindowMagnificationController.setScale(scale); + } + } + + @MainThread + void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + //TODO: b/144080869 support multi-display. + if (mWindowMagnificationController != null) { + mWindowMagnificationController.moveWindowMagnifier(offsetX, offsetY); + } + } + + @MainThread + void disableWindowMagnification(int displayId) { + //TODO: b/144080869 support multi-display. if (mWindowMagnificationController != null) { mWindowMagnificationController.deleteWindowMagnification(); } mWindowMagnificationController = null; } + + @Override + public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) { + if (mWindowMagnificationConnectionImpl != null) { + mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame); + } + } + + private static class WindowMagnificationConnectionImpl extends + IWindowMagnificationConnection.Stub { + + private static final String TAG = "WindowMagnificationConnectionImpl"; + + private IWindowMagnificationConnectionCallback mConnectionCallback; + private final WindowMagnification mWindowMagnification; + private final Handler mHandler; + + WindowMagnificationConnectionImpl(@NonNull WindowMagnification windowMagnification, + @Main Handler mainHandler) { + mWindowMagnification = windowMagnification; + mHandler = mainHandler; + } + + @Override + public void enableWindowMagnification(int displayId, float scale, float centerX, + float centerY) { + mHandler.post( + () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX, + centerY)); + } + + @Override + public void setScale(int displayId, float scale) { + mHandler.post(() -> mWindowMagnification.setScale(displayId, scale)); + } + + @Override + public void disableWindowMagnification(int displayId) { + mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId)); + } + + @Override + public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + mHandler.post( + () -> mWindowMagnification.moveWindowMagnifier(displayId, offsetX, offsetY)); + } + + @Override + public void setConnectionCallback(IWindowMagnificationConnectionCallback callback) { + mConnectionCallback = callback; + } + + void onWindowMagnifierBoundsChanged(int displayId, Rect frame) { + if (mConnectionCallback != null) { + try { + mConnectionCallback.onWindowMagnifierBoundsChanged(displayId, frame); + } catch (RemoteException e) { + Log.e(TAG, "Failed to inform bounds changed", e); + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 3944923bd928b..a4b8efe43ff36 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.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.ActivityInfo; @@ -29,6 +30,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.os.AsyncTask; import android.os.Binder; import android.os.RemoteException; import android.util.Log; @@ -53,7 +55,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper; /** * Class to handle adding and removing a window magnification. */ -public class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback, +class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback, MirrorWindowControl.MirrorWindowDelegate { private static final String TAG = "WindowMagnificationController"; @@ -71,6 +73,7 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf private float mScale; private final Rect mTmpRect = new Rect(); + private final Rect mMirrorViewBounds = new Rect(); // The root of the mirrored content private SurfaceControl mMirrorSurface; @@ -82,6 +85,7 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf private View mBottomDrag; private final PointF mLastDrag = new PointF(); + @NonNull private final WindowMagnifierCallback mWindowMagnifierCallback; private View mMirrorView; private SurfaceView mMirrorSurfaceView; @@ -95,8 +99,10 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf @Nullable private MirrorWindowControl mMirrorWindowControl; - WindowMagnificationController(Context context, MirrorWindowControl mirrorWindowControl) { + WindowMagnificationController(Context context, MirrorWindowControl mirrorWindowControl, + @NonNull WindowMagnifierCallback callback) { mContext = context; + mWindowMagnifierCallback = callback; Display display = mContext.getDisplay(); display.getRealSize(mDisplaySize); mDisplayId = mContext.getDisplayId(); @@ -112,6 +118,7 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf if (mMirrorWindowControl != null) { mMirrorWindowControl.setWindowDelegate(this); } + setInitialStartBounds(); } private void updateDimensions() { @@ -123,18 +130,6 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf R.dimen.magnification_outer_border_margin); } - /** - * Creates a magnification window if it doesn't already exist. - */ - void createWindowMagnification() { - if (mMirrorView != null) { - return; - } - setInitialStartBounds(); - setMagnificationFrameBoundary(); - createOverlayWindow(); - } - private void createOverlayWindow() { WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, @@ -240,9 +235,8 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height); final RectF transformedRect = new RectF(mTmpRect); matrix.mapRect(transformedRect); - final int offsetX = (int) transformedRect.left - mTmpRect.left; - final int offsetY = (int) transformedRect.top - mTmpRect.top; - moveMirrorWindow(offsetX, offsetY); + moveWindowMagnifier(transformedRect.left - mTmpRect.left, + transformedRect.top - mTmpRect.top); } /** Returns the rotation degree change of two {@link Surface.Rotation} */ @@ -289,6 +283,14 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + mMirrorView.addOnLayoutChangeListener( + (View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, + int oldRight, int oldBottom) + -> AsyncTask.execute(() -> { + mMirrorView.getBoundsOnScreen(mMirrorViewBounds); + mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(mDisplayId, + mMirrorViewBounds); + })); mWm.addView(mMirrorView, params); SurfaceHolder holder = mMirrorSurfaceView.getHolder(); @@ -323,9 +325,10 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf private void setInitialStartBounds() { // Sets the initial frame area for the mirror and places it in the center of the display. - int initSize = Math.min(mDisplaySize.x, mDisplaySize.y) / 2 + 2 * mMirrorSurfaceMargin; - int initX = mDisplaySize.x / 2 - initSize / 2; - int initY = mDisplaySize.y / 2 - initSize / 2; + final int initSize = Math.min(mDisplaySize.x, mDisplaySize.y) / 2 + + 2 * mMirrorSurfaceMargin; + final int initX = mDisplaySize.x / 2 - initSize / 2; + final int initY = mDisplaySize.y / 2 - initSize / 2; mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); } @@ -423,22 +426,13 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf mLastDrag.set(event.getRawX(), event.getRawY()); return true; case MotionEvent.ACTION_MOVE: - int xDiff = (int) (event.getRawX() - mLastDrag.x); - int yDiff = (int) (event.getRawY() - mLastDrag.y); - moveMirrorWindow(xDiff, yDiff); + moveWindowMagnifier(event.getRawX() - mLastDrag.x, event.getRawY() - mLastDrag.y); mLastDrag.set(event.getRawX(), event.getRawY()); return true; } return false; } - private void moveMirrorWindow(int xOffset, int yOffset) { - if (updateMagnificationFramePosition(xOffset, yOffset)) { - modifyWindowMagnification(mTransaction); - mTransaction.apply(); - } - } - /** * Calculates the desired source bounds. This will be the area under from the center of the * displayFrame, factoring in scale. @@ -515,11 +509,61 @@ public class WindowMagnificationController implements View.OnTouchListener, Surf @Override public void move(int xOffset, int yOffset) { + moveWindowMagnifier(xOffset, yOffset); + } + + /** + * Enables window magnification with specified parameters. + * + * @param scale the target scale + * @param centerX the screen-relative X coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + * @param centerY the screen-relative Y coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + */ + void enableWindowMagnification(float scale, float centerX, float centerY) { + final float offsetX = Float.isNaN(centerX) ? 0 + : centerX - mMagnificationFrame.exactCenterX(); + final float offsetY = Float.isNaN(centerY) ? 0 + : centerY - mMagnificationFrame.exactCenterY(); + mScale = scale; + setMagnificationFrameBoundary(); + updateMagnificationFramePosition((int) offsetX, (int) offsetY); + if (mMirrorView == null) { + createOverlayWindow(); + } else { + modifyWindowMagnification(mTransaction); + mTransaction.apply(); + } + } + + /** + * Sets the scale of the magnified region if it's visible. + * + * @param scale the target scale + */ + void setScale(float scale) { + if (mMirrorView == null || mScale == scale) { + return; + } + enableWindowMagnification(scale, Float.NaN, Float.NaN); + } + + /** + * Moves the window magnifier with specified offset in pixels unit. + * + * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in + * current screen pixels. + * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in + * current screen pixels. + */ + void moveWindowMagnifier(float offsetX, float offsetY) { if (mMirrorSurfaceView == null) { return; } - mMagnificationFrame.offset(xOffset, yOffset); - modifyWindowMagnification(mTransaction); - mTransaction.apply(); + if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) { + modifyWindowMagnification(mTransaction); + mTransaction.apply(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java new file mode 100644 index 0000000000000..2adb3ebb7bce4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java @@ -0,0 +1,32 @@ +/* + * 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.graphics.Rect; + +/** + * A callback to inform {@link com.android.server.accessibility.AccessibilityManagerService} about + * the UI state change or the user interaction. + */ +interface WindowMagnifierCallback { + /** + * Called when the bounds of window magnifier is changed. + * @param displayId The logical display id. + * @param bounds The bounds of window magnifier on the screen. + */ + void onWindowMagnifierBoundsChanged(int displayId, Rect bounds); +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 08a61723aba21..68a9b9223994c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -42,6 +42,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Mock MirrorWindowControl mMirrorWindowControl; + @Mock + WindowMagnifierCallback mWindowMagnifierCallback; private WindowMagnificationController mWindowMagnificationController; private Instrumentation mInstrumentation; @@ -50,7 +52,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mInstrumentation = InstrumentationRegistry.getInstrumentation(); mWindowMagnificationController = new WindowMagnificationController(getContext(), - mMirrorWindowControl); + mMirrorWindowControl, mWindowMagnifierCallback); verify(mMirrorWindowControl).setWindowDelegate( any(MirrorWindowControl.MirrorWindowDelegate.class)); } @@ -66,7 +68,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void createWindowMagnification_showControl() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.createWindowMagnification(); + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); }); mInstrumentation.waitForIdleSync(); verify(mMirrorWindowControl).showControl(any(IBinder.class)); @@ -75,7 +78,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_destroyControl() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.createWindowMagnification(); + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); }); mInstrumentation.waitForIdleSync();