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

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

Merge "Add new floating action menu for Accessibility targets. (2/n)"

parents 0d186240 c26ac8c6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -21,9 +21,9 @@
    <item android:id="@+id/menu_background_item">
        <shape android:shape="rectangle">
            <corners
                android:bottomLeftRadius="@dimen/accessibility_floating_menu_single_radius"
                android:bottomLeftRadius="@dimen/accessibility_floating_menu_small_single_radius"
                android:bottomRightRadius="0dp"
                android:topLeftRadius="@dimen/accessibility_floating_menu_single_radius"
                android:topLeftRadius="@dimen/accessibility_floating_menu_small_single_radius"
                android:topRightRadius="0dp"/>
            <solid
                android:color="@color/accessibility_floating_menu_background"/>
+4 −3
Original line number Diff line number Diff line
@@ -19,14 +19,15 @@
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/accessibility_floating_menu_padding"
    android:paddingStart="@dimen/accessibility_floating_menu_padding"
    android:paddingEnd="@dimen/accessibility_floating_menu_padding"
    android:orientation="vertical"
    android:gravity="center">

    <View
        android:id="@+id/icon_view"
        android:layout_width="@dimen/accessibility_floating_menu_width_height"
        android:layout_height="@dimen/accessibility_floating_menu_width_height"/>
        android:layout_width="@dimen/accessibility_floating_menu_small_width_height"
        android:layout_height="@dimen/accessibility_floating_menu_small_width_height"/>

    <View
        android:id="@+id/transparent_divider"
+6 −3
Original line number Diff line number Diff line
@@ -1367,9 +1367,12 @@
    <dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
    <dimen name="accessibility_floating_menu_margin">16dp</dimen>
    <dimen name="accessibility_floating_menu_padding">6dp</dimen>
    <dimen name="accessibility_floating_menu_width_height">36dp</dimen>
    <dimen name="accessibility_floating_menu_single_radius">25dp</dimen>
    <dimen name="accessibility_floating_menu_multiple_radius">20dp</dimen>
    <dimen name="accessibility_floating_menu_small_width_height">36dp</dimen>
    <dimen name="accessibility_floating_menu_small_single_radius">25dp</dimen>
    <dimen name="accessibility_floating_menu_small_multiple_radius">20dp</dimen>
    <dimen name="accessibility_floating_menu_large_width_height">56dp</dimen>
    <dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
    <dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>

    <dimen name="rounded_slider_height">48dp</dimen>
    <!-- rounded_slider_height / 2 -->
+56 −5
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package com.android.systemui.accessibility.floatingmenu;

import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;

import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;

import android.content.Context;
import android.database.ContentObserver;
@@ -44,6 +48,22 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
                }
            };

    private final ContentObserver mSizeContentObserver =
            new ContentObserver(new Handler(Looper.getMainLooper())) {
                @Override
                public void onChange(boolean selfChange) {
                    mMenuView.setSizeType(getSizeType(mContext));
                }
            };

    private final ContentObserver mShapeContentObserver =
            new ContentObserver(new Handler(Looper.getMainLooper())) {
                @Override
                public void onChange(boolean selfChange) {
                    mMenuView.setShapeType(getShapeType(mContext));
                }
            };

    public AccessibilityFloatingMenu(Context context) {
        mContext = context;
        mMenuView = new AccessibilityFloatingMenuView(context);
@@ -66,13 +86,12 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
            return;
        }

        mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON));
        mMenuView.show();
        mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON));
        mMenuView.setSizeType(getSizeType(mContext));
        mMenuView.setShapeType(getShapeType(mContext));

        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
                /* notifyForDescendants */ false, mContentObserver,
                UserHandle.USER_CURRENT);
        registerContentObservers();
    }

    @Override
@@ -83,6 +102,38 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {

        mMenuView.hide();

        unregisterContentObservers();
    }

    private static int getSizeType(Context context) {
        return Settings.Secure.getInt(
                context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_SIZE, SizeType.SMALL);
    }

    private static int getShapeType(Context context) {
        return Settings.Secure.getInt(
                context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
                ShapeType.CIRCLE);
    }

    private void registerContentObservers() {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
                /* notifyForDescendants */ false, mContentObserver,
                UserHandle.USER_CURRENT);
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
                /* notifyForDescendants */ false, mSizeContentObserver,
                UserHandle.USER_CURRENT);
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE),
                /* notifyForDescendants */ false, mShapeContentObserver,
                UserHandle.USER_CURRENT);
    }

    private void unregisterContentObservers() {
        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
        mContext.getContentResolver().unregisterContentObserver(mSizeContentObserver);
        mContext.getContentResolver().unregisterContentObserver(mShapeContentObserver);
    }
}
+170 −32
Original line number Diff line number Diff line
@@ -16,15 +16,18 @@

package com.android.systemui.accessibility.floatingmenu;

import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -34,8 +37,11 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

@@ -50,21 +56,59 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
    private static final float DEFAULT_LOCATION_Y_PERCENTAGE = 0.8f;
    private static final int INDEX_MENU_ITEM = 0;
    private boolean mIsShowing;
    private RecyclerView mListView;
    @SizeType
    private int mSizeType = SizeType.SMALL;
    private int mMargin;
    private int mPadding;
    private int mScreenHeight;
    private int mScreenWidth;
    private int mIconWidth;
    private int mIconHeight;
    private final RecyclerView mListView;
    private final AccessibilityTargetAdapter mAdapter;
    private final WindowManager.LayoutParams mLayoutParams;
    private final WindowManager mWindowManager;
    private final AccessibilityTargetAdapter mAdapter;
    private final List<AccessibilityTarget> mTargets = new ArrayList<>();

    @IntDef({
            SizeType.SMALL,
            SizeType.LARGE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface SizeType {
        int SMALL = 0;
        int LARGE = 1;
    }

    @IntDef({
            ShapeType.CIRCLE,
            ShapeType.HALF_CIRCLE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface ShapeType {
        int CIRCLE = 0;
        int HALF_CIRCLE = 1;
    }

    public AccessibilityFloatingMenuView(Context context) {
        this(context, new RecyclerView(context));
    }

    @VisibleForTesting
    AccessibilityFloatingMenuView(Context context,
            RecyclerView listView) {
        super(context);

        mListView = listView;
        mWindowManager = context.getSystemService(WindowManager.class);
        mLayoutParams = createDefaultLayoutParams();
        mAdapter = new AccessibilityTargetAdapter(mTargets);

        initListView(context, mAdapter);
        updateStroke();
        updateDimensions();
        initListView();

        final int uiMode = context.getResources().getConfiguration().uiMode;
        updateStrokeWith(uiMode);
    }

    void show() {
@@ -94,41 +138,100 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
        mTargets.addAll(newTargets);
        mAdapter.notifyDataSetChanged();

        final boolean hasMoreItems = mAdapter.getItemCount() > 1;
        final int resId = hasMoreItems
                ? R.dimen.accessibility_floating_menu_multiple_radius
                : R.dimen.accessibility_floating_menu_single_radius;
        setRadius(resId);
        updateRadiusWith(mSizeType, mTargets.size());
    }

    private void setRadius(@DimenRes int radiusResId) {
        final float radius = getResources().getDimension(radiusResId);
    void setSizeType(@SizeType int newSizeType) {
        mSizeType = newSizeType;

        updateIconSizeWith(newSizeType);
        updateRadiusWith(newSizeType, mTargets.size());

        // When the icon sized changed, the menu size and location will be impacted.
        updateLocation();
    }

    void setShapeType(@ShapeType int newShapeType) {
        final boolean isCircleShape =
                newShapeType == ShapeType.CIRCLE;
        final float offset =
                isCircleShape
                        ? 0
                        : getLayoutWidth() / 2.0f;
        mListView.animate().translationX(offset);

        setOnTouchListener(
                isCircleShape
                        ? null
                        : (view, event) -> onTouched(event));
    }

    private boolean onTouched(MotionEvent event) {
        final int currentX = (int) event.getX();
        final int currentY = (int) event.getY();

        final int menuHalfWidth = getLayoutWidth() / 2;
        final Rect touchDelegateBounds =
                new Rect(mMargin, mMargin, mMargin + menuHalfWidth, mMargin + getLayoutHeight());
        if (touchDelegateBounds.contains(currentX, currentY)) {
            // In order to correspond to the correct item of list view.
            event.setLocation(currentX - mMargin, currentY - mMargin);
            return mListView.dispatchTouchEvent(event);
        }

        return false;
    }

    private void setRadius(float radius) {
        final float[] radii = new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
        getMenuGradientDrawable().setCornerRadii(radii);
    }

    private void initListView(Context context, AccessibilityTargetAdapter adapter) {
        final Resources res = context.getResources();
        final int margin =
    private void updateDimensions() {
        final Resources res = getResources();
        final DisplayMetrics dm = res.getDisplayMetrics();
        mScreenWidth = dm.widthPixels;
        mScreenHeight = dm.heightPixels;
        mMargin =
                res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
        final int elevation =
                res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
        mPadding =
                res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_padding);
    }

    private void updateIconSizeWith(@SizeType int sizeType) {
        final Resources res = getResources();
        final int iconResId =
                sizeType == SizeType.SMALL
                        ? R.dimen.accessibility_floating_menu_small_width_height
                        : R.dimen.accessibility_floating_menu_large_width_height;
        mIconWidth = res.getDimensionPixelSize(iconResId);
        mIconHeight = mIconWidth;

        mAdapter.setIconWidthHeight(mIconWidth);
        mAdapter.notifyDataSetChanged();
    }

    private void initListView() {
        final Drawable listViewBackground =
                context.getDrawable(R.drawable.accessibility_floating_menu_background);
                getContext().getDrawable(R.drawable.accessibility_floating_menu_background);
        final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        mListView.setBackground(listViewBackground);
        mListView.setAdapter(mAdapter);
        mListView.setLayoutManager(layoutManager);
        updateListView();

        mListView = new RecyclerView(context);
        final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
        addView(mListView);
    }

    private void updateListView() {
        final int elevation =
                getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
        final LayoutParams layoutParams =
                new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.setMarginsRelative(margin, margin, /* end= */ 0, margin);
        layoutParams.setMarginsRelative(mMargin, mMargin, /* end= */ 0, mMargin);
        mListView.setLayoutParams(layoutParams);
        mListView.setElevation(elevation);
        mListView.setBackground(listViewBackground);
        mListView.setAdapter(adapter);
        mListView.setLayoutManager(layoutManager);

        addView(mListView);
    }

    private WindowManager.LayoutParams createDefaultLayoutParams() {
@@ -150,9 +253,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        updateLocation();
        updateDimensions();
        updateListView();
        updateIconSizeWith(mSizeType);
        updateColor();
        updateStroke();
        updateStrokeWith(newConfig.uiMode);
        updateLocation();
    }

    private LayerDrawable getMenuLayerDrawable() {
@@ -163,10 +269,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
        return (GradientDrawable) getMenuLayerDrawable().getDrawable(INDEX_MENU_ITEM);
    }

    /**
     * Updates the floating menu to be fixed at the side of the screen.
     */
    private void updateLocation() {
        final DisplayMetrics dm = getResources().getDisplayMetrics();
        mLayoutParams.x = dm.widthPixels;
        mLayoutParams.y = (int) (dm.heightPixels * DEFAULT_LOCATION_Y_PERCENTAGE);
        mLayoutParams.x = mScreenWidth - mMargin - getLayoutWidth();
        mLayoutParams.y = (int) (mScreenHeight * DEFAULT_LOCATION_Y_PERCENTAGE);
        mWindowManager.updateViewLayout(this, mLayoutParams);
    }

@@ -175,10 +283,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
        getMenuGradientDrawable().setColor(getResources().getColor(menuColorResId));
    }

    private void updateStroke() {
    private void updateStrokeWith(int uiMode) {
        final Resources res = getResources();
        final boolean isNightMode =
                (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
                (uiMode & Configuration.UI_MODE_NIGHT_MASK)
                        == Configuration.UI_MODE_NIGHT_YES;

        final int inset =
@@ -192,4 +300,34 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
        final int strokeColor = res.getColor(R.color.accessibility_floating_menu_stroke_dark);
        getMenuGradientDrawable().setStroke(strokeWidth, strokeColor);
    }

    private void updateRadiusWith(@SizeType int sizeType, int itemCount) {
        setRadius(getResources().getDimensionPixelSize(getRadiusResId(sizeType, itemCount)));
    }

    private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) {
        return sizeType == SizeType.SMALL
                ? getSmallSizeResIdWith(itemCount)
                : getLargeSizeResIdWith(itemCount);
    }

    private int getSmallSizeResIdWith(int itemCount) {
        return itemCount > 1
                ? R.dimen.accessibility_floating_menu_small_multiple_radius
                : R.dimen.accessibility_floating_menu_small_single_radius;
    }

    private int getLargeSizeResIdWith(int itemCount) {
        return itemCount > 1
                ? R.dimen.accessibility_floating_menu_large_multiple_radius
                : R.dimen.accessibility_floating_menu_large_single_radius;
    }

    private int getLayoutWidth() {
        return mPadding * 2 + mIconWidth;
    }

    private int getLayoutHeight() {
        return (mPadding + mIconHeight) * mTargets.size() + mPadding;
    }
}
Loading