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

Commit 4831205e authored by Riley Jones's avatar Riley Jones Committed by Android (Google) Code Review
Browse files

Merge "Added support to animate transitions for menu radii" into main

parents 755dd4b3 d9786b75
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -22,3 +22,10 @@ flag {
    description: "Adjusts bounds to allow the floating menu to render on top of navigation bars."
    bug: "283768342"
}

flag {
    name: "floating_menu_radii_animation"
    namespace: "accessibility"
    description: "Animates the floating menu's transition between curved and jagged edges."
    bug: "281140482"
}
 No newline at end of file
+21 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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;

interface IRadiiAnimationListener {
    void onRadiiAnimationUpdate(float[] radii);
}
+11 −1
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ class MenuAnimationController {
    private static final int FADE_EFFECT_DURATION_MS = 3000;

    private final MenuView mMenuView;
    private final MenuViewAppearance mMenuViewAppearance;
    private final ValueAnimator mFadeOutAnimator;
    private final Handler mHandler;
    private boolean mIsFadeEffectEnabled;
@@ -81,14 +82,19 @@ class MenuAnimationController {
    final HashMap<DynamicAnimation.ViewProperty, DynamicAnimation> mPositionAnimations =
            new HashMap<>();

    MenuAnimationController(MenuView menuView) {
    @VisibleForTesting
    final RadiiAnimator mRadiiAnimator;

    MenuAnimationController(MenuView menuView, MenuViewAppearance menuViewAppearance) {
        mMenuView = menuView;
        mMenuViewAppearance = menuViewAppearance;

        mHandler = createUiHandler();
        mFadeOutAnimator = new ValueAnimator();
        mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS);
        mFadeOutAnimator.addUpdateListener(
                (animation) -> menuView.setAlpha((float) animation.getAnimatedValue()));
        mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), mMenuView::setRadii);
    }

    void moveToPosition(PointF position) {
@@ -427,6 +433,10 @@ class MenuAnimationController {
                .start();
    }

    void startRadiiAnimation(float[] endRadii) {
        mRadiiAnimator.startAnimation(endRadii);
    }

    private void onSpringAnimationsEnd(PointF position, boolean writeToPosition) {
        mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
        constrainPositionAndUpdate(position, writeToPosition);
+17 −4
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ class MenuView extends FrameLayout implements

        mMenuViewModel = menuViewModel;
        mMenuViewAppearance = menuViewAppearance;
        mMenuAnimationController = new MenuAnimationController(this);
        mMenuAnimationController = new MenuAnimationController(this, menuViewAppearance);
        mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
        mTargetFeaturesView = new RecyclerView(context);
        mTargetFeaturesView.setAdapter(mAdapter);
@@ -184,9 +184,17 @@ class MenuView extends FrameLayout implements
                insets[3]);

        final GradientDrawable gradientDrawable = getContainerViewGradient();
        gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
        gradientDrawable.setStroke(mMenuViewAppearance.getMenuStrokeWidth(),
                mMenuViewAppearance.getMenuStrokeColor());
        if (Flags.floatingMenuRadiiAnimation()) {
            mMenuAnimationController.startRadiiAnimation(mMenuViewAppearance.getMenuRadii());
        } else {
            gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
        }
    }

    void setRadii(float[] radii) {
        getContainerViewGradient().setCornerRadii(radii);
    }

    private void onMoveToTucked(boolean isMoveToTucked) {
@@ -391,9 +399,14 @@ class MenuView extends FrameLayout implements
        getContainerViewInsetLayer().setLayerInset(INDEX_MENU_ITEM, insets[0], insets[1], insets[2],
                insets[3]);

        if (Flags.floatingMenuRadiiAnimation()) {
            mMenuAnimationController.startRadiiAnimation(
                    mMenuViewAppearance.getMenuMovingStateRadii());
        } else {
            final GradientDrawable gradientDrawable = getContainerViewGradient();
            gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuMovingStateRadii());
        }
    }

    void onBoundsInParentChanged(int newLeft, int newTop) {
        mBoundsInParent.offsetTo(newLeft, newTop);
+104 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.animation.Animator;
import android.animation.ValueAnimator;
import android.util.MathUtils;

import androidx.annotation.NonNull;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;

/**
 * Manages the animation of the floating menu's radii.
 * <p>
 * There are 8 output values total. There are 4 corners,
 * and each corner has a value for the x and y axes.
 */
class RadiiAnimator {
    static final int RADII_COUNT = 8;

    private float[] mStartValues;
    private float[] mEndValues;
    private final ValueAnimator mAnimationDriver = ValueAnimator.ofFloat(0.0f, 1.0f);

    RadiiAnimator(float[] initialValues, IRadiiAnimationListener animationListener) {
        if (initialValues.length != RADII_COUNT) {
            initialValues = Arrays.copyOf(initialValues, RADII_COUNT);
        }

        mStartValues = initialValues;
        mEndValues = initialValues;

        mAnimationDriver.setRepeatCount(0);
        mAnimationDriver.addUpdateListener(
                animation -> animationListener.onRadiiAnimationUpdate(
                        evaluate(animation.getAnimatedFraction())));
        mAnimationDriver.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(@NonNull Animator animation) {
                animationListener.onRadiiAnimationUpdate(evaluate(/* t = */ 0.0f));
            }

            @Override
            public void onAnimationEnd(@NonNull Animator animation) {}

            @Override
            public void onAnimationCancel(@NonNull Animator animation) {
                animationListener.onRadiiAnimationUpdate(
                        evaluate(mAnimationDriver.getAnimatedFraction()));
            }

            @Override
            public void onAnimationRepeat(@NonNull Animator animation) {}
        });
        mAnimationDriver.setInterpolator(new android.view.animation.BounceInterpolator());
    }

    void startAnimation(float[] endValues) {
        if (mAnimationDriver.isStarted()) {
            mAnimationDriver.cancel();
            mStartValues = evaluate(mAnimationDriver.getAnimatedFraction());
        } else {
            mStartValues = mEndValues;
        }
        mEndValues = endValues;

        mAnimationDriver.start();
    }

    void skipAnimationToEnd() {
        mAnimationDriver.end();
    }

    @VisibleForTesting
    float[] evaluate(float time /* interpolator value between 0.0 and 1.0 */) {
        float[] out = new float[8];
        for (int i = 0; i < RADII_COUNT; i++) {
            out[i] = MathUtils.lerp(mStartValues[i], mEndValues[i], time);
        }
        return out;
    }

    boolean isStarted() {
        return mAnimationDriver.isStarted();
    }
}
Loading