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

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

Merge "Animate Handle Menu Open & Caption Handle Vanish" into main

parents 1068dcd8 fd6831c3
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/app_info_pill"
        android:layout_width="match_parent"
        android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
        android:layout_marginTop="@dimen/desktop_mode_handle_menu_margin_top"
@@ -66,6 +67,7 @@
    </LinearLayout>

    <LinearLayout
        android:id="@+id/windowing_pill"
        android:layout_width="match_parent"
        android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
        android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
@@ -116,6 +118,7 @@
    </LinearLayout>

    <LinearLayout
        android:id="@+id/more_actions_pill"
        android:layout_width="match_parent"
        android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
        android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
+5 −2
Original line number Diff line number Diff line
@@ -455,7 +455,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
    }

    /**
     * Create and display handle menu window
     * Create and display handle menu window.
     */
    void createHandleMenu() {
        mHandleMenu = new HandleMenu.Builder(this)
@@ -466,15 +466,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                .setLayoutId(mRelayoutParams.mLayoutResId)
                .setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
                .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
                .setCaptionHeight(mResult.mCaptionHeight)
                .build();
        mWindowDecorViewHolder.onHandleMenuOpened();
        mHandleMenu.show();
    }

    /**
     * Close the handle menu window
     * Close the handle menu window.
     */
    void closeHandleMenu() {
        if (!isHandleMenuActive()) return;
        mWindowDecorViewHolder.onHandleMenuClosed();
        mHandleMenu.close();
        mHandleMenu = null;
    }
+28 −2
Original line number Diff line number Diff line
@@ -71,10 +71,13 @@ class HandleMenu {
    private int mMenuHeight;
    private int mMenuWidth;

    private final int mCaptionHeight;


    HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
            View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
            Drawable appIcon, CharSequence appName, boolean shouldShowWindowingPill) {
            Drawable appIcon, CharSequence appName, boolean shouldShowWindowingPill,
            int captionHeight) {
        mParentDecor = parentDecor;
        mContext = mParentDecor.mDecorWindowContext;
        mTaskInfo = mParentDecor.mTaskInfo;
@@ -86,6 +89,7 @@ class HandleMenu {
        mAppIcon = appIcon;
        mAppName = appName;
        mShouldShowWindowingPill = shouldShowWindowingPill;
        mCaptionHeight = captionHeight;
        loadHandleMenuDimensions();
        updateHandleMenuPillPositions();
    }
@@ -98,6 +102,7 @@ class HandleMenu {
        ssg.addTransaction(t);
        ssg.markSyncReady();
        setupHandleMenu();
        animateHandleMenu();
    }

    private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
@@ -108,6 +113,21 @@ class HandleMenu {
                t, ssg, x, y, mMenuWidth, mMenuHeight);
    }

    /**
     * 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();
        } else {
            handleMenuAnimator.animateOpen();
        }
    }

    /**
     * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
     * pill.
@@ -322,6 +342,7 @@ class HandleMenu {
        private int mCaptionX;
        private int mCaptionY;
        private boolean mShowWindowingPill;
        private int mCaptionHeight;


        Builder(@NonNull WindowDecoration parent) {
@@ -364,9 +385,14 @@ class HandleMenu {
            return this;
        }

        Builder setCaptionHeight(int captionHeight) {
            mCaptionHeight = captionHeight;
            return this;
        }

        HandleMenu build() {
            return new HandleMenu(mParent, mLayoutId, mCaptionX, mCaptionY, mOnClickListener,
                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill);
                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
        }
    }
}
+245 −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.wm.shell.windowdecor

import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View
import android.view.View.ALPHA
import android.view.View.SCALE_X
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.view.children
import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators

/** Animates the Handle Menu opening. */
class HandleMenuAnimator(
    private val handleMenu: View,
    private val menuWidth: Int,
    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

        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
    }

    private val animators: MutableList<Animator> = mutableListOf()

    private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill)
    private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill)
    private val moreActionsPill: ViewGroup = handleMenu.requireViewById(R.id.more_actions_pill)

    /** Animates the opening of the handle menu. */
    fun animateOpen() {
        prepareMenuForAnimation()
        appInfoPillExpand()
        animateAppInfoPill()
        animateWindowingPill()
        animateMoreActionsPill()
        runAnimations()
    }

    /**
     * Animates the opening of the handle menu. The caption handle in full screen and split screen
     * will expand until it assumes the shape of the app info pill. Then, the other two pills will
     * appear.
     */
    fun animateCaptionHandleExpandToOpen() {
        prepareMenuForAnimation()
        captionHandleExpandIntoAppInfoPill()
        animateAppInfoPill()
        animateWindowingPill()
        animateMoreActionsPill()
        runAnimations()
    }

    /**
     * Prepares the handle menu for animation. Presets the opacity of necessary menu components.
     * Presets pivots of handle menu and body pills for scaling animation.
     */
    private fun prepareMenuForAnimation() {
        // Preset opacity
        appInfoPill.children.forEach { it.alpha = 0f }
        windowingPill.alpha = 0f
        moreActionsPill.alpha = 0f

        // Setup pivots.
        handleMenu.pivotX = menuWidth / 2f
        handleMenu.pivotY = 0f

        windowingPill.pivotX = menuWidth / 2f
        windowingPill.pivotY = appInfoPill.measuredHeight.toFloat()

        moreActionsPill.pivotX = menuWidth / 2f
        moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat()
    }

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

        // Content Opacity Animation
        appInfoPill.children.forEach {
            animators +=
                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
                    startDelay = HEADER_CONTENT_ALPHA_DELAY
                    duration = HEADER_CONTENT_ALPHA_DURATION
                }
        }
    }

    private fun captionHandleExpandIntoAppInfoPill() {
        // Header scaling animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f)
                .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }

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

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

    private fun appInfoPillExpand() {
        // Header scaling animation
        animators +=
            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
                duration = HEADER_FREEFORM_SCALE_DURATION
            }

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

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

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

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

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

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

    private fun animateMoreActionsPill() {
        // 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
            }

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

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

        // More Actions Elevation Animation
        animators +=
            ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply {
                startDelay = ELEVATION_DELAY
                duration = BODY_ELEVATION_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
                    interpolator = Interpolators.FAST_OUT_SLOW_IN
                }
        }
    }

    /** Runs the list of animators concurrently. */
    private fun runAnimations() {
        val animatorSet = AnimatorSet()
        animatorSet.playTogether(animators)
        animatorSet.start()
        animators.clear()
    }
}
+6 −4
Original line number Diff line number Diff line
@@ -269,10 +269,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
                    .build();
        }

        final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
        outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
        final int captionWidth = taskBounds.width();

        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, outResult.mCaptionHeight)
                .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                .show(mCaptionContainerSurface);

@@ -283,7 +283,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
            mCaptionInsetsRect.set(taskBounds);
            if (mIsCaptionVisible) {
                mCaptionInsetsRect.bottom =
                        mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
                        mCaptionInsetsRect.top + outResult.mCaptionHeight + params.mCaptionY;
                wct.addInsetsSource(mTaskInfo.token,
                        mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
                wct.addInsetsSource(mTaskInfo.token,
@@ -348,7 +348,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        // Caption view
        mCaptionWindowManager.setConfiguration(taskConfig);
        final WindowManager.LayoutParams lp =
                new WindowManager.LayoutParams(captionWidth, captionHeight,
                new WindowManager.LayoutParams(captionWidth, outResult.mCaptionHeight,
                        WindowManager.LayoutParams.TYPE_APPLICATION,
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
@@ -569,6 +569,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
    }

    static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
        int mCaptionHeight;
        int mWidth;
        int mHeight;
        T mRootView;
@@ -576,6 +577,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        void reset() {
            mWidth = 0;
            mHeight = 0;
            mCaptionHeight = 0;
            mRootView = null;
        }
    }
Loading