Merge "Maintain the position of accessibility floating menu" into sc-dev
This commit is contained in:
@@ -74,7 +74,8 @@ public final class Prefs {
|
||||
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
|
||||
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
|
||||
Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
|
||||
Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP
|
||||
Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP,
|
||||
Key.ACCESSIBILITY_FLOATING_MENU_POSITION
|
||||
})
|
||||
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
|
||||
public @interface Key {
|
||||
@@ -125,6 +126,7 @@ public final class Prefs {
|
||||
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
|
||||
String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
|
||||
"HasSeenAccessibilityFloatingMenuDockTooltip";
|
||||
String ACCESSIBILITY_FLOATING_MENU_POSITION = "AccessibilityFloatingMenuPosition";
|
||||
}
|
||||
|
||||
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
|
||||
|
||||
@@ -28,12 +28,16 @@ import static com.android.systemui.Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MEN
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
|
||||
|
||||
import android.annotation.FloatRange;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.Prefs;
|
||||
@@ -44,7 +48,13 @@ import com.android.systemui.Prefs;
|
||||
public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
|
||||
private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
private static final float DEFAULT_OPACITY_VALUE = 0.55f;
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
private static final float DEFAULT_POSITION_X_PERCENT = 1.0f;
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
private static final float DEFAULT_POSITION_Y_PERCENT = 0.8f;
|
||||
|
||||
private final Context mContext;
|
||||
private final AccessibilityFloatingMenuView mMenuView;
|
||||
private final MigrationTooltipView mMigrationTooltipView;
|
||||
@@ -85,7 +95,10 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
};
|
||||
|
||||
public AccessibilityFloatingMenu(Context context) {
|
||||
this(context, new AccessibilityFloatingMenuView(context));
|
||||
mContext = context;
|
||||
mMenuView = new AccessibilityFloatingMenuView(context, getPosition(context));
|
||||
mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
|
||||
mDockTooltipView = new DockTooltipView(mContext, mMenuView);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -113,7 +126,7 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
getOpacityValue(mContext));
|
||||
mMenuView.setSizeType(getSizeType(mContext));
|
||||
mMenuView.setShapeType(getShapeType(mContext));
|
||||
mMenuView.setOnDragEndListener(this::showDockTooltipIfNecessary);
|
||||
mMenuView.setOnDragEndListener(this::onDragEnd);
|
||||
|
||||
showMigrationTooltipIfNecessary();
|
||||
|
||||
@@ -127,12 +140,25 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
}
|
||||
|
||||
mMenuView.hide();
|
||||
mMenuView.setOnDragEndListener(null);
|
||||
mMigrationTooltipView.hide();
|
||||
mDockTooltipView.hide();
|
||||
|
||||
unregisterContentObservers();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Position getPosition(Context context) {
|
||||
final String absolutePositionString = Prefs.getString(context,
|
||||
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
|
||||
|
||||
if (TextUtils.isEmpty(absolutePositionString)) {
|
||||
return new Position(DEFAULT_POSITION_X_PERCENT, DEFAULT_POSITION_Y_PERCENT);
|
||||
} else {
|
||||
return Position.fromString(absolutePositionString);
|
||||
}
|
||||
}
|
||||
|
||||
// Migration tooltip was the android S feature. It's just used on the Android version from R
|
||||
// to S. In addition, it only shows once.
|
||||
private void showMigrationTooltipIfNecessary() {
|
||||
@@ -150,18 +176,28 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
|
||||
}
|
||||
|
||||
private void onDragEnd(Position position) {
|
||||
savePosition(mContext, position);
|
||||
showDockTooltipIfNecessary(mContext);
|
||||
}
|
||||
|
||||
private void savePosition(Context context, Position position) {
|
||||
Prefs.putString(context, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
|
||||
position.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows tooltip when user drags accessibility floating menu for the first time.
|
||||
*/
|
||||
private void showDockTooltipIfNecessary() {
|
||||
if (!Prefs.get(mContext).getBoolean(
|
||||
private void showDockTooltipIfNecessary(Context context) {
|
||||
if (!Prefs.get(context).getBoolean(
|
||||
HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, false)) {
|
||||
// if the menu is an oval, the user has already dragged it out, so show the tooltip.
|
||||
if (mMenuView.isOvalShape()) {
|
||||
mDockTooltipView.show();
|
||||
}
|
||||
|
||||
Prefs.putBoolean(mContext, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
|
||||
Prefs.putBoolean(context, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import static java.util.Objects.requireNonNull;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.IntDef;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -85,7 +86,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
private static final int FADE_EFFECT_DURATION_MS = 3000;
|
||||
private static final int SNAP_TO_LOCATION_DURATION_MS = 150;
|
||||
private static final int MIN_WINDOW_Y = 0;
|
||||
private static final float LOCATION_Y_PERCENTAGE = 0.8f;
|
||||
|
||||
private static final int ANIMATION_START_OFFSET = 600;
|
||||
private static final int ANIMATION_DURATION_MS = 600;
|
||||
@@ -97,7 +97,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
private boolean mIsDragging = false;
|
||||
private boolean mImeVisibility;
|
||||
@Alignment
|
||||
private int mAlignment = Alignment.RIGHT;
|
||||
private int mAlignment;
|
||||
@SizeType
|
||||
private int mSizeType = SizeType.SMALL;
|
||||
@VisibleForTesting
|
||||
@@ -105,7 +105,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
int mShapeType = ShapeType.OVAL;
|
||||
private int mTemporaryShapeType;
|
||||
@RadiusType
|
||||
private int mRadiusType = RadiusType.LEFT_HALF_OVAL;
|
||||
private int mRadiusType;
|
||||
private int mMargin;
|
||||
private int mPadding;
|
||||
private int mScreenHeight;
|
||||
@@ -118,7 +118,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
private int mRelativeToPointerDownX;
|
||||
private int mRelativeToPointerDownY;
|
||||
private float mRadius;
|
||||
private float mPercentageY = LOCATION_Y_PERCENTAGE;
|
||||
private final Position mPosition;
|
||||
private float mSquareScaledTouchSlop;
|
||||
private final Configuration mLastConfiguration;
|
||||
private Optional<OnDragEndListener> mOnDragEndListener = Optional.empty();
|
||||
@@ -182,25 +182,35 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
interface OnDragEndListener {
|
||||
|
||||
/**
|
||||
* Invoked when the floating menu has dragged end.
|
||||
* Called when a drag is completed.
|
||||
*
|
||||
* @param position Stores information about the position
|
||||
*/
|
||||
void onDragEnd();
|
||||
void onDragEnd(Position position);
|
||||
}
|
||||
|
||||
public AccessibilityFloatingMenuView(Context context) {
|
||||
this(context, new RecyclerView(context));
|
||||
public AccessibilityFloatingMenuView(Context context, @NonNull Position position) {
|
||||
this(context, position, new RecyclerView(context));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AccessibilityFloatingMenuView(Context context,
|
||||
AccessibilityFloatingMenuView(Context context, @NonNull Position position,
|
||||
RecyclerView listView) {
|
||||
super(context);
|
||||
|
||||
mListView = listView;
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mCurrentLayoutParams = createDefaultLayoutParams();
|
||||
mAdapter = new AccessibilityTargetAdapter(mTargets);
|
||||
mUiHandler = createUiHandler();
|
||||
mPosition = position;
|
||||
mAlignment = transformToAlignment(mPosition.getPercentageX());
|
||||
mRadiusType = (mAlignment == Alignment.RIGHT)
|
||||
? RadiusType.LEFT_HALF_OVAL
|
||||
: RadiusType.RIGHT_HALF_OVAL;
|
||||
|
||||
updateDimensions();
|
||||
|
||||
mCurrentLayoutParams = createDefaultLayoutParams();
|
||||
|
||||
mFadeOutAnimator = ValueAnimator.ofFloat(1.0f, mFadeOutValue);
|
||||
mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS);
|
||||
@@ -213,10 +223,11 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
mDragAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAlignment = calculateCurrentAlignment();
|
||||
mPercentageY = calculateCurrentPercentageY();
|
||||
mPosition.update(transformCurrentPercentageXToEdge(),
|
||||
calculateCurrentPercentageY());
|
||||
mAlignment = transformToAlignment(mPosition.getPercentageX());
|
||||
|
||||
updateLocationWith(mAlignment, mPercentageY);
|
||||
updateLocationWith(mPosition);
|
||||
|
||||
updateInsetWith(getResources().getConfiguration().uiMode, mAlignment);
|
||||
|
||||
@@ -227,13 +238,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
|
||||
fadeOut();
|
||||
|
||||
mOnDragEndListener.ifPresent(OnDragEndListener::onDragEnd);
|
||||
mOnDragEndListener.ifPresent(
|
||||
onDragEndListener -> onDragEndListener.onDragEnd(mPosition));
|
||||
}
|
||||
});
|
||||
|
||||
mLastConfiguration = new Configuration(getResources().getConfiguration());
|
||||
|
||||
updateDimensions();
|
||||
initListView();
|
||||
updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment);
|
||||
}
|
||||
@@ -423,7 +434,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
updateRadiusWith(newSizeType, mRadiusType, mTargets.size());
|
||||
|
||||
// When the icon sized changed, the menu size and location will be impacted.
|
||||
updateLocationWith(mAlignment, mPercentageY);
|
||||
updateLocationWith(mPosition);
|
||||
updateScrollModeWith(hasExceededMaxLayoutHeight());
|
||||
updateOffsetWith(mShapeType, mAlignment);
|
||||
setSystemGestureExclusion();
|
||||
@@ -446,14 +457,14 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
fadeOut();
|
||||
}
|
||||
|
||||
public void setOnDragEndListener(OnDragEndListener onDragListener) {
|
||||
mOnDragEndListener = Optional.ofNullable(onDragListener);
|
||||
public void setOnDragEndListener(OnDragEndListener onDragEndListener) {
|
||||
mOnDragEndListener = Optional.ofNullable(onDragEndListener);
|
||||
}
|
||||
|
||||
void startTranslateXAnimation() {
|
||||
fadeIn();
|
||||
|
||||
final float toXValue = mAlignment == Alignment.RIGHT
|
||||
final float toXValue = (mAlignment == Alignment.RIGHT)
|
||||
? ANIMATION_TO_X_VALUE
|
||||
: -ANIMATION_TO_X_VALUE;
|
||||
final TranslateAnimation animation =
|
||||
@@ -581,7 +592,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
final boolean currentImeVisibility = insets.isVisible(ime());
|
||||
if (currentImeVisibility != mImeVisibility) {
|
||||
mImeVisibility = currentImeVisibility;
|
||||
updateLocationWith(mAlignment, mPercentageY);
|
||||
updateLocationWith(mPosition);
|
||||
}
|
||||
|
||||
return insets;
|
||||
@@ -697,8 +708,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
params.receiveInsetsIgnoringZOrder = true;
|
||||
params.windowAnimations = android.R.style.Animation_Translucent;
|
||||
params.gravity = Gravity.START | Gravity.TOP;
|
||||
params.x = getMaxWindowX();
|
||||
params.y = (int) (getMaxWindowY() * mPercentageY);
|
||||
params.x = (mAlignment == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
|
||||
// params.y = (int) (mPosition.getPercentageY() * getMaxWindowY());
|
||||
final int currentLayoutY = (int) (mPosition.getPercentageY() * getMaxWindowY());
|
||||
params.y = Math.max(MIN_WINDOW_Y, currentLayoutY - getInterval());
|
||||
updateAccessibilityTitle(params);
|
||||
return params;
|
||||
}
|
||||
@@ -716,7 +729,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
updateItemViewWith(mSizeType);
|
||||
updateColor();
|
||||
updateStrokeWith(newConfig.uiMode, mAlignment);
|
||||
updateLocationWith(mAlignment, mPercentageY);
|
||||
updateLocationWith(mPosition);
|
||||
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
|
||||
updateScrollModeWith(hasExceededMaxLayoutHeight());
|
||||
setSystemGestureExclusion();
|
||||
@@ -765,9 +778,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
/**
|
||||
* Updates the floating menu to be fixed at the side of the screen.
|
||||
*/
|
||||
private void updateLocationWith(@Alignment int side, float percentageCurrentY) {
|
||||
mCurrentLayoutParams.x = (side == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
|
||||
final int currentLayoutY = (int) (percentageCurrentY * getMaxWindowY());
|
||||
private void updateLocationWith(Position position) {
|
||||
final @Alignment int alignment = transformToAlignment(position.getPercentageX());
|
||||
mCurrentLayoutParams.x = (alignment == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
|
||||
final int currentLayoutY = (int) (position.getPercentageY() * getMaxWindowY());
|
||||
mCurrentLayoutParams.y = Math.max(MIN_WINDOW_Y, currentLayoutY - getInterval());
|
||||
mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
|
||||
}
|
||||
@@ -861,10 +875,17 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
}
|
||||
|
||||
@Alignment
|
||||
private int calculateCurrentAlignment() {
|
||||
return mCurrentLayoutParams.x >= ((getMinWindowX() + getMaxWindowX()) / 2)
|
||||
? Alignment.RIGHT
|
||||
: Alignment.LEFT;
|
||||
private int transformToAlignment(@FloatRange(from = 0.0, to = 1.0) float percentageX) {
|
||||
return (percentageX < 0.5f) ? Alignment.LEFT : Alignment.RIGHT;
|
||||
}
|
||||
|
||||
private float transformCurrentPercentageXToEdge() {
|
||||
final float percentageX = calculateCurrentPercentageX();
|
||||
return (percentageX < 0.5) ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
private float calculateCurrentPercentageX() {
|
||||
return mCurrentLayoutParams.x / (float) getMaxWindowX();
|
||||
}
|
||||
|
||||
private float calculateCurrentPercentageY() {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.floatingmenu;
|
||||
|
||||
import android.annotation.FloatRange;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Stores information about the position, which includes percentage of X-axis of the screen,
|
||||
* percentage of Y-axis of the screen.
|
||||
*/
|
||||
public class Position {
|
||||
|
||||
private static final char STRING_SEPARATOR = ',';
|
||||
private static final TextUtils.SimpleStringSplitter sStringCommaSplitter =
|
||||
new TextUtils.SimpleStringSplitter(STRING_SEPARATOR);
|
||||
|
||||
private float mPercentageX;
|
||||
private float mPercentageY;
|
||||
|
||||
/**
|
||||
* Creates a {@link Position} from a encoded string described in {@link #toString()}.
|
||||
*
|
||||
* @param positionString A string conform to the format described in {@link #toString()}
|
||||
* @return A {@link Position} with the given value retrieved from {@code absolutePositionString}
|
||||
* @throws IllegalArgumentException If {@code positionString} does not conform to the format
|
||||
* described in {@link #toString()}
|
||||
*/
|
||||
public static Position fromString(String positionString) {
|
||||
sStringCommaSplitter.setString(positionString);
|
||||
if (sStringCommaSplitter.hasNext()) {
|
||||
final float percentageX = Float.parseFloat(sStringCommaSplitter.next());
|
||||
final float percentageY = Float.parseFloat(sStringCommaSplitter.next());
|
||||
return new Position(percentageX, percentageY);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid Position string: " + positionString);
|
||||
}
|
||||
|
||||
Position(float percentageX, float percentageY) {
|
||||
update(percentageX, percentageY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mPercentageX + ", " + mPercentageY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position with {@code percentageX} and {@code percentageY}.
|
||||
*
|
||||
* @param percentageX the new percentage of X-axis of the screen, from 0.0 to 1.0.
|
||||
* @param percentageY the new percentage of Y-axis of the screen, from 0.0 to 1.0.
|
||||
*/
|
||||
public void update(@FloatRange(from = 0.0, to = 1.0) float percentageX,
|
||||
@FloatRange(from = 0.0, to = 1.0) float percentageY) {
|
||||
mPercentageX = percentageX;
|
||||
mPercentageY = percentageY;
|
||||
}
|
||||
|
||||
public float getPercentageX() {
|
||||
return mPercentageX;
|
||||
}
|
||||
|
||||
public float getPercentageY() {
|
||||
return mPercentageY;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -31,15 +30,16 @@ import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -50,6 +50,9 @@ import java.util.List;
|
||||
@TestableLooper.RunWithLooper
|
||||
public class AccessibilityFloatingMenuTest extends SysuiTestCase {
|
||||
|
||||
@Rule
|
||||
public MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private AccessibilityManager mAccessibilityManager;
|
||||
|
||||
@@ -58,18 +61,14 @@ public class AccessibilityFloatingMenuTest extends SysuiTestCase {
|
||||
|
||||
@Before
|
||||
public void initMenu() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
mTargets.add(mock(AccessibilityTarget.class));
|
||||
|
||||
final List<String> assignedTargets = new ArrayList<>();
|
||||
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
|
||||
assignedTargets.add(MAGNIFICATION_CONTROLLER_NAME);
|
||||
doReturn(assignedTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
|
||||
anyInt());
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext);
|
||||
final Position position = new Position(0, 0);
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, position);
|
||||
mMenu = new AccessibilityFloatingMenu(mContext, mMenuView);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -63,13 +62,16 @@ import com.android.systemui.accessibility.MotionEventHelper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Tests for {@link AccessibilityFloatingMenuView}. */
|
||||
@@ -77,22 +79,26 @@ import java.util.List;
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
|
||||
@Rule
|
||||
public MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
|
||||
private final List<AccessibilityTarget> mTargets = new ArrayList<>(
|
||||
Collections.singletonList(mock(AccessibilityTarget.class)));
|
||||
|
||||
private final Rect mAvailableBounds = new Rect(100, 200, 300, 400);
|
||||
private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
|
||||
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
@Mock
|
||||
private ViewPropertyAnimator mAnimator;
|
||||
|
||||
@Mock
|
||||
private WindowMetrics mWindowMetrics;
|
||||
|
||||
private MotionEvent mInterceptMotionEvent;
|
||||
|
||||
private RecyclerView mListView;
|
||||
|
||||
private Rect mAvailableBounds = new Rect(100, 200, 300, 400);
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
private RecyclerView mListView = new RecyclerView(mContext);
|
||||
|
||||
private int mScreenHeight;
|
||||
private int mMenuWindowHeight;
|
||||
@@ -101,23 +107,21 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
private int mScreenHalfWidth;
|
||||
private int mScreenHalfHeight;
|
||||
private int mMaxWindowX;
|
||||
|
||||
private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
|
||||
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
private int mMaxWindowY;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
public void initMenuView() {
|
||||
final WindowManager wm = mContext.getSystemService(WindowManager.class);
|
||||
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
|
||||
mWindowManager).getMaximumWindowMetrics();
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
|
||||
mTargets.add(mock(AccessibilityTarget.class));
|
||||
mListView = new RecyclerView(mContext);
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, mListView);
|
||||
mMenuView = spy(
|
||||
new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition, mListView));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpMatrices() {
|
||||
final Resources res = mContext.getResources();
|
||||
final int margin =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
|
||||
@@ -135,6 +139,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
mScreenHalfHeight = mScreenHeight / 2;
|
||||
mMaxWindowX = screenWidth - margin - menuWidth;
|
||||
mMenuWindowHeight = menuHeight + margin * 2;
|
||||
mMaxWindowY = mScreenHeight - mMenuWindowHeight;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -180,42 +185,46 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListViewRadius_singleTarget_matchResult() {
|
||||
final float radius =
|
||||
getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_small_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
public void onTargetsChanged_singleTarget_expectedRadii() {
|
||||
final Position alignRightPosition = new Position(1.0f, 0.0f);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
alignRightPosition);
|
||||
setupBasicMenuView(menuView);
|
||||
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
final View view = mMenuView.getChildAt(0);
|
||||
menuView.onTargetsChanged(mTargets);
|
||||
|
||||
final View view = menuView.getChildAt(0);
|
||||
final LayerDrawable layerDrawable = (LayerDrawable) view.getBackground();
|
||||
final GradientDrawable gradientDrawable =
|
||||
(GradientDrawable) layerDrawable.getDrawable(0);
|
||||
final float[] actualRadii = gradientDrawable.getCornerRadii();
|
||||
|
||||
assertThat(actualRadii).isEqualTo(expectedRadii);
|
||||
final float smallRadius =
|
||||
getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_small_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[]{smallRadius, smallRadius, 0.0f, 0.0f, 0.0f, 0.0f, smallRadius,
|
||||
smallRadius};
|
||||
assertThat(gradientDrawable.getCornerRadii()).isEqualTo(expectedRadii);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSizeType_largeSize_matchResult() {
|
||||
final int shapeType = 2;
|
||||
final float radius = getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_large_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
final Drawable listViewBackground =
|
||||
mContext.getDrawable(R.drawable.accessibility_floating_menu_background);
|
||||
mListView = spy(new RecyclerView(mContext));
|
||||
mListView.setBackground(listViewBackground);
|
||||
public void setSizeType_alignRightAndLargeSize_expectedRadii() {
|
||||
final RecyclerView listView = spy(new RecyclerView(mContext));
|
||||
final Position alignRightPosition = new Position(1.0f, 0.0f);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
alignRightPosition, listView);
|
||||
setupBasicMenuView(menuView);
|
||||
|
||||
menuView.setSizeType(/* largeSize */ 1);
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, mListView);
|
||||
mMenuView.setSizeType(shapeType);
|
||||
final LayerDrawable layerDrawable =
|
||||
(LayerDrawable) mListView.getBackground();
|
||||
(LayerDrawable) listView.getBackground();
|
||||
final GradientDrawable gradientDrawable =
|
||||
(GradientDrawable) layerDrawable.getDrawable(0);
|
||||
|
||||
final float largeRadius = getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_large_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[] {largeRadius, largeRadius, 0.0f, 0.0f, 0.0f, 0.0f, largeRadius,
|
||||
largeRadius};
|
||||
assertThat(gradientDrawable.getCornerRadii()).isEqualTo(expectedRadii);
|
||||
}
|
||||
|
||||
@@ -223,49 +232,43 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
public void setShapeType_halfCircle_translationX() {
|
||||
final RecyclerView listView = spy(new RecyclerView(mContext));
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
new AccessibilityFloatingMenuView(mContext, listView);
|
||||
final int shapeType = 2;
|
||||
new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition, listView);
|
||||
setupBasicMenuView(menuView);
|
||||
doReturn(mAnimator).when(listView).animate();
|
||||
|
||||
menuView.setShapeType(shapeType);
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
verify(mAnimator).translationX(anyFloat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTargetsChanged_fadeInOut() {
|
||||
final AccessibilityFloatingMenuView menuView = spy(mMenuView);
|
||||
final InOrder inOrderMenuView = inOrder(menuView);
|
||||
final InOrder inOrderMenuView = inOrder(mMenuView);
|
||||
|
||||
menuView.onTargetsChanged(mTargets);
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
|
||||
inOrderMenuView.verify(menuView).fadeIn();
|
||||
inOrderMenuView.verify(menuView).fadeOut();
|
||||
inOrderMenuView.verify(mMenuView).fadeIn();
|
||||
inOrderMenuView.verify(mMenuView).fadeOut();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSizeType_fadeInOut() {
|
||||
final AccessibilityFloatingMenuView menuView = spy(mMenuView);
|
||||
final InOrder inOrderMenuView = inOrder(menuView);
|
||||
final int smallSize = 0;
|
||||
menuView.setSizeType(smallSize);
|
||||
final InOrder inOrderMenuView = inOrder(mMenuView);
|
||||
|
||||
inOrderMenuView.verify(menuView).fadeIn();
|
||||
inOrderMenuView.verify(menuView).fadeOut();
|
||||
mMenuView.setSizeType(/* smallSize */ 0);
|
||||
|
||||
inOrderMenuView.verify(mMenuView).fadeIn();
|
||||
inOrderMenuView.verify(mMenuView).fadeOut();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tapOnAndDragMenu_interceptUpEvent() {
|
||||
final RecyclerView listView = new RecyclerView(mContext);
|
||||
final TestAccessibilityFloatingMenu menuView =
|
||||
new TestAccessibilityFloatingMenu(mContext, listView);
|
||||
|
||||
menuView.show();
|
||||
menuView.onTargetsChanged(mTargets);
|
||||
menuView.setSizeType(0);
|
||||
menuView.setShapeType(0);
|
||||
final int currentWindowX = mMenuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = mMenuView.mCurrentLayoutParams.y;
|
||||
new TestAccessibilityFloatingMenu(mContext, mPlaceholderPosition, listView);
|
||||
setupBasicMenuView(menuView);
|
||||
final int currentWindowX = menuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = menuView.mCurrentLayoutParams.y;
|
||||
final MotionEvent downEvent =
|
||||
mMotionEventHelper.obtainMotionEvent(0, 1,
|
||||
MotionEvent.ACTION_DOWN,
|
||||
@@ -283,6 +286,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
/* screenCenterX */ mScreenHalfWidth
|
||||
- /* offsetXToScreenLeftHalfRegion */ 10,
|
||||
/* screenCenterY */ mScreenHalfHeight);
|
||||
|
||||
listView.dispatchTouchEvent(downEvent);
|
||||
listView.dispatchTouchEvent(moveEvent);
|
||||
listView.dispatchTouchEvent(upEvent);
|
||||
@@ -292,12 +296,15 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void tapOnAndDragMenu_matchLocation() {
|
||||
mMenuView.show();
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
mMenuView.setSizeType(0);
|
||||
mMenuView.setShapeType(0);
|
||||
final int currentWindowX = mMenuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = mMenuView.mCurrentLayoutParams.y;
|
||||
final float expectedX = 1.0f;
|
||||
final float expectedY = 0.7f;
|
||||
final Position position = new Position(expectedX, expectedY);
|
||||
final RecyclerView listView = new RecyclerView(mContext);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
position, listView);
|
||||
setupBasicMenuView(menuView);
|
||||
final int currentWindowX = menuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = menuView.mCurrentLayoutParams.y;
|
||||
final MotionEvent downEvent =
|
||||
mMotionEventHelper.obtainMotionEvent(0, 1,
|
||||
MotionEvent.ACTION_DOWN,
|
||||
@@ -315,25 +322,28 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
/* screenCenterX */ mScreenHalfWidth
|
||||
+ /* offsetXToScreenRightHalfRegion */ 10,
|
||||
/* screenCenterY */ mScreenHalfHeight);
|
||||
mListView.dispatchTouchEvent(downEvent);
|
||||
mListView.dispatchTouchEvent(moveEvent);
|
||||
mListView.dispatchTouchEvent(upEvent);
|
||||
mMenuView.mDragAnimator.end();
|
||||
|
||||
assertThat(mMenuView.mCurrentLayoutParams.x).isEqualTo(mMaxWindowX);
|
||||
assertThat(mMenuView.mCurrentLayoutParams.y).isEqualTo(
|
||||
listView.dispatchTouchEvent(downEvent);
|
||||
listView.dispatchTouchEvent(moveEvent);
|
||||
listView.dispatchTouchEvent(upEvent);
|
||||
menuView.mDragAnimator.end();
|
||||
|
||||
assertThat((float) menuView.mCurrentLayoutParams.x).isWithin(1.0f).of(mMaxWindowX);
|
||||
assertThat((float) menuView.mCurrentLayoutParams.y).isWithin(1.0f).of(
|
||||
/* newWindowY = screenCenterY - offsetY */ mScreenHalfHeight - mMenuHalfHeight);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void tapOnAndDragMenuToScreenSide_transformShapeHalfOval() {
|
||||
mMenuView.show();
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
mMenuView.setSizeType(0);
|
||||
mMenuView.setShapeType(/* oval */ 0);
|
||||
final int currentWindowX = mMenuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = mMenuView.mCurrentLayoutParams.y;
|
||||
final Position alignRightPosition = new Position(1.0f, 0.8f);
|
||||
final RecyclerView listView = new RecyclerView(mContext);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
alignRightPosition, listView);
|
||||
setupBasicMenuView(menuView);
|
||||
|
||||
final int currentWindowX = menuView.mCurrentLayoutParams.x;
|
||||
final int currentWindowY = menuView.mCurrentLayoutParams.y;
|
||||
final MotionEvent downEvent =
|
||||
mMotionEventHelper.obtainMotionEvent(0, 1,
|
||||
MotionEvent.ACTION_DOWN,
|
||||
@@ -351,125 +361,110 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
/* downX */(currentWindowX + mMenuHalfWidth)
|
||||
+ /* offsetXToScreenRightSide */ mMenuHalfWidth,
|
||||
/* downY */ (currentWindowY + mMenuHalfHeight));
|
||||
mListView.dispatchTouchEvent(downEvent);
|
||||
mListView.dispatchTouchEvent(moveEvent);
|
||||
mListView.dispatchTouchEvent(upEvent);
|
||||
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* halfOval */ 1);
|
||||
listView.dispatchTouchEvent(downEvent);
|
||||
listView.dispatchTouchEvent(moveEvent);
|
||||
listView.dispatchTouchEvent(upEvent);
|
||||
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* halfOval */ 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAccessibilityActionList_matchResult() {
|
||||
final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
|
||||
mMenuView.onInitializeAccessibilityNodeInfo(infos);
|
||||
final AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
||||
|
||||
assertThat(infos.getActionList().size()).isEqualTo(5);
|
||||
mMenuView.onInitializeAccessibilityNodeInfo(info);
|
||||
|
||||
assertThat(info.getActionList().size()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityActionMove_halfOval_moveTopLeft_success() {
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext));
|
||||
doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds();
|
||||
mMenuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
final boolean isActionPerformed =
|
||||
menuView.performAccessibilityAction(R.id.action_move_top_left, null);
|
||||
final boolean moveTopLeftAction =
|
||||
mMenuView.performAccessibilityAction(R.id.action_move_top_left, null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top);
|
||||
assertThat(moveTopLeftAction).isTrue();
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityActionMove_halfOval_moveTopRight_success() {
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext));
|
||||
doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds();
|
||||
mMenuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
final boolean isActionPerformed =
|
||||
menuView.performAccessibilityAction(R.id.action_move_top_right, null);
|
||||
final boolean moveTopRightAction =
|
||||
mMenuView.performAccessibilityAction(R.id.action_move_top_right, null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top);
|
||||
assertThat(moveTopRightAction).isTrue();
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityActionMove_halfOval_moveBottomLeft_success() {
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext));
|
||||
doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds();
|
||||
mMenuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
final boolean isActionPerformed =
|
||||
menuView.performAccessibilityAction(R.id.action_move_bottom_left, null);
|
||||
final boolean moveBottomLeftAction =
|
||||
mMenuView.performAccessibilityAction(R.id.action_move_bottom_left, null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom);
|
||||
assertThat(moveBottomLeftAction).isTrue();
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityActionMove_halfOval_moveBottomRight_success() {
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext));
|
||||
doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds();
|
||||
mMenuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
final boolean isActionPerformed =
|
||||
menuView.performAccessibilityAction(R.id.action_move_bottom_right, null);
|
||||
final boolean moveBottomRightAction =
|
||||
mMenuView.performAccessibilityAction(R.id.action_move_bottom_right, null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom);
|
||||
assertThat(moveBottomRightAction).isTrue();
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityActionMove_halfOval_moveOutEdgeAndShow_success() {
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext));
|
||||
doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
|
||||
menuView.setShapeType(/* halfOvalShape */ 1);
|
||||
doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds();
|
||||
mMenuView.setShapeType(/* halfOvalShape */ 1);
|
||||
|
||||
final boolean isActionPerformed =
|
||||
menuView.performAccessibilityAction(R.id.action_move_out_edge_and_show, null);
|
||||
final boolean moveOutEdgeAndShowAction =
|
||||
mMenuView.performAccessibilityAction(R.id.action_move_out_edge_and_show, null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
assertThat(moveOutEdgeAndShowAction).isTrue();
|
||||
assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setupAccessibilityActions_oval_hasActionMoveToEdgeAndHide() {
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
|
||||
menuView.setShapeType(/* ovalShape */ 0);
|
||||
final AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
||||
mMenuView.setShapeType(/* ovalShape */ 0);
|
||||
|
||||
final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
|
||||
menuView.onInitializeAccessibilityNodeInfo(infos);
|
||||
mMenuView.onInitializeAccessibilityNodeInfo(info);
|
||||
|
||||
assertThat(infos.getActionList().stream().anyMatch(
|
||||
assertThat(info.getActionList().stream().anyMatch(
|
||||
action -> action.getId() == R.id.action_move_to_edge_and_hide)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTargetsChanged_exceedAvailableHeight_overScrollAlways() {
|
||||
final RecyclerView listView = new RecyclerView(mContext);
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext, listView));
|
||||
doReturn(true).when(menuView).hasExceededMaxLayoutHeight();
|
||||
doReturn(true).when(mMenuView).hasExceededMaxLayoutHeight();
|
||||
|
||||
menuView.onTargetsChanged(mTargets);
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
|
||||
assertThat(listView.getOverScrollMode()).isEqualTo(OVER_SCROLL_ALWAYS);
|
||||
assertThat(mListView.getOverScrollMode()).isEqualTo(OVER_SCROLL_ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTargetsChanged_notExceedAvailableHeight_overScrollNever() {
|
||||
final RecyclerView listView = new RecyclerView(mContext);
|
||||
final AccessibilityFloatingMenuView menuView =
|
||||
spy(new AccessibilityFloatingMenuView(mContext, listView));
|
||||
doReturn(false).when(menuView).hasExceededMaxLayoutHeight();
|
||||
doReturn(false).when(mMenuView).hasExceededMaxLayoutHeight();
|
||||
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
|
||||
@@ -480,21 +475,24 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
public void showMenuView_insetsListener_overlapWithIme_menuViewShifted() {
|
||||
final int offset = 200;
|
||||
|
||||
showMenuWithLatestStatus();
|
||||
final WindowInsets imeInset = fakeImeInsetWith(offset);
|
||||
final Position alignRightPosition = new Position(1.0f, 0.8f);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
alignRightPosition);
|
||||
setupBasicMenuView(menuView);
|
||||
final WindowInsets imeInset = fakeImeInsetWith(menuView, offset);
|
||||
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
|
||||
when(mWindowMetrics.getWindowInsets()).thenReturn(imeInset);
|
||||
final int expectedLayoutY = mMenuView.mCurrentLayoutParams.y - offset;
|
||||
mMenuView.dispatchApplyWindowInsets(imeInset);
|
||||
final int expectedLayoutY = menuView.mCurrentLayoutParams.y - offset;
|
||||
menuView.dispatchApplyWindowInsets(imeInset);
|
||||
|
||||
assertThat(mMenuView.mCurrentLayoutParams.y).isEqualTo(expectedLayoutY);
|
||||
assertThat(menuView.mCurrentLayoutParams.y).isEqualTo(expectedLayoutY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hideIme_onMenuViewShifted_menuViewMovedBack() {
|
||||
final int offset = 200;
|
||||
showMenuWithLatestStatus();
|
||||
final WindowInsets imeInset = fakeImeInsetWith(offset);
|
||||
setupBasicMenuView(mMenuView);
|
||||
final WindowInsets imeInset = fakeImeInsetWith(mMenuView, offset);
|
||||
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
|
||||
when(mWindowMetrics.getWindowInsets()).thenReturn(imeInset);
|
||||
final int expectedLayoutY = mMenuView.mCurrentLayoutParams.y;
|
||||
@@ -510,8 +508,8 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
public void showMenuAndIme_withHigherIme_alignScreenTopEdge() {
|
||||
final int offset = 99999;
|
||||
|
||||
showMenuWithLatestStatus();
|
||||
final WindowInsets imeInset = fakeImeInsetWith(offset);
|
||||
setupBasicMenuView(mMenuView);
|
||||
final WindowInsets imeInset = fakeImeInsetWith(mMenuView, offset);
|
||||
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
|
||||
when(mWindowMetrics.getWindowInsets()).thenReturn(imeInset);
|
||||
mMenuView.dispatchApplyWindowInsets(imeInset);
|
||||
@@ -519,31 +517,47 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
assertThat(mMenuView.mCurrentLayoutParams.y).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructor_withPosition_expectedPosition() {
|
||||
final float expectedX = 1.0f;
|
||||
final float expectedY = 0.7f;
|
||||
final Position position = new Position(expectedX, expectedY);
|
||||
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
position);
|
||||
setupBasicMenuView(menuView);
|
||||
|
||||
assertThat((float) menuView.mCurrentLayoutParams.x).isWithin(1.0f).of(mMaxWindowX);
|
||||
assertThat((float) menuView.mCurrentLayoutParams.y).isWithin(1.0f).of(
|
||||
expectedY * mMaxWindowY);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mInterceptMotionEvent = null;
|
||||
mMotionEventHelper.recycleEvents();
|
||||
mListView = null;
|
||||
}
|
||||
|
||||
private void showMenuWithLatestStatus() {
|
||||
mMenuView.show();
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
mMenuView.setSizeType(0);
|
||||
mMenuView.setShapeType(0);
|
||||
private void setupBasicMenuView(AccessibilityFloatingMenuView menuView) {
|
||||
menuView.show();
|
||||
menuView.onTargetsChanged(mTargets);
|
||||
menuView.setSizeType(0);
|
||||
menuView.setShapeType(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the current menu status, fake the ime inset component {@link WindowInsets} used
|
||||
* for testing.
|
||||
*
|
||||
* @param offset is used for the y-axis position of ime higher than the y-axis position of menu.
|
||||
* @param menuView {@link AccessibilityFloatingMenuView} that needs to be changed
|
||||
* @param offset is used for the y-axis position of ime higher than the y-axis position of menu
|
||||
* @return the ime inset
|
||||
*/
|
||||
private WindowInsets fakeImeInsetWith(int offset) {
|
||||
private WindowInsets fakeImeInsetWith(AccessibilityFloatingMenuView menuView, int offset) {
|
||||
// Ensure the keyboard has overlapped on the menu view.
|
||||
final int fakeImeHeight =
|
||||
mScreenHeight - (mMenuView.mCurrentLayoutParams.y + mMenuWindowHeight) + offset;
|
||||
|
||||
mScreenHeight - (menuView.mCurrentLayoutParams.y + mMenuWindowHeight) + offset;
|
||||
return new WindowInsets.Builder()
|
||||
.setVisible(ime() | navigationBars(), true)
|
||||
.setInsets(ime() | navigationBars(), Insets.of(0, 0, 0, fakeImeHeight))
|
||||
@@ -551,8 +565,8 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private class TestAccessibilityFloatingMenu extends AccessibilityFloatingMenuView {
|
||||
TestAccessibilityFloatingMenu(Context context, RecyclerView listView) {
|
||||
super(context, listView);
|
||||
TestAccessibilityFloatingMenu(Context context, Position position, RecyclerView listView) {
|
||||
super(context, position, listView);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,6 +55,7 @@ public class BaseTooltipViewTest extends SysuiTestCase {
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
private BaseTooltipView mToolTipView;
|
||||
|
||||
private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
|
||||
private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
|
||||
|
||||
@Before
|
||||
@@ -66,7 +67,7 @@ public class BaseTooltipViewTest extends SysuiTestCase {
|
||||
mWindowManager).getMaximumWindowMetrics();
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext);
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition);
|
||||
mToolTipView = new BaseTooltipView(mContext, mMenuView);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ public class DockTooltipViewTest extends SysuiTestCase {
|
||||
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
private DockTooltipView mDockTooltipView;
|
||||
private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
|
||||
private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
|
||||
|
||||
@Before
|
||||
@@ -62,7 +63,7 @@ public class DockTooltipViewTest extends SysuiTestCase {
|
||||
mWindowManager).getMaximumWindowMetrics();
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
|
||||
mMenuView = spy(new AccessibilityFloatingMenuView(mContext));
|
||||
mMenuView = spy(new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition));
|
||||
mDockTooltipView = new DockTooltipView(mContext, mMenuView);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,12 @@ import org.junit.runner.RunWith;
|
||||
public class MigrationTooltipViewTest extends SysuiTestCase {
|
||||
|
||||
private TextView mTextView;
|
||||
private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
|
||||
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
|
||||
mPlaceholderPosition);
|
||||
final MigrationTooltipView toolTipView = new MigrationTooltipView(mContext, menuView);
|
||||
mTextView = toolTipView.findViewById(R.id.text);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.floatingmenu;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for {@link Position}. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class PositionTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void fromString_correctFormat_expectedValues() {
|
||||
final float expectedX = 0.0f;
|
||||
final float expectedY = 0.7f;
|
||||
final String correctStringFormat = expectedX + ", " + expectedY;
|
||||
|
||||
final Position position = Position.fromString(correctStringFormat);
|
||||
|
||||
assertThat(position.getPercentageX()).isEqualTo(expectedX);
|
||||
assertThat(position.getPercentageY()).isEqualTo(expectedY);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void fromString_incorrectFormat_throwsException() {
|
||||
final String incorrectStringFormat = "0.0: 1.0";
|
||||
|
||||
// expect to throw IllegalArgumentException for the incorrect separator ":"
|
||||
Position.fromString(incorrectStringFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor() {
|
||||
final float expectedX = 0.5f;
|
||||
final float expectedY = 0.9f;
|
||||
|
||||
final Position position = new Position(expectedX, expectedY);
|
||||
|
||||
assertThat(position.getPercentageX()).isEqualTo(expectedX);
|
||||
assertThat(position.getPercentageY()).isEqualTo(expectedY);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user