Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff5466d3 authored by Peter_Liang's avatar Peter_Liang
Browse files

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
parent 834d8fa5
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -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);
    }
}
+98 −1
Original line number Diff line number Diff line
@@ -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);
+18 −1
Original line number Diff line number Diff line
@@ -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();
    }
}