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

Commit 4e1d9ecd authored by Jorge Gil's avatar Jorge Gil
Browse files

Optimize resize veil relayouts

To avoid redrawing the veil's background color and app icon drawables on
every size change of a drag-resize gesture, which is causing jank and
missed frames, this change draws the background color on a separate
color SurfaceControl and moves the icon to its own SurfaceControl that
is only drawn once and thus only needs to be repositioned when the task
resizes.

Bug: 330626246
Test: manual - drag resize and see veil as usual; check perfetto trace
does not skip frames on every ACTION_MOVE anymore

Change-Id: I97821d5a70f1eca7ec77cb868869dbe2187a6af3
parent e6cd1bb1
Loading
Loading
Loading
Loading
+0 −20
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ 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.
  -->
<shape android:shape="rectangle"
       xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/white" />
</shape>
+3 −4
Original line number Original line Diff line number Diff line
@@ -16,13 +16,12 @@
  -->
  -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_height="match_parent">
              android:background="@drawable/desktop_mode_resize_veil_background">


    <ImageView
    <ImageView
        android:id="@+id/veil_application_icon"
        android:id="@+id/veil_application_icon"
        android:layout_width="96dp"
        android:layout_width="@dimen/desktop_mode_resize_veil_icon_size"
        android:layout_height="96dp"
        android:layout_height="@dimen/desktop_mode_resize_veil_icon_size"
        android:layout_gravity="center"
        android:layout_gravity="center"
        android:contentDescription="@string/app_icon_text" />
        android:contentDescription="@string/app_icon_text" />
</FrameLayout>
</FrameLayout>
 No newline at end of file
+3 −0
Original line number Original line Diff line number Diff line
@@ -506,6 +506,9 @@
    <!-- The radius of the caption menu shadow. -->
    <!-- The radius of the caption menu shadow. -->
    <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
    <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>


    <!-- The size of the icon shown in the resize veil. -->
    <dimen name="desktop_mode_resize_veil_icon_size">96dp</dimen>

    <dimen name="freeform_resize_handle">15dp</dimen>
    <dimen name="freeform_resize_handle">15dp</dimen>


    <dimen name="freeform_resize_corner">44dp</dimen>
    <dimen name="freeform_resize_corner">44dp</dimen>
+1 −1
Original line number Original line Diff line number Diff line
@@ -451,7 +451,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
     * until a resize event calls showResizeVeil below.
     * until a resize event calls showResizeVeil below.
     */
     */
    void createResizeVeil() {
    void createResizeVeil() {
        mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo,
        mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo, mTaskSurface,
                mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
                mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
    }
    }


+118 −42
Original line number Original line Diff line number Diff line
@@ -23,13 +23,16 @@ import android.annotation.ColorRes;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.view.Display;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.View;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.view.WindowlessWindowManager;
@@ -37,6 +40,7 @@ import android.widget.ImageView;
import android.window.TaskConstants;
import android.window.TaskConstants;


import com.android.wm.shell.R;
import com.android.wm.shell.R;
import com.android.wm.shell.common.SurfaceUtils;


import java.util.function.Supplier;
import java.util.function.Supplier;


@@ -45,19 +49,36 @@ import java.util.function.Supplier;
 */
 */
public class ResizeVeil {
public class ResizeVeil {
    private static final int RESIZE_ALPHA_DURATION = 100;
    private static final int RESIZE_ALPHA_DURATION = 100;

    private static final int VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL;
    /** The background is a child of the veil container layer and goes at the bottom. */
    private static final int VEIL_BACKGROUND_LAYER = 0;
    /** The icon is a child of the veil container layer and goes in front of the background. */
    private static final int VEIL_ICON_LAYER = 1;

    private final Context mContext;
    private final Context mContext;
    private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
    private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
    private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
    private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
    private final SurfaceSession mSurfaceSession = new SurfaceSession();
    private final Drawable mAppIcon;
    private final Drawable mAppIcon;
    private ImageView mIconView;
    private ImageView mIconView;
    private int mIconSize;
    private SurfaceControl mParentSurface;
    private SurfaceControl mParentSurface;

    /** A container surface to host the veil background and icon child surfaces. */
    private SurfaceControl mVeilSurface;
    private SurfaceControl mVeilSurface;
    /** A color surface for the veil background. */
    private SurfaceControl mBackgroundSurface;
    /** A surface that hosts a windowless window with the app icon. */
    private SurfaceControl mIconSurface;

    private final RunningTaskInfo mTaskInfo;
    private final RunningTaskInfo mTaskInfo;
    private SurfaceControlViewHost mViewHost;
    private SurfaceControlViewHost mViewHost;
    private final Display mDisplay;
    private final Display mDisplay;
    private ValueAnimator mVeilAnimator;
    private ValueAnimator mVeilAnimator;


    public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo,
    public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo,
            SurfaceControl taskSurface,
            Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display,
            Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display,
            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
        mContext = context;
        mContext = context;
@@ -65,6 +86,7 @@ public class ResizeVeil {
        mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
        mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
        mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
        mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
        mTaskInfo = taskInfo;
        mTaskInfo = taskInfo;
        mParentSurface = taskSurface;
        mDisplay = display;
        mDisplay = display;
        setupResizeVeil();
        setupResizeVeil();
    }
    }
@@ -73,34 +95,44 @@ public class ResizeVeil {
     * Create the veil in its default invisible state.
     * Create the veil in its default invisible state.
     */
     */
    private void setupResizeVeil() {
    private void setupResizeVeil() {
        SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
        mVeilSurface = mSurfaceControlBuilderSupplier.get()
        final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
                .setContainerLayer()
        mVeilSurface = builder
                .setName("Resize veil of Task=" + mTaskInfo.taskId)
                .setName("Resize veil of Task=" + mTaskInfo.taskId)
                .setHidden(true)
                .setParent(mParentSurface)
                .setCallsite("ResizeVeil#setupResizeVeil")
                .build();
        mBackgroundSurface = SurfaceUtils.makeColorLayer(mVeilSurface,
                "Resize veil background of Task=" + mTaskInfo.taskId, mSurfaceSession);
        mIconSurface = mSurfaceControlBuilderSupplier.get()
                .setName("Resize veil icon of Task= " + mTaskInfo.taskId)
                .setContainerLayer()
                .setContainerLayer()
                .setParent(mVeilSurface)
                .setHidden(true)
                .setCallsite("ResizeVeil#setupResizeVeil")
                .build();
                .build();
        View v = LayoutInflater.from(mContext)
                .inflate(R.layout.desktop_mode_resize_veil, null);


        t.setPosition(mVeilSurface, 0, 0)
        mIconSize = mContext.getResources()
            .setLayer(mVeilSurface, TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL)
                .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size);
            .apply();
        final View root = LayoutInflater.from(mContext)
        Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
                .inflate(R.layout.desktop_mode_resize_veil, null /* root */);
        mIconView = root.findViewById(R.id.veil_application_icon);
        mIconView.setImageDrawable(mAppIcon);

        final WindowManager.LayoutParams lp =
        final WindowManager.LayoutParams lp =
                new WindowManager.LayoutParams(taskBounds.width(),
                new WindowManager.LayoutParams(
                        taskBounds.height(),
                        mIconSize,
                        mIconSize,
                        WindowManager.LayoutParams.TYPE_APPLICATION,
                        WindowManager.LayoutParams.TYPE_APPLICATION,
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                        PixelFormat.TRANSPARENT);
                        PixelFormat.TRANSPARENT);
        lp.setTitle("Resize veil of Task=" + mTaskInfo.taskId);
        lp.setTitle("Resize veil icon window of Task=" + mTaskInfo.taskId);
        lp.setTrustedOverlay();
        lp.setTrustedOverlay();
        WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
                mVeilSurface, null /* hostInputToken */);
        mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil");
        mViewHost.setView(v, lp);


        mIconView = mViewHost.getView().findViewById(R.id.veil_application_icon);
        final WindowlessWindowManager wwm = new WindowlessWindowManager(mTaskInfo.configuration,
        mIconView.setImageDrawable(mAppIcon);
                mIconSurface, null /* hostInputToken */);
        mViewHost = new SurfaceControlViewHost(mContext, mDisplay, wwm, "ResizeVeil");
        mViewHost.setView(root, lp);
    }
    }


    /**
    /**
@@ -120,46 +152,74 @@ public class ResizeVeil {
            mParentSurface = parentSurface;
            mParentSurface = parentSurface;
        }
        }


        int backgroundColorId = getBackgroundColorId();
        t.show(mVeilSurface);
        mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId));
        t.setLayer(mVeilSurface, VEIL_CONTAINER_LAYER);
        t.setLayer(mIconSurface, VEIL_ICON_LAYER);
        t.setLayer(mBackgroundSurface, VEIL_BACKGROUND_LAYER);
        t.setColor(mBackgroundSurface,
                Color.valueOf(mContext.getColor(getBackgroundColorId())).getComponents());


        relayout(taskBounds, t);
        relayout(taskBounds, t);
        if (fadeIn) {
        if (fadeIn) {
            cancelAnimation();
            cancelAnimation();
            final SurfaceControl.Transaction veilAnimT = mSurfaceControlTransactionSupplier.get();
            mVeilAnimator = new ValueAnimator();
            mVeilAnimator = new ValueAnimator();
            mVeilAnimator.setFloatValues(0f, 1f);
            mVeilAnimator.setFloatValues(0f, 1f);
            mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
            mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
            mVeilAnimator.addUpdateListener(animation -> {
            mVeilAnimator.addUpdateListener(animation -> {
                t.setAlpha(mVeilSurface, mVeilAnimator.getAnimatedFraction());
                veilAnimT.setAlpha(mBackgroundSurface, mVeilAnimator.getAnimatedFraction());
                t.apply();
                veilAnimT.apply();
            });
            });
            mVeilAnimator.addListener(new AnimatorListenerAdapter() {
            mVeilAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    veilAnimT.show(mBackgroundSurface)
                            .setAlpha(mBackgroundSurface, 0)
                            .apply();
                }

                @Override
                @Override
                public void onAnimationEnd(Animator animation) {
                public void onAnimationEnd(Animator animation) {
                    t.setAlpha(mVeilSurface, 1);
                    veilAnimT.setAlpha(mBackgroundSurface, 1).apply();
                    t.apply();
                }
                }
            });
            });


            final SurfaceControl.Transaction iconAnimT = mSurfaceControlTransactionSupplier.get();
            final ValueAnimator iconAnimator = new ValueAnimator();
            final ValueAnimator iconAnimator = new ValueAnimator();
            iconAnimator.setFloatValues(0f, 1f);
            iconAnimator.setFloatValues(0f, 1f);
            iconAnimator.setDuration(RESIZE_ALPHA_DURATION);
            iconAnimator.setDuration(RESIZE_ALPHA_DURATION);
            iconAnimator.addUpdateListener(animation -> {
            iconAnimator.addUpdateListener(animation -> {
                mIconView.setAlpha(animation.getAnimatedFraction());
                iconAnimT.setAlpha(mIconSurface, animation.getAnimatedFraction());
                iconAnimT.apply();
            });
            iconAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    iconAnimT.show(mIconSurface)
                            .setAlpha(mIconSurface, 0)
                            .apply();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    iconAnimT.setAlpha(mIconSurface, 1).apply();
                }
            });
            });
            // Let the animators show it with the correct alpha value once the animation starts.
            t.hide(mIconSurface);
            t.hide(mBackgroundSurface);
            t.apply();


            t.show(mVeilSurface)
                    .addTransactionCommittedListener(
                            mContext.getMainExecutor(), () -> {
            mVeilAnimator.start();
            mVeilAnimator.start();
            iconAnimator.start();
            iconAnimator.start();
                            })
                    .setAlpha(mVeilSurface, 0);
        } else {
        } else {
            // Show the veil immediately at full opacity.
            // Show the veil immediately.
            t.show(mVeilSurface).setAlpha(mVeilSurface, 1);
            t.show(mIconSurface);
            t.show(mBackgroundSurface);
            t.setAlpha(mIconSurface, 1);
            t.setAlpha(mBackgroundSurface, 1);
            t.apply();
        }
        }
        mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
    }
    }


    /**
    /**
@@ -175,8 +235,9 @@ public class ResizeVeil {
     * @param newBounds bounds to update veil to.
     * @param newBounds bounds to update veil to.
     */
     */
    private void relayout(Rect newBounds, SurfaceControl.Transaction t) {
    private void relayout(Rect newBounds, SurfaceControl.Transaction t) {
        mViewHost.relayout(newBounds.width(), newBounds.height());
        t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height());
        t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height());
        final PointF iconPosition = calculateAppIconPosition(newBounds);
        t.setPosition(mIconSurface, iconPosition.x, iconPosition.y);
        t.setPosition(mParentSurface, newBounds.left, newBounds.top);
        t.setPosition(mParentSurface, newBounds.left, newBounds.top);
        t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height());
        t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height());
    }
    }
@@ -204,7 +265,7 @@ public class ResizeVeil {
            mVeilAnimator.end();
            mVeilAnimator.end();
        }
        }
        relayout(newBounds, t);
        relayout(newBounds, t);
        mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
        t.apply();
    }
    }


    /**
    /**
@@ -217,14 +278,16 @@ public class ResizeVeil {
        mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
        mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
        mVeilAnimator.addUpdateListener(animation -> {
        mVeilAnimator.addUpdateListener(animation -> {
            SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
            SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
            t.setAlpha(mVeilSurface, 1 - mVeilAnimator.getAnimatedFraction());
            t.setAlpha(mBackgroundSurface, 1 - mVeilAnimator.getAnimatedFraction());
            t.setAlpha(mIconSurface, 1 - mVeilAnimator.getAnimatedFraction());
            t.apply();
            t.apply();
        });
        });
        mVeilAnimator.addListener(new AnimatorListenerAdapter() {
        mVeilAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            @Override
            public void onAnimationEnd(Animator animation) {
            public void onAnimationEnd(Animator animation) {
                SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
                SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
                t.hide(mVeilSurface);
                t.hide(mBackgroundSurface);
                t.hide(mIconSurface);
                t.apply();
                t.apply();
            }
            }
        });
        });
@@ -242,6 +305,11 @@ public class ResizeVeil {
        }
        }
    }
    }


    private PointF calculateAppIconPosition(Rect parentBounds) {
        return new PointF((float) parentBounds.width() / 2 - (float) mIconSize / 2,
                (float) parentBounds.height() / 2 - (float) mIconSize / 2);
    }

    private void cancelAnimation() {
    private void cancelAnimation() {
        if (mVeilAnimator != null) {
        if (mVeilAnimator != null) {
            mVeilAnimator.removeAllUpdateListeners();
            mVeilAnimator.removeAllUpdateListeners();
@@ -260,11 +328,19 @@ public class ResizeVeil {
            mViewHost.release();
            mViewHost.release();
            mViewHost = null;
            mViewHost = null;
        }
        }
        if (mVeilSurface != null) {
        final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
        final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
        if (mBackgroundSurface != null) {
            t.remove(mBackgroundSurface);
            mBackgroundSurface = null;
        }
        if (mIconSurface != null) {
            t.remove(mIconSurface);
            mIconSurface = null;
        }
        if (mVeilSurface != null) {
            t.remove(mVeilSurface);
            t.remove(mVeilSurface);
            mVeilSurface = null;
            mVeilSurface = null;
            t.apply();
        }
        }
        t.apply();
    }
    }
}
}