From ff5466d31ad1c384ace586646dd092a82af19a93 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Tue, 19 Jan 2021 19:13:27 +0800 Subject: [PATCH] Add new floating action menu for Accessibility targets. (3/n) Patch action: Obsevers the settings key and update the corresponding status. Bug: 173958541 Test: atest AccessibilityFloatingMenuTest AccessibilityFloatingMenuViewTest AccessibilityTargetAdapterTest Change-Id: Ic3a499f48613519313802298ee09b1e06abf1b1d --- .../AccessibilityFloatingMenu.java | 36 +++++++ .../AccessibilityFloatingMenuView.java | 99 ++++++++++++++++++- .../AccessibilityFloatingMenuViewTest.java | 19 +++- 3 files changed, 152 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java index b25a76a59ba82..b71e135fe644d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java @@ -16,7 +16,9 @@ package com.android.systemui.accessibility.floatingmenu; +import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED; import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE; +import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY; import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; @@ -37,6 +39,8 @@ import com.android.internal.annotations.VisibleForTesting; * Contains logic for an accessibility floating menu view. */ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { + private static final int DEFAULT_FADE_EFFECT_ENABLED = 1; + private static final float DEFAULT_OPACITY_VALUE = 0.55f; private final Context mContext; private final AccessibilityFloatingMenuView mMenuView; @@ -64,6 +68,15 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { } }; + private final ContentObserver mFadeOutContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + mMenuView.updateOpacityWith(isFadeEffectEnabled(mContext), + getOpacityValue(mContext)); + } + }; + public AccessibilityFloatingMenu(Context context) { mContext = context; mMenuView = new AccessibilityFloatingMenuView(context); @@ -88,6 +101,8 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { mMenuView.show(); mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON)); + mMenuView.updateOpacityWith(isFadeEffectEnabled(mContext), + getOpacityValue(mContext)); mMenuView.setSizeType(getSizeType(mContext)); mMenuView.setShapeType(getShapeType(mContext)); @@ -105,6 +120,18 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { unregisterContentObservers(); } + private static boolean isFadeEffectEnabled(Context context) { + return Settings.Secure.getInt( + context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, + DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1; + } + + private static float getOpacityValue(Context context) { + return Settings.Secure.getFloat( + context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_OPACITY, + DEFAULT_OPACITY_VALUE); + } + private static int getSizeType(Context context) { return Settings.Secure.getInt( context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_SIZE, SizeType.SMALL); @@ -129,11 +156,20 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu { Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE), /* notifyForDescendants */ false, mShapeContentObserver, UserHandle.USER_CURRENT); + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED), + /* notifyForDescendants */ false, mFadeOutContentObserver, + UserHandle.USER_CURRENT); + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY), + /* notifyForDescendants */ false, mFadeOutContentObserver, + UserHandle.USER_CURRENT); } private void unregisterContentObservers() { mContext.getContentResolver().unregisterContentObserver(mContentObserver); mContext.getContentResolver().unregisterContentObserver(mSizeContentObserver); mContext.getContentResolver().unregisterContentObserver(mShapeContentObserver); + mContext.getContentResolver().unregisterContentObserver(mFadeOutContentObserver); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java index 9f147ee936172..3aed1d857cd16 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -16,6 +16,7 @@ package com.android.systemui.accessibility.floatingmenu; +import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.content.res.Configuration; @@ -25,6 +26,8 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; +import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.MotionEvent; @@ -33,6 +36,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; import androidx.annotation.DimenRes; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -52,9 +56,14 @@ import java.util.List; *

The number of items would depend on strings key * {@link android.provider.Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}. */ -public class AccessibilityFloatingMenuView extends FrameLayout { +public class AccessibilityFloatingMenuView extends FrameLayout + implements RecyclerView.OnItemTouchListener { private static final float DEFAULT_LOCATION_Y_PERCENTAGE = 0.8f; private static final int INDEX_MENU_ITEM = 0; + private static final int FADE_OUT_DURATION_MS = 1000; + private static final int FADE_EFFECT_DURATION_MS = 3000; + + private boolean mIsFadeEffectEnabled; private boolean mIsShowing; @SizeType private int mSizeType = SizeType.SMALL; @@ -66,6 +75,9 @@ public class AccessibilityFloatingMenuView extends FrameLayout { private int mIconHeight; private final RecyclerView mListView; private final AccessibilityTargetAdapter mAdapter; + private float mFadeOutValue; + private final ValueAnimator mFadeOutAnimator; + private final Handler mUiHandler; private final WindowManager.LayoutParams mLayoutParams; private final WindowManager mWindowManager; private final List mTargets = new ArrayList<>(); @@ -103,6 +115,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout { mWindowManager = context.getSystemService(WindowManager.class); mLayoutParams = createDefaultLayoutParams(); mAdapter = new AccessibilityTargetAdapter(mTargets); + mUiHandler = createUiHandler(); + + mFadeOutAnimator = ValueAnimator.ofFloat(1.0f, mFadeOutValue); + mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS); + mFadeOutAnimator.addUpdateListener( + (animation) -> setAlpha((float) animation.getAnimatedValue())); updateDimensions(); initListView(); @@ -111,6 +129,36 @@ public class AccessibilityFloatingMenuView extends FrameLayout { updateStrokeWith(uiMode); } + @Override + public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, + @NonNull MotionEvent event) { + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + fadeIn(); + break; + case MotionEvent.ACTION_MOVE: + // Do nothing + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + fadeOut(); + break; + default: // Do nothing + } + return false; + } + + @Override + public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) { + // Do Nothing + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean b) { + // Do Nothing + } + void show() { if (isShowing()) { return; @@ -134,14 +182,20 @@ public class AccessibilityFloatingMenuView extends FrameLayout { } void onTargetsChanged(List newTargets) { + fadeIn(); + mTargets.clear(); mTargets.addAll(newTargets); mAdapter.notifyDataSetChanged(); updateRadiusWith(mSizeType, mTargets.size()); + + fadeOut(); } void setSizeType(@SizeType int newSizeType) { + fadeIn(); + mSizeType = newSizeType; updateIconSizeWith(newSizeType); @@ -149,9 +203,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout { // When the icon sized changed, the menu size and location will be impacted. updateLocation(); + + fadeOut(); } void setShapeType(@ShapeType int newShapeType) { + fadeIn(); + final boolean isCircleShape = newShapeType == ShapeType.CIRCLE; final float offset = @@ -164,6 +222,36 @@ public class AccessibilityFloatingMenuView extends FrameLayout { isCircleShape ? null : (view, event) -> onTouched(event)); + + fadeOut(); + } + + void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) { + mIsFadeEffectEnabled = isFadeEffectEnabled; + mFadeOutValue = newOpacityValue; + + mFadeOutAnimator.cancel(); + mFadeOutAnimator.setFloatValues(1.0f, mFadeOutValue); + setAlpha(mIsFadeEffectEnabled ? mFadeOutValue : /* completely opaque */ 1.0f); + } + + @VisibleForTesting + void fadeIn() { + if (!mIsFadeEffectEnabled) { + return; + } + + mUiHandler.removeCallbacksAndMessages(null); + mUiHandler.post(() -> setAlpha(/* completely opaque */ 1.0f)); + } + + @VisibleForTesting + void fadeOut() { + if (!mIsFadeEffectEnabled) { + return; + } + + mUiHandler.postDelayed(() -> mFadeOutAnimator.start(), FADE_EFFECT_DURATION_MS); } private boolean onTouched(MotionEvent event) { @@ -187,6 +275,14 @@ public class AccessibilityFloatingMenuView extends FrameLayout { getMenuGradientDrawable().setCornerRadii(radii); } + private Handler createUiHandler() { + final Looper looper = Looper.myLooper(); + if (looper == null) { + throw new IllegalArgumentException("looper must not be null"); + } + return new Handler(looper); + } + private void updateDimensions() { final Resources res = getResources(); final DisplayMetrics dm = res.getDisplayMetrics(); @@ -218,6 +314,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout { mListView.setBackground(listViewBackground); mListView.setAdapter(mAdapter); mListView.setLayoutManager(layoutManager); + mListView.addOnItemTouchListener(this); updateListView(); addView(mListView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java index 9ff697cc9957a..124a749e2be12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java @@ -82,7 +82,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { mTargets.add(mock(AccessibilityTarget.class)); mListView = spy(new RecyclerView(mContext)); - mMenuView = new AccessibilityFloatingMenuView(mContext); + mMenuView = spy(new AccessibilityFloatingMenuView(mContext)); } @Test @@ -177,4 +177,21 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { verify(mAnimator).translationX(anyFloat()); } + + @Test + public void onTargetsChanged_fadeInOut() { + mMenuView.onTargetsChanged(mTargets); + + verify(mMenuView).fadeIn(); + verify(mMenuView).fadeOut(); + } + + @Test + public void setSizeType_fadeInOut() { + final int smallSize = 0; + mMenuView.setSizeType(smallSize); + + verify(mMenuView).fadeIn(); + verify(mMenuView).fadeOut(); + } }