Merge "Refactor the design and improve the animations of Accessibility Floating Menu(4/n)."
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
@@ -33,7 +34,7 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu {
|
||||
|
||||
MenuViewLayerController(Context context, WindowManager windowManager) {
|
||||
mWindowManager = windowManager;
|
||||
mMenuViewLayer = new MenuViewLayer(context);
|
||||
mMenuViewLayer = new MenuViewLayer(context, windowManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,9 +69,10 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu {
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
params.receiveInsetsIgnoringZOrder = true;
|
||||
params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
|
||||
params.windowAnimations = android.R.style.Animation_Translucent;
|
||||
|
||||
params.setFitInsetsTypes(WindowInsets.Type.navigationBars());
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
|
||||
private final MutableLiveData<List<AccessibilityTarget>> mTargetFeaturesData =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>();
|
||||
private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
|
||||
private final MenuInfoRepository mInfoRepository;
|
||||
|
||||
MenuViewModel(Context context) {
|
||||
@@ -49,6 +50,11 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
|
||||
mSizeTypeData.setValue(newSizeType);
|
||||
}
|
||||
|
||||
LiveData<Position> getPercentagePositionData() {
|
||||
mInfoRepository.loadMenuPosition(mPercentagePositionData::setValue);
|
||||
return mPercentagePositionData;
|
||||
}
|
||||
|
||||
LiveData<Integer> getSizeTypeData() {
|
||||
mInfoRepository.loadMenuSizeType(mSizeTypeData::setValue);
|
||||
return mSizeTypeData;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
@@ -61,6 +62,13 @@ public class Position {
|
||||
return mPercentageX + ", " + mPercentageY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position with {@code percentagePosition}.
|
||||
*/
|
||||
public void update(@NonNull Position percentagePosition) {
|
||||
update(percentagePosition.getPercentageX(), percentagePosition.getPercentageY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position with {@code percentageX} and {@code percentageY}.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for {@link MenuAnimationController}. */
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class MenuAnimationControllerTest extends SysuiTestCase {
|
||||
private MenuView mMenuView;
|
||||
private MenuAnimationController mMenuAnimationController;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
|
||||
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
|
||||
stubWindowManager);
|
||||
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext);
|
||||
mMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance);
|
||||
mMenuAnimationController = new MenuAnimationController(mMenuView);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveToPosition_matchPosition() {
|
||||
final PointF destination = new PointF(50, 60);
|
||||
|
||||
mMenuAnimationController.moveToPosition(destination);
|
||||
|
||||
assertThat(mMenuView.getTranslationX()).isEqualTo(50);
|
||||
assertThat(mMenuView.getTranslationY()).isEqualTo(60);
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,24 @@
|
||||
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static android.view.WindowInsets.Type.displayCutout;
|
||||
import static android.view.WindowInsets.Type.systemBars;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -38,6 +49,7 @@ import org.mockito.junit.MockitoRule;
|
||||
|
||||
/** Tests for {@link MenuViewLayerController}. */
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
public class MenuViewLayerControllerTest extends SysuiTestCase {
|
||||
@Rule
|
||||
@@ -46,10 +58,20 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
@Mock
|
||||
private WindowMetrics mWindowMetrics;
|
||||
|
||||
private MenuViewLayerController mMenuViewLayerController;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final WindowManager wm = mContext.getSystemService(WindowManager.class);
|
||||
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
|
||||
mWindowManager).getMaximumWindowMetrics();
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
|
||||
when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
|
||||
when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
|
||||
mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager);
|
||||
}
|
||||
|
||||
@@ -68,4 +90,14 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
|
||||
|
||||
verify(mWindowManager).removeView(any(View.class));
|
||||
}
|
||||
|
||||
private WindowInsets stubDisplayInsets() {
|
||||
final int stubStatusBarHeight = 118;
|
||||
final int stubNavigationBarHeight = 125;
|
||||
return new WindowInsets.Builder()
|
||||
.setVisible(systemBars() | displayCutout(), true)
|
||||
.setInsets(systemBars() | displayCutout(),
|
||||
Insets.of(0, stubStatusBarHeight, 0, stubNavigationBarHeight))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -44,7 +45,8 @@ public class MenuViewLayerTest extends SysuiTestCase {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mMenuViewLayer = new MenuViewLayer(mContext);
|
||||
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
|
||||
mMenuViewLayer = new MenuViewLayer(mContext, stubWindowManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
|
||||
import android.app.UiModeManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -52,7 +53,9 @@ public class MenuViewTest extends SysuiTestCase {
|
||||
mNightMode = mUiModeManager.getNightMode();
|
||||
mUiModeManager.setNightMode(MODE_NIGHT_YES);
|
||||
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext);
|
||||
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext);
|
||||
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
|
||||
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
|
||||
stubWindowManager);
|
||||
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user