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