Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc24a595 authored by PETER LIANG's avatar PETER LIANG Committed by Android (Google) Code Review
Browse files

Merge "Refactor the design and improve the animations of Accessibility Floating Menu(4/n)."

parents 7d2d5aab a8fecd7e
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.graphics.PointF;

import androidx.dynamicanimation.animation.DynamicAnimation;

/**
 * Controls the interaction animations of the menu view {@link MenuView}.
 */
class MenuAnimationController {
    private final MenuView mMenuView;

    MenuAnimationController(MenuView menuView) {
        mMenuView = menuView;
    }

    void moveToPosition(PointF position) {
        DynamicAnimation.TRANSLATION_X.setValue(mMenuView, position.x);
        DynamicAnimation.TRANSLATION_Y.setValue(mMenuView, position.y);

        mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
    }
}
+31 −0
Original line number Diff line number Diff line
@@ -23,15 +23,18 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance.MenuSizeType.SMALL;

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 com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;

import java.util.List;

@@ -39,9 +42,16 @@ import java.util.List;
 * Stores and observe the settings contents for the menu view.
 */
class MenuInfoRepository {
    @FloatRange(from = 0.0, to = 1.0)
    private static final float DEFAULT_MENU_POSITION_X_PERCENT = 1.0f;

    @FloatRange(from = 0.0, to = 1.0)
    private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.9f;

    private final Context mContext;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final OnSettingsContentsChanged mSettingsContentsCallback;
    private Position mPercentagePosition;

    private final ContentObserver mMenuTargetFeaturesContentObserver =
            new ContentObserver(mHandler) {
@@ -65,6 +75,12 @@ class MenuInfoRepository {
    MenuInfoRepository(Context context, OnSettingsContentsChanged settingsContentsChanged) {
        mContext = context;
        mSettingsContentsCallback = settingsContentsChanged;

        mPercentagePosition = getStartPosition();
    }

    void loadMenuPosition(OnInfoReady<Position> callback) {
        callback.onReady(mPercentagePosition);
    }

    void loadMenuTargetFeatures(OnInfoReady<List<AccessibilityTarget>> callback) {
@@ -75,6 +91,21 @@ class MenuInfoRepository {
        callback.onReady(getMenuSizeTypeFromSettings(mContext));
    }

    void updateMenuSavingPosition(Position percentagePosition) {
        mPercentagePosition = percentagePosition;
        Prefs.putString(mContext, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
                percentagePosition.toString());
    }

    private Position getStartPosition() {
        final String absolutePositionString = Prefs.getString(mContext,
                Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);

        return TextUtils.isEmpty(absolutePositionString)
                ? new Position(DEFAULT_MENU_POSITION_X_PERCENT, DEFAULT_MENU_POSITION_Y_PERCENT)
                : Position.fromString(absolutePositionString);
    }

    void registerContentObservers() {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
+58 −1
Original line number Diff line number Diff line
@@ -21,7 +21,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

import androidx.lifecycle.Observer;
@@ -31,18 +35,25 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.accessibility.dialog.AccessibilityTarget;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * The container view displays the accessibility features.
 */
@SuppressLint("ViewConstructor")
class MenuView extends FrameLayout {
class MenuView extends FrameLayout implements
        ViewTreeObserver.OnComputeInternalInsetsListener {
    private static final int INDEX_MENU_ITEM = 0;
    private final List<AccessibilityTarget> mTargetFeatures = new ArrayList<>();
    private final AccessibilityTargetAdapter mAdapter;
    private final MenuViewModel mMenuViewModel;
    private final MenuAnimationController mMenuAnimationController;
    private final Rect mBoundsInParent = new Rect();
    private final RecyclerView mTargetFeaturesView;
    private final ViewTreeObserver.OnDrawListener mSystemGestureExcludeUpdater =
            this::updateSystemGestureExcludeRects;
    private final Observer<Position> mPercentagePositionObserver = this::onPercentagePosition;
    private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
    private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
            this::onTargetFeaturesChanged;
@@ -53,6 +64,7 @@ class MenuView extends FrameLayout {

        mMenuViewModel = menuViewModel;
        mMenuViewAppearance = menuViewAppearance;
        mMenuAnimationController = new MenuAnimationController(this);
        mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
        mTargetFeaturesView = new RecyclerView(context);
        mTargetFeaturesView.setAdapter(mAdapter);
@@ -60,16 +72,25 @@ class MenuView extends FrameLayout {
        setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
        // Avoid drawing out of bounds of the parent view
        setClipToOutline(true);

        loadLayoutResources();

        addView(mTargetFeaturesView);
    }

    @Override
    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
        inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
        inoutInfo.touchableRegion.set(mBoundsInParent);
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        loadLayoutResources();

        mTargetFeaturesView.setOverScrollMode(mMenuViewAppearance.getMenuScrollMode());
    }

    @SuppressLint("NotifyDataSetChanged")
@@ -80,6 +101,10 @@ class MenuView extends FrameLayout {
    }

    private void onSizeChanged() {
        mBoundsInParent.set(mBoundsInParent.left, mBoundsInParent.top,
                mBoundsInParent.left + mMenuViewAppearance.getMenuWidth(),
                mBoundsInParent.top + mMenuViewAppearance.getMenuHeight());

        final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
        layoutParams.height = mMenuViewAppearance.getMenuHeight();
        setLayoutParams(layoutParams);
@@ -96,6 +121,18 @@ class MenuView extends FrameLayout {
                mMenuViewAppearance.getMenuStrokeColor());
    }

    private void onPercentagePosition(Position percentagePosition) {
        mMenuViewAppearance.setPercentagePosition(percentagePosition);

        onPositionChanged();
    }

    private void onPositionChanged() {
        final PointF position = mMenuViewAppearance.getMenuPosition();
        mMenuAnimationController.moveToPosition(position);
        onBoundsInParentChanged((int) position.x, (int) position.y);
    }

    @SuppressLint("NotifyDataSetChanged")
    private void onSizeTypeChanged(int newSizeType) {
        mMenuViewAppearance.setSizeType(newSizeType);
@@ -106,6 +143,7 @@ class MenuView extends FrameLayout {

        onSizeChanged();
        onEdgeChanged();
        onPositionChanged();
    }

    private void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures) {
@@ -113,24 +151,37 @@ class MenuView extends FrameLayout {
        mTargetFeatures.clear();
        mTargetFeatures.addAll(newTargetFeatures);
        mMenuViewAppearance.setTargetFeaturesSize(mTargetFeatures.size());
        mTargetFeaturesView.setOverScrollMode(mMenuViewAppearance.getMenuScrollMode());
        mAdapter.notifyDataSetChanged();

        onSizeChanged();
        onEdgeChanged();
        onPositionChanged();
    }

    void show() {
        mMenuViewModel.getPercentagePositionData().observeForever(mPercentagePositionObserver);
        mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
        mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
        setVisibility(VISIBLE);
        mMenuViewModel.registerContentObservers();
        getViewTreeObserver().addOnComputeInternalInsetsListener(this);
        getViewTreeObserver().addOnDrawListener(mSystemGestureExcludeUpdater);
    }

    void hide() {
        setVisibility(GONE);
        mBoundsInParent.setEmpty();
        mMenuViewModel.getPercentagePositionData().removeObserver(mPercentagePositionObserver);
        mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
        mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
        mMenuViewModel.unregisterContentObservers();
        getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
    }

    void onBoundsInParentChanged(int newLeft, int newTop) {
        mBoundsInParent.offsetTo(newLeft, newTop);
    }

    void loadLayoutResources() {
@@ -141,6 +192,7 @@ class MenuView extends FrameLayout {
        onItemSizeChanged();
        onSizeChanged();
        onEdgeChanged();
        onPositionChanged();
    }

    private InstantInsetLayerDrawable getContainerViewInsetLayer() {
@@ -150,4 +202,9 @@ class MenuView extends FrameLayout {
    private GradientDrawable getContainerViewGradient() {
        return (GradientDrawable) getContainerViewInsetLayer().getDrawable(INDEX_MENU_ITEM);
    }

    private void updateSystemGestureExcludeRects() {
        final ViewGroup parentView = (ViewGroup) getParent();
        parentView.setSystemGestureExclusionRects(Collections.singletonList(mBoundsInParent));
    }
}
+99 −8
Original line number Diff line number Diff line
@@ -16,12 +16,21 @@

package com.android.systemui.accessibility.floatingmenu;

import static android.view.View.OVER_SCROLL_ALWAYS;
import static android.view.View.OVER_SCROLL_NEVER;

import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance.MenuSizeType.SMALL;

import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;

import androidx.annotation.DimenRes;

@@ -34,9 +43,13 @@ import java.lang.annotation.RetentionPolicy;
 * Provides the layout resources information of the {@link MenuView}.
 */
class MenuViewAppearance {
    private final WindowManager mWindowManager;
    private final Resources mRes;
    private final Position mPercentagePosition = new Position(/* percentageX= */
            0f, /* percentageY= */ 0f);
    private int mTargetFeaturesSize;
    private int mSizeType;
    private int mMargin;
    private int mSmallPadding;
    private int mLargePadding;
    private int mSmallIconSize;
@@ -62,13 +75,15 @@ class MenuViewAppearance {
        int LARGE = 1;
    }

    MenuViewAppearance(Context context) {
    MenuViewAppearance(Context context, WindowManager windowManager) {
        mWindowManager = windowManager;
        mRes = context.getResources();

        update();
    }

    void update() {
        mMargin = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
        mSmallPadding =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_padding);
        mLargePadding =
@@ -81,7 +96,7 @@ class MenuViewAppearance {
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_single_radius);
        mSmallMultipleRadius = mRes.getDimensionPixelSize(
                R.dimen.accessibility_floating_menu_small_multiple_radius);
        mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
        mRadii = createRadii(isMenuOnLeftSide(), getMenuRadius(mTargetFeaturesSize));
        mLargeSingleRadius =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_single_radius);
        mLargeMultipleRadius = mRes.getDimensionPixelSize(
@@ -98,13 +113,48 @@ class MenuViewAppearance {
    void setSizeType(int sizeType) {
        mSizeType = sizeType;

        mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
        mRadii = createRadii(isMenuOnLeftSide(), getMenuRadius(mTargetFeaturesSize));
    }

    void setTargetFeaturesSize(int targetFeaturesSize) {
        mTargetFeaturesSize = targetFeaturesSize;

        mRadii = createRadii(getMenuRadius(targetFeaturesSize));
        mRadii = createRadii(isMenuOnLeftSide(), getMenuRadius(targetFeaturesSize));
    }

    void setPercentagePosition(Position percentagePosition) {
        mPercentagePosition.update(percentagePosition);

        mRadii = createRadii(isMenuOnLeftSide(), getMenuRadius(mTargetFeaturesSize));
    }

    Rect getMenuDraggableBounds() {
        final int margin = getMenuMargin();
        final Rect draggableBounds = getWindowAvailableBounds();

        // Initializes start position for mapping the translation of the menu view.
        final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
        final WindowInsets windowInsets = windowMetrics.getWindowInsets();
        final Insets displayCutoutInsets = windowInsets.getInsetsIgnoringVisibility(
                WindowInsets.Type.displayCutout());
        draggableBounds.offset(-displayCutoutInsets.left, -displayCutoutInsets.top);

        draggableBounds.top += margin;
        draggableBounds.right -= getMenuWidth();
        draggableBounds.bottom -= Math.min(
                getWindowAvailableBounds().height() - draggableBounds.top,
                calculateActualMenuHeight() + margin);
        return draggableBounds;
    }

    PointF getMenuPosition() {
        final Rect draggableBounds = getMenuDraggableBounds();

        return new PointF(
                draggableBounds.left
                        + draggableBounds.width() * mPercentagePosition.getPercentageX(),
                draggableBounds.top
                        + draggableBounds.height() * mPercentagePosition.getPercentageY());
    }

    Drawable getMenuBackground() {
@@ -115,20 +165,32 @@ class MenuViewAppearance {
        return mElevation;
    }

    int getMenuWidth() {
        return getMenuPadding() * 2 + getMenuIconSize();
    }

    int getMenuHeight() {
        return calculateActualMenuHeight();
        return Math.min(getWindowAvailableBounds().height() - mMargin * 2,
                calculateActualMenuHeight());
    }

    int getMenuIconSize() {
        return mSizeType == SMALL ? mSmallIconSize : mLargeIconSize;
    }

    private int getMenuMargin() {
        return mMargin;
    }

    int getMenuPadding() {
        return mSizeType == SMALL ? mSmallPadding : mLargePadding;
    }

    int[] getMenuInsets() {
        return new int[]{mInset, 0, 0, 0};
        final int left = isMenuOnLeftSide() ? mInset : 0;
        final int right = isMenuOnLeftSide() ? 0 : mInset;

        return new int[]{left, 0, right, 0};
    }

    int getMenuStrokeWidth() {
@@ -147,6 +209,14 @@ class MenuViewAppearance {
        return mSizeType == SMALL ? getSmallSize(itemCount) : getLargeSize(itemCount);
    }

    int getMenuScrollMode() {
        return hasExceededMaxWindowHeight() ? OVER_SCROLL_ALWAYS : OVER_SCROLL_NEVER;
    }

    private boolean hasExceededMaxWindowHeight() {
        return calculateActualMenuHeight() > getWindowAvailableBounds().height();
    }

    @DimenRes
    private int getSmallSize(int itemCount) {
        return itemCount > 1 ? mSmallMultipleRadius : mSmallSingleRadius;
@@ -157,8 +227,29 @@ class MenuViewAppearance {
        return itemCount > 1 ? mLargeMultipleRadius : mLargeSingleRadius;
    }

    private static float[] createRadii(float radius) {
        return new float[]{0.0f, 0.0f, radius, radius, radius, radius, 0.0f, 0.0f};
    private static float[] createRadii(boolean isMenuOnLeftSide, float radius) {
        return isMenuOnLeftSide
                ? new float[]{0.0f, 0.0f, radius, radius, radius, radius, 0.0f, 0.0f}
                : new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
    }

    private Rect getWindowAvailableBounds() {
        final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
        final WindowInsets windowInsets = windowMetrics.getWindowInsets();
        final Insets insets = windowInsets.getInsetsIgnoringVisibility(
                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());

        final Rect bounds = new Rect(windowMetrics.getBounds());
        bounds.left += insets.left;
        bounds.right -= insets.right;
        bounds.top += insets.top;
        bounds.bottom -= insets.bottom;

        return bounds;
    }

    private boolean isMenuOnLeftSide() {
        return mPercentagePosition.getPercentageX() < 0.5f;
    }

    private int calculateActualMenuHeight() {
+4 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.accessibility.floatingmenu;
import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
@@ -41,11 +42,12 @@ class MenuViewLayer extends FrameLayout {
        int MENU_VIEW = 0;
    }

    MenuViewLayer(@NonNull Context context) {
    MenuViewLayer(@NonNull Context context, WindowManager windowManager) {
        super(context);

        final MenuViewModel menuViewModel = new MenuViewModel(context);
        final MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context);
        final MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context,
                windowManager);
        mMenuView = new MenuView(context, menuViewModel, menuViewAppearance);

        addView(mMenuView, LayerIndex.MENU_VIEW);
Loading