diff options
author | jasonwshsu <jasonwshsu@google.com> | 2021-05-05 01:22:51 +0800 |
---|---|---|
committer | jasonwshsu <jasonwshsu@google.com> | 2021-06-03 21:41:27 +0800 |
commit | d2f7022ad153dafac494cc60c48e0df8a899d180 (patch) | |
tree | 643aca13e2c1afca88b1f281b89077e5e1b13c7d /packages/SystemUI/src | |
parent | f142b5a368edaf8393d093393708b62fa4d87f94 (diff) |
Maintain the position of accessibility floating menu
* Use percentage of X-aixs and Y-axis as last position, so it could be
restored at approximately position when device screen size changed.
* Fine-tune AccessibilityFloatingMenuViewTest
Bug: 183342667
Test: atest AccessibilityFloatingMenuViewTest PositionTest
Change-Id: I58ed608a03bc20f15a9d0852c95907107516c9ba
Diffstat (limited to 'packages/SystemUI/src')
4 files changed, 178 insertions, 36 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index bf09975bb034..782cd29b0179 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java index ee6276813512..47f373920b90 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java index 259a9f7c6f3b..5bb55222e09b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -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() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/Position.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/Position.java new file mode 100644 index 000000000000..7b7eda84df4c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/Position.java @@ -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; + } +} |