summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values-night/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java153
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java81
15 files changed, 1011 insertions, 7 deletions
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
index d2d4198b7728..9fc86db48c80 100644
--- a/packages/SystemUI/res/values-night/dimens.xml
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -20,4 +20,7 @@
<!-- Height of the background gradient behind the screenshot UI (taller in dark mode) -->
<dimen name="screenshot_bg_protection_height">375dp</dimen>
+ <!-- Accessibility floating menu -->
+ <dimen name="accessibility_floating_menu_stroke_width">1dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c6e6068c4b1..fa3ed21e203e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1323,8 +1323,8 @@
<!-- Accessibility floating menu -->
<dimen name="accessibility_floating_menu_elevation">3dp</dimen>
- <dimen name="accessibility_floating_menu_stroke_width">1dp</dimen>
- <dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_width">0dp</dimen>
+ <dimen name="accessibility_floating_menu_stroke_inset">0dp</dimen>
<dimen name="accessibility_floating_menu_margin">16dp</dimen>
<dimen name="accessibility_floating_menu_small_padding">6dp</dimen>
<dimen name="accessibility_floating_menu_small_width_height">36dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 403941f8e639..ea334b27fa09 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -17,10 +17,17 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+
+import static com.android.systemui.flags.Flags.A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.view.Display;
+import android.view.WindowManager;
import androidx.annotation.MainThread;
@@ -31,6 +38,7 @@ import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import javax.inject.Inject;
@@ -46,6 +54,9 @@ public class AccessibilityFloatingMenuController implements
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private Context mContext;
+ private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
+ private final FeatureFlags mFeatureFlags;
@VisibleForTesting
IAccessibilityFloatingMenu mFloatingMenu;
private int mBtnMode;
@@ -83,13 +94,19 @@ public class AccessibilityFloatingMenuController implements
@Inject
public AccessibilityFloatingMenuController(Context context,
+ WindowManager windowManager,
+ DisplayManager displayManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ FeatureFlags featureFlags) {
mContext = context;
+ mWindowManager = windowManager;
+ mDisplayManager = displayManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mFeatureFlags = featureFlags;
mIsKeyguardVisible = false;
}
@@ -159,7 +176,14 @@ public class AccessibilityFloatingMenuController implements
private void showFloatingMenu() {
if (mFloatingMenu == null) {
- mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ if (mFeatureFlags.isEnabled(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS)) {
+ final Display defaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ mFloatingMenu = new MenuViewLayerController(
+ mContext.createWindowContext(defaultDisplay,
+ TYPE_NAVIGATION_BAR_PANEL, /* options= */ null), mWindowManager);
+ } else {
+ mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ }
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
new file mode 100644
index 000000000000..698d60a5b13e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -0,0 +1,112 @@
+/*
+ * 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 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;
+import android.os.Handler;
+import android.os.Looper;
+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;
+
+/**
+ * Stores and observe the settings contents for the menu view.
+ */
+class MenuInfoRepository {
+ private final Context mContext;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final OnSettingsContentsChanged mSettingsContentsCallback;
+
+ private final ContentObserver mMenuTargetFeaturesContentObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mSettingsContentsCallback.onTargetFeaturesChanged(
+ getTargets(mContext, ACCESSIBILITY_BUTTON));
+ }
+ };
+
+ @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;
+ }
+
+ void loadMenuTargetFeatures(OnInfoReady<List<AccessibilityTarget>> callback) {
+ 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),
+ /* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
+ UserHandle.USER_CURRENT);
+ mContext.getContentResolver().registerContentObserver(
+ 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);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
new file mode 100644
index 000000000000..576f23ee780e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -0,0 +1,153 @@
+/*
+ * 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 android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.GradientDrawable;
+import android.widget.FrameLayout;
+
+import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The container view displays the accessibility features.
+ */
+@SuppressLint("ViewConstructor")
+class MenuView extends FrameLayout {
+ 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 RecyclerView mTargetFeaturesView;
+ private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
+ private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
+ this::onTargetFeaturesChanged;
+ private final MenuViewAppearance mMenuViewAppearance;
+
+ MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance) {
+ super(context);
+
+ mMenuViewModel = menuViewModel;
+ mMenuViewAppearance = menuViewAppearance;
+ mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
+ mTargetFeaturesView = new RecyclerView(context);
+ 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);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ loadLayoutResources();
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void onItemSizeChanged() {
+ mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
+ mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
+ 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],
+ insets[3]);
+
+ final GradientDrawable gradientDrawable = getContainerViewGradient();
+ gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
+ gradientDrawable.setStroke(mMenuViewAppearance.getMenuStrokeWidth(),
+ mMenuViewAppearance.getMenuStrokeColor());
+ }
+
+ @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();
+ mTargetFeatures.addAll(newTargetFeatures);
+ mMenuViewAppearance.setTargetFeaturesSize(mTargetFeatures.size());
+ mAdapter.notifyDataSetChanged();
+
+ onSizeChanged();
+ onEdgeChanged();
+ }
+
+ void show() {
+ mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
+ mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
+ setVisibility(VISIBLE);
+ mMenuViewModel.registerContentObservers();
+ }
+
+ void hide() {
+ setVisibility(GONE);
+ mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
+ mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
+ mMenuViewModel.unregisterContentObservers();
+ }
+
+ void loadLayoutResources() {
+ mMenuViewAppearance.update();
+
+ setBackground(mMenuViewAppearance.getMenuBackground());
+ setElevation(mMenuViewAppearance.getMenuElevation());
+ onItemSizeChanged();
+ onSizeChanged();
+ onEdgeChanged();
+ }
+
+ private InstantInsetLayerDrawable getContainerViewInsetLayer() {
+ return (InstantInsetLayerDrawable) getBackground();
+ }
+
+ private GradientDrawable getContainerViewGradient() {
+ return (GradientDrawable) getContainerViewInsetLayer().getDrawable(INDEX_MENU_ITEM);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
new file mode 100644
index 000000000000..b9b7732605c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -0,0 +1,169 @@
+/*
+ * 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.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;
+
+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;
+ private int mElevation;
+ 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();
+
+ update();
+ }
+
+ 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);
+ mElevation = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
+ final Drawable drawable =
+ mRes.getDrawable(R.drawable.accessibility_floating_menu_background);
+ mBackgroundDrawable = new InstantInsetLayerDrawable(new Drawable[]{drawable});
+ }
+
+ void setSizeType(int sizeType) {
+ mSizeType = sizeType;
+
+ mRadii = createRadii(getMenuRadius(mTargetFeaturesSize));
+ }
+
+ void setTargetFeaturesSize(int targetFeaturesSize) {
+ mTargetFeaturesSize = targetFeaturesSize;
+
+ mRadii = createRadii(getMenuRadius(targetFeaturesSize));
+ }
+
+ Drawable getMenuBackground() {
+ return mBackgroundDrawable;
+ }
+
+ int getMenuElevation() {
+ return mElevation;
+ }
+
+ int getMenuHeight() {
+ return calculateActualMenuHeight();
+ }
+
+ int getMenuIconSize() {
+ return mSizeType == SMALL ? mSmallIconSize : mLargeIconSize;
+ }
+
+ int getMenuPadding() {
+ return mSizeType == SMALL ? mSmallPadding : mLargePadding;
+ }
+
+ int[] getMenuInsets() {
+ return new int[]{mInset, 0, 0, 0};
+ }
+
+ int getMenuStrokeWidth() {
+ return mStrokeWidth;
+ }
+
+ int getMenuStrokeColor() {
+ return mStrokeColor;
+ }
+
+ float[] getMenuRadii() {
+ return mRadii;
+ }
+
+ private int getMenuRadius(int itemCount) {
+ return mSizeType == SMALL ? getSmallSize(itemCount) : getLargeSize(itemCount);
+ }
+
+ @DimenRes
+ private int getSmallSize(int itemCount) {
+ 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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
new file mode 100644
index 000000000000..4ea2f7799c30
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.annotation.IntDef;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The basic interactions with the child view {@link MenuView}.
+ */
+@SuppressLint("ViewConstructor")
+class MenuViewLayer extends FrameLayout {
+ private final MenuView mMenuView;
+
+ @IntDef({
+ LayerIndex.MENU_VIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface LayerIndex {
+ int MENU_VIEW = 0;
+ }
+
+ MenuViewLayer(@NonNull Context context) {
+ super(context);
+
+ final MenuViewModel menuViewModel = new MenuViewModel(context);
+ final MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context);
+ mMenuView = new MenuView(context, menuViewModel, menuViewAppearance);
+
+ addView(mMenuView, LayerIndex.MENU_VIEW);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mMenuView.show();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mMenuView.hide();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
new file mode 100644
index 000000000000..1e15a599f796
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.WindowManager;
+
+/**
+ * Controls the {@link MenuViewLayer} whether to be attached to the window via the interface
+ * of {@link IAccessibilityFloatingMenu}.
+ */
+class MenuViewLayerController implements IAccessibilityFloatingMenu {
+ private final WindowManager mWindowManager;
+ private final MenuViewLayer mMenuViewLayer;
+ private boolean mIsShowing;
+
+ MenuViewLayerController(Context context, WindowManager windowManager) {
+ mWindowManager = windowManager;
+ mMenuViewLayer = new MenuViewLayer(context);
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mIsShowing;
+ }
+
+ @Override
+ public void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ mIsShowing = true;
+ mWindowManager.addView(mMenuViewLayer, createDefaultLayerLayoutParams());
+ }
+
+ @Override
+ public void hide() {
+ if (!isShowing()) {
+ return;
+ }
+
+ mIsShowing = false;
+ mWindowManager.removeView(mMenuViewLayer);
+ }
+
+ private static WindowManager.LayoutParams createDefaultLayerLayoutParams() {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+ params.windowAnimations = android.R.style.Animation_Translucent;
+
+ return params;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
new file mode 100644
index 000000000000..c3ba43950b6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.Context;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+
+import java.util.List;
+
+/**
+ * The view model provides the menu information from the repository{@link MenuInfoRepository} for
+ * the menu view{@link MenuView}.
+ */
+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) {
+ mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this);
+ }
+
+ @Override
+ public void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures) {
+ 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;
+ }
+
+ void registerContentObservers() {
+ mInfoRepository.registerContentObservers();
+ }
+
+ void unregisterContentObservers() {
+ mInfoRepository.unregisterContentObservers();
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index aa57175420ef..e3342b2befa3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -306,6 +306,10 @@ public class Flags {
// 1500 - chooser
public static final UnreleasedFlag CHOOSER_UNBUNDLED = new UnreleasedFlag(1500);
+ // 1600 - accessibility
+ public static final UnreleasedFlag A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS =
+ new UnreleasedFlag(1600);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 8ca17b974100..19a6c66652dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.flags.Flags.A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,10 +29,12 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.ContextWrapper;
+import android.hardware.display.DisplayManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -40,6 +44,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.flags.FakeFeatureFlags;
import org.junit.After;
import org.junit.Before;
@@ -53,7 +58,7 @@ import org.mockito.junit.MockitoRule;
/** Test for {@link AccessibilityFloatingMenuController}. */
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@@ -70,6 +75,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private KeyguardUpdateMonitorCallback mKeyguardCallback;
+ private int mLastButtonMode;
+ private String mLastButtonTargets;
@Before
public void setUp() throws Exception {
@@ -79,6 +86,11 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
return getBaseContext();
}
};
+
+ mLastButtonTargets = Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
+ mLastButtonMode = Settings.Secure.getIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, UserHandle.USER_CURRENT);
}
@After
@@ -87,6 +99,13 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mController.onAccessibilityButtonTargetsChanged("");
mController = null;
}
+
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastButtonTargets,
+ UserHandle.USER_CURRENT);
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mLastButtonMode,
+ UserHandle.USER_CURRENT);
}
@Test
@@ -287,13 +306,50 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
assertThat(mController.mFloatingMenu).isNull();
}
+ @Test
+ public void onTargetsChanged_flingSpringAnimationsDisabled_floatingMenuIsCreated() {
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
+ UserHandle.USER_CURRENT);
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, false);
+
+ mController = setUpController();
+ mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
+
+ assertThat(mController.mFloatingMenu).isInstanceOf(AccessibilityFloatingMenu.class);
+ }
+
+ @Test
+ public void onTargetsChanged_isFloatingViewLayerControllerCreated() {
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
+ UserHandle.USER_CURRENT);
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, true);
+
+ mController = setUpController(featureFlags);
+ mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
+
+ assertThat(mController.mFloatingMenu).isInstanceOf(MenuViewLayerController.class);
+ }
+
private AccessibilityFloatingMenuController setUpController() {
+ final FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+ featureFlags.set(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS, false);
+ return setUpController(featureFlags);
+ }
+
+ private AccessibilityFloatingMenuController setUpController(FakeFeatureFlags featureFlags) {
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
- new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
- mModeObserver, mKeyguardUpdateMonitor);
+ new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
+ displayManager, mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor,
+ featureFlags);
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
new file mode 100644
index 000000000000..d8b10e04705e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -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());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
new file mode 100644
index 000000000000..f782a446c627
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.any;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+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 MenuViewLayerController}. */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class MenuViewLayerControllerTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private MenuViewLayerController mMenuViewLayerController;
+
+ @Before
+ public void setUp() throws Exception {
+ mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager);
+ }
+
+ @Test
+ public void show_shouldAddViewToWindow() {
+ mMenuViewLayerController.show();
+
+ verify(mWindowManager).addView(any(View.class), any(ViewGroup.LayoutParams.class));
+ }
+
+ @Test
+ public void hide_menuIsShowing_removeViewFromWindow() {
+ mMenuViewLayerController.show();
+
+ mMenuViewLayerController.hide();
+
+ verify(mWindowManager).removeView(any(View.class));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
new file mode 100644
index 000000000000..8883cb783438
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+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 MenuViewLayer}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewLayerTest extends SysuiTestCase {
+ private MenuViewLayer mMenuViewLayer;
+
+ @Before
+ public void setUp() throws Exception {
+ mMenuViewLayer = new MenuViewLayer(mContext);
+ }
+
+ @Test
+ public void onAttachedToWindow_menuIsVisible() {
+ mMenuViewLayer.onAttachedToWindow();
+ final View menuView = mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
+
+ assertThat(menuView.getVisibility()).isEqualTo(VISIBLE);
+ }
+
+ @Test
+ public void onAttachedToWindow_menuIsGone() {
+ mMenuViewLayer.onDetachedFromWindow();
+ final View menuView = mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
+
+ assertThat(menuView.getVisibility()).isEqualTo(GONE);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
new file mode 100644
index 000000000000..513044d2c20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 android.app.UiModeManager.MODE_NIGHT_YES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.UiModeManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MenuView}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewTest extends SysuiTestCase {
+ private static final int INDEX_MENU_ITEM = 0;
+ private int mNightMode;
+ private UiModeManager mUiModeManager;
+ private MenuView mMenuView;
+
+ @Before
+ public void setUp() throws Exception {
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ mNightMode = mUiModeManager.getNightMode();
+ mUiModeManager.setNightMode(MODE_NIGHT_YES);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext);
+ final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext);
+ mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance));
+ }
+
+ @Test
+ public void onConfigurationChanged_updateViewModel() {
+ mMenuView.onConfigurationChanged(/* newConfig= */ null);
+
+ verify(mMenuView).loadLayoutResources();
+ }
+
+ @Test
+ public void insetsOnDarkTheme_menuOnLeft_matchInsets() {
+ mMenuView.onConfigurationChanged(/* newConfig= */ null);
+ final InstantInsetLayerDrawable insetLayerDrawable =
+ (InstantInsetLayerDrawable) mMenuView.getBackground();
+ final boolean areInsetsMatched = insetLayerDrawable.getLayerInsetLeft(INDEX_MENU_ITEM) != 0
+ && insetLayerDrawable.getLayerInsetRight(INDEX_MENU_ITEM) == 0;
+
+ assertThat(areInsetsMatched).isTrue();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUiModeManager.setNightMode(mNightMode);
+ }
+}