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

Commit 990ba82c authored by Sukesh Ram's avatar Sukesh Ram Committed by Android (Google) Code Review
Browse files

Merge "Animate Handle Menu Closing" into main

parents b30e092e 2976b029
Loading
Loading
Loading
Loading
+15 −8
Original line number Diff line number Diff line
@@ -70,8 +70,8 @@ class HandleMenu {
    private int mMarginMenuStart;
    private int mMenuHeight;
    private int mMenuWidth;

    private final int mCaptionHeight;
    private HandleMenuAnimator mHandleMenuAnimator;


    HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
@@ -111,20 +111,19 @@ class HandleMenu {
        mHandleMenuWindow = mParentDecor.addWindow(
                R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
                t, ssg, x, y, mMenuWidth, mMenuHeight);
        final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
        mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight);
    }

    /**
     * Animates the appearance of the handle menu and its three pills.
     */
    private void animateHandleMenu() {
        final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
        final HandleMenuAnimator handleMenuAnimator = new HandleMenuAnimator(handleMenuView,
                mMenuWidth, mCaptionHeight);
        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
            handleMenuAnimator.animateCaptionHandleExpandToOpen();
            mHandleMenuAnimator.animateCaptionHandleExpandToOpen();
        } else {
            handleMenuAnimator.animateOpen();
            mHandleMenuAnimator.animateOpen();
        }
    }

@@ -328,8 +327,16 @@ class HandleMenu {
    }

    void close() {
        final Runnable after = () -> {
            mHandleMenuWindow.releaseView();
            mHandleMenuWindow = null;
        };
        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
            mHandleMenuAnimator.animateCollapseIntoHandleClose(after);
        } else {
            mHandleMenuAnimator.animateClose(after);
        }
    }

    static final class Builder {
+218 −58
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.view.View.SCALE_Y
import android.view.View.TRANSLATION_Y
import android.view.View.TRANSLATION_Z
import android.view.ViewGroup
import androidx.core.animation.doOnEnd
import androidx.core.view.children
import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators
@@ -37,27 +38,36 @@ class HandleMenuAnimator(
    private val captionHeight: Float
) {
    companion object {
        private const val MENU_Y_TRANSLATION_DURATION: Long = 150
        private const val HEADER_NONFREEFORM_SCALE_DURATION: Long = 150
        private const val HEADER_FREEFORM_SCALE_DURATION: Long = 217
        private const val HEADER_ELEVATION_DURATION: Long = 83
        private const val HEADER_CONTENT_ALPHA_DURATION: Long = 100
        private const val BODY_SCALE_DURATION: Long = 180
        private const val BODY_ALPHA_DURATION: Long = 150
        private const val BODY_ELEVATION_DURATION: Long = 83
        private const val BODY_CONTENT_ALPHA_DURATION: Long = 167

        private const val ELEVATION_DELAY: Long = 33
        private const val HEADER_CONTENT_ALPHA_DELAY: Long = 67
        private const val BODY_SCALE_DELAY: Long = 50
        private const val BODY_ALPHA_DELAY: Long = 133
        // Open animation constants
        private const val MENU_Y_TRANSLATION_OPEN_DURATION: Long = 150
        private const val HEADER_NONFREEFORM_SCALE_OPEN_DURATION: Long = 150
        private const val HEADER_FREEFORM_SCALE_OPEN_DURATION: Long = 217
        private const val HEADER_ELEVATION_OPEN_DURATION: Long = 83
        private const val HEADER_CONTENT_ALPHA_OPEN_DURATION: Long = 100
        private const val BODY_SCALE_OPEN_DURATION: Long = 180
        private const val BODY_ALPHA_OPEN_DURATION: Long = 150
        private const val BODY_ELEVATION_OPEN_DURATION: Long = 83
        private const val BODY_CONTENT_ALPHA_OPEN_DURATION: Long = 167

        private const val ELEVATION_OPEN_DELAY: Long = 33
        private const val HEADER_CONTENT_ALPHA_OPEN_DELAY: Long = 67
        private const val BODY_SCALE_OPEN_DELAY: Long = 50
        private const val BODY_ALPHA_OPEN_DELAY: Long = 133

        private const val HALF_INITIAL_SCALE: Float = 0.5f
        private const val NONFREEFORM_HEADER_INITIAL_SCALE_X: Float = 0.6f
        private const val NONFREEFORM_HEADER_INITIAL_SCALE_Y: Float = 0.05f

        // Close animation constants
        private const val HEADER_CLOSE_DELAY: Long = 20
        private const val HEADER_CLOSE_DURATION: Long = 50
        private const val HEADER_CONTENT_OPACITY_CLOSE_DELAY: Long = 25
        private const val HEADER_CONTENT_OPACITY_CLOSE_DURATION: Long = 25
        private const val BODY_CLOSE_DURATION: Long = 50
    }

    private val animators: MutableList<Animator> = mutableListOf()
    private var runningAnimation: AnimatorSet? = null

    private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill)
    private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill)
@@ -67,9 +77,9 @@ class HandleMenuAnimator(
    fun animateOpen() {
        prepareMenuForAnimation()
        appInfoPillExpand()
        animateAppInfoPill()
        animateWindowingPill()
        animateMoreActionsPill()
        animateAppInfoPillOpen()
        animateWindowingPillOpen()
        animateMoreActionsPillOpen()
        runAnimations()
    }

@@ -81,12 +91,43 @@ class HandleMenuAnimator(
    fun animateCaptionHandleExpandToOpen() {
        prepareMenuForAnimation()
        captionHandleExpandIntoAppInfoPill()
        animateAppInfoPill()
        animateWindowingPill()
        animateMoreActionsPill()
        animateAppInfoPillOpen()
        animateWindowingPillOpen()
        animateMoreActionsPillOpen()
        runAnimations()
    }

    /**
     * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then,
     * the app info pill will collapse into the shape of the caption handle in full screen and split
     * screen.
     *
     * @param after runs after the animation finishes.
     */
    fun animateCollapseIntoHandleClose(after: Runnable) {
        appInfoCollapseToHandle()
        animateAppInfoPillFadeOut()
        windowingPillClose()
        moreActionsPillClose()
        runAnimations(after)
    }

    /**
     * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then,
     * the app info pill will collapse into the shape of the caption handle in full screen and split
     * screen.
     *
     * @param after runs after animation finishes.
     *
     */
    fun animateClose(after: Runnable) {
        appInfoPillCollapse()
        animateAppInfoPillFadeOut()
        windowingPillClose()
        moreActionsPillClose()
        runAnimations(after)
    }

    /**
     * Prepares the handle menu for animation. Presets the opacity of necessary menu components.
     * Presets pivots of handle menu and body pills for scaling animation.
@@ -108,20 +149,20 @@ class HandleMenuAnimator(
        moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat()
    }

    private fun animateAppInfoPill() {
    private fun animateAppInfoPillOpen() {
        // Header Elevation Animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Z, 1f).apply {
                startDelay = ELEVATION_DELAY
                duration = HEADER_ELEVATION_DURATION
                startDelay = ELEVATION_OPEN_DELAY
                duration = HEADER_ELEVATION_OPEN_DURATION
            }

        // Content Opacity Animation
        appInfoPill.children.forEach {
            animators +=
                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
                    startDelay = HEADER_CONTENT_ALPHA_DELAY
                    duration = HEADER_CONTENT_ALPHA_DURATION
                    startDelay = HEADER_CONTENT_ALPHA_OPEN_DELAY
                    duration = HEADER_CONTENT_ALPHA_OPEN_DURATION
                }
        }
    }
@@ -130,17 +171,17 @@ class HandleMenuAnimator(
        // Header scaling animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f)
                .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
                .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION }

        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y, 1f)
                .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
                .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION }

        // Downward y-translation animation
        val yStart: Float = -captionHeight / 2
        animators +=
            ObjectAnimator.ofFloat(handleMenu, TRANSLATION_Y, yStart, 0f).apply {
                duration = MENU_Y_TRANSLATION_DURATION
                duration = MENU_Y_TRANSLATION_OPEN_DURATION
            }
    }

@@ -148,98 +189,217 @@ class HandleMenuAnimator(
        // Header scaling animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
                duration = HEADER_FREEFORM_SCALE_DURATION
                duration = HEADER_FREEFORM_SCALE_OPEN_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
                duration = HEADER_FREEFORM_SCALE_DURATION
                duration = HEADER_FREEFORM_SCALE_OPEN_DURATION
            }
    }

    private fun animateWindowingPill() {
    private fun animateWindowingPillOpen() {
        // Windowing X & Y Scaling Animation
        animators +=
            ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
                startDelay = BODY_SCALE_DELAY
                duration = BODY_SCALE_DURATION
                startDelay = BODY_SCALE_OPEN_DELAY
                duration = BODY_SCALE_OPEN_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
                startDelay = BODY_SCALE_DELAY
                duration = BODY_SCALE_DURATION
                startDelay = BODY_SCALE_OPEN_DELAY
                duration = BODY_SCALE_OPEN_DURATION
            }

        // Windowing Opacity Animation
        animators +=
            ObjectAnimator.ofFloat(windowingPill, ALPHA, 1f).apply {
                startDelay = BODY_ALPHA_DELAY
                duration = BODY_ALPHA_DURATION
                startDelay = BODY_ALPHA_OPEN_DELAY
                duration = BODY_ALPHA_OPEN_DURATION
            }

        // Windowing Elevation Animation
        animators +=
            ObjectAnimator.ofFloat(windowingPill, TRANSLATION_Z, 1f).apply {
                startDelay = ELEVATION_DELAY
                duration = BODY_ELEVATION_DURATION
                startDelay = ELEVATION_OPEN_DELAY
                duration = BODY_ELEVATION_OPEN_DURATION
            }

        // Windowing Content Opacity Animation
        windowingPill.children.forEach {
            animators +=
                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
                    startDelay = BODY_ALPHA_DELAY
                    duration = BODY_CONTENT_ALPHA_DURATION
                    startDelay = BODY_ALPHA_OPEN_DELAY
                    duration = BODY_CONTENT_ALPHA_OPEN_DURATION
                    interpolator = Interpolators.FAST_OUT_SLOW_IN
                }
        }
    }

    private fun animateMoreActionsPill() {
    private fun animateMoreActionsPillOpen() {
        // More Actions X & Y Scaling Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
                startDelay = BODY_SCALE_DELAY
                duration = BODY_SCALE_DURATION
                startDelay = BODY_SCALE_OPEN_DELAY
                duration = BODY_SCALE_OPEN_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
                startDelay = BODY_SCALE_DELAY
                duration = BODY_SCALE_DURATION
                startDelay = BODY_SCALE_OPEN_DELAY
                duration = BODY_SCALE_OPEN_DURATION
            }

        // More Actions Opacity Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 1f).apply {
                startDelay = BODY_ALPHA_DELAY
                duration = BODY_ALPHA_DURATION
                startDelay = BODY_ALPHA_OPEN_DELAY
                duration = BODY_ALPHA_OPEN_DURATION
            }

        // More Actions Elevation Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply {
                startDelay = ELEVATION_DELAY
                duration = BODY_ELEVATION_DURATION
                startDelay = ELEVATION_OPEN_DELAY
                duration = BODY_ELEVATION_OPEN_DURATION
            }

        // More Actions Content Opacity Animation
        moreActionsPill.children.forEach {
            animators +=
                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
                    startDelay = BODY_ALPHA_DELAY
                    duration = BODY_CONTENT_ALPHA_DURATION
                    startDelay = BODY_ALPHA_OPEN_DELAY
                    duration = BODY_CONTENT_ALPHA_OPEN_DURATION
                    interpolator = Interpolators.FAST_OUT_SLOW_IN
                }
        }
    }

    /** Runs the list of animators concurrently. */
    private fun runAnimations() {
        val animatorSet = AnimatorSet()
        animatorSet.playTogether(animators)
        animatorSet.start()
    private fun appInfoPillCollapse() {
        // Header scaling animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, 0f).apply {
                startDelay = HEADER_CLOSE_DELAY
                duration = HEADER_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, 0f).apply {
                startDelay = HEADER_CLOSE_DELAY
                duration = HEADER_CLOSE_DURATION
            }
    }

    private fun appInfoCollapseToHandle() {
        // Header X & Y Scaling Animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X).apply {
                startDelay = HEADER_CLOSE_DELAY
                duration = HEADER_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y).apply {
                startDelay = HEADER_CLOSE_DELAY
                duration = HEADER_CLOSE_DURATION
            }
        // Upward y-translation animation
        val yStart: Float = -captionHeight / 2
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Y, yStart).apply {
                startDelay = HEADER_CLOSE_DELAY
                duration = HEADER_CLOSE_DURATION
            }
    }

    private fun animateAppInfoPillFadeOut() {
        // Header Content Opacity Animation
        appInfoPill.children.forEach {
            animators +=
                ObjectAnimator.ofFloat(it, ALPHA, 0f).apply {
                    startDelay = HEADER_CONTENT_OPACITY_CLOSE_DELAY
                    duration = HEADER_CONTENT_OPACITY_CLOSE_DURATION
                }
        }
    }

    private fun windowingPillClose() {
        // Windowing X & Y Scaling Animation
        animators +=
            ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE).apply {
                duration = BODY_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
                duration = BODY_CLOSE_DURATION
            }

        // windowing Animation
        animators +=
            ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply {
                duration = BODY_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply {
                duration = BODY_CLOSE_DURATION
            }
    }

    private fun moreActionsPillClose() {
        // More Actions X & Y Scaling Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE).apply {
                duration = BODY_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
                duration = BODY_CLOSE_DURATION
            }

        // More Actions Opacity Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply {
                duration = BODY_CLOSE_DURATION
            }

        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply {
                duration = BODY_CLOSE_DURATION
            }

        // upward more actions pill y-translation animation
        val yStart: Float = -captionHeight / 2
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Y, yStart).apply {
                duration = BODY_CLOSE_DURATION
            }
    }

    /**
     * Runs the list of hide animators concurrently.
     *
     * @param after runs after animation finishes.
     */
    private fun runAnimations(after: Runnable? = null) {
        runningAnimation?.apply {
            // Remove all listeners, so that after runnable isn't triggered upon cancel.
            removeAllListeners()
            // If an animation runs while running animation is triggered, gracefully cancel.
            cancel()
        }

        runningAnimation = AnimatorSet().apply {
            playTogether(animators)
            animators.clear()
            doOnEnd {
                after?.run()
                runningAnimation = null
            }
            start()
        }
    }
}