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
This commit is contained in:
Peter Liang
2022-09-04 21:22:36 +08:00
parent 28665aa07f
commit b32a9d206f
5 changed files with 172 additions and 4 deletions

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
@@ -55,4 +66,4 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
void unregisterContentObservers() {
mInfoRepository.unregisterContentObservers();
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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());
}
}