Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +25 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager import android.graphics.Rect import android.os.RemoteException import android.os.SystemProperties import android.util.DisplayMetrics Loading Loading @@ -138,6 +139,30 @@ object PipUtils { } } /** * Returns a fake source rect hint for animation purposes when app-provided one is invalid. * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible. */ @JvmStatic fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect { val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height() val width: Int val height: Int var left = 0 var top = 0 if (appBoundsAspRatio < aspectRatio) { width = appBounds.width() height = Math.round(width / aspectRatio) top = (appBounds.height() - height) / 2 } else { height = appBounds.height() width = Math.round(height * aspectRatio) left = (appBounds.width() - width) / 2 } return Rect(left, top, left + width, top + height) } private var isPip2ExperimentEnabled: Boolean? = null /** Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +3 −13 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; Loading Loading @@ -619,19 +620,8 @@ public class PipAnimationController { // This is done for entering case only. if (isInPipDirection(direction)) { final float aspectRatio = endValue.width() / (float) endValue.height(); if ((startValue.width() / (float) startValue.height()) > aspectRatio) { // use the full height. adjustedSourceRectHint.set(0, 0, (int) (startValue.height() * aspectRatio), startValue.height()); adjustedSourceRectHint.offset( (startValue.width() - adjustedSourceRectHint.width()) / 2, 0); } else { // use the full width. adjustedSourceRectHint.set(0, 0, startValue.width(), (int) (startValue.width() / aspectRatio)); adjustedSourceRectHint.offset( 0, (startValue.height() - adjustedSourceRectHint.height()) / 2); } adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint( startValue, aspectRatio)); } } else { adjustedSourceRectHint.set(sourceRectHint); Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +8 −1 Original line number Diff line number Diff line Loading @@ -19,11 +19,13 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import java.lang.annotation.Retention; Loading @@ -45,6 +47,7 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; Loading @@ -55,7 +58,8 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; public PipAlphaAnimator(SurfaceControl leash, public PipAlphaAnimator(Context context, SurfaceControl leash, SurfaceControl.Transaction tx, @Fade int direction) { mLeash = leash; Loading @@ -67,6 +71,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); mEnterAnimationDuration = context.getResources() .getInteger(R.integer.config_pipEnterAnimationDuration); setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +37 −34 Original line number Diff line number Diff line Loading @@ -293,37 +293,32 @@ public class PipTransition extends PipTransitionController implements return false; } SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams; Rect srcRectHint = params.getSourceRectHint(); Rect startBounds = pipChange.getStartAbsBounds(); Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds(); Rect destinationBounds = pipChange.getEndAbsBounds(); float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat(); // We fake the source rect hint when the one prvided by the app is invalid for // the animation with an app icon overlay. Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint() : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio); WindowContainerTransaction finishWct = new WindowContainerTransaction(); SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) { final float scale = (float) destinationBounds.width() / srcRectHint.width(); startTransaction.setWindowCrop(pipLeash, srcRectHint); final float scale = (float) destinationBounds.width() / animationSrcRectHint.width(); startTransaction.setWindowCrop(pipLeash, animationSrcRectHint); startTransaction.setPosition(pipLeash, destinationBounds.left - srcRectHint.left * scale, destinationBounds.top - srcRectHint.top * scale); // Reset the scale in case we are in the multi-activity case. // TO_FRONT transition already scales down the task in single-activity case, but // in multi-activity case, reparenting yields new reset scales coming from pinned task. destinationBounds.left - animationSrcRectHint.left * scale, destinationBounds.top - animationSrcRectHint.top * scale); startTransaction.setScale(pipLeash, scale, scale); } else { final float scaleX = (float) destinationBounds.width() / startBounds.width(); final float scaleY = (float) destinationBounds.height() / startBounds.height(); if (overlayLeash != null) { final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize( mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds); SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) .setScale(pipLeash, scaleX, scaleY) .setWindowCrop(pipLeash, startBounds) .reparent(overlayLeash, pipLeash) .setLayer(overlayLeash, Integer.MAX_VALUE); // Overlay needs to be adjusted once a new draw comes in resetting surface transform. tx.setScale(overlayLeash, 1f, 1f); Loading Loading @@ -390,15 +385,23 @@ public class PipTransition extends PipTransitionController implements if (pipChange == null) { return false; } // cache the PiP task token and leash WindowContainerToken pipTaskToken = pipChange.getContainer(); Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition."); // start transition with 0 alpha startTransaction.setAlpha(mPipLeash, 0f); PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash, startTransaction, PipAlphaAnimator.FADE_IN); animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null)); Rect destinationBounds = pipChange.getEndAbsBounds(); SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition."); // Start transition with 0 alpha at the entry bounds. startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height()) .setAlpha(pipLeash, 0f); PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction, PipAlphaAnimator.FADE_IN); animator.setAnimationEndCallback(() -> { finishCallback.onTransitionFinished(null); // This should update the pip transition state accordingly after we stop playing. onClientDrawAtTransitionEnd(); }); animator.start(); return true; Loading Loading @@ -480,10 +483,10 @@ public class PipTransition extends PipTransitionController implements private boolean isLegacyEnter(@NonNull TransitionInfo info) { TransitionInfo.Change pipChange = getPipChange(info); // If the only change in the changes list is a TO_FRONT mode PiP task, // If the only change in the changes list is a opening type PiP task, // then this is legacy-enter PiP. return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT && info.getChanges().size() == 1; return pipChange != null && info.getChanges().size() == 1 && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN); } private boolean isRemovePipTransition(@NonNull TransitionInfo info) { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +25 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager import android.graphics.Rect import android.os.RemoteException import android.os.SystemProperties import android.util.DisplayMetrics Loading Loading @@ -138,6 +139,30 @@ object PipUtils { } } /** * Returns a fake source rect hint for animation purposes when app-provided one is invalid. * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible. */ @JvmStatic fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect { val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height() val width: Int val height: Int var left = 0 var top = 0 if (appBoundsAspRatio < aspectRatio) { width = appBounds.width() height = Math.round(width / aspectRatio) top = (appBounds.height() - height) / 2 } else { height = appBounds.height() width = Math.round(height * aspectRatio) left = (appBounds.width() - width) / 2 } return Rect(left, top, left + width, top + height) } private var isPip2ExperimentEnabled: Boolean? = null /** Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +3 −13 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; Loading Loading @@ -619,19 +620,8 @@ public class PipAnimationController { // This is done for entering case only. if (isInPipDirection(direction)) { final float aspectRatio = endValue.width() / (float) endValue.height(); if ((startValue.width() / (float) startValue.height()) > aspectRatio) { // use the full height. adjustedSourceRectHint.set(0, 0, (int) (startValue.height() * aspectRatio), startValue.height()); adjustedSourceRectHint.offset( (startValue.width() - adjustedSourceRectHint.width()) / 2, 0); } else { // use the full width. adjustedSourceRectHint.set(0, 0, startValue.width(), (int) (startValue.width() / aspectRatio)); adjustedSourceRectHint.offset( 0, (startValue.height() - adjustedSourceRectHint.height()) / 2); } adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint( startValue, aspectRatio)); } } else { adjustedSourceRectHint.set(sourceRectHint); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +8 −1 Original line number Diff line number Diff line Loading @@ -19,11 +19,13 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import java.lang.annotation.Retention; Loading @@ -45,6 +47,7 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; Loading @@ -55,7 +58,8 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; public PipAlphaAnimator(SurfaceControl leash, public PipAlphaAnimator(Context context, SurfaceControl leash, SurfaceControl.Transaction tx, @Fade int direction) { mLeash = leash; Loading @@ -67,6 +71,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); mEnterAnimationDuration = context.getResources() .getInteger(R.integer.config_pipEnterAnimationDuration); setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +37 −34 Original line number Diff line number Diff line Loading @@ -293,37 +293,32 @@ public class PipTransition extends PipTransitionController implements return false; } SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams; Rect srcRectHint = params.getSourceRectHint(); Rect startBounds = pipChange.getStartAbsBounds(); Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds(); Rect destinationBounds = pipChange.getEndAbsBounds(); float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat(); // We fake the source rect hint when the one prvided by the app is invalid for // the animation with an app icon overlay. Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint() : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio); WindowContainerTransaction finishWct = new WindowContainerTransaction(); SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) { final float scale = (float) destinationBounds.width() / srcRectHint.width(); startTransaction.setWindowCrop(pipLeash, srcRectHint); final float scale = (float) destinationBounds.width() / animationSrcRectHint.width(); startTransaction.setWindowCrop(pipLeash, animationSrcRectHint); startTransaction.setPosition(pipLeash, destinationBounds.left - srcRectHint.left * scale, destinationBounds.top - srcRectHint.top * scale); // Reset the scale in case we are in the multi-activity case. // TO_FRONT transition already scales down the task in single-activity case, but // in multi-activity case, reparenting yields new reset scales coming from pinned task. destinationBounds.left - animationSrcRectHint.left * scale, destinationBounds.top - animationSrcRectHint.top * scale); startTransaction.setScale(pipLeash, scale, scale); } else { final float scaleX = (float) destinationBounds.width() / startBounds.width(); final float scaleY = (float) destinationBounds.height() / startBounds.height(); if (overlayLeash != null) { final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize( mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds); SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) .setScale(pipLeash, scaleX, scaleY) .setWindowCrop(pipLeash, startBounds) .reparent(overlayLeash, pipLeash) .setLayer(overlayLeash, Integer.MAX_VALUE); // Overlay needs to be adjusted once a new draw comes in resetting surface transform. tx.setScale(overlayLeash, 1f, 1f); Loading Loading @@ -390,15 +385,23 @@ public class PipTransition extends PipTransitionController implements if (pipChange == null) { return false; } // cache the PiP task token and leash WindowContainerToken pipTaskToken = pipChange.getContainer(); Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition."); // start transition with 0 alpha startTransaction.setAlpha(mPipLeash, 0f); PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash, startTransaction, PipAlphaAnimator.FADE_IN); animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null)); Rect destinationBounds = pipChange.getEndAbsBounds(); SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition."); // Start transition with 0 alpha at the entry bounds. startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height()) .setAlpha(pipLeash, 0f); PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction, PipAlphaAnimator.FADE_IN); animator.setAnimationEndCallback(() -> { finishCallback.onTransitionFinished(null); // This should update the pip transition state accordingly after we stop playing. onClientDrawAtTransitionEnd(); }); animator.start(); return true; Loading Loading @@ -480,10 +483,10 @@ public class PipTransition extends PipTransitionController implements private boolean isLegacyEnter(@NonNull TransitionInfo info) { TransitionInfo.Change pipChange = getPipChange(info); // If the only change in the changes list is a TO_FRONT mode PiP task, // If the only change in the changes list is a opening type PiP task, // then this is legacy-enter PiP. return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT && info.getChanges().size() == 1; return pipChange != null && info.getChanges().size() == 1 && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN); } private boolean isRemovePipTransition(@NonNull TransitionInfo info) { Loading