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

Commit 0843711f authored by Maryam Dehaini's avatar Maryam Dehaini Committed by Android (Google) Code Review
Browse files

Merge "Animate caption showing/hiding" into main

parents 2bb746d5 4cb975cd
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
import android.window.DesktopModeFlags;
import android.window.WindowContainerTransaction;

import androidx.annotation.NonNull;
@@ -88,6 +89,9 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
        updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);

        relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
        if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
            setCaptionVisibility(isCaptionVisible);
        }
        // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
        mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));

@@ -102,6 +106,15 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
        }
    }

    private void setCaptionVisibility(boolean visible) {
        if (mRootView == null) {
            return;
        }
        final int v = visible ? View.VISIBLE : View.GONE;
        final View captionView = mRootView.findViewById(getCaptionViewId());
        captionView.setVisibility(v);
    }

    @Override
    @NonNull
    Rect calculateValidDragArea() {
+6 −3
Original line number Diff line number Diff line
@@ -863,7 +863,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        if (!isAppHandle(mWindowDecorViewHolder)) return;
        asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
                mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
                mResult.mCaptionHeight, isCaptionVisible()
                mResult.mCaptionHeight, /* showInputLayer= */ isCaptionVisible(),
                /* isCaptionVisible= */ isCaptionVisible()
        ));
    }

@@ -876,7 +877,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                inFullImmersive,
                hasGlobalFocus,
                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
                    /* animatingTaskResizeOrReposition= */ false)
                    /* animatingTaskResizeOrReposition= */ false),
                isCaptionVisible()
        ));
    }

@@ -1866,7 +1868,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
                inFullImmersive,
                isFocused(),
                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition)));
                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition),
                isCaptionVisible()));
    }

    /**
+4 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.SurfaceSyncGroup;
import android.window.TaskConstants;
import android.window.WindowContainerToken;
@@ -652,8 +653,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
     */
    private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) {
        mIsCaptionVisible = params.mIsCaptionVisible;
        if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
            setCaptionVisibility(rootView, mIsCaptionVisible);
        }
    }

    void setTaskDragResizer(TaskDragResizer taskDragResizer) {
        mTaskDragResizer = taskDragResizer;
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.ObjectAnimator
import android.view.View
import android.view.View.Visibility
import android.view.animation.PathInterpolator
import android.widget.ImageButton
import androidx.core.animation.doOnEnd
import com.android.wm.shell.shared.animation.Interpolators

/**
 * Animates the Desktop View's app handle.
 */
class AppHandleAnimator(
    private val appHandleView: View,
    private val captionHandle: ImageButton,
) {
    companion object {
        //  Constants for animating the whole caption
        private const val APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS: Long = 275L
        private const val APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS: Long = 340
        private val APP_HANDLE_ANIMATION_INTERPOLATOR = PathInterpolator(
            0.4f,
            0f,
            0.2f,
            1f
        )

        // Constants for animating the caption's handle
        private const val HANDLE_ANIMATION_DURATION: Long = 100
        private val HANDLE_ANIMATION_INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN
    }

    private var animator: ObjectAnimator? = null

    /** Animates the given caption view to the given visibility after a visibility change. */
    fun animateVisibilityChange(@Visibility visible: Int) {
        when (visible) {
            View.VISIBLE -> animateShowAppHandle()
            else -> animateHideAppHandle()
        }
    }

    /** Animate appearance/disappearance of caption's handle. */
    fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
        cancel()
        animator = ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
            duration = HANDLE_ANIMATION_DURATION
            interpolator = HANDLE_ANIMATION_INTERPOLATOR
            start()
        }
    }

    private fun animateShowAppHandle() {
        cancel()
        appHandleView.alpha = 0f
        appHandleView.visibility = View.VISIBLE
        animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 1f).apply {
            duration = APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS
            interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
            start()
        }
    }

    private fun animateHideAppHandle() {
        cancel()
        animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 0f).apply {
            duration = APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS
            interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
            doOnEnd {
                appHandleView.visibility = View.GONE
            }
            start()
        }
    }

    /**
     * Cancels any active animations.
     */
    fun cancel() {
        animator?.removeAllListeners()
        animator?.cancel()
        animator = null
    }
}
+30 −21
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.wm.shell.windowdecor.viewholder

import android.animation.ObjectAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.res.ColorStateList
@@ -40,8 +39,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.windowdecor.AppHandleAnimator
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer

@@ -57,22 +56,20 @@ class AppHandleViewHolder(
    private val handler: Handler
) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) {

    companion object {
        private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
    }

    data class HandleData(
        val taskInfo: RunningTaskInfo,
        val position: Point,
        val width: Int,
        val height: Int,
        val showInputLayer: Boolean
        val showInputLayer: Boolean,
        val isCaptionVisible: Boolean,
    ) : Data()

    private lateinit var taskInfo: RunningTaskInfo
    private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
    private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
    private val inputManager = context.getSystemService(InputManager::class.java)
    private val animator: AppHandleAnimator = AppHandleAnimator(rootView, captionHandle)
    private var statusBarInputLayerExists = false

    // An invisible View that takes up the same coordinates as captionHandle but is layered
@@ -101,7 +98,14 @@ class AppHandleViewHolder(
    }

    override fun bindData(data: HandleData) {
        bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
        bindData(
            data.taskInfo,
            data.position,
            data.width,
            data.height,
            data.showInputLayer,
            data.isCaptionVisible
        )
    }

    private fun bindData(
@@ -109,8 +113,10 @@ class AppHandleViewHolder(
        position: Point,
        width: Int,
        height: Int,
        showInputLayer: Boolean
        showInputLayer: Boolean,
        isCaptionVisible: Boolean
    ) {
        setVisibility(isCaptionVisible)
        captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
        this.taskInfo = taskInfo
        // If handle is not in status bar region(i.e., bottom stage in vertical split),
@@ -131,11 +137,11 @@ class AppHandleViewHolder(
    }

    override fun onHandleMenuOpened() {
        animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
        animator.animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
    }

    override fun onHandleMenuClosed() {
        animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
        animator.animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
    }

    private fun createStatusBarInputLayer(handlePosition: Point,
@@ -239,6 +245,17 @@ class AppHandleViewHolder(
        }
    }

    private fun setVisibility(visible: Boolean) {
        val v = if (visible) View.VISIBLE else View.GONE
        if (
            captionView.visibility == v ||
                !DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()
        ) {
            return
        }
        animator.animateVisibilityChange(v)
    }

    private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
        return if (shouldUseLightCaptionColors(taskInfo)) {
            context.getColor(R.color.desktop_mode_caption_handle_bar_light)
@@ -264,17 +281,9 @@ class AppHandleViewHolder(
            } ?: false
    }

    /** Animate appearance/disappearance of caption handle as the handle menu is animated. */
    private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
        val animator =
            ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
                duration = CAPTION_HANDLE_ANIMATION_DURATION
                interpolator = Interpolators.FAST_OUT_SLOW_IN
    override fun close() {
        animator.cancel()
    }
        animator.start()
    }

    override fun close() {}

    /** Factory class for creating [AppHandleViewHolder] objects. */
    class Factory {
Loading