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

Commit b32a9d20 authored by Peter Liang's avatar Peter Liang
Browse files

Refactor the design and improve the animations of Accessibility Floating Menu(3/n).

Actions of this change:
1) Support the change of the menu size type.

Bug: 227715451
Test: atest MenuInfoRepositoryTest
Change-Id: Id8085500b3a2b2571e41a14758e1a1b05501fe53
parent 28665aa0
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package com.android.systemui.accessibility.floatingmenu;

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

import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance.MenuSizeType.SMALL;

import android.content.Context;
import android.database.ContentObserver;
@@ -29,6 +31,7 @@ import android.os.UserHandle;
import android.provider.Settings;

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

import java.util.List;

@@ -49,6 +52,16 @@ class MenuInfoRepository {
                }
            };

    @VisibleForTesting
    final ContentObserver mMenuSizeContentObserver =
            new ContentObserver(mHandler) {
                @Override
                public void onChange(boolean selfChange) {
                    mSettingsContentsCallback.onSizeTypeChanged(
                            getMenuSizeTypeFromSettings(mContext));
                }
            };

    MenuInfoRepository(Context context, OnSettingsContentsChanged settingsContentsChanged) {
        mContext = context;
        mSettingsContentsCallback = settingsContentsChanged;
@@ -58,6 +71,10 @@ class MenuInfoRepository {
        callback.onReady(getTargets(mContext, ACCESSIBILITY_BUTTON));
    }

    void loadMenuSizeType(OnInfoReady<Integer> callback) {
        callback.onReady(getMenuSizeTypeFromSettings(mContext));
    }

    void registerContentObservers() {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
@@ -67,17 +84,29 @@ class MenuInfoRepository {
                Settings.Secure.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
                /* notifyForDescendants */ false,
                mMenuTargetFeaturesContentObserver, UserHandle.USER_CURRENT);
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
                /* notifyForDescendants */ false, mMenuSizeContentObserver,
                UserHandle.USER_CURRENT);
    }

    void unregisterContentObservers() {
        mContext.getContentResolver().unregisterContentObserver(mMenuTargetFeaturesContentObserver);
        mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
    }

    interface OnSettingsContentsChanged {
        void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures);

        void onSizeTypeChanged(int newSizeType);
    }

    interface OnInfoReady<T> {
        void onReady(T info);
    }

    private static int getMenuSizeTypeFromSettings(Context context) {
        return Settings.Secure.getIntForUser(context.getContentResolver(),
                ACCESSIBILITY_FLOATING_MENU_SIZE, SMALL, UserHandle.USER_CURRENT);
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ class MenuView extends FrameLayout {
    private final AccessibilityTargetAdapter mAdapter;
    private final MenuViewModel mMenuViewModel;
    private final RecyclerView mTargetFeaturesView;
    private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
    private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
            this::onTargetFeaturesChanged;
    private final MenuViewAppearance mMenuViewAppearance;
@@ -57,6 +58,8 @@ class MenuView extends FrameLayout {
        mTargetFeaturesView.setAdapter(mAdapter);
        mTargetFeaturesView.setLayoutManager(new LinearLayoutManager(context));
        setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
        // Avoid drawing out of bounds of the parent view
        setClipToOutline(true);
        loadLayoutResources();

        addView(mTargetFeaturesView);
@@ -76,6 +79,12 @@ class MenuView extends FrameLayout {
        mAdapter.notifyDataSetChanged();
    }

    private void onSizeChanged() {
        final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
        layoutParams.height = mMenuViewAppearance.getMenuHeight();
        setLayoutParams(layoutParams);
    }

    private void onEdgeChanged() {
        final int[] insets = mMenuViewAppearance.getMenuInsets();
        getContainerViewInsetLayer().setLayerInset(INDEX_MENU_ITEM, insets[0], insets[1], insets[2],
@@ -88,6 +97,17 @@ class MenuView extends FrameLayout {
    }

    @SuppressLint("NotifyDataSetChanged")
    private void onSizeTypeChanged(int newSizeType) {
        mMenuViewAppearance.setSizeType(newSizeType);

        mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
        mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
        mAdapter.notifyDataSetChanged();

        onSizeChanged();
        onEdgeChanged();
    }

    private void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures) {
        // TODO(b/252756133): Should update specific item instead of the whole list
        mTargetFeatures.clear();
@@ -95,11 +115,13 @@ class MenuView extends FrameLayout {
        mMenuViewAppearance.setTargetFeaturesSize(mTargetFeatures.size());
        mAdapter.notifyDataSetChanged();

        onSizeChanged();
        onEdgeChanged();
    }

    void show() {
        mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
        mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
        setVisibility(VISIBLE);
        mMenuViewModel.registerContentObservers();
    }
@@ -107,6 +129,7 @@ class MenuView extends FrameLayout {
    void hide() {
        setVisibility(GONE);
        mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
        mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
        mMenuViewModel.unregisterContentObservers();
    }

@@ -116,6 +139,7 @@ class MenuView extends FrameLayout {
        setBackground(mMenuViewAppearance.getMenuBackground());
        setElevation(mMenuViewAppearance.getMenuElevation());
        onItemSizeChanged();
        onSizeChanged();
        onEdgeChanged();
    }

+53 −3
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.accessibility.floatingmenu;

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.drawable.Drawable;
@@ -24,16 +27,24 @@ import androidx.annotation.DimenRes;

import com.android.systemui.R;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Provides the layout resources information of the {@link MenuView}.
 */
class MenuViewAppearance {
    private final Resources mRes;
    private int mTargetFeaturesSize;
    private int mSizeType;
    private int mSmallPadding;
    private int mLargePadding;
    private int mSmallIconSize;
    private int mLargeIconSize;
    private int mSmallSingleRadius;
    private int mSmallMultipleRadius;
    private int mLargeSingleRadius;
    private int mLargeMultipleRadius;
    private int mStrokeWidth;
    private int mStrokeColor;
    private int mInset;
@@ -41,6 +52,16 @@ class MenuViewAppearance {
    private float[] mRadii;
    private Drawable mBackgroundDrawable;

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

    MenuViewAppearance(Context context) {
        mRes = context.getResources();

@@ -50,13 +71,21 @@ class MenuViewAppearance {
    void update() {
        mSmallPadding =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_padding);
        mLargePadding =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_padding);
        mSmallIconSize =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_width_height);
        mLargeIconSize =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_width_height);
        mSmallSingleRadius =
                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));
        mLargeSingleRadius =
                mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_single_radius);
        mLargeMultipleRadius = mRes.getDimensionPixelSize(
                R.dimen.accessibility_floating_menu_large_multiple_radius);
        mStrokeWidth = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_stroke_width);
        mStrokeColor = mRes.getColor(R.color.accessibility_floating_menu_stroke_dark);
        mInset = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_stroke_inset);
@@ -66,6 +95,12 @@ class MenuViewAppearance {
        mBackgroundDrawable = new InstantInsetLayerDrawable(new Drawable[]{drawable});
    }

    void setSizeType(int sizeType) {
        mSizeType = sizeType;

        mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
    }

    void setTargetFeaturesSize(int targetFeaturesSize) {
        mTargetFeaturesSize = targetFeaturesSize;

@@ -80,12 +115,16 @@ class MenuViewAppearance {
        return mElevation;
    }

    int getMenuHeight() {
        return calculateActualMenuHeight();
    }

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

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

    int[] getMenuInsets() {
@@ -105,7 +144,7 @@ class MenuViewAppearance {
    }

    private int getMenuRadius(int itemCount) {
        return getSmallSize(itemCount);
        return mSizeType == SMALL ? getSmallSize(itemCount) : getLargeSize(itemCount);
    }

    @DimenRes
@@ -113,7 +152,18 @@ class MenuViewAppearance {
        return itemCount > 1 ? mSmallMultipleRadius : mSmallSingleRadius;
    }

    @DimenRes
    private int getLargeSize(int itemCount) {
        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 int calculateActualMenuHeight() {
        final int menuPadding = getMenuPadding();

        return (menuPadding + getMenuIconSize()) * mTargetFeaturesSize + menuPadding;
    }
}
+12 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import java.util.List;
class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
    private final MutableLiveData<List<AccessibilityTarget>> mTargetFeaturesData =
            new MutableLiveData<>();
    private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>();
    private final MenuInfoRepository mInfoRepository;

    MenuViewModel(Context context) {
@@ -43,6 +44,16 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
        mTargetFeaturesData.setValue(newTargetFeatures);
    }

    @Override
    public void onSizeTypeChanged(int newSizeType) {
        mSizeTypeData.setValue(newSizeType);
    }

    LiveData<Integer> getSizeTypeData() {
        mInfoRepository.loadMenuSizeType(mSizeTypeData::setValue);
        return mSizeTypeData;
    }

    LiveData<List<AccessibilityTarget>> getTargetFeaturesData() {
        mInfoRepository.loadMenuTargetFeatures(mTargetFeaturesData::setValue);
        return mTargetFeaturesData;
+54 −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 static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;

import android.testing.AndroidTestingRunner;

import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

/** Tests for {@link MenuInfoRepository}. */
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class MenuInfoRepositoryTest extends SysuiTestCase {
    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();

    @Mock
    private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;

    @Test
    public void menuSizeTypeChanged_verifyOnSizeTypeChanged() {
        final MenuInfoRepository menuInfoRepository =
                new MenuInfoRepository(mContext,  mMockSettingsContentsChanged);

        menuInfoRepository.mMenuSizeContentObserver.onChange(true);

        verify(mMockSettingsContentsChanged).onSizeTypeChanged(anyInt());
    }
}