Makes magnification switch UI never overlap navigationBar and statusBar

This change contrains the x,y postion of switch button to make
the button never overlap navigation bar window and status bar window.
When the orientation is changed or navigation mode is change, the
position of switch button should be ajusted by the draggable bounds
because the window insets are updated.

Bug: 185288922
Test: atest MagnificationModeSwitchTest; atest ModeSwitchesControllerTest;
Change-Id: Idc03275f39418c18a190cdff6ffb0e94280c6281
This commit is contained in:
mincheli
2021-04-22 13:42:47 +08:00
parent 1d2868c45b
commit 732aed15d7
5 changed files with 311 additions and 140 deletions

View File

@@ -1293,7 +1293,7 @@
<dimen name="magnification_drag_view_size">36dp</dimen>
<dimen name="magnification_controls_size">90dp</dimen>
<dimen name="magnification_switch_button_size">48dp</dimen>
<dimen name="magnification_switch_button_padding">6dp</dimen>
<dimen name="magnification_switch_button_margin">16dp</dimen>
<dimen name="magnifier_left_right_controls_width">35dp</dimen>
<dimen name="magnifier_left_right_controls_height">45dp</dimen>
<dimen name="magnifier_up_down_controls_width">45dp</dimen>

View File

@@ -22,14 +22,17 @@ import android.annotation.NonNull;
import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityManager;
@@ -38,6 +41,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import java.util.Collections;
@@ -45,8 +49,8 @@ import java.util.Collections;
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
* The button icon is movable by dragging. And the button UI would automatically be dismissed after
* displaying for a period of time.
* The button icon is movable by dragging and it would not overlap navigation bar window.
* And the button UI would automatically be dismissed after displaying for a period of time.
*/
class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener {
@@ -64,25 +68,29 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
private final ImageView mImageView;
private final Runnable mWindowInsetChangeRunnable;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
private final LayoutParams mParams;
@VisibleForTesting
final Rect mDraggableWindowBounds = new Rect();
private boolean mIsVisible = false;
private final MagnificationGestureDetector mGestureDetector;
private boolean mSingleTapDetected = false;
MagnificationModeSwitch(@UiContext Context context) {
this(context, createView(context));
this(context, createView(context), new SfVsyncFrameCallbackProvider());
}
@VisibleForTesting
MagnificationModeSwitch(Context context, @NonNull ImageView imageView) {
MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
mContext = context;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
mWindowManager = mContext.getSystemService(WindowManager.class);
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mParams = createLayoutParams(context);
mImageView = imageView;
applyResourcesValues();
mImageView.setOnTouchListener(this::onTouch);
mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -107,6 +115,15 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
return super.performAccessibilityAction(host, action, args);
}
});
mWindowInsetChangeRunnable = this::onWindowInsetChanged;
mImageView.setOnApplyWindowInsetsListener((v, insets) -> {
// Adds a pending post check to avoiding redundant calculation because this callback
// is sent frequently when the switch icon window dragged by the users.
if (!mImageView.getHandler().hasCallbacks(mWindowInsetChangeRunnable)) {
mImageView.getHandler().post(mWindowInsetChangeRunnable);
}
return v.onApplyWindowInsets(insets);
});
mFadeInAnimationTask = () -> {
mImageView.animate()
@@ -133,11 +150,16 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
return mContext.getResources().getString(stringId);
}
private void applyResourcesValues() {
final int padding = mContext.getResources().getDimensionPixelSize(
R.dimen.magnification_switch_button_padding);
mImageView.setPadding(padding, padding, padding, padding);
mImageView.setImageResource(getIconResId(mMagnificationMode));
private void applyResourcesValuesWithDensityChanged() {
final int size = mContext.getResources().getDimensionPixelSize(
R.dimen.magnification_switch_button_size);
mParams.height = size;
mParams.width = size;
if (mIsVisible) {
mWindowManager.updateViewLayout(mImageView, mParams);
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
}
}
private boolean onTouch(View v, MotionEvent event) {
@@ -176,9 +198,11 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
private void moveButton(float offsetX, float offsetY) {
mParams.x -= offsetX;
mParams.y -= offsetY;
mWindowManager.updateViewLayout(mImageView, mParams);
mSfVsyncFrameProvider.postFrameCallback(l -> {
mParams.x += offsetX;
mParams.y += offsetY;
updateButtonViewLayoutIfNeeded();
});
}
void removeButton() {
@@ -193,16 +217,31 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mImageView.setAlpha(0f);
mWindowManager.removeView(mImageView);
mIsVisible = false;
mParams.x = 0;
mParams.y = 0;
}
void showButton(int mode) {
showButton(mode, true);
}
/**
* Shows magnification switch button for the specified magnification mode.
* When the button is going to be visible by calling this method, the layout position can be
* reset depending on the flag.
*
* @param mode The magnification mode
* @param resetPosition if the button position needs be reset
*/
private void showButton(int mode, boolean resetPosition) {
if (mMagnificationMode != mode) {
mMagnificationMode = mode;
mImageView.setImageResource(getIconResId(mode));
}
if (!mIsVisible) {
if (resetPosition) {
mDraggableWindowBounds.set(getDraggableWindowBounds());
mParams.x = mDraggableWindowBounds.right;
mParams.y = mDraggableWindowBounds.bottom;
}
mWindowManager.addView(mImageView, mParams);
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
@@ -229,12 +268,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
void onConfigurationChanged(int configDiff) {
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
applyResourcesValues();
if (mIsVisible) {
mWindowManager.updateViewLayout(mImageView, mParams);
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
}
applyResourcesValuesWithDensityChanged();
return;
}
if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
@@ -243,6 +277,25 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
}
private void onWindowInsetChanged() {
final Rect newBounds = getDraggableWindowBounds();
if (mDraggableWindowBounds.equals(newBounds)) {
return;
}
mDraggableWindowBounds.set(newBounds);
updateButtonViewLayoutIfNeeded();
}
private void updateButtonViewLayoutIfNeeded() {
if (mIsVisible) {
mParams.x = MathUtils.constrain(mParams.x, mDraggableWindowBounds.left,
mDraggableWindowBounds.right);
mParams.y = MathUtils.constrain(mParams.y, mDraggableWindowBounds.top,
mDraggableWindowBounds.bottom);
mWindowManager.updateViewLayout(mImageView, mParams);
}
}
private void updateAccessibilityWindowTitle() {
mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
if (mIsVisible) {
@@ -283,17 +336,34 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
private static LayoutParams createLayoutParams(Context context) {
final int size = context.getResources().getDimensionPixelSize(
R.dimen.magnification_switch_button_size);
final LayoutParams params = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
size,
size,
LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
params.gravity = Gravity.TOP | Gravity.LEFT;
params.accessibilityTitle = getAccessibilityWindowTitle(context);
return params;
}
private Rect getDraggableWindowBounds() {
final int layoutMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.magnification_switch_button_margin);
final Rect boundRect = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
final Insets systemBars =
mWindowManager.getCurrentWindowMetrics().getWindowInsets()
.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
final Rect insets = new Rect(layoutMargin,
systemBars.top + layoutMargin,
mParams.width + layoutMargin,
mParams.height + layoutMargin + systemBars.bottom);
boundRect.inset(insets);
return boundRect;
}
private static String getAccessibilityWindowTitle(Context context) {
return context.getString(com.android.internal.R.string.android_system_label);
}
@@ -305,5 +375,4 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
new Rect(0, 0, mImageView.getWidth(), mImageView.getHeight())));
});
}
}

View File

@@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
import static com.android.systemui.accessibility.MagnificationModeSwitch.DEFAULT_FADE_OUT_ANIMATION_DELAY_MS;
@@ -33,6 +34,7 @@ import static junit.framework.Assert.assertNotNull;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -41,19 +43,27 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -61,6 +71,7 @@ import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -77,6 +88,7 @@ import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class MagnificationModeSwitchTest extends SysuiTestCase {
private static final float FADE_IN_ALPHA = 1f;
@@ -86,7 +98,10 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
private WindowManager mWindowManager;
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private Handler mHandler;
private TestableWindowManager mWindowManager;
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
private View.OnTouchListener mTouchListener;
@@ -96,10 +111,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
WindowManager wm = mContext.getSystemService(WindowManager.class);
doAnswer(invocation ->
wm.getMaximumWindowMetrics()
).when(mWindowManager).getMaximumWindowMetrics();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
mSpyImageView = Mockito.spy(new ImageView(mContext));
@@ -110,7 +123,14 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
return null;
}).when(mSpyImageView).setOnTouchListener(
any(View.OnTouchListener.class));
mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
doAnswer(invocation -> {
Choreographer.FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(Choreographer.FrameCallback.class));
mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView,
mSfVsyncFrameProvider);
assertNotNull(mTouchListener);
}
@@ -178,19 +198,41 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
}
@Test
public void onConfigurationChanged_buttonIsShowing_updateResourcesAndLayout() {
public void onDensityChanged_buttonIsShowing_updateResourcesAndLayout() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
resetAndStubMockImageViewAndAnimator();
mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
verify(mSpyImageView).setPadding(anyInt(), anyInt(), anyInt(), anyInt());
verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
verify(mWindowManager).updateViewLayout(eq(mSpyImageView), any());
verify(mSpyImageView).setSystemGestureExclusionRects(any(List.class));
}
@Test
public void onApplyWindowInsetsWithBoundsChange_buttonIsShowing_updateLayoutPosition() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationModeSwitch.mDraggableWindowBounds.inset(10, 10);
mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED);
verify(mWindowManager).updateViewLayout(eq(mSpyImageView),
any(WindowManager.LayoutParams.class));
assertLayoutPosition();
}
@Test
public void onApplyWindowInsetsWithWindowInsetsChange_buttonIsShowing_draggableBoundsChanged() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds);
mWindowManager.setWindowInsets(new WindowInsets.Builder()
.setInsetsIgnoringVisibility(systemBars(), Insets.of(0, 20, 0, 20))
.build());
mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED);
assertNotEquals(oldDraggableBounds, mMagnificationModeSwitch.mDraggableWindowBounds);
}
@Test
public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -344,12 +386,12 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void showButton_hasAccessibilityWindowTitle() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
WindowManager.LayoutParams.class);
verify(mWindowManager).addView(eq(mSpyImageView), paramsArgumentCaptor.capture());
final WindowManager.LayoutParams layoutPrams =
mWindowManager.getLayoutParamsFromAttachedView();
assertNotNull(layoutPrams);
assertEquals(getContext().getResources().getString(
com.android.internal.R.string.android_system_label),
paramsArgumentCaptor.getValue().accessibilityTitle);
layoutPrams.accessibilityTitle);
}
@Test
@@ -361,10 +403,10 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
com.android.internal.R.string.android_system_label, newA11yWindowTitle);
mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
WindowManager.LayoutParams.class);
verify(mWindowManager).updateViewLayout(eq(mSpyImageView), paramsArgumentCaptor.capture());
assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
final WindowManager.LayoutParams layoutParams =
mWindowManager.getLayoutParamsFromAttachedView();
assertNotNull(layoutParams);
assertEquals(newA11yWindowTitle, layoutParams.accessibilityTitle);
}
private void assertModeUnchanged(int expectedMode) {
@@ -392,6 +434,13 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private void resetAndStubMockImageViewAndAnimator() {
resetAndStubMockAnimator();
Mockito.reset(mSpyImageView);
final Handler handler = mock(Handler.class);
when(mSpyImageView.getHandler()).thenReturn(handler);
doAnswer(invocation -> {
final Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
}).when(handler).post(any(Runnable.class));
doAnswer(invocation -> {
final Runnable runnable = invocation.getArgument(0);
runnable.run();
@@ -440,4 +489,14 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mFadeOutAnimation.run();
mFadeOutAnimation = null;
}
private void assertLayoutPosition() {
final int expectedX = mMagnificationModeSwitch.mDraggableWindowBounds.right;
final int expectedY = mMagnificationModeSwitch.mDraggableWindowBounds.bottom;
final WindowManager.LayoutParams layoutParams =
mWindowManager.getLayoutParamsFromAttachedView();
assertNotNull(layoutParams);
assertEquals(expectedX, layoutParams.x);
assertEquals(expectedY, layoutParams.y);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.accessibility;
import static android.view.WindowInsets.Type.systemGestures;
import android.graphics.Insets;
import android.graphics.Region;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
public class TestableWindowManager implements WindowManager {
private final WindowManager mWindowManager;
private View mView;
private WindowInsets mWindowInsets = null;
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(WindowManager.KeyboardShortcutsReceiver receiver,
int deviceId) {
mWindowManager.requestAppKeyboardShortcuts(receiver, deviceId);
}
@Override
public Region getCurrentImeTouchRegion() {
return mWindowManager.getCurrentImeTouchRegion();
}
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mView = view;
mWindowManager.addView(view, params);
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
mWindowManager.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mView = 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(),
mWindowInsets == null ? insets : mWindowInsets);
return windowMetrics;
}
@Override
public WindowMetrics getMaximumWindowMetrics() {
return mWindowManager.getMaximumWindowMetrics();
}
public View getAttachedView() {
return mView;
}
public WindowManager.LayoutParams getLayoutParamsFromAttachedView() {
if (mView == null) {
return null;
}
return (WindowManager.LayoutParams) mView.getLayoutParams();
}
public void setWindowInsets(WindowInsets insets) {
mWindowInsets = insets;
}
}

View File

@@ -17,7 +17,6 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
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;
@@ -46,9 +45,7 @@ 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;
@@ -58,10 +55,7 @@ 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;
@@ -96,14 +90,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private MirrorWindowControl mMirrorWindowControl;
@Mock
private WindowMagnifierCallback mWindowMagnifierCallback;
@Mock (answer = Answers.RETURNS_DEEP_STUBS)
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction;
private WindowManager mWindowManager;
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
private View mMirrorView;
@Before
public void setUp() {
@@ -157,7 +150,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onWindowMagnifierBoundsChanged(
eq(mContext.getDisplayId()), boundsCaptor.capture());
final Rect actualBounds = new Rect();
mMirrorView.getBoundsOnScreen(actualBounds);
final View mirrorView = mWindowManager.getAttachedView();
assertNotNull(mirrorView);
mirrorView.getBoundsOnScreen(actualBounds);
assertEquals(actualBounds, boundsCaptor.getValue());
}
@@ -218,7 +213,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mHandler).postDelayed(runnableArgumentCaptor.capture(), anyLong());
runnableArgumentCaptor.getValue().run();
assertThat(mMirrorView.getStateDescription().toString(), containsString("300"));
final View mirrorView = mWindowManager.getAttachedView();
assertNotNull(mirrorView);
assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
}
@Test
@@ -300,10 +297,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
Float.NaN);
});
assertNotNull(mMirrorView);
final View mirrorView = mWindowManager.getAttachedView();
assertNotNull(mirrorView);
final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mMirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
assertNotNull(nodeInfo.getContentDescription());
assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
@@ -323,24 +321,24 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
Float.NaN);
});
assertNotNull(mMirrorView);
final View mirrorView = mWindowManager.getAttachedView();
assertTrue(
mMirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
// Minimum scale is 2.0.
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(2.0f));
assertTrue(mMirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(3.5f));
// TODO: Verify the final state when the mirror surface is visible.
assertTrue(mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
assertTrue(
mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
assertTrue(
mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
assertTrue(
mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
}
@Test
@@ -351,7 +349,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
final View mirrorView = mWindowManager.getAttachedView();
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
}
@@ -363,12 +362,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
WindowManager.LayoutParams.class);
verify(mWindowManager).addView(eq(mMirrorView), paramsArgumentCaptor.capture());
assertEquals(getContext().getResources().getString(
com.android.internal.R.string.android_system_label),
paramsArgumentCaptor.getValue().accessibilityTitle);
com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
}
@Test
@@ -400,15 +395,15 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onSingleTap();
});
final View mirrorView = mWindowManager.getAttachedView();
final long timeout = SystemClock.uptimeMillis() + 1000;
while (SystemClock.uptimeMillis() < timeout) {
SystemClock.sleep(10);
if (Float.compare(1.0f, mMirrorView.getScaleX()) < 0) {
if (Float.compare(1.0f, mirrorView.getScaleX()) < 0) {
return;
}
}
fail("mMirrorView scale is not changed");
fail("MirrorView scale is not changed");
}
@Test
@@ -428,77 +423,16 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
private CharSequence getAccessibilityWindowTitle() {
if (mMirrorView == null) {
return null;
final View mirrorView = mWindowManager.getAttachedView();
if (mirrorView == null) {
return null;
}
WindowManager.LayoutParams layoutParams =
(WindowManager.LayoutParams) mMirrorView.getLayoutParams();
(WindowManager.LayoutParams) mirrorView.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();
}
}
}