Merge changes from topic "magnification_switch" into sc-dev am: b23e8bc3c3
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14060607 Change-Id: I560d52e9c7833c62400e0b49eb1883407479e136
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,30 @@ 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;
|
||||
private boolean mToLeftScreenEdge = 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 +116,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 +151,17 @@ 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) {
|
||||
stickToScreenEdge(mToLeftScreenEdge);
|
||||
// Reset button to make its window layer always above the mirror window.
|
||||
removeButton();
|
||||
showButton(mMagnificationMode, /* resetPosition= */false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onTouch(View v, MotionEvent event) {
|
||||
@@ -168,6 +192,12 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
|
||||
|
||||
@Override
|
||||
public boolean onFinish(float xOffset, float yOffset) {
|
||||
if (mIsVisible) {
|
||||
final int windowWidth = mWindowManager.getCurrentWindowMetrics().getBounds().width();
|
||||
final int halfWindowWidth = windowWidth / 2;
|
||||
mToLeftScreenEdge = (mParams.x < halfWindowWidth);
|
||||
stickToScreenEdge(mToLeftScreenEdge);
|
||||
}
|
||||
if (!mSingleTapDetected) {
|
||||
showButton(mMagnificationMode);
|
||||
}
|
||||
@@ -175,10 +205,18 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
|
||||
return true;
|
||||
}
|
||||
|
||||
private void stickToScreenEdge(boolean toLeftScreenEdge) {
|
||||
mParams.x = toLeftScreenEdge
|
||||
? mDraggableWindowBounds.left : mDraggableWindowBounds.right;
|
||||
updateButtonViewLayoutIfNeeded();
|
||||
}
|
||||
|
||||
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 +231,32 @@ 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;
|
||||
mToLeftScreenEdge = false;
|
||||
}
|
||||
mWindowManager.addView(mImageView, mParams);
|
||||
// Exclude magnification switch button from system gesture area.
|
||||
setSystemGestureExclusion();
|
||||
@@ -229,12 +283,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 +292,25 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
|
||||
}
|
||||
}
|
||||
|
||||
private void onWindowInsetChanged() {
|
||||
final Rect newBounds = getDraggableWindowBounds();
|
||||
if (mDraggableWindowBounds.equals(newBounds)) {
|
||||
return;
|
||||
}
|
||||
mDraggableWindowBounds.set(newBounds);
|
||||
stickToScreenEdge(mToLeftScreenEdge);
|
||||
}
|
||||
|
||||
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 +351,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 +390,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;
|
||||
|
||||
@@ -69,6 +80,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
@@ -77,6 +89,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 +99,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 +112,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 +124,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 +199,64 @@ 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());
|
||||
InOrder inOrder = Mockito.inOrder(mWindowManager);
|
||||
inOrder.verify(mWindowManager).updateViewLayout(eq(mSpyImageView), any());
|
||||
inOrder.verify(mWindowManager).removeView(eq(mSpyImageView));
|
||||
inOrder.verify(mWindowManager).addView(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(/* toLeftScreenEdge= */ false);
|
||||
}
|
||||
|
||||
@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 onDraggingGestureFinish_buttonIsShowing_stickToRightEdge() {
|
||||
final int windowHalfWidth =
|
||||
mWindowManager.getCurrentWindowMetrics().getBounds().width() / 2;
|
||||
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
|
||||
|
||||
// Drag button to right side on screen
|
||||
final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10;
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
|
||||
downTime, 0, ACTION_DOWN, 100, 100));
|
||||
mTouchListener.onTouch(mSpyImageView,
|
||||
obtainMotionEvent(downTime, downTime, ACTION_MOVE, windowHalfWidth - offset, 100));
|
||||
|
||||
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
|
||||
downTime, downTime, ACTION_UP, windowHalfWidth - offset, 100));
|
||||
|
||||
assertLayoutPosition(/* toLeftScreenEdge= */false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() {
|
||||
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
|
||||
@@ -344,12 +410,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 +427,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) {
|
||||
@@ -374,7 +440,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private void assertShowFadingAnimation(float alpha) {
|
||||
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
if (alpha == FADE_IN_ALPHA) { // Fade-in
|
||||
verify(mSpyImageView).postOnAnimation(runnableCaptor.capture());
|
||||
} else { // Fade-out
|
||||
@@ -392,6 +458,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 +513,16 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
|
||||
mFadeOutAnimation.run();
|
||||
mFadeOutAnimation = null;
|
||||
}
|
||||
|
||||
private void assertLayoutPosition(boolean toLeftScreenEdge) {
|
||||
final int expectedX =
|
||||
toLeftScreenEdge ? mMagnificationModeSwitch.mDraggableWindowBounds.left
|
||||
: 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