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:
@@ -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>
|
||||
|
||||
@@ -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())));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user