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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user