Support triple-tap and hold for window magnification.

1. Detect triple-tap and hold gesture.
2. Magnify the content at a user's tap location.
3. Offset the frame to the top-left position about a half of frame size
4. The definition and the implementation of
WindowMagnificationController's getCenterX and getCenterY are different
due to these patches. We need to correct them. (ref: b/207812394)

Bug: 202465806
Test: atest IWindowMagnificationConnectionTest
	atest MagnificationControllerTest
	atest WindowMagnificationAnimationControllerTest (@Ignore)
	atest WindowMagnificationConnectionWrapperTest
	atest WindowMagnificationControllerTest
	atest WindowMagnificationGestureHandlerTest
	atest WindowMagnificationManagerTest
Change-Id: I59bf8a3e35e42c963de1698bec915a4a590e5c5f
This commit is contained in:
Daniel Hsieh
2021-11-23 07:07:28 +00:00
parent 7918a89b24
commit 6618b7f58a
17 changed files with 557 additions and 102 deletions

View File

@@ -38,9 +38,14 @@ oneway interface IWindowMagnificationConnection {
* 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.
* @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between
* frame position X and centerX
* @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between
* frame position Y and centerY
* @param callback The callback called when the animation is completed or interrupted.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
in IRemoteMagnificationAnimationCallback callback);
/**

View File

@@ -158,12 +158,13 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.enableWindowMagnification(scale, centerX,
centerY, callback);
windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
}
}

View File

@@ -62,6 +62,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
private final ValueAnimator mValueAnimator;
private final AnimationSpec mStartSpec = new AnimationSpec();
private final AnimationSpec mEndSpec = new AnimationSpec();
private float mMagnificationFrameOffsetRatioX = 0f;
private float mMagnificationFrameOffsetRatioY = 0f;
private final Context mContext;
// Called when the animation is ended successfully without cancelling or mStartSpec and
// mEndSpec are equal.
@@ -88,7 +90,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
}
/**
* Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
* Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float,
* float, float, IRemoteMagnificationAnimationCallback)}
* with transition animation. If the window magnification is not enabled, the scale will start
* from 1.0 and the center won't be changed during the animation. If {@link #mState} is
* {@code STATE_DISABLING}, the animation runs in reverse.
@@ -106,16 +109,48 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
enableWindowMagnification(scale, centerX, centerY, 0f, 0f, animationCallback);
}
/**
* Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float,
* float, float, IRemoteMagnificationAnimationCallback)}
* with transition animation. If the window magnification is not enabled, the scale will start
* from 1.0 and the center won't be changed during the animation. If {@link #mState} is
* {@code STATE_DISABLING}, the animation runs in reverse.
*
* @param scale The target scale, or {@link Float#NaN} to leave unchanged.
* @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between
* frame position X and centerX
* @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between
* frame position Y and centerY
* @param animationCallback Called when the transition is complete, the given arguments
* are as same as current values, or the transition is interrupted
* due to the new transition request.
*
* @see #onAnimationUpdate(ValueAnimator)
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
if (mController == null) {
return;
}
sendAnimationCallback(false);
mMagnificationFrameOffsetRatioX = magnificationFrameOffsetRatioX;
mMagnificationFrameOffsetRatioY = magnificationFrameOffsetRatioY;
// Enable window magnification without animation immediately.
if (animationCallback == null) {
if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
mController.enableWindowMagnification(scale, centerX, centerY);
mController.enableWindowMagnificationInternal(scale, centerX, centerY,
mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
setState(STATE_ENABLED);
return;
}
@@ -123,7 +158,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
mController.enableWindowMagnification(scale, centerX, centerY);
mController.enableWindowMagnificationInternal(scale, centerX, centerY,
mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
} else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
@@ -273,7 +309,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract;
final float centerY =
mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract;
mController.enableWindowMagnification(sentScale, centerX, centerY);
mController.enableWindowMagnificationInternal(sentScale, centerX, centerY,
mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
}
private static ValueAnimator newValueAnimator(Resources resources) {

View File

@@ -49,11 +49,13 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
@Override
public void enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, IRemoteMagnificationAnimationCallback callback) {
public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
IRemoteMagnificationAnimationCallback callback) {
mHandler.post(
() -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
centerY, callback));
centerY, magnificationFrameOffsetRatioX,
magnificationFrameOffsetRatioY, callback));
}
@Override

View File

@@ -95,17 +95,46 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@Surface.Rotation
@VisibleForTesting
int mRotation;
private final Rect mMagnificationFrame = new Rect();
private final SurfaceControl.Transaction mTransaction;
private final WindowManager mWm;
private float mScale;
/**
* MagnificationFrame represents the bound of {@link #mMirrorSurface} and is constrained
* by the {@link #mMagnificationFrameBoundary}.
* We use MagnificationFrame to calculate the position of {@link #mMirrorView}.
* We combine MagnificationFrame with {@link #mMagnificationFrameOffsetX} and
* {@link #mMagnificationFrameOffsetY} to calculate the position of {@link #mSourceBounds}.
*/
private final Rect mMagnificationFrame = new Rect();
private final Rect mTmpRect = new Rect();
/**
* MirrorViewBounds is the bound of the {@link #mMirrorView} which displays the magnified
* content.
* {@link #mMirrorView}'s center is equal to {@link #mMagnificationFrame}'s center.
*/
private final Rect mMirrorViewBounds = new Rect();
/**
* SourceBound is the bound of the magnified region which projects the magnified content.
* SourceBound's center is equal to the parameters centerX and centerY in
* {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float, float)}}
* but it is calculated from {@link #mMagnificationFrame}'s center in the runtime.
*/
private final Rect mSourceBounds = new Rect();
/**
* The relation of centers between {@link #mSourceBounds} and {@link #mMagnificationFrame} is
* calculated in {@link #calculateSourceBounds(Rect, float)} and the equations are as following:
* MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset
* SourceBound = MagnificationFrame - MagnificationFrameOffset
*/
private int mMagnificationFrameOffsetX = 0;
private int mMagnificationFrameOffsetY = 0;
// The root of the mirrored content
private SurfaceControl mMirrorSurface;
@@ -123,6 +152,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final Runnable mMirrorViewRunnable;
private final Runnable mUpdateStateDescriptionRunnable;
private final Runnable mWindowInsetChangeRunnable;
// MirrorView is the mirror window which displays the magnified content.
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -339,7 +369,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
// window size changed not caused by rotation.
if (isWindowVisible() && reCreateWindow) {
deleteWindowMagnification();
enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN);
}
}
@@ -633,6 +663,26 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale));
int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale));
mSourceBounds.set(left, top, right, bottom);
// SourceBound's center is equal to center[X,Y] but calculated from MagnificationFrame's
// center. The relation between SourceBound and MagnificationFrame is as following:
// MagnificationFrame = SourceBound (center[X,Y]) + MagnificationFrameOffset
// SourceBound = MagnificationFrame - MagnificationFrameOffset
mSourceBounds.offset(-mMagnificationFrameOffsetX, -mMagnificationFrameOffsetY);
if (mSourceBounds.left < 0) {
mSourceBounds.offsetTo(0, mSourceBounds.top);
} else if (mSourceBounds.right > mWindowBounds.width()) {
mSourceBounds.offsetTo(mWindowBounds.width() - mSourceBounds.width(),
mSourceBounds.top);
}
if (mSourceBounds.top < 0) {
mSourceBounds.offsetTo(mSourceBounds.left, 0);
} else if (mSourceBounds.bottom > mWindowBounds.height()) {
mSourceBounds.offsetTo(mSourceBounds.left,
mWindowBounds.height() - mSourceBounds.height());
}
}
private void calculateMagnificationFrameBoundary() {
@@ -646,11 +696,31 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
final int scaledWidth = (int) (halfWidth / mScale);
// The scaled half height of magnified region.
final int scaledHeight = (int) (halfHeight / mScale);
final int exceededWidth = halfWidth - scaledWidth;
final int exceededHeight = halfHeight - scaledHeight;
mMagnificationFrameBoundary.set(-exceededWidth, -exceededHeight,
mWindowBounds.width() + exceededWidth, mWindowBounds.height() + exceededHeight);
// MagnificationFrameBoundary constrain the space of MagnificationFrame, and it also has
// to leave enough space for SourceBound to magnify the whole screen space.
// However, there is an offset between SourceBound and MagnificationFrame.
// The relation between SourceBound and MagnificationFrame is as following:
// SourceBound = MagnificationFrame - MagnificationFrameOffset
// Therefore, we have to adjust the exceededBoundary based on the offset.
//
// We have to increase the offset space for the SourceBound edges which are located in
// the MagnificationFrame. For example, if the offsetX and offsetY are negative, which
// means SourceBound is at right-bottom size of MagnificationFrame, the left and top
// edges of SourceBound are located in MagnificationFrame. So, we have to leave extra
// offset space at left and top sides and don't have to leave extra space at right and
// bottom sides.
final int exceededLeft = Math.max(halfWidth - scaledWidth - mMagnificationFrameOffsetX, 0);
final int exceededRight = Math.max(halfWidth - scaledWidth + mMagnificationFrameOffsetX, 0);
final int exceededTop = Math.max(halfHeight - scaledHeight - mMagnificationFrameOffsetY, 0);
final int exceededBottom = Math.max(halfHeight - scaledHeight + mMagnificationFrameOffsetY,
0);
mMagnificationFrameBoundary.set(
-exceededLeft,
-exceededTop,
mWindowBounds.width() + exceededRight,
mWindowBounds.height() + exceededBottom);
}
/**
@@ -711,24 +781,30 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
/**
* Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
* Wraps {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float,
* float, float, float)}
* with transition animation. If the window magnification is not enabled, the scale will start
* from 1.0 and the center won't be changed during the animation. If animator is
* {@code STATE_DISABLING}, the animation runs in reverse.
*
* @param scale The target scale, or {@link Float#NaN} to leave unchanged.
* @param centerX The screen-relative X coordinate around which to center,
* @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param magnificationFrameOffsetRatioX Indicate the X coordinate offset
* between frame position X and centerX
* @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset
* between frame position Y and centerY
* @param animationCallback Called when the transition is complete, the given arguments
* are as same as current values, or the transition is interrupted
* due to the new transition request.
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
mAnimationController.enableWindowMagnification(scale, centerX,
centerY, animationCallback);
mAnimationController.enableWindowMagnification(scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, animationCallback);
}
/**
@@ -738,21 +814,56 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* be consistent with the behavior of display magnification.
*
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to center,
* @param centerX the screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* @param centerY the screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
*/
void enableWindowMagnification(float scale, float centerX, float centerY) {
void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
enableWindowMagnificationInternal(scale, centerX, centerY, Float.NaN, Float.NaN);
}
/**
* Enables window magnification with specified parameters. If the given scale is <strong>less
* than or equal to 1.0f<strong>, then
* {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to
* be consistent with the behavior of display magnification.
*
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param magnificationFrameOffsetRatioX Indicate the X coordinate offset
* between frame position X and centerX,
* or {@link Float#NaN} to leave unchanged.
* @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset
* between frame position Y and centerY,
* or {@link Float#NaN} to leave unchanged.
*/
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY) {
if (Float.compare(scale, 1.0f) <= 0) {
deleteWindowMagnification();
return;
}
mMagnificationFrameOffsetX = Float.isNaN(magnificationFrameOffsetRatioX)
? mMagnificationFrameOffsetX
: (int) (mMagnificationFrame.width() / 2 * magnificationFrameOffsetRatioX);
mMagnificationFrameOffsetY = Float.isNaN(magnificationFrameOffsetRatioY)
? mMagnificationFrameOffsetY
: (int) (mMagnificationFrame.height() / 2 * magnificationFrameOffsetRatioY);
// The relation of centers between SourceBound and MagnificationFrame is as following:
// MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset
final float newMagnificationFrameCenterX = centerX + mMagnificationFrameOffsetX;
final float newMagnificationFrameCenterY = centerY + mMagnificationFrameOffsetY;
final float offsetX = Float.isNaN(centerX) ? 0
: centerX - mMagnificationFrame.exactCenterX();
: newMagnificationFrameCenterX - mMagnificationFrame.exactCenterX();
final float offsetY = Float.isNaN(centerY) ? 0
: centerY - mMagnificationFrame.exactCenterY();
: newMagnificationFrameCenterY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
calculateMagnificationFrameBoundary();
@@ -774,7 +885,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (mAnimationController.isAnimating() || !isWindowVisible() || mScale == scale) {
return;
}
enableWindowMagnification(scale, Float.NaN, Float.NaN);
enableWindowMagnificationInternal(scale, Float.NaN, Float.NaN);
mHandler.removeCallbacks(mUpdateStateDescriptionRunnable);
mHandler.postDelayed(mUpdateStateDescriptionRunnable, UPDATE_STATE_DESCRIPTION_DELAY_MS);
}

View File

@@ -100,11 +100,11 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_passThrough() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
Float.NaN, mAnimationCallback);
Float.NaN, 0f, 0f, mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f),
eq(Float.NaN), eq(Float.NaN), eq(mAnimationCallback));
eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback));
}
@Test

View File

@@ -110,7 +110,7 @@ public class TestableWindowManager implements WindowManager {
}
/**
* Sets the given window insets to the current window metics.
* Sets the given window insets to the current window metrics.
*
* @param insets the window insets.
*/

View File

@@ -17,22 +17,27 @@
package com.android.systemui.accessibility;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
@@ -40,6 +45,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
@@ -56,7 +62,6 @@ import org.mockito.MockitoAnnotations;
import java.util.concurrent.atomic.AtomicReference;
@Ignore
@LargeTest
@RunWith(AndroidTestingRunner.class)
@@ -74,6 +79,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class);
private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class);
@Mock
Handler mHandler;
@@ -94,10 +101,16 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
private long mWaitingAnimationPeriod;
private long mWaitIntermediateAnimationPeriod;
private TestableWindowManager mWindowManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
@@ -119,12 +132,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, 1.0f);
verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
verify(mAnimationCallback).onResult(true);
}
@@ -162,8 +178,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y);
verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y, 0f, 0f);
verify(mAnimationCallback).onResult(true);
}
@@ -187,11 +203,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -213,11 +233,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
// It presents the window magnification is disabled.
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
@@ -256,7 +280,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -286,9 +310,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verify(mAnimationCallback).onResult(false);
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
//Animating in reverse, so we only check if the start values are greater than current.
assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
@@ -336,7 +361,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mSpyController, never()).deleteWindowMagnification();
verify(mAnimationCallback).onResult(false);
@@ -362,15 +387,43 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback2).onResult(true);
}
@Test
public void enableWindowMagnificationWithOffset_expectedValues() {
final float offsetRatio = -0.1f;
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
windowBounds.exactCenterX(), windowBounds.exactCenterY(),
offsetRatio, offsetRatio, mAnimationCallback);
});
SystemClock.sleep(mWaitingAnimationPeriod);
final View attachedView = mWindowManager.getAttachedView();
assertNotNull(attachedView);
final Rect mirrorViewBound = new Rect();
final View mirrorView = attachedView.findViewById(R.id.surface_view);
assertNotNull(mirrorView);
mirrorView.getBoundsOnScreen(mirrorViewBound);
assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(),
Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f);
assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(),
Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f);
}
@Test
public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
throws RemoteException {
@@ -378,7 +431,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationCallback).onResult(true);
}
@@ -390,11 +443,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(true);
}
@@ -433,8 +490,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mCurrentCenterY.set(mController.getCenterY());
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
//The animation is in verse, so we only check the start values should no be greater than
// the current one.
@@ -442,6 +501,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -471,9 +532,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -571,9 +636,18 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Override
void enableWindowMagnification(float scale, float centerX, float centerY) {
super.enableWindowMagnification(scale, centerX, centerY);
mSpyController.enableWindowMagnification(scale, centerX, centerY);
void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
super.enableWindowMagnificationInternal(scale, centerX, centerY);
mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY);
}
@Override
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) {
super.enableWindowMagnificationInternal(scale, centerX, centerY,
magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY,
magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
}
@Override

View File

@@ -146,7 +146,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -159,7 +159,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
// Wait for Rects updated.
@@ -180,7 +180,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
mInstrumentation.runOnMainSync(() -> {
controller.enableWindowMagnification(Float.NaN, Float.NaN,
controller.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -195,7 +195,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void deleteWindowMagnification_destroyControl() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -213,7 +213,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
bounds.bottom);
});
ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
@@ -229,7 +229,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void moveMagnifier_schedulesFrame() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
});
@@ -246,8 +246,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}).when(mHandler).postDelayed(any(Runnable.class), anyLong());
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.enableWindowMagnification(2.0f, Float.NaN,
Float.NaN));
() -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
Float.NaN, Float.NaN));
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
@@ -283,8 +283,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final float displayWidth = windowBounds.width();
final PointF magnifiedCenter = new PointF(center, center + 5f);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, magnifiedCenter.x,
magnifiedCenter.y);
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
magnifiedCenter.x, magnifiedCenter.y);
// Get the center again in case the center we set is out of screen.
magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
mWindowMagnificationController.getCenterY());
@@ -327,7 +327,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
testWindowBounds.right + 100, testWindowBounds.bottom + 100);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
mWindowManager.setWindowBounds(testWindowBounds);
@@ -347,7 +347,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void screenSizeIsChangedToLarge_enabled_windowSizeIsConstrained() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
final int screenSize = mContext.getResources().getDimensionPixelSize(
@@ -369,7 +369,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
Mockito.reset(mWindowManager);
Mockito.reset(mMirrorWindowControl);
@@ -398,7 +398,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void initializeA11yNode_enabled_expectedValues() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
final View mirrorView = mWindowManager.getAttachedView();
@@ -422,7 +422,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void performA11yActions_visible_expectedResults() {
final int displayId = mContext.getDisplayId();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
@@ -449,7 +449,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
final int displayId = mContext.getDisplayId();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
@@ -462,7 +462,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_hasA11yWindowTitle() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -473,12 +473,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(0.9f, Float.NaN,
Float.NaN);
});
@@ -489,7 +489,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void onLocaleChanged_enabled_updateA11yWindowTitle() {
final String newA11yWindowTitle = "new a11y window title";
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
final TestableResources testableResources = getContext().getOrCreateTestableResources();
@@ -506,7 +506,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void onSingleTap_enabled_scaleIsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -530,7 +530,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});

View File

@@ -59,15 +59,19 @@ class WindowMagnificationConnectionWrapper {
}
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable MagnificationAnimationCallback callback) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".enableWindowMagnification",
FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ ";centerY=" + centerY + ";callback=" + callback);
+ ";centerY=" + centerY + ";magnificationFrameOffsetRatioX="
+ magnificationFrameOffsetRatioX + ";magnificationFrameOffsetRatioY="
+ magnificationFrameOffsetRatioY + ";callback=" + callback);
}
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {

View File

@@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static java.util.Arrays.asList;
@@ -78,6 +79,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
final DetectingState mDetectingState;
@VisibleForTesting
final PanningScalingGestureState mObservePanningScalingState;
@VisibleForTesting
final ViewportDraggingState mViewportDraggingState;
@VisibleForTesting
State mCurrentState;
@@ -105,6 +108,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
policyFlags));
mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
mDetectingState = new DetectingState(context, mDetectTripleTap);
mViewportDraggingState = new ViewportDraggingState();
mObservePanningScalingState = new PanningScalingGestureState(
new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
new PanningScalingHandler.MagnificationDelegate() {
@@ -158,7 +162,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
public void handleShortcutTriggered() {
final Point screenSize = mTempPoint;
getScreenSize(mTempPoint);
toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
}
private void getScreenSize(Point outSize) {
@@ -171,14 +176,17 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
}
private void enableWindowMagnifier(float centerX, float centerY) {
private void enableWindowMagnifier(float centerX, float centerY,
@WindowMagnificationManager.WindowPosition int windowPosition) {
if (DEBUG_ALL) {
Slog.i(mLogTag, "enableWindowMagnifier :" + centerX + ", " + centerY);
Slog.i(mLogTag, "enableWindowMagnifier :"
+ centerX + ", " + centerY + ", " + windowPosition);
}
final float scale = MathUtils.constrain(
mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY);
mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
windowPosition);
}
private void disableWindowMagnifier() {
@@ -188,11 +196,12 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
}
private void toggleMagnification(float centerX, float centerY) {
private void toggleMagnification(float centerX, float centerY,
@WindowMagnificationManager.WindowPosition int windowPosition) {
if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
disableWindowMagnifier();
} else {
enableWindowMagnifier(centerX, centerY);
enableWindowMagnifier(centerX, centerY, windowPosition);
}
}
@@ -200,7 +209,17 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTap()");
}
toggleMagnification(up.getX(), up.getY());
toggleMagnification(up.getX(), up.getY(),
WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
}
private void onTripleTapAndHold(MotionEvent up) {
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTapAndHold()");
}
enableWindowMagnifier(up.getX(), up.getY(),
WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
transitionTo(mViewportDraggingState);
}
void resetToDetectState() {
@@ -319,6 +338,65 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
}
}
/**
* This class handles motion events when the event dispatcher has
* determined that the user is performing a single-finger drag of the
* magnification viewport.
*
* Leaving this state until receiving {@link MotionEvent#ACTION_UP}
* or {@link MotionEvent#ACTION_CANCEL}.
*/
final class ViewportDraggingState implements State {
private float mLastX = Float.NaN;
private float mLastY = Float.NaN;
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final int action = event.getActionMasked();
switch (action) {
case ACTION_MOVE: {
if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
float offsetX = event.getX() - mLastX;
float offsetY = event.getY() - mLastY;
mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
offsetY);
}
mLastX = event.getX();
mLastY = event.getY();
}
break;
case ACTION_UP:
case ACTION_CANCEL: {
mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
transitionTo(mDetectingState);
}
break;
}
}
@Override
public void clear() {
mLastX = Float.NaN;
mLastY = Float.NaN;
}
@Override
public void onExit() {
clear();
}
@Override
public String toString() {
return "ViewportDraggingState{"
+ "mLastX=" + mLastX
+ ",mLastY=" + mLastY
+ '}';
}
}
/**
* This class handles motion events in a duration to determine if the user is going to
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
@@ -405,6 +483,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
onTripleTap(motionEvent);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD) {
onTripleTapAndHold(motionEvent);
} else {
mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
lastDownEventTime);
@@ -439,6 +519,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
return "WindowMagnificationGestureHandler{"
+ "mDetectingState=" + mDetectingState
+ ", mDelegatingState=" + mDelegatingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ ", mMagnifiedInteractionState=" + mObservePanningScalingState
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)

View File

@@ -20,12 +20,14 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNI
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
@@ -44,6 +46,9 @@ import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
@@ -58,6 +63,25 @@ public class WindowMagnificationManager implements
private static final String TAG = "WindowMagnificationMgr";
/**
* Indicate that the magnification window is at the magnification center.
*/
public static final int WINDOW_POSITION_AT_CENTER = 0;
/**
* Indicate that the magnification window is at the top-left side of the magnification
* center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner
* of the window is at the magnification center.
*/
public static final int WINDOW_POSITION_AT_TOP_LEFT = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = {
WINDOW_POSITION_AT_CENTER,
WINDOW_POSITION_AT_TOP_LEFT
})
public @interface WindowPosition {}
private final Object mLock = new Object();
private final Context mContext;
@VisibleForTesting
@@ -281,20 +305,60 @@ public class WindowMagnificationManager implements
}
/**
* Enables window magnification with specified center and scale on the specified display and
* Enables window magnification with specified center and scale on the given display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
* @param centerX The screen-relative X coordinate around which to center,
* @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param animationCallback Called when the animation result is valid.
* @return {@code true} if the magnification is enabled successfully.
*/
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, @Nullable MagnificationAnimationCallback animationCallback) {
return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback,
WINDOW_POSITION_AT_CENTER);
}
/**
* Enables window magnification with specified center and scale on the given display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
* @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param windowPosition Indicate the offset between window position and (centerX, centerY).
* @return {@code true} if the magnification is enabled successfully.
*/
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, @WindowPosition int windowPosition) {
return enableWindowMagnification(displayId, scale, centerX, centerY,
STUB_ANIMATION_CALLBACK, windowPosition);
}
/**
* Enables window magnification with specified center and scale on the given display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
* @param centerX The screen-relative X coordinate around which to center for
* magnification, or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center for
* magnification, or {@link Float#NaN} to leave unchanged.
* @param animationCallback Called when the animation result is valid.
* @param windowPosition Indicate the offset between window position and (centerX, centerY).
* @return {@code true} if the magnification is enabled successfully.
*/
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, @Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition) {
final boolean enabled;
boolean previousEnabled;
synchronized (mLock) {
@@ -307,7 +371,7 @@ public class WindowMagnificationManager implements
}
previousEnabled = magnifier.mEnabled;
enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
animationCallback);
animationCallback, windowPosition);
}
if (enabled && !previousEnabled) {
@@ -662,6 +726,8 @@ public class WindowMagnificationManager implements
// The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
mDisplayId = displayId;
mWindowMagnificationManager = windowMagnificationManager;
@@ -669,14 +735,17 @@ public class WindowMagnificationManager implements
@GuardedBy("mLock")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback) {
@Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition) {
// Handle defaults. The scale may be NAN when just updating magnification center.
if (Float.isNaN(scale)) {
scale = getScale();
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
centerX, centerY, animationCallback)) {
centerX, centerY, mMagnificationFrameOffsetRatio.x,
mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
@@ -685,6 +754,19 @@ public class WindowMagnificationManager implements
return false;
}
void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) {
switch (windowPosition) {
case WINDOW_POSITION_AT_CENTER: {
mMagnificationFrameOffsetRatio.set(0f, 0f);
}
break;
case WINDOW_POSITION_AT_TOP_LEFT: {
mMagnificationFrameOffsetRatio.set(-1f, -1f);
}
break;
}
}
@GuardedBy("mLock")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
@@ -768,9 +850,15 @@ public class WindowMagnificationManager implements
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
float centerY, MagnificationAnimationCallback animationCallback) {
return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
displayId, scale, centerX, centerY, animationCallback);
float centerY, float magnificationFrameOffsetRatioX,
float magnificationFrameOffsetRatioY,
MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
displayId, scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
animationCallback);
}
}
private boolean setScaleInternal(int displayId, float scale) {

View File

@@ -155,7 +155,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController, never()).reset(anyInt(),
any(MagnificationAnimationCallback.class));
verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
anyFloat(), anyFloat(), anyFloat(),
anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(),
nullable(IRemoteMagnificationAnimationCallback.class));
}

View File

@@ -93,7 +93,7 @@ class MockWindowMagnificationConnection {
final float scale = invocation.getArgument(1);
mScale = Float.isNaN(scale) ? mScale : scale;
computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
setAnimationCallback(invocation.getArgument(4));
setAnimationCallback(invocation.getArgument(6));
computeSourceBounds();
mHasPendingCallback = true;
if (!mSuspendCallback) {
@@ -101,7 +101,7 @@ class MockWindowMagnificationConnection {
}
return null;
}).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(),
nullable(IRemoteMagnificationAnimationCallback.class));
anyFloat(), anyFloat(), nullable(IRemoteMagnificationAnimationCallback.class));
}
private void stubDisableWindowMagnification() throws RemoteException {

View File

@@ -67,7 +67,7 @@ public class WindowMagnificationConnectionWrapperTest {
@Test
public void enableWindowMagnification() throws RemoteException {
mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f,
mAnimationCallback);
0f, 0f, mAnimationCallback);
verify(mAnimationCallback).onResult(true);
}

View File

@@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -58,10 +59,11 @@ public class WindowMagnificationGestureHandlerTest {
public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP;
public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
@@ -178,6 +180,12 @@ public class WindowMagnificationGestureHandlerTest {
== mWindowMagnificationGestureHandler.mDetectingState, state);
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mViewportDraggingState, state);
}
break;
case STATE_TWO_FINGERS_DOWN: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
@@ -229,6 +237,13 @@ public class WindowMagnificationGestureHandlerTest {
tap();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
// Perform triple tap and hold gesture
tap();
tap();
tapAndHold();
}
break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -262,6 +277,10 @@ public class WindowMagnificationGestureHandlerTest {
tap();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -308,6 +327,11 @@ public class WindowMagnificationGestureHandlerTest {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
private void tapAndHold() {
send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
}
private String stateDump() {
return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState;
}

View File

@@ -143,8 +143,7 @@ public class WindowMagnificationManagerTest {
* new connection.
*/
@Test
public void
setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
MockWindowMagnificationConnection secondConnection =
@@ -177,7 +176,7 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
eq(200f), eq(300f), notNull());
eq(200f), eq(300f), eq(0f), eq(0f), notNull());
}
@Test
@@ -189,7 +188,8 @@ public class WindowMagnificationManagerTest {
mAnimationCallback);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class));
eq(200f), eq(300f), eq(0f), eq(0f),
any(IRemoteMagnificationAnimationCallback.class));
verify(mAnimationCallback).onResult(true);
}
@@ -410,6 +410,34 @@ public class WindowMagnificationManagerTest {
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
}
@Test
public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
throws RemoteException {
mWindowMagnificationManager.requestConnection(true);
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
eq(100f), eq(200f), eq(0f), eq(0f), notNull());
}
@Test
public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
throws RemoteException {
mWindowMagnificationManager.requestConnection(true);
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
}
@Test
public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
mWindowMagnificationManager.requestConnection(true);