summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java9
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt64
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