Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +15 −10 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.app.TaskInfo; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.view.Surface; import android.view.SurfaceControl; Loading Loading @@ -361,22 +362,26 @@ public class PipAnimationController { } void setColorContentOverlay(Context context) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); if (mContentOverlay != null) { mContentOverlay.detach(tx); } mContentOverlay = new PipContentOverlay.PipColorOverlay(context); mContentOverlay.attach(tx, mLeash); reattachContentOverlay(new PipContentOverlay.PipColorOverlay(context)); } 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 = mSurfaceControlTransactionFactory.getTransaction(); if (mContentOverlay != null) { mContentOverlay.detach(tx); } mContentOverlay = new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint); mContentOverlay = overlay; mContentOverlay.attach(tx, mLeash); } Loading Loading @@ -570,8 +575,9 @@ public class PipAnimationController { final Rect base = getBaseValue(); final Rect start = getStartValue(); final Rect end = getEndValue(); Rect bounds = mRectEvaluator.evaluate(fraction, start, end); if (mContentOverlay != null) { mContentOverlay.onAnimationUpdate(tx, fraction); mContentOverlay.onAnimationUpdate(tx, bounds, fraction); } if (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when Loading @@ -579,7 +585,6 @@ public class PipAnimationController { applyRotation(tx, leash, fraction, start, end); return; } Rect bounds = mRectEvaluator.evaluate(fraction, start, end); float angle = (1.0f - fraction) * startingAngle; setCurrentValue(bounds); if (inScaleTransition() || sourceHintRect == null) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +134 −7 Original line number Diff line number Diff line Loading @@ -16,11 +16,21 @@ package com.android.wm.shell.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import android.annotation.Nullable; 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.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.TaskSnapshot; Loading Loading @@ -51,9 +61,11 @@ public abstract class PipContentOverlay { * Animates the internal {@link #mLeash} by a given fraction. * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly * 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. */ 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}. Loading @@ -66,13 +78,15 @@ public abstract class PipContentOverlay { /** A {@link PipContentOverlay} uses solid color. */ public static final class PipColorOverlay extends PipContentOverlay { private static final String TAG = PipColorOverlay.class.getSimpleName(); private final Context mContext; public PipColorOverlay(Context context) { mContext = context; mLeash = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName(PipColorOverlay.class.getSimpleName()) .setCallsite(TAG) .setName(TAG) .setColorLayer() .build(); } Loading @@ -88,7 +102,8 @@ public abstract class PipContentOverlay { } @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); } Loading @@ -114,6 +129,8 @@ public abstract class PipContentOverlay { /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */ public static final class PipSnapshotOverlay extends PipContentOverlay { private static final String TAG = PipSnapshotOverlay.class.getSimpleName(); private final TaskSnapshot mSnapshot; private final Rect mSourceRectHint; Loading @@ -121,8 +138,8 @@ public abstract class PipContentOverlay { mSnapshot = snapshot; mSourceRectHint = new Rect(sourceRectHint); mLeash = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName(PipSnapshotOverlay.class.getSimpleName()) .setCallsite(TAG) .setName(TAG) .build(); } Loading @@ -143,7 +160,8 @@ public abstract class PipContentOverlay { } @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. } Loading @@ -152,4 +170,113 @@ public abstract class PipContentOverlay { 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; } } } libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +8 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import android.view.Choreographer; import android.view.Display; Loading Loading @@ -1568,7 +1569,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // Similar to auto-enter-pip transition, we use content overlay when there is no // source rect hint to enter PiP use bounds animation. 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); } } else { final TaskSnapshot snapshot = PipUtils.getTaskSnapshot( mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */); Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +8 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.os.SystemProperties; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -792,8 +793,14 @@ public class PipTransition extends PipTransitionController { if (sourceHintRect == null) { // We use content overlay when there is no source rect hint to enter PiP use bounds // animation. if (SystemProperties.getBoolean( "persist.wm.debug.enable_pip_app_icon_overlay", false)) { animator.setAppIconContentOverlay( mContext, currentBounds, taskInfo.topActivityInfo); } else { animator.setColorContentOverlay(mContext); } } } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +15 −10 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.app.TaskInfo; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.view.Surface; import android.view.SurfaceControl; Loading Loading @@ -361,22 +362,26 @@ public class PipAnimationController { } void setColorContentOverlay(Context context) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); if (mContentOverlay != null) { mContentOverlay.detach(tx); } mContentOverlay = new PipContentOverlay.PipColorOverlay(context); mContentOverlay.attach(tx, mLeash); reattachContentOverlay(new PipContentOverlay.PipColorOverlay(context)); } 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 = mSurfaceControlTransactionFactory.getTransaction(); if (mContentOverlay != null) { mContentOverlay.detach(tx); } mContentOverlay = new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint); mContentOverlay = overlay; mContentOverlay.attach(tx, mLeash); } Loading Loading @@ -570,8 +575,9 @@ public class PipAnimationController { final Rect base = getBaseValue(); final Rect start = getStartValue(); final Rect end = getEndValue(); Rect bounds = mRectEvaluator.evaluate(fraction, start, end); if (mContentOverlay != null) { mContentOverlay.onAnimationUpdate(tx, fraction); mContentOverlay.onAnimationUpdate(tx, bounds, fraction); } if (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when Loading @@ -579,7 +585,6 @@ public class PipAnimationController { applyRotation(tx, leash, fraction, start, end); return; } Rect bounds = mRectEvaluator.evaluate(fraction, start, end); float angle = (1.0f - fraction) * startingAngle; setCurrentValue(bounds); if (inScaleTransition() || sourceHintRect == null) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +134 −7 Original line number Diff line number Diff line Loading @@ -16,11 +16,21 @@ package com.android.wm.shell.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import android.annotation.Nullable; 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.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.TaskSnapshot; Loading Loading @@ -51,9 +61,11 @@ public abstract class PipContentOverlay { * Animates the internal {@link #mLeash} by a given fraction. * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly * 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. */ 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}. Loading @@ -66,13 +78,15 @@ public abstract class PipContentOverlay { /** A {@link PipContentOverlay} uses solid color. */ public static final class PipColorOverlay extends PipContentOverlay { private static final String TAG = PipColorOverlay.class.getSimpleName(); private final Context mContext; public PipColorOverlay(Context context) { mContext = context; mLeash = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName(PipColorOverlay.class.getSimpleName()) .setCallsite(TAG) .setName(TAG) .setColorLayer() .build(); } Loading @@ -88,7 +102,8 @@ public abstract class PipContentOverlay { } @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); } Loading @@ -114,6 +129,8 @@ public abstract class PipContentOverlay { /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */ public static final class PipSnapshotOverlay extends PipContentOverlay { private static final String TAG = PipSnapshotOverlay.class.getSimpleName(); private final TaskSnapshot mSnapshot; private final Rect mSourceRectHint; Loading @@ -121,8 +138,8 @@ public abstract class PipContentOverlay { mSnapshot = snapshot; mSourceRectHint = new Rect(sourceRectHint); mLeash = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName(PipSnapshotOverlay.class.getSimpleName()) .setCallsite(TAG) .setName(TAG) .build(); } Loading @@ -143,7 +160,8 @@ public abstract class PipContentOverlay { } @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. } Loading @@ -152,4 +170,113 @@ public abstract class PipContentOverlay { 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; } } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +8 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import android.view.Choreographer; import android.view.Display; Loading Loading @@ -1568,7 +1569,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // Similar to auto-enter-pip transition, we use content overlay when there is no // source rect hint to enter PiP use bounds animation. 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); } } else { final TaskSnapshot snapshot = PipUtils.getTaskSnapshot( mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +8 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.os.SystemProperties; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -792,8 +793,14 @@ public class PipTransition extends PipTransitionController { if (sourceHintRect == null) { // We use content overlay when there is no source rect hint to enter PiP use bounds // animation. if (SystemProperties.getBoolean( "persist.wm.debug.enable_pip_app_icon_overlay", false)) { animator.setAppIconContentOverlay( mContext, currentBounds, taskInfo.topActivityInfo); } else { animator.setColorContentOverlay(mContext); } } } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); Loading