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

Commit d2515411 authored by Hongwei Wang's avatar Hongwei Wang Committed by Automerger Merge Worker
Browse files

Merge "Implement app icon overlay when entering PiP" into tm-qpr-dev am: 55176db4 am: 6a59ef8f

parents 2008ffc4 6a59ef8f
Loading
Loading
Loading
Loading
+15 −10
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.app.TaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.graphics.Rect;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
@@ -361,22 +362,26 @@ public class PipAnimationController {
        }
        }


        void setColorContentOverlay(Context context) {
        void setColorContentOverlay(Context context) {
            final SurfaceControl.Transaction tx =
            reattachContentOverlay(new PipContentOverlay.PipColorOverlay(context));
                    mSurfaceControlTransactionFactory.getTransaction();
            if (mContentOverlay != null) {
                mContentOverlay.detach(tx);
            }
            mContentOverlay = new PipContentOverlay.PipColorOverlay(context);
            mContentOverlay.attach(tx, mLeash);
        }
        }


        void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
        void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
            reattachContentOverlay(
                    new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint));
        }

        void setAppIconContentOverlay(Context context, Rect bounds, ActivityInfo activityInfo) {
            reattachContentOverlay(
                    new PipContentOverlay.PipAppIconOverlay(context, bounds, activityInfo));
        }

        private void reattachContentOverlay(PipContentOverlay overlay) {
            final SurfaceControl.Transaction tx =
            final SurfaceControl.Transaction tx =
                    mSurfaceControlTransactionFactory.getTransaction();
                    mSurfaceControlTransactionFactory.getTransaction();
            if (mContentOverlay != null) {
            if (mContentOverlay != null) {
                mContentOverlay.detach(tx);
                mContentOverlay.detach(tx);
            }
            }
            mContentOverlay = new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint);
            mContentOverlay = overlay;
            mContentOverlay.attach(tx, mLeash);
            mContentOverlay.attach(tx, mLeash);
        }
        }


@@ -570,8 +575,9 @@ public class PipAnimationController {
                    final Rect base = getBaseValue();
                    final Rect base = getBaseValue();
                    final Rect start = getStartValue();
                    final Rect start = getStartValue();
                    final Rect end = getEndValue();
                    final Rect end = getEndValue();
                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                    if (mContentOverlay != null) {
                    if (mContentOverlay != null) {
                        mContentOverlay.onAnimationUpdate(tx, fraction);
                        mContentOverlay.onAnimationUpdate(tx, bounds, fraction);
                    }
                    }
                    if (rotatedEndRect != null) {
                    if (rotatedEndRect != null) {
                        // Animate the bounds in a different orientation. It only happens when
                        // Animate the bounds in a different orientation. It only happens when
@@ -579,7 +585,6 @@ public class PipAnimationController {
                        applyRotation(tx, leash, fraction, start, end);
                        applyRotation(tx, leash, fraction, start, end);
                        return;
                        return;
                    }
                    }
                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                    float angle = (1.0f - fraction) * startingAngle;
                    float angle = (1.0f - fraction) * startingAngle;
                    setCurrentValue(bounds);
                    setCurrentValue(bounds);
                    if (inScaleTransition() || sourceHintRect == null) {
                    if (inScaleTransition() || sourceHintRect == null) {
+134 −7
Original line number Original line Diff line number Diff line
@@ -16,11 +16,21 @@


package com.android.wm.shell.pip;
package com.android.wm.shell.pip;


import static android.util.TypedValue.COMPLEX_UNIT_DIP;

import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.SurfaceSession;
import android.window.TaskSnapshot;
import android.window.TaskSnapshot;
@@ -51,9 +61,11 @@ public abstract class PipContentOverlay {
     * Animates the internal {@link #mLeash} by a given fraction.
     * Animates the internal {@link #mLeash} by a given fraction.
     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
     *                 call apply on this transaction, it should be applied on the caller side.
     *                 call apply on this transaction, it should be applied on the caller side.
     * @param currentBounds {@link Rect} of the current animation bounds.
     * @param fraction progress of the animation ranged from 0f to 1f.
     * @param fraction progress of the animation ranged from 0f to 1f.
     */
     */
    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction);
    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
            Rect currentBounds, float fraction);


    /**
    /**
     * Callback when reaches the end of animation on the internal {@link #mLeash}.
     * Callback when reaches the end of animation on the internal {@link #mLeash}.
@@ -66,13 +78,15 @@ public abstract class PipContentOverlay {


    /** A {@link PipContentOverlay} uses solid color. */
    /** A {@link PipContentOverlay} uses solid color. */
    public static final class PipColorOverlay extends PipContentOverlay {
    public static final class PipColorOverlay extends PipContentOverlay {
        private static final String TAG = PipColorOverlay.class.getSimpleName();

        private final Context mContext;
        private final Context mContext;


        public PipColorOverlay(Context context) {
        public PipColorOverlay(Context context) {
            mContext = context;
            mContext = context;
            mLeash = new SurfaceControl.Builder(new SurfaceSession())
            mLeash = new SurfaceControl.Builder(new SurfaceSession())
                    .setCallsite("PipAnimation")
                    .setCallsite(TAG)
                    .setName(PipColorOverlay.class.getSimpleName())
                    .setName(TAG)
                    .setColorLayer()
                    .setColorLayer()
                    .build();
                    .build();
        }
        }
@@ -88,7 +102,8 @@ public abstract class PipContentOverlay {
        }
        }


        @Override
        @Override
        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
                Rect currentBounds, float fraction) {
            atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
            atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
        }
        }


@@ -114,6 +129,8 @@ public abstract class PipContentOverlay {


    /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
    /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
    public static final class PipSnapshotOverlay extends PipContentOverlay {
    public static final class PipSnapshotOverlay extends PipContentOverlay {
        private static final String TAG = PipSnapshotOverlay.class.getSimpleName();

        private final TaskSnapshot mSnapshot;
        private final TaskSnapshot mSnapshot;
        private final Rect mSourceRectHint;
        private final Rect mSourceRectHint;


@@ -121,8 +138,8 @@ public abstract class PipContentOverlay {
            mSnapshot = snapshot;
            mSnapshot = snapshot;
            mSourceRectHint = new Rect(sourceRectHint);
            mSourceRectHint = new Rect(sourceRectHint);
            mLeash = new SurfaceControl.Builder(new SurfaceSession())
            mLeash = new SurfaceControl.Builder(new SurfaceSession())
                    .setCallsite("PipAnimation")
                    .setCallsite(TAG)
                    .setName(PipSnapshotOverlay.class.getSimpleName())
                    .setName(TAG)
                    .build();
                    .build();
        }
        }


@@ -143,7 +160,8 @@ public abstract class PipContentOverlay {
        }
        }


        @Override
        @Override
        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
                Rect currentBounds, float fraction) {
            // Do nothing. Keep the snapshot till animation ends.
            // Do nothing. Keep the snapshot till animation ends.
        }
        }


@@ -152,4 +170,113 @@ public abstract class PipContentOverlay {
            atomicTx.remove(mLeash);
            atomicTx.remove(mLeash);
        }
        }
    }
    }

    /** A {@link PipContentOverlay} shows app icon on solid color background. */
    public static final class PipAppIconOverlay extends PipContentOverlay {
        private static final String TAG = PipAppIconOverlay.class.getSimpleName();
        private static final int APP_ICON_SIZE_DP = 48;

        private final Context mContext;
        private final int mAppIconSizePx;
        private final Rect mAppBounds;
        private final Matrix mTmpTransform = new Matrix();
        private final float[] mTmpFloat9 = new float[9];

        private Bitmap mBitmap;

        public PipAppIconOverlay(Context context, Rect appBounds, ActivityInfo activityInfo) {
            mContext = context;
            mAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, APP_ICON_SIZE_DP,
                    context.getResources().getDisplayMetrics());
            mAppBounds = new Rect(appBounds);
            mBitmap = Bitmap.createBitmap(appBounds.width(), appBounds.height(),
                    Bitmap.Config.ARGB_8888);
            prepareAppIconOverlay(activityInfo);
            mLeash = new SurfaceControl.Builder(new SurfaceSession())
                    .setCallsite(TAG)
                    .setName(TAG)
                    .build();
        }

        @Override
        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
            tx.show(mLeash);
            tx.setLayer(mLeash, Integer.MAX_VALUE);
            tx.setBuffer(mLeash, mBitmap.getHardwareBuffer());
            tx.reparent(mLeash, parentLeash);
            tx.apply();
        }

        @Override
        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
                Rect currentBounds, float fraction) {
            mTmpTransform.reset();
            // Scale back the bitmap with the pivot point at center.
            mTmpTransform.postScale(
                    (float) mAppBounds.width() / currentBounds.width(),
                    (float) mAppBounds.height() / currentBounds.height(),
                    mAppBounds.centerX(),
                    mAppBounds.centerY());
            atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
                    .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
        }

        @Override
        public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
            atomicTx.remove(mLeash);
        }

        @Override
        public void detach(SurfaceControl.Transaction tx) {
            super.detach(tx);
            if (mBitmap != null && !mBitmap.isRecycled()) {
                mBitmap.recycle();
            }
        }

        private void prepareAppIconOverlay(ActivityInfo activityInfo) {
            final Canvas canvas = new Canvas();
            canvas.setBitmap(mBitmap);
            final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
                    android.R.attr.colorBackground });
            try {
                int colorAccent = ta.getColor(0, 0);
                canvas.drawRGB(
                        Color.red(colorAccent),
                        Color.green(colorAccent),
                        Color.blue(colorAccent));
            } finally {
                ta.recycle();
            }
            final Drawable appIcon = loadActivityInfoIcon(activityInfo,
                    mContext.getResources().getConfiguration().densityDpi);
            final Rect appIconBounds = new Rect(
                    mAppBounds.centerX() - mAppIconSizePx / 2,
                    mAppBounds.centerY() - mAppIconSizePx / 2,
                    mAppBounds.centerX() + mAppIconSizePx / 2,
                    mAppBounds.centerY() + mAppIconSizePx / 2);
            appIcon.setBounds(appIconBounds);
            appIcon.draw(canvas);
            mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
        }

        // Copied from com.android.launcher3.icons.IconProvider#loadActivityInfoIcon
        private Drawable loadActivityInfoIcon(ActivityInfo ai, int density) {
            final int iconRes = ai.getIconResource();
            Drawable icon = null;
            // Get the preferred density icon from the app's resources
            if (density != 0 && iconRes != 0) {
                try {
                    final Resources resources = mContext.getPackageManager()
                            .getResourcesForApplication(ai.applicationInfo);
                    icon = resources.getDrawableForDensity(iconRes, density);
                } catch (PackageManager.NameNotFoundException | Resources.NotFoundException exc) { }
            }
            // Get the default density icon
            if (icon == null) {
                icon = ai.loadIcon(mContext.getPackageManager());
            }
            return icon;
        }
    }
}
}
+8 −1
Original line number Original line Diff line number Diff line
@@ -62,6 +62,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Log;
import android.view.Choreographer;
import android.view.Choreographer;
import android.view.Display;
import android.view.Display;
@@ -1568,7 +1569,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            // Similar to auto-enter-pip transition, we use content overlay when there is no
            // Similar to auto-enter-pip transition, we use content overlay when there is no
            // source rect hint to enter PiP use bounds animation.
            // source rect hint to enter PiP use bounds animation.
            if (sourceHintRect == null) {
            if (sourceHintRect == null) {
                if (SystemProperties.getBoolean(
                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
                    animator.setAppIconContentOverlay(
                            mContext, currentBounds, mTaskInfo.topActivityInfo);
                } else {
                    animator.setColorContentOverlay(mContext);
                    animator.setColorContentOverlay(mContext);
                }
            } else {
            } else {
                final TaskSnapshot snapshot = PipUtils.getTaskSnapshot(
                final TaskSnapshot snapshot = PipUtils.getTaskSnapshot(
                        mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */);
                        mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */);
+8 −1
Original line number Original line Diff line number Diff line
@@ -50,6 +50,7 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.IBinder;
import android.os.SystemProperties;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManager;
@@ -792,8 +793,14 @@ public class PipTransition extends PipTransitionController {
            if (sourceHintRect == null) {
            if (sourceHintRect == null) {
                // We use content overlay when there is no source rect hint to enter PiP use bounds
                // We use content overlay when there is no source rect hint to enter PiP use bounds
                // animation.
                // animation.
                if (SystemProperties.getBoolean(
                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
                    animator.setAppIconContentOverlay(
                            mContext, currentBounds, taskInfo.topActivityInfo);
                } else {
                    animator.setColorContentOverlay(mContext);
                    animator.setColorContentOverlay(mContext);
                }
                }
            }
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
            animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
            animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
                    0f, 1f);
                    0f, 1f);