From 0b0634c4a9c132a6061387a03b9e6690a17ac521 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Wed, 5 May 2021 20:01:47 +0800 Subject: [PATCH] Fix hard dragging the window magnification from the bottom of the screen The window magnifier is hard to drag when it is at the bottom of the screen. This is due to that launcher is detecting swipe-up system gesture. To fix it, we set the system ui state flag to true when the window is overlapped with the gesture detection area. We also update the gesture dection area via OnApplyWindowInsetsListener Once the area is changed, we will update the flag again if necessary. In the testing part, we use real WindowManager to verify the functionality. Bug: 179648683 Test: atest com.android.systemui.accessibility manually test and use the following command to check the flag adb shell dumpsys activity service TouchInteractionService Change-Id: Ia09bd26b3eb164790614c173594d997d45f4ecdc --- .../shared/system/QuickStepContract.java | 6 +- .../accessibility/DisplayIdIndexSupplier.java | 12 +- .../MagnificationGestureDetector.java | 2 +- .../accessibility/WindowMagnification.java | 66 ++++-- ...indowMagnificationAnimationController.java | 9 + .../WindowMagnificationController.java | 109 ++++++--- .../IWindowMagnificationConnectionTest.java | 12 +- ...wMagnificationAnimationControllerTest.java | 16 +- .../WindowMagnificationControllerTest.java | 213 ++++++++++++++---- .../WindowMagnificationTest.java | 81 +++++-- 10 files changed, 393 insertions(+), 133 deletions(-) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 927bce08268d6..2cf3ad2dcd496 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -105,6 +105,8 @@ public class QuickStepContract { public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17; // The IME is showing public static final int SYSUI_STATE_IME_SHOWING = 1 << 18; + // The window magnification is overlapped with system gesture insets at the bottom. + public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -125,7 +127,8 @@ public class QuickStepContract { SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, SYSUI_STATE_ONE_HANDED_ACTIVE, SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, - SYSUI_STATE_IME_SHOWING + SYSUI_STATE_IME_SHOWING, + SYSUI_STATE_MAGNIFICATION_OVERLAP }) public @interface SystemUiStateFlags {} @@ -153,6 +156,7 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0 ? "allow_gesture" : ""); str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : ""); + str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : ""); return str.toString(); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java index b69001d1ba075..c472457b02063 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java @@ -63,17 +63,15 @@ abstract class DisplayIdIndexSupplier { } /** - * Gets the object by the element index. + * Returns the object with the given display id. * - *

If the index is bigger than the array size, an {@link ArrayIndexOutOfBoundsException} is - * thrown for apps targeting {@link android.os.Build.VERSION_CODES#Q} and later

* - * @param index the element index + * @param displayId the logical display Id * @return T - * @see SparseArray#valueAt(int) */ - public T valueAt(int index) { - return mSparseArray.valueAt(index); + @Nullable + public T valueAt(int displayId) { + return mSparseArray.get(displayId); } @NonNull diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java index 4c892e29f3868..4b30ec3e6f6f5 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java @@ -113,7 +113,7 @@ class MagnificationGestureDetector { final float rawX = event.getRawX(); final float rawY = event.getRawY(); boolean handled = false; - switch (event.getAction()) { + switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mPointerDown.set(rawX, rawY); mHandler.postAtTime(mCancelTapGestureRunnable, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 4f5fdc90e9298..cee395bd9a6a8 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -18,10 +18,11 @@ package com.android.systemui.accessibility; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; + import android.annotation.MainThread; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.display.DisplayManager; @@ -37,9 +38,13 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.model.SysUiState; +import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.CommandQueue; +import java.io.FileDescriptor; +import java.io.PrintWriter; + import javax.inject.Inject; /** @@ -52,34 +57,33 @@ import javax.inject.Inject; public class WindowMagnification extends SystemUI implements WindowMagnifierCallback, CommandQueue.Callbacks { private static final String TAG = "WindowMagnification"; - private static final int CONFIG_MASK = - ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION - | ActivityInfo.CONFIG_LOCALE; private final ModeSwitchesController mModeSwitchesController; private final Handler mHandler; private final AccessibilityManager mAccessibilityManager; private final CommandQueue mCommandQueue; + private final OverviewProxyService mOverviewProxyService; private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl; private Configuration mLastConfiguration; + private SysUiState mSysUiState; private static class AnimationControllerSupplier extends DisplayIdIndexSupplier { private final Context mContext; private final Handler mHandler; - private final NavigationModeController mNavigationModeController; private final WindowMagnifierCallback mWindowMagnifierCallback; + private final SysUiState mSysUiState; AnimationControllerSupplier(Context context, Handler handler, - NavigationModeController navigationModeController, - WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager) { + WindowMagnifierCallback windowMagnifierCallback, + DisplayManager displayManager, SysUiState sysUiState) { super(displayManager); mContext = context; mHandler = handler; - mNavigationModeController = navigationModeController; mWindowMagnifierCallback = windowMagnifierCallback; + mSysUiState = sysUiState; } @Override @@ -89,10 +93,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall final WindowMagnificationController controller = new WindowMagnificationController( mContext, mHandler, new SfVsyncFrameCallbackProvider(), null, - new SurfaceControl.Transaction(), mWindowMagnifierCallback); - final int navBarMode = mNavigationModeController.addListener( - controller::onNavigationModeChanged); - controller.onNavigationModeChanged(navBarMode); + new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState); return new WindowMagnificationAnimationController(windowContext, controller); } } @@ -103,24 +104,22 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall @Inject public WindowMagnification(Context context, @Main Handler mainHandler, CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, - NavigationModeController navigationModeController) { + SysUiState sysUiState, OverviewProxyService overviewProxyService) { super(context); mHandler = mainHandler; mLastConfiguration = new Configuration(context.getResources().getConfiguration()); mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mCommandQueue = commandQueue; mModeSwitchesController = modeSwitchesController; + mSysUiState = sysUiState; + mOverviewProxyService = overviewProxyService; mAnimationControllerSupplier = new AnimationControllerSupplier(context, - mHandler, navigationModeController, this, - context.getSystemService(DisplayManager.class)); + mHandler, this, context.getSystemService(DisplayManager.class), sysUiState); } @Override public void onConfigurationChanged(Configuration newConfig) { final int configDiff = newConfig.diff(mLastConfiguration); - if ((configDiff & CONFIG_MASK) == 0) { - return; - } mLastConfiguration.setTo(newConfig); mAnimationControllerSupplier.forEach( animationController -> animationController.onConfigurationChanged(configDiff)); @@ -132,6 +131,28 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall @Override public void start() { mCommandQueue.addCallback(this); + mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() { + @Override + public void onConnectionChanged(boolean isConnected) { + if (isConnected) { + updateSysUiStateFlag(); + } + } + }); + } + + private void updateSysUiStateFlag() { + //TODO(b/187510533): support multi-display once SysuiState supports it. + final WindowMagnificationAnimationController controller = + mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY); + if (controller != null) { + controller.updateSysUiStateFlag(); + } else { + // The instance is initialized when there is an IPC request. Considering + // self-crash cases, we need to reset the flag in such situation. + mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false) + .commitUpdate(Display.DEFAULT_DISPLAY); + } } @MainThread @@ -210,6 +231,13 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall } } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(TAG); + mAnimationControllerSupplier.forEach( + animationController -> animationController.dump(pw)); + } + private void setWindowMagnificationConnection() { if (mWindowMagnificationConnectionImpl == null) { mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java index 5758b1575f5ac..36fef3ed6b512 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -31,6 +31,7 @@ import android.view.animation.AccelerateInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -269,6 +270,14 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp mController.enableWindowMagnification(sentScale, centerX, centerY); } + public void updateSysUiStateFlag() { + mController.updateSysUIStateFlag(); + } + + void dump(PrintWriter pw) { + mController.dump(pw); + } + private static ValueAnimator newValueAnimator(Resources resources) { final ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration( diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index ae16703405a5a..fcb090a76d5b1 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -16,9 +16,10 @@ package com.android.systemui.accessibility; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.WindowManager.LayoutParams; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -28,6 +29,7 @@ import android.annotation.UiContext; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -52,14 +54,17 @@ import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import android.view.WindowMetrics; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; +import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.WindowManagerWrapper; +import java.io.PrintWriter; import java.text.NumberFormat; import java.util.Locale; @@ -111,6 +116,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener; private final Runnable mMirrorViewRunnable; private final Runnable mUpdateStateDescriptionRunnable; + private final Runnable mWindowInsetChangeRunnable; private View mMirrorView; private SurfaceView mMirrorSurfaceView; private int mMirrorSurfaceMargin; @@ -119,9 +125,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private int mOuterBorderSize; // The boundary of magnification frame. private final Rect mMagnificationFrameBoundary = new Rect(); - - private int mNavBarMode; - private int mNavGestureHeight; + // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid. + private int mSystemGestureTop = -1; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; private final MagnificationGestureDetector mGestureDetector; @@ -130,6 +135,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private Locale mLocale; private NumberFormat mPercentFormat; private float mBounceEffectAnimationScale; + private SysUiState mSysUiState; + // Set it to true when the view is overlapped with the gesture insets at the bottom. + private boolean mOverlapWithGestureInsets; @Nullable private MirrorWindowControl mMirrorWindowControl; @@ -137,11 +145,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold WindowMagnificationController(@UiContext Context context, @NonNull Handler handler, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, - @NonNull WindowMagnifierCallback callback) { + @NonNull WindowMagnifierCallback callback, SysUiState sysUiState) { mContext = context; mHandler = handler; mSfVsyncFrameProvider = sfVsyncFrameProvider; mWindowMagnifierCallback = callback; + mSysUiState = sysUiState; final Display display = mContext.getDisplay(); mDisplayId = mContext.getDisplayId(); @@ -170,13 +179,18 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMirrorViewRunnable = () -> { if (mMirrorView != null) { mMirrorView.getBoundsOnScreen(mMirrorViewBounds); + updateSystemUIStateIfNeeded(); mWindowMagnifierCallback.onWindowMagnifierBoundsChanged( mDisplayId, mMirrorViewBounds); } }; mMirrorViewLayoutChangeListener = - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + if (!mHandler.hasCallbacks(mMirrorViewRunnable)) { mHandler.post(mMirrorViewRunnable); + } + }; + mMirrorSurfaceViewLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> applyTapExcludeRegion(); @@ -199,6 +213,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMirrorView.setStateDescription(formatStateDescription(mScale)); } }; + mWindowInsetChangeRunnable = this::onWindowInsetChanged; } private void updateDimensions() { @@ -210,7 +225,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold R.dimen.magnification_drag_view_size); mOuterBorderSize = mResources.getDimensionPixelSize( R.dimen.magnification_outer_border_margin); - updateNavigationBarDimensions(); } private void computeBounceAnimationScale() { @@ -220,16 +234,16 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE); } - private void updateNavigationBarDimensions() { - if (!supportsSwipeUpGesture()) { - mNavGestureHeight = 0; - return; + private boolean updateSystemGestureInsetsTop() { + final WindowMetrics windowMetrics = mWm.getCurrentWindowMetrics(); + final Insets insets = windowMetrics.getWindowInsets().getInsets(systemGestures()); + final int gestureTop = + insets.bottom != 0 ? windowMetrics.getBounds().bottom - insets.bottom : -1; + if (gestureTop != mSystemGestureTop) { + mSystemGestureTop = gestureTop; + return true; } - mNavGestureHeight = (mWindowBounds.width() > mWindowBounds.height()) - ? mResources.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height_landscape) - : mResources.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_gesture_height); + return false; } /** @@ -255,6 +269,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold if (mMirrorWindowControl != null) { mMirrorWindowControl.destroyControl(); } + updateSystemUIStateIfNeeded(); } /** @@ -277,6 +292,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } + private void updateSystemUIStateIfNeeded() { + updateSysUIState(false); + } + private void updateAccessibilityWindowTitleIfNeeded() { if (!isWindowVisible()) return; LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams(); @@ -284,13 +303,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWm.updateViewLayout(mMirrorView, params); } - /** Handles MirrorWindow position when the navigation bar mode changed. */ - public void onNavigationModeChanged(int mode) { - mNavBarMode = mode; - updateNavigationBarDimensions(); - updateMirrorViewLayout(); - } - /** Handles MirrorWindow position when the device rotation changed. */ private void onRotate() { final Display display = mContext.getDisplay(); @@ -299,7 +311,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold setMagnificationFrameBoundary(); mRotation = display.getRotation(); - updateNavigationBarDimensions(); if (!isWindowVisible()) { return; @@ -351,6 +362,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + params.receiveInsetsIgnoringZOrder = true; params.setTitle(mContext.getString(R.string.magnification_window_title)); params.accessibilityTitle = getAccessibilityWindowTitle(); @@ -368,6 +380,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener); mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate()); + mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> { + if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) { + mHandler.post(mWindowInsetChangeRunnable); + } + return v.onApplyWindowInsets(insets); + }); mWm.addView(mMirrorView, params); @@ -377,6 +395,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold addDragTouchListeners(); } + private void onWindowInsetChanged() { + if (updateSystemGestureInsetsTop()) { + updateSystemUIStateIfNeeded(); + } + } + private void applyTapExcludeRegion() { final Region tapExcludeRegion = calculateTapExclude(); final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken()); @@ -464,17 +488,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return; } final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth(); - final int maxMirrorViewY = - mWindowBounds.height() - mMirrorView.getHeight() - mNavGestureHeight; + final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight(); + LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams(); params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; - // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not - // overlap nav bar window to prevent window-dragging obscured. - if (supportsSwipeUpGesture()) { - params.y = Math.min(params.y, maxMirrorViewY); - } // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView // able to move close to the screen edges. @@ -508,6 +527,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } + public void updateSysUIStateFlag() { + updateSysUIState(true); + } + /** * Calculates the desired source bounds. This will be the area under from the center of the * displayFrame, factoring in scale. @@ -569,6 +592,16 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } + private void updateSysUIState(boolean force) { + final boolean overlap = isWindowVisible() && mSystemGestureTop > 0 + && mMirrorViewBounds.bottom > mSystemGestureTop; + if (force || overlap != mOverlapWithGestureInsets) { + mOverlapWithGestureInsets = overlap; + mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, mOverlapWithGestureInsets) + .commitUpdate(mDisplayId); + } + } + @Override public void surfaceCreated(SurfaceHolder holder) { createMirror(); @@ -676,10 +709,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return mMirrorView != null; } - private boolean supportsSwipeUpGesture() { - return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL; - } - private CharSequence formatStateDescription(float scale) { // Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed // non-null, so the first time this is called we will always get the appropriate @@ -722,6 +751,14 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold scaleAnimator.start(); } + public void dump(PrintWriter pw) { + pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):"); + pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); + pw.println(" mScale:" + mScale); + pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mSystemGestureTop:" + mSystemGestureTop); + } + private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate { @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java index 1d9eaae4f2332..77286b11f652e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java @@ -37,7 +37,8 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.model.SysUiState; +import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.CommandQueue; import org.junit.Before; @@ -67,9 +68,12 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { @Mock private ModeSwitchesController mModeSwitchesController; @Mock - private NavigationModeController mNavigationModeController; + private SysUiState mSysUiState; @Mock private IRemoteMagnificationAnimationCallback mAnimationCallback; + @Mock + private OverviewProxyService mOverviewProxyService; + private IWindowMagnificationConnection mIWindowMagnificationConnection; private WindowMagnification mWindowMagnification; @@ -83,8 +87,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { }).when(mAccessibilityManager).setWindowMagnificationConnection( any(IWindowMagnificationConnection.class)); mWindowMagnification = new WindowMagnification(getContext(), - getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController, - mNavigationModeController); + getContext().getMainThreadHandler(), mCommandQueue, + mModeSwitchesController, mSysUiState, mOverviewProxyService); mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier( mContext.getSystemService(DisplayManager.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 4e4c33a27da80..045fb57f11f92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -41,6 +41,7 @@ import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; +import com.android.systemui.model.SysUiState; import org.junit.After; import org.junit.Before; @@ -83,6 +84,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { IRemoteMagnificationAnimationCallback mAnimationCallback; @Mock IRemoteMagnificationAnimationCallback mAnimationCallback2; + @Mock + SysUiState mSysUiState; private SpyWindowMagnificationController mController; private WindowMagnificationController mSpyController; private WindowMagnificationAnimationController mWindowMagnificationAnimationController; @@ -98,7 +101,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; mController = new SpyWindowMagnificationController(mContext, mHandler, mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), - mWindowMagnifierCallback); + mWindowMagnifierCallback, mSysUiState); mSpyController = mController.getSpyController(); mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( mContext, mController, newValueAnimator()); @@ -394,6 +397,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mSpyController).onConfigurationChanged(100); } + @Test + public void updateSysUiStateFlag_passThrough() { + mWindowMagnificationAnimationController.updateSysUiStateFlag(); + + verify(mSpyController).updateSysUIStateFlag(); + } + private void verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY) { assertEquals(expectedScale, mController.getScale(), 0f); @@ -440,9 +450,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SpyWindowMagnificationController(Context context, Handler handler, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, - WindowMagnifierCallback callback) { + WindowMagnifierCallback callback, SysUiState sysUiState) { super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction, - callback); + callback, sysUiState); mSpyController = Mockito.mock(WindowMagnificationController.class); } 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 7d617db6cc0e7..b8734df678d60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -17,12 +17,15 @@ package com.android.systemui.accessibility; import static android.view.Choreographer.FrameCallback; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; + import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItems; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -33,6 +36,9 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -40,49 +46,60 @@ import android.app.Instrumentation; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.Rect; +import android.graphics.Region; import android.os.Handler; import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableResources; +import android.text.TextUtils; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowMetrics; import android.view.accessibility.AccessibilityNodeInfo; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; +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; +import com.android.systemui.util.leak.ReferenceTestUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@SmallTest +@LargeTest @RunWith(AndroidTestingRunner.class) public class WindowMagnificationControllerTest extends SysuiTestCase { + private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000; @Mock - Handler mHandler; + private Handler mHandler; @Mock - SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; + private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @Mock - MirrorWindowControl mMirrorWindowControl; - @Mock - WindowMagnifierCallback mWindowMagnifierCallback; - @Mock - SurfaceControl.Transaction mTransaction; + private MirrorWindowControl mMirrorWindowControl; @Mock + private WindowMagnifierCallback mWindowMagnifierCallback; + @Mock (answer = Answers.RETURNS_DEEP_STUBS) + private SurfaceControl.Transaction mTransaction; private WindowManager mWindowManager; + private SysUiState mSysUiState = new SysUiState(); private Resources mResources; private WindowMagnificationController mWindowMagnificationController; private Instrumentation mInstrumentation; @@ -93,37 +110,30 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mContext = Mockito.spy(getContext()); mInstrumentation = InstrumentationRegistry.getInstrumentation(); - WindowManager wm = mContext.getSystemService(WindowManager.class); - doAnswer(invocation -> - wm.getMaximumWindowMetrics() - ).when(mWindowManager).getMaximumWindowMetrics(); - doAnswer(invocation -> - wm.getCurrentWindowMetrics() - ).when(mWindowManager).getCurrentWindowMetrics(); + final WindowManager wm = mContext.getSystemService(WindowManager.class); + mWindowManager = spy(new TestableWindowManager(wm)); + mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); - doAnswer(invocation -> { - mMirrorView = invocation.getArgument(0); - WindowManager.LayoutParams lp = invocation.getArgument(1); - mMirrorView.setLayoutParams(lp); - return null; - }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class)); - doAnswer(invocation -> { - mMirrorView = null; - return null; - }).when(mWindowManager).removeView(any(View.class)); doAnswer(invocation -> { FrameCallback callback = invocation.getArgument(0); callback.doFrame(0); return null; }).when(mSfVsyncFrameProvider).postFrameCallback( any(FrameCallback.class)); - when(mTransaction.remove(any())).thenReturn(mTransaction); - when(mTransaction.setGeometry(any(), any(), any(), - anyInt())).thenReturn(mTransaction); + doAnswer(invocation -> { + final Runnable runnable = invocation.getArgument(0); + runnable.run(); + return null; + }).when(mHandler).post( + any(Runnable.class)); + + mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class)); + mResources = getContext().getOrCreateTestableResources().getResources(); mWindowMagnificationController = new WindowMagnificationController(mContext, mHandler, mSfVsyncFrameProvider, - mMirrorWindowControl, mTransaction, mWindowMagnifierCallback); + mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState); + verify(mMirrorWindowControl).setWindowDelegate( any(MirrorWindowControl.MirrorWindowDelegate.class)); } @@ -135,12 +145,21 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } @Test - public void enableWindowMagnification_showControl() { + public void enableWindowMagnification_showControlAndNotifyBoundsChanged() { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); }); + verify(mMirrorWindowControl).showControl(); + ArgumentCaptor boundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, + timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onWindowMagnifierBoundsChanged( + eq(mContext.getDisplayId()), boundsCaptor.capture()); + final Rect actualBounds = new Rect(); + mMirrorView.getBoundsOnScreen(actualBounds); + assertEquals(actualBounds, boundsCaptor.getValue()); + } @Test @@ -157,6 +176,25 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { verify(mMirrorWindowControl).destroyControl(); } + @Test + public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() { + final WindowManager wm = mContext.getSystemService(WindowManager.class); + final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + bounds.bottom); + }); + ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.deleteWindowMagnification(); + }); + + verify(mMirrorWindowControl).destroyControl(); + assertFalse(hasMagnificationOverlapFlag()); + } + @Test public void moveMagnifier_schedulesFrame() { mInstrumentation.runOnMainSync(() -> { @@ -210,7 +248,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { }); assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); - verify(mWindowManager).updateViewLayout(any(), any()); + // The first invocation is called when the surface is created. + verify(mWindowManager, times(2)).updateViewLayout(any(), any()); } @Test @@ -317,17 +356,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId)); } - @Test - public void onNavigationModeChanged_updateMirrorViewLayout() { - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, - Float.NaN); - mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL); - }); - - verify(mWindowManager).updateViewLayout(eq(mMirrorView), any()); - } - @Test public void enableWindowMagnification_hasA11yWindowTitle() { mInstrumentation.runOnMainSync(() -> { @@ -353,16 +381,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { final TestableResources testableResources = getContext().getOrCreateTestableResources(); testableResources.addOverride(com.android.internal.R.string.android_system_label, newA11yWindowTitle); - when(mContext.getResources()).thenReturn(testableResources.getResources()); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); }); - ArgumentCaptor paramsArgumentCaptor = ArgumentCaptor.forClass( - WindowManager.LayoutParams.class); - verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture()); - assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle); + assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle())); } @Test @@ -386,4 +410,95 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } fail("mMirrorView scale is not changed"); } + + @Test + public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() { + final WindowManager wm = mContext.getSystemService(WindowManager.class); + final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifier(0, bounds.height()); + }); + + ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag()); + } + + private CharSequence getAccessibilityWindowTitle() { + if (mMirrorView == null) { + return null; + } + WindowManager.LayoutParams layoutParams = + (WindowManager.LayoutParams) mMirrorView.getLayoutParams(); + return layoutParams.accessibilityTitle; + } + + private boolean hasMagnificationOverlapFlag() { + return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0; + } + + private class TestableWindowManager implements WindowManager { + + private final WindowManager mWindowManager; + + TestableWindowManager(WindowManager windowManager) { + mWindowManager = windowManager; + } + + @Override + public Display getDefaultDisplay() { + return mWindowManager.getDefaultDisplay(); + } + + @Override + public void removeViewImmediate(View view) { + mWindowManager.removeViewImmediate(view); + } + + @Override + public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) { + mWindowManager.requestAppKeyboardShortcuts(receiver, deviceId); + } + + @Override + public Region getCurrentImeTouchRegion() { + return mWindowManager.getCurrentImeTouchRegion(); + } + + @Override + public void addView(View view, ViewGroup.LayoutParams params) { + mMirrorView = view; + mWindowManager.addView(view, params); + } + + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { + mWindowManager.updateViewLayout(view, params); + } + + @Override + public void removeView(View view) { + mMirrorView = null; + mWindowManager.removeView(view); + } + + @Override + public WindowMetrics getCurrentWindowMetrics() { + final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10); + final WindowInsets insets = new WindowInsets.Builder() + .setInsets(systemGestures(), systemGesturesInsets) + .build(); + final WindowMetrics windowMetrics = new WindowMetrics( + mWindowManager.getCurrentWindowMetrics().getBounds(), insets); + return windowMetrics; + } + + @Override + public WindowMetrics getMaximumWindowMetrics() { + return mWindowManager.getMaximumWindowMetrics(); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java index 78331143af94d..6ef7cc3f0af8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java @@ -16,15 +16,21 @@ package com.android.systemui.accessibility; +import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.hardware.display.DisplayManager; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -36,12 +42,14 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.model.SysUiState; +import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.CommandQueue; 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; @@ -50,17 +58,21 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper public class WindowMagnificationTest extends SysuiTestCase { + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; @Mock private AccessibilityManager mAccessibilityManager; @Mock private ModeSwitchesController mModeSwitchesController; @Mock - private NavigationModeController mNavigationModeController; + private SysUiState mSysUiState; @Mock private IWindowMagnificationConnectionCallback mConnectionCallback; + @Mock + private OverviewProxyService mOverviewProxyService; + private CommandQueue mCommandQueue; private WindowMagnification mWindowMagnification; - + private OverviewProxyListener mOverviewProxyListener; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -72,11 +84,18 @@ public class WindowMagnificationTest extends SysuiTestCase { }).when(mAccessibilityManager).setWindowMagnificationConnection( any(IWindowMagnificationConnection.class)); + when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); + mCommandQueue = new CommandQueue(getContext()); mWindowMagnification = new WindowMagnification(getContext(), getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController, - mNavigationModeController); + mSysUiState, mOverviewProxyService); mWindowMagnification.start(); + + final ArgumentCaptor listenerArgumentCaptor = + ArgumentCaptor.forClass(OverviewProxyListener.class); + verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture()); + mOverviewProxyListener = listenerArgumentCaptor.getValue(); } @Test @@ -99,10 +118,9 @@ public class WindowMagnificationTest extends SysuiTestCase { mCommandQueue.requestWindowMagnificationConnection(true); waitForIdleSync(); - mWindowMagnification.onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY, testBounds); + mWindowMagnification.onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds); - verify(mConnectionCallback).onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY, - testBounds); + verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds); } @Test @@ -111,10 +129,9 @@ public class WindowMagnificationTest extends SysuiTestCase { mCommandQueue.requestWindowMagnificationConnection(true); waitForIdleSync(); - mWindowMagnification.onPerformScaleAction(Display.DEFAULT_DISPLAY, newScale); + mWindowMagnification.onPerformScaleAction(TEST_DISPLAY, newScale); - verify(mConnectionCallback).onPerformScaleAction(eq(Display.DEFAULT_DISPLAY), - eq(newScale)); + verify(mConnectionCallback).onPerformScaleAction(TEST_DISPLAY, newScale); } @Test @@ -122,9 +139,9 @@ public class WindowMagnificationTest extends SysuiTestCase { mCommandQueue.requestWindowMagnificationConnection(true); waitForIdleSync(); - mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY); + mWindowMagnification.onAccessibilityActionPerformed(TEST_DISPLAY); - verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY)); + verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY); } @Test @@ -135,4 +152,42 @@ public class WindowMagnificationTest extends SysuiTestCase { verify(mModeSwitchesController).onConfigurationChanged(anyInt()); } + + @Test + public void overviewProxyIsConnected_noController_resetFlag() { + mOverviewProxyListener.onConnectionChanged(true); + + verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false); + verify(mSysUiState).commitUpdate(mContext.getDisplayId()); + } + + @Test + public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() { + final WindowMagnificationAnimationController mController = mock( + WindowMagnificationAnimationController.class); + mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier( + mContext.getSystemService(DisplayManager.class), mController); + mWindowMagnification.mAnimationControllerSupplier.get(TEST_DISPLAY); + + mOverviewProxyListener.onConnectionChanged(true); + + verify(mController).updateSysUiStateFlag(); + } + + private static class FakeAnimationControllerSupplier extends + DisplayIdIndexSupplier { + + private final WindowMagnificationAnimationController mController; + + FakeAnimationControllerSupplier(DisplayManager displayManager, + WindowMagnificationAnimationController controller) { + super(displayManager); + mController = controller; + } + + @Override + protected WindowMagnificationAnimationController createInstance(Display display) { + return mController; + } + } }