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
This commit is contained in:
Peter_Liang
2021-01-19 19:13:27 +08:00
parent 834d8fa5e0
commit ff5466d31a
3 changed files with 152 additions and 2 deletions

View File

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

View File

@@ -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;
* <p>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<AccessibilityTarget> 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<AccessibilityTarget> 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);

View File

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