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

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

Merge "Add a Visual Indicator for fullscreen to freeform transitions" into udc-dev

parents f735be31 6f177a57
Loading
Loading
Loading
Loading
+144 −36
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.wm.shell.desktopmode;

import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -32,12 +35,12 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;

import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;

/**
@@ -56,6 +59,9 @@ public class DesktopModeVisualIndicator {
    private final SyncTransactionQueue mSyncQueue;
    private SurfaceControlViewHost mViewHost;

    private View mView;
    private boolean mIsFullscreen;

    public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue,
            ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
            Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer,
@@ -67,21 +73,19 @@ public class DesktopModeVisualIndicator {
        mTaskSurface = taskSurface;
        mTaskOrganizer = taskOrganizer;
        mRootTdaOrganizer = taskDisplayAreaOrganizer;
        createView();
    }

    /**
     * Create and animate the indicator for the exit desktop mode transition.
     * Create a fullscreen indicator with no animation
     */
    public void createFullscreenIndicator() {
    private void createView() {
        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
        final Resources resources = mContext.getResources();
        final DisplayMetrics metrics = resources.getDisplayMetrics();
        final int screenWidth = metrics.widthPixels;
        final int screenHeight = metrics.heightPixels;
        final int padding = mDisplayController
                .getDisplayLayout(mTaskInfo.displayId).stableInsets().top;
        final ImageView v = new ImageView(mContext);
        v.setImageResource(R.drawable.desktop_windowing_transition_background);
        mView = new View(mContext);
        final SurfaceControl.Builder builder = new SurfaceControl.Builder();
        mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
        mLeash = builder
@@ -101,7 +105,7 @@ public class DesktopModeVisualIndicator {
        mViewHost = new SurfaceControlViewHost(mContext,
                mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
                "FullscreenVisualIndicator");
        mViewHost.setView(v, lp);
        mViewHost.setView(mView, lp);
        // We want this indicator to be behind the dragged task, but in front of all others.
        t.setRelativeLayer(mLeash, mTaskSurface, -1);

@@ -109,17 +113,56 @@ public class DesktopModeVisualIndicator {
            transaction.merge(t);
            t.close();
        });
        final Rect startBounds = new Rect(padding, padding,
                screenWidth - padding, screenHeight - padding);
        final VisualIndicatorAnimator animator = VisualIndicatorAnimator.fullscreenIndicator(v,
                startBounds);
    }

    /**
     * Create fullscreen indicator and fades it in.
     */
    public void createFullscreenIndicator() {
        mIsFullscreen = true;
        mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
        final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator(
                mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
        animator.start();
    }

    /**
     * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards.
     */
    public void createFullscreenIndicatorWithAnimatedBounds() {
        mIsFullscreen = true;
        mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
        final VisualIndicatorAnimator animator = VisualIndicatorAnimator
                .toFullscreenAnimatorWithAnimatedBounds(mView,
                        mDisplayController.getDisplayLayout(mTaskInfo.displayId));
        animator.start();
    }

    /**
     * Takes existing fullscreen indicator and animates it to freeform bounds
     */
    public void transitionFullscreenIndicatorToFreeform() {
        mIsFullscreen = false;
        final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator(
                mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
        animator.start();
    }

    /**
     * Takes the existing freeform indicator and animates it to fullscreen
     */
    public void transitionFreeformIndicatorToFullscreen() {
        mIsFullscreen = true;
        final VisualIndicatorAnimator animator =
                VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds(
                mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
        animator.start();
    }

    /**
     * Release the indicator and its components when it is no longer needed.
     */
    public void releaseFullscreenIndicator() {
    public void releaseVisualIndicator() {
        if (mViewHost == null) return;
        if (mViewHost != null) {
            mViewHost.release();
@@ -136,20 +179,28 @@ public class DesktopModeVisualIndicator {
            });
        }
    }

    /**
     * Returns true if visual indicator is fullscreen
     */
    public boolean isFullscreen() {
        return mIsFullscreen;
    }

    /**
     * Animator for Desktop Mode transitions which supports bounds and alpha animation.
     */
    private static class VisualIndicatorAnimator extends ValueAnimator {
        private static final int FULLSCREEN_INDICATOR_DURATION = 200;
        private static final float SCALE_ADJUSTMENT_PERCENT = 0.015f;
        private static final float FULLSCREEN_SCALE_ADJUSTMENT_PERCENT = 0.015f;
        private static final float INDICATOR_FINAL_OPACITY = 0.7f;

        private final ImageView mView;
        private final View mView;
        private final Rect mStartBounds;
        private final Rect mEndBounds;
        private final RectEvaluator mRectEvaluator;

        private VisualIndicatorAnimator(ImageView view, Rect startBounds,
        private VisualIndicatorAnimator(View view, Rect startBounds,
                Rect endBounds) {
            mView = view;
            mStartBounds = new Rect(startBounds);
@@ -162,29 +213,65 @@ public class DesktopModeVisualIndicator {
         * Create animator for visual indicator of fullscreen transition
         *
         * @param view the view for this indicator
         * @param startBounds the starting bounds of the fullscreen indicator
         */
        public static VisualIndicatorAnimator fullscreenIndicator(ImageView view,
                Rect startBounds) {
            view.getDrawable().setBounds(startBounds);
            int width = startBounds.width();
            int height = startBounds.height();
            Rect endBounds = new Rect((int) (startBounds.left - (SCALE_ADJUSTMENT_PERCENT * width)),
                    (int) (startBounds.top - (SCALE_ADJUSTMENT_PERCENT * height)),
                    (int) (startBounds.right + (SCALE_ADJUSTMENT_PERCENT * width)),
                    (int) (startBounds.bottom + (SCALE_ADJUSTMENT_PERCENT * height)));
            VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
                    view, startBounds, endBounds);
         * @param displayLayout information about the display the transitioning task is currently on
         */
        public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view,
                @NonNull DisplayLayout displayLayout) {
            final Rect bounds = getMaxBounds(displayLayout);
            final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
                    view, bounds, bounds);
            animator.setInterpolator(new DecelerateInterpolator());
            setupIndicatorAnimation(animator);
            return animator;
        }


        /**
         * Create animator for visual indicator of fullscreen transition
         *
         * @param view the view for this indicator
         * @param displayLayout information about the display the transitioning task is currently on
         */
        public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds(
                @NonNull View view, @NonNull DisplayLayout displayLayout) {
            final int padding = displayLayout.stableInsets().top;
            Rect startBounds = new Rect(padding, padding,
                    displayLayout.width() - padding, displayLayout.height() - padding);
            view.getBackground().setBounds(startBounds);

            final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
                    view, startBounds, getMaxBounds(displayLayout));
            animator.setInterpolator(new DecelerateInterpolator());
            setupIndicatorAnimation(animator);
            return animator;
        }

        /**
         * Create animator for visual indicator of freeform transition
         *
         * @param view the view for this indicator
         * @param displayLayout information about the display the transitioning task is currently on
         */
        public static VisualIndicatorAnimator toFreeformAnimator(@NonNull View view,
                @NonNull DisplayLayout displayLayout) {
            final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE;
            final int width = displayLayout.width();
            final int height = displayLayout.height();
            Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2),
                    (int) (adjustmentPercentage * height / 2),
                    (int) (displayLayout.width() - (adjustmentPercentage * width / 2)),
                    (int) (displayLayout.height() - (adjustmentPercentage * height / 2)));
            final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
                    view, getMaxBounds(displayLayout), endBounds);
            animator.setInterpolator(new DecelerateInterpolator());
            setupFullscreenIndicatorAnimation(animator);
            setupIndicatorAnimation(animator);
            return animator;
        }

        /**
         * Add necessary listener for animation of fullscreen indicator
         * Add necessary listener for animation of indicator
         */
        private static void setupFullscreenIndicatorAnimation(
                VisualIndicatorAnimator animator) {
        private static void setupIndicatorAnimation(@NonNull VisualIndicatorAnimator animator) {
            animator.addUpdateListener(a -> {
                if (animator.mView != null) {
                    animator.updateBounds(a.getAnimatedFraction(), animator.mView);
@@ -196,7 +283,7 @@ public class DesktopModeVisualIndicator {
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    animator.mView.getDrawable().setBounds(animator.mEndBounds);
                    animator.mView.getBackground().setBounds(animator.mEndBounds);
                }
            });
            animator.setDuration(FULLSCREEN_INDICATOR_DURATION);
@@ -210,9 +297,12 @@ public class DesktopModeVisualIndicator {
         * @param fraction fraction to use, compared against previous fraction
         * @param view     the view to update
         */
        private void updateBounds(float fraction, ImageView view) {
        private void updateBounds(float fraction, View view) {
            if (mStartBounds.equals(mEndBounds)) {
                return;
            }
            Rect currentBounds = mRectEvaluator.evaluate(fraction, mStartBounds, mEndBounds);
            view.getDrawable().setBounds(currentBounds);
            view.getBackground().setBounds(currentBounds);
        }

        /**
@@ -223,5 +313,23 @@ public class DesktopModeVisualIndicator {
        private void updateIndicatorAlpha(float fraction, View view) {
            view.setAlpha(fraction * INDICATOR_FINAL_OPACITY);
        }

        /**
         * Return the max bounds of a fullscreen indicator
         */
        private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) {
            final int padding = displayLayout.stableInsets().top;
            final int width = displayLayout.width() - 2 * padding;
            final int height = displayLayout.height() - 2 * padding;
            Rect endBounds = new Rect((int) (padding
                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
                    (int) (padding
                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)),
                    (int) (displayLayout.width() - padding
                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
                    (int) (displayLayout.height() - padding
                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)));
            return endBounds;
        }
    }
}
+67 −11
Original line number Diff line number Diff line
@@ -122,7 +122,7 @@ class DesktopTasksController(
    }

    /** Move a task to desktop */
    fun moveToDesktop(task: ActivityManager.RunningTaskInfo) {
    fun moveToDesktop(task: RunningTaskInfo) {
        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDesktop: %d", task.taskId)

        val wct = WindowContainerTransaction()
@@ -181,7 +181,7 @@ class DesktopTasksController(
    }

    /** Move a task to fullscreen */
    fun moveToFullscreen(task: ActivityManager.RunningTaskInfo) {
    fun moveToFullscreen(task: RunningTaskInfo) {
        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId)

        val wct = WindowContainerTransaction()
@@ -206,7 +206,7 @@ class DesktopTasksController(
    }

    /** Move a task to the front **/
    fun moveTaskToFront(taskInfo: ActivityManager.RunningTaskInfo) {
    fun moveTaskToFront(taskInfo: RunningTaskInfo) {
        val wct = WindowContainerTransaction()
        wct.reorder(taskInfo.token, true)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -273,7 +273,7 @@ class DesktopTasksController(
        request: TransitionRequestInfo
    ): WindowContainerTransaction? {
        // Check if we should skip handling this transition
        val task: ActivityManager.RunningTaskInfo? = request.triggerTask
        val task: RunningTaskInfo? = request.triggerTask
        val shouldHandleRequest =
            when {
                // Only handle open or to front transitions
@@ -382,16 +382,15 @@ class DesktopTasksController(
            taskSurface: SurfaceControl,
            y: Float
    ) {
        val statusBarHeight = displayController
                .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
        if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
            val statusBarHeight = getStatusBarHeight(taskInfo)
            if (y <= statusBarHeight && visualIndicator == null) {
                visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
                        displayController, context, taskSurface, shellTaskOrganizer,
                        rootTaskDisplayAreaOrganizer)
                visualIndicator?.createFullscreenIndicator()
                visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
            } else if (y > statusBarHeight && visualIndicator != null) {
                visualIndicator?.releaseFullscreenIndicator()
                visualIndicator?.releaseVisualIndicator()
                visualIndicator = null
            }
        }
@@ -407,15 +406,72 @@ class DesktopTasksController(
            taskInfo: RunningTaskInfo,
            y: Float
    ) {
        val statusBarHeight = displayController
                .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
        val statusBarHeight = getStatusBarHeight(taskInfo)
        if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
            visualIndicator?.releaseFullscreenIndicator()
            visualIndicator?.releaseVisualIndicator()
            visualIndicator = null
            moveToFullscreenWithAnimation(taskInfo)
        }
    }

    /**
     * Perform checks required on drag move. Create/release fullscreen indicator and transitions
     * indicator to freeform or fullscreen dimensions as needed.
     *
     * @param taskInfo the task being dragged.
     * @param taskSurface SurfaceControl of dragged task.
     * @param y coordinate of dragged task. Used for checks against status bar height.
     */
    fun onDragPositioningMoveThroughStatusBar(
            taskInfo: RunningTaskInfo,
            taskSurface: SurfaceControl,
            y: Float
    ) {
        if (visualIndicator == null) {
            visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
                    displayController, context, taskSurface, shellTaskOrganizer,
                    rootTaskDisplayAreaOrganizer)
            visualIndicator?.createFullscreenIndicator()
        }
        val indicator = visualIndicator ?: return
        if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) {
            if (indicator.isFullscreen) {
                indicator.transitionFullscreenIndicatorToFreeform()
            }
        } else if (!indicator.isFullscreen) {
            indicator.transitionFreeformIndicatorToFullscreen()
        }
    }

    /**
     * Perform checks required when drag ends under status bar area.
     *
     * @param taskInfo the task being dragged.
     * @param y height of drag, to be checked against status bar height.
     */
    fun onDragPositioningEndThroughStatusBar(
            taskInfo: RunningTaskInfo,
            freeformBounds: Rect
    ) {
        moveToDesktopWithAnimation(taskInfo, freeformBounds)
        visualIndicator?.releaseVisualIndicator()
        visualIndicator = null
    }


    private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
        return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
    }

    /**
     * Returns the threshold at which we transition a task into freeform when dragging a
     * fullscreen task down from the status bar
     */
    private fun getFreeformTransitionStatusBarDragThreshold(taskInfo: RunningTaskInfo): Int {
        return 2 * getStatusBarHeight(taskInfo)
    }


    /**
     * Adds a listener to find out about changes in the visibility of freeform tasks.
     *
+9 −5
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import java.util.function.Supplier;

public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
    private static final String TAG = "DesktopModeWindowDecorViewModel";

    private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
    private final ActivityTaskManager mActivityTaskManager;
    private final ShellTaskOrganizer mTaskOrganizer;
@@ -542,7 +543,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                    mTransitionDragActive = false;
                    final int statusBarHeight = getStatusBarHeight(
                            relevantDecor.mTaskInfo.displayId);
                    if (ev.getY() > statusBarHeight) {
                    if (ev.getY() > 2 * statusBarHeight) {
                        if (DesktopModeStatus.isProto2Enabled()) {
                            mPauseRelayoutForTask = relevantDecor.mTaskInfo.taskId;
                            centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
@@ -567,9 +568,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                    return;
                }
                if (mTransitionDragActive) {
                    final int statusBarHeight = mDisplayController
                            .getDisplayLayout(
                                    relevantDecor.mTaskInfo.displayId).stableInsets().top;
                    mDesktopTasksController.ifPresent(
                            c -> c.onDragPositioningMoveThroughStatusBar(relevantDecor.mTaskInfo,
                            relevantDecor.mTaskSurface, ev.getY()));
                    final int statusBarHeight = getStatusBarHeight(
                            relevantDecor.mTaskInfo.displayId);
                    if (ev.getY() > statusBarHeight) {
                        if (!mDragToDesktopAnimationStarted) {
                            mDragToDesktopAnimationStarted = true;
@@ -644,7 +647,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            @Override
            public void onAnimationEnd(Animator animation) {
                mDesktopTasksController.ifPresent(
                        c -> c.moveToDesktopWithAnimation(relevantDecor.mTaskInfo,
                        c -> c.onDragPositioningEndThroughStatusBar(
                                relevantDecor.mTaskInfo,
                                calculateFreeformBounds(FINAL_FREEFORM_SCALE)));
            }
        });