diff options
6 files changed, 106 insertions, 20 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 57cc28d1dde5..8eb4a5a1a4c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -427,10 +427,10 @@ public class PipAnimationController { new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint)); } - void setAppIconContentOverlay(Context context, Rect bounds, ActivityInfo activityInfo, - int appIconSizePx) { + void setAppIconContentOverlay(Context context, Rect appBounds, Rect destinationBounds, + ActivityInfo activityInfo, int appIconSizePx) { reattachContentOverlay( - new PipContentOverlay.PipAppIconOverlay(context, bounds, + new PipContentOverlay.PipAppIconOverlay(context, appBounds, destinationBounds, new IconProvider(context).getIcon(activityInfo), appIconSizePx)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java index c701b9581ca2..850b06a0fb7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java @@ -180,20 +180,34 @@ public abstract class PipContentOverlay { private final Context mContext; private final int mAppIconSizePx; private final Rect mAppBounds; + private final int mOverlayHalfSize; private final Matrix mTmpTransform = new Matrix(); private final float[] mTmpFloat9 = new float[9]; private Bitmap mBitmap; - public PipAppIconOverlay(Context context, Rect appBounds, + public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds, Drawable appIcon, int appIconSizePx) { mContext = context; final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics()); mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx); - mAppBounds = new Rect(appBounds); - mBitmap = Bitmap.createBitmap(appBounds.width(), appBounds.height(), - Bitmap.Config.ARGB_8888); + + final int appWidth = appBounds.width(); + final int appHeight = appBounds.height(); + + // In order to have the overlay always cover the pip window during the transition, the + // overlay will be drawn with the max size of the start and end bounds in different + // rotation. + final int overlaySize = Math.max(Math.max(appWidth, appHeight), + Math.max(destinationBounds.width(), destinationBounds.height())) + 1; + mOverlayHalfSize = overlaySize >> 1; + + // When the activity is in the secondary split, make sure the scaling center is not + // offset. + mAppBounds = new Rect(0, 0, appWidth, appHeight); + + mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888); prepareAppIconOverlay(appIcon); mLeash = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite(TAG) @@ -215,12 +229,19 @@ public abstract class PipContentOverlay { public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, Rect currentBounds, float fraction) { mTmpTransform.reset(); + // In order for the overlay to always cover the pip window, the overlay may have a + // size larger than the pip window. Make sure that app icon is at the center. + final int appBoundsCenterX = mAppBounds.centerX(); + final int appBoundsCenterY = mAppBounds.centerY(); + mTmpTransform.setTranslate( + appBoundsCenterX - mOverlayHalfSize, + appBoundsCenterY - mOverlayHalfSize); // 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()); + appBoundsCenterX, + appBoundsCenterY); atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9) .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } @@ -253,10 +274,10 @@ public abstract class PipContentOverlay { ta.recycle(); } final Rect appIconBounds = new Rect( - mAppBounds.centerX() - mAppIconSizePx / 2, - mAppBounds.centerY() - mAppIconSizePx / 2, - mAppBounds.centerX() + mAppIconSizePx / 2, - mAppBounds.centerY() + mAppIconSizePx / 2); + mOverlayHalfSize - mAppIconSizePx / 2, + mOverlayHalfSize - mAppIconSizePx / 2, + mOverlayHalfSize + mAppIconSizePx / 2, + mOverlayHalfSize + mAppIconSizePx / 2); appIcon.setBounds(appIconBounds); appIcon.draw(canvas); mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 9e8f9c68d43d..083cd08e9a17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1718,7 +1718,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, sourceHintRect = computeRotatedBounds(rotationDelta, direction, destinationBounds, sourceHintRect); } - Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE + final Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE ? mPipBoundsState.getBounds() : currentBounds; final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null && mPipAnimationController.getCurrentAnimator().isRunning(); @@ -1741,7 +1741,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final boolean hasTopActivityInfo = mTaskInfo.topActivityInfo != null; if (hasTopActivityInfo) { animator.setAppIconContentOverlay( - mContext, currentBounds, mTaskInfo.topActivityInfo, + mContext, currentBounds, destinationBounds, mTaskInfo.topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); } else { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 018d674e5427..d5b29e384c09 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -916,7 +916,7 @@ public class PipTransition extends PipTransitionController { final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null; if (hasTopActivityInfo) { animator.setAppIconContentOverlay( - mContext, currentBounds, taskInfo.topActivityInfo, + mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); } else { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index e82f3221f0a4..5227a52545f4 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2095,6 +2095,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } final TaskFragment organizedTf = r.getOrganizedTaskFragment(); + final TaskFragment taskFragment = r.getTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; if (singleActivity) { rootTask = task; @@ -2137,7 +2138,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> .setIntent(r.intent) .setDeferTaskAppear(true) .setHasBeenVisible(true) - .setWindowingMode(task.getRequestedOverrideWindowingMode()) + // In case the activity is in system split screen, or Activity Embedding + // split, we need to animate the PIP Task from the original TaskFragment + // bounds, so also setting the windowing mode, otherwise the bounds may + // be reset to fullscreen. + .setWindowingMode(taskFragment.getWindowingMode()) .build(); // Establish bi-directional link between the original and pinned task. r.setLastParentBeforePip(launchIntoPipHostActivity); @@ -2150,7 +2155,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // current bounds. // Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode // will be updated later after this is collected in transition. - rootTask.setBoundsUnchecked(r.getTaskFragment().getBounds()); + rootTask.setBoundsUnchecked(taskFragment.getBounds()); // Move the last recents animation transaction from original task to the new one. if (task.mLastRecentsAnimationTransaction != null) { diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index 790da34b1b08..12a57d52491a 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.activityembedding.pip import android.platform.test.annotations.Presubmit import android.tools.common.datatypes.Rect +import android.tools.common.flicker.subject.layers.LayersTraceSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -149,8 +150,12 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible } pipLayerList.zipWithNext { previous, current -> - // TODO(b/290987990): Add checks for visibleRegion. - current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3) + if (startDisplayBounds.width > startDisplayBounds.height) { + // Only verify when the display is landscape, because otherwise the final pip + // window can be to the left of the original secondary activity. + current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3) + } + current.screenBounds.overlaps(previous.screenBounds.region) current.screenBounds.notBiggerThan(previous.screenBounds.region) } } @@ -161,6 +166,61 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : } } + /** The secondary layer should never jump to the left. */ + @Presubmit + @Test + fun secondaryLayerNotJumpToLeft() { + flicker.assertLayers { + invoke("secondaryLayerNotJumpToLeft") { + val secondaryVisibleRegion = + it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + if (secondaryVisibleRegion.region.isNotEmpty) { + check { "left" } + .that(secondaryVisibleRegion.region.bounds.left) + .isGreater(0) + } + } + } + } + + /** + * The pip overlay layer should cover exactly the secondary activity layer when both are + * visible. + */ + @Presubmit + @Test + fun pipContentOverlayLayerCoversExactlySecondaryLayer() { + flicker.assertLayers { + isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) + .then() + .isVisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) + .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .invoke("pipContentOverlayLayerCoversExactlySecondaryLayer") { + val overlayVisibleRegion = + it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY) + val secondaryVisibleRegion = + it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region) + } + .then() + .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) + .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + @Presubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + // Expected for the main activity to become invisible for 1-2 frames because the snapshot + // covers it. + flicker.assertLayers { + visibleLayersShownMoreThanOneConsecutiveEntry( + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + + listOf(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + ) + } + } + companion object { /** {@inheritDoc} */ private var startDisplayBounds = Rect.EMPTY |