diff options
Diffstat (limited to 'libs')
9 files changed, 268 insertions, 44 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 01f5feb9b13e..3ec8843838fe 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -16,6 +16,8 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule; @@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen mSplitRules.clear(); mSplitRules.addAll(rules); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - updateAnimationOverride(mTaskContainers.keyAt(i)); + updateAnimationOverride(mTaskContainers.valueAt(i)); } } @@ -147,15 +149,31 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } + final boolean wasInPip = isInPictureInPicture(container); container.setInfo(taskFragmentInfo); + final boolean isInPip = isInPictureInPicture(container); // Check if there are no running activities - consider the container empty if there are no // non-finishing activities left. if (!taskFragmentInfo.hasRunningActivity()) { + // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP + // Task. // Do not finish the dependents if this TaskFragment was cleared due to launching // activity in the Task. final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse(); mPresenter.cleanupContainer(container, shouldFinishDependent); + } else if (wasInPip && isInPip) { + // No update until exit PIP. + return; + } else if (isInPip) { + // Enter PIP. + // All overrides will be cleanup. + container.setLastRequestedBounds(null /* bounds */); + cleanupForEnterPip(container); + } else if (wasInPip) { + // Exit PIP. + // Updates the presentation of the container. Expand or launch placeholder if needed. + mPresenter.updateContainer(container); } updateCallbackIfNecessary(); } @@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { - TaskFragmentContainer container = getContainer(fragmentToken); + final TaskFragmentContainer container = getContainer(fragmentToken); if (container != null) { - onTaskBoundsMayChange(container.getTaskId(), - parentConfig.windowConfiguration.getBounds()); + onTaskConfigurationChanged(container.getTaskId(), parentConfig); + if (isInPictureInPicture(parentConfig)) { + // No need to update presentation in PIP until the Task exit PIP. + return; + } mPresenter.updateContainer(container); updateCallbackIfNecessary(); } @@ -199,44 +220,70 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - private void onTaskBoundsMayChange(int taskId, @NonNull Rect taskBounds) { + private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) { final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer != null && !taskBounds.isEmpty() - && !taskContainer.mTaskBounds.equals(taskBounds)) { + if (taskContainer == null) { + return; + } + final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration); + final boolean isInPIp = isInPictureInPicture(config); + taskContainer.mConfiguration = config; + + // We need to check the animation override when enter/exit PIP or has bounds changed. + boolean shouldUpdateAnimationOverride = wasInPip != isInPIp; + if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds()) + && !isInPIp) { + // We don't care the bounds change when it has already entered PIP. + shouldUpdateAnimationOverride = true; + } + if (shouldUpdateAnimationOverride) { + updateAnimationOverride(taskContainer); + } + } + + /** Returns {@code true} if the bounds is changed. */ + private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer, + @NonNull Rect taskBounds) { + if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) { taskContainer.mTaskBounds.set(taskBounds); - updateAnimationOverride(taskId); + return true; } + return false; } /** * Updates if we should override transition animation. We only want to override if the Task * bounds is large enough for at least one split rule. */ - private void updateAnimationOverride(int taskId) { - final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) { + private void updateAnimationOverride(@NonNull TaskContainer taskContainer) { + if (!taskContainer.isTaskBoundsInitialized()) { // We don't know about the Task bounds yet. return; } + // We only want to override if it supports split. + if (supportSplit(taskContainer)) { + mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId); + } else { + mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId); + } + } + + private boolean supportSplit(@NonNull TaskContainer taskContainer) { + // No split inside PIP. + if (isInPictureInPicture(taskContainer.mConfiguration)) { + return false; + } // Check if the parent container bounds can support any split rule. - boolean supportSplit = false; for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof SplitRule)) { continue; } if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) { - supportSplit = true; - break; + return true; } } - - // We only want to override if it supports split. - if (supportSplit) { - mPresenter.startOverrideSplitAnimation(taskId); - } else { - mPresenter.stopOverrideSplitAnimation(taskId); - } + return false; } void onActivityCreated(@NonNull Activity launchedActivity) { @@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ // TODO(b/190433398): Break down into smaller functions. void handleActivityCreated(@NonNull Activity launchedActivity) { + if (isInPictureInPicture(launchedActivity)) { + // We don't embed activity when it is in PIP. + return; + } final List<EmbeddingRule> splitRules = getSplitRules(); final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken()); @@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } private void onActivityConfigurationChanged(@NonNull Activity activity) { + if (isInPictureInPicture(activity)) { + // We don't embed activity when it is in PIP. + return; + } final TaskFragmentContainer currentContainer = getContainerWithActivity( activity.getActivityToken()); @@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskContainer taskContainer = mTaskContainers.get(taskId); taskContainer.mContainers.add(container); - if (activity != null && !taskContainer.isTaskBoundsInitialized()) { + if (activity != null && !taskContainer.isTaskBoundsInitialized() + && onTaskBoundsMayChange(taskContainer, + SplitPresenter.getTaskBoundsFromActivity(activity))) { // Initial check before any TaskFragment has appeared. - onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity)); + updateAnimationOverride(taskContainer); } return container; } @@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer); } + /** Cleanups all the dependencies when the TaskFragment is entering PIP. */ + private void cleanupForEnterPip(@NonNull TaskFragmentContainer container) { + final int taskId = container.getTaskId(); + final TaskContainer taskContainer = mTaskContainers.get(taskId); + if (taskContainer == null) { + return; + } + final List<SplitContainer> splitsToRemove = new ArrayList<>(); + final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>(); + for (SplitContainer splitContainer : taskContainer.mSplitContainers) { + if (splitContainer.getPrimaryContainer() != container + && splitContainer.getSecondaryContainer() != container) { + continue; + } + splitsToRemove.add(splitContainer); + final TaskFragmentContainer splitTf = splitContainer.getPrimaryContainer() == container + ? splitContainer.getSecondaryContainer() + : splitContainer.getPrimaryContainer(); + containersToUpdate.add(splitTf); + // We don't want the PIP TaskFragment to be removed as a result of any of its dependents + // being removed. + splitTf.removeContainerToFinishOnExit(container); + if (container.getTopNonFinishingActivity() != null) { + splitTf.removeActivityToFinishOnExit(container.getTopNonFinishingActivity()); + } + } + container.resetDependencies(); + taskContainer.mSplitContainers.removeAll(splitsToRemove); + // If there is any TaskFragment split with the PIP TaskFragment, update their presentations + // since the split is dismissed. + // We don't want to close any of them even if they are dependencies of the PIP TaskFragment. + mPresenter.updateContainers(containersToUpdate); + } + /** * Removes the container from bookkeeping records. */ @@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return super.onStartActivity(who, intent, options); } final Activity launchingActivity = (Activity) who; + if (isInPictureInPicture(launchingActivity)) { + // We don't embed activity when it is in PIP. + return super.onStartActivity(who, intent, options); + } if (shouldExpand(null, intent, getSplitRules())) { setLaunchingInExpandedContainer(launchingActivity, options); @@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return !pairRule.shouldClearTop(); } + private static boolean isInPictureInPicture(@NonNull Activity activity) { + return isInPictureInPicture(activity.getResources().getConfiguration()); + } + + private static boolean isInPictureInPicture(@NonNull TaskFragmentContainer tf) { + return isInPictureInPicture(tf.getInfo().getConfiguration()); + } + + private static boolean isInPictureInPicture(@Nullable Configuration configuration) { + return configuration != null + && configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; + } + /** Represents TaskFragments and split pairs below a Task. */ @VisibleForTesting static class TaskContainer { @@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final Set<IBinder> mFinishedContainer = new ArraySet<>(); /** Available window bounds of this Task. */ final Rect mTaskBounds = new Rect(); + /** Configuration of the Task. */ + @Nullable + Configuration mConfiguration; TaskContainer(int taskId) { mTaskId = taskId; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index e4d9edeb4c6e..b55c16e3b45d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -36,6 +36,7 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.Collection; import java.util.concurrent.Executor; /** @@ -65,13 +66,27 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { /** * Updates the presentation of the provided container. */ - void updateContainer(TaskFragmentContainer container) { + void updateContainer(@NonNull TaskFragmentContainer container) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mController.updateContainer(wct, container); applyTransaction(wct); } /** + * Updates the presentation of the provided containers. + */ + void updateContainers(@NonNull Collection<TaskFragmentContainer> containers) { + if (containers.isEmpty()) { + return; + } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + for (TaskFragmentContainer container : containers) { + mController.updateContainer(wct, container); + } + applyTransaction(wct); + } + + /** * Deletes the specified container and all other associated and dependent containers in the same * transaction. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 9a12669f078a..20c929b26acb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -192,6 +192,13 @@ class TaskFragmentContainer { } /** + * Removes a container that should be finished when this container is finished. + */ + void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) { + mContainersToFinishOnExit.remove(containerToRemove); + } + + /** * Adds an activity that should be finished when this container is finished. */ void addActivityToFinishOnExit(@NonNull Activity activityToFinish) { @@ -199,6 +206,19 @@ class TaskFragmentContainer { } /** + * Removes an activity that should be finished when this container is finished. + */ + void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) { + mActivitiesToFinishOnExit.remove(activityToRemove); + } + + /** Removes all dependencies that should be finished when this container is finished. */ + void resetDependencies() { + mContainersToFinishOnExit.clear(); + mActivitiesToFinishOnExit.clear(); + } + + /** * Removes all activities that belong to this process and finishes other containers/activities * configured to finish together. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 1a1cd5b27c53..b6fb82852c2f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1225,12 +1225,6 @@ public class BubbleController { mOverflowListener.applyUpdate(update); } - // Collapsing? Do this first before remaining steps. - if (update.expandedChanged && !update.expanded) { - mStackView.setExpanded(false); - mSysuiProxy.requestNotificationShadeTopUi(false, TAG); - } - // Do removals, if any. ArrayList<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>(update.removedBubbles); @@ -1307,6 +1301,11 @@ public class BubbleController { mStackView.updateBubbleOrder(update.bubbles); } + if (update.expandedChanged && !update.expanded) { + mStackView.setExpanded(false); + mSysuiProxy.requestNotificationShadeTopUi(false, TAG); + } + if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java index ea074993bae1..a3048bd8fabe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java @@ -35,7 +35,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * TV specific values of the current state of the PIP bounds. + * TV specific values of the current state of the PiP bounds. */ public class TvPipBoundsState extends PipBoundsState { @@ -86,7 +86,7 @@ public class TvPipBoundsState extends PipBoundsState { mTvPipGravity = DEFAULT_TV_GRAVITY; } - /** Set the tv expanded bounds of PIP */ + /** Set the tv expanded bounds of PiP */ public void setTvExpandedSize(@Nullable Size size) { mTvExpandedSize = size; } @@ -97,20 +97,24 @@ public class TvPipBoundsState extends PipBoundsState { return mTvExpandedSize; } - /** Set the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */ + /** Set the PiP aspect ratio for the expanded PiP (TV) that is desired by the app. */ public void setDesiredTvExpandedAspectRatio(float aspectRatio, boolean override) { - if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED || aspectRatio == 0) { + if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED) { mDesiredTvExpandedAspectRatio = aspectRatio; resetTvPipState(); return; } if ((aspectRatio > 1 && mTvFixedPipOrientation == ORIENTATION_HORIZONTAL) - || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL)) { + || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL) + || aspectRatio == 0) { mDesiredTvExpandedAspectRatio = aspectRatio; } } - /** Get the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */ + /** + * Get the aspect ratio for the expanded PiP (TV) that is desired, or {@code 0} if it is not + * enabled by the app. + */ public float getDesiredTvExpandedAspectRatio() { return mDesiredTvExpandedAspectRatio; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt index 4e8e71bd04c4..09d202abfbde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt @@ -133,7 +133,8 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { val pipAnchorBoundsWithAllDecors = getNormalPipAnchorBounds(pipSizeWithAllDecors, transformedMovementBounds) - val pipAnchorBoundsWithPermanentDecors = removeTemporaryDecors(pipAnchorBoundsWithAllDecors) + val pipAnchorBoundsWithPermanentDecors = + removeTemporaryDecorsTransformed(pipAnchorBoundsWithAllDecors) val result = calculatePipPositionTransformed( pipAnchorBoundsWithPermanentDecors, transformedRestrictedAreas, @@ -471,12 +472,10 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { } fun setPipPermanentDecorInsets(insets: Insets) { - if (pipPermanentDecorInsets == insets) return pipPermanentDecorInsets = insets } fun setPipTemporaryDecorInsets(insets: Insets) { - if (pipTemporaryDecorInsets == insets) return pipTemporaryDecorInsets = insets } @@ -781,6 +780,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { /** * Removes the space that was reserved for permanent decorations around the pip + * @param bounds the bounds (in screen space) to remove the insets from */ private fun removePermanentDecors(bounds: Rect): Rect { val pipDecorReverseInsets = Insets.subtract(Insets.NONE, pipPermanentDecorInsets) @@ -790,11 +790,15 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { /** * Removes the space that was reserved for temporary decorations around the pip + * @param bounds the bounds (in base case) to remove the insets from */ - private fun removeTemporaryDecors(bounds: Rect): Rect { - val pipDecorReverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets) - bounds.inset(pipDecorReverseInsets) - return bounds + private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect { + if (pipTemporaryDecorInsets == Insets.NONE) return bounds + + var reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets) + var boundsInScreenSpace = fromTransformedSpace(bounds) + boundsInScreenSpace.inset(reverseInsets) + return toTransformedSpace(boundsInScreenSpace) } private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 3ea57b0520b7..9154226b7b22 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -417,7 +417,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK; final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0; - if (isOpenOrCloseTransition && !isTranslucent) { + if (isOpenOrCloseTransition && !isTranslucent + && wallpaperTransit == WALLPAPER_TRANSITION_NONE) { // Use the overview background as the background for the animation final Context uiContext = ActivityThread.currentActivityThread() .getSystemUiContext(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt index e6ba70e1b60e..9919214642fc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.tv +import android.graphics.Insets import android.graphics.Rect import android.testing.AndroidTestingRunner import android.util.Size @@ -432,6 +433,64 @@ class TvPipKeepClearAlgorithmTest { assertEquals(currentTime + algorithm.stashDuration, placement.unstashTime) } + @Test + fun test_PipInsets() { + val permInsets = Insets.of(-1, -2, -3, -4) + algorithm.setPipPermanentDecorInsets(permInsets) + testInsetsForAllPositions(permInsets) + + val tempInsets = Insets.of(-4, -3, -2, -1) + algorithm.setPipPermanentDecorInsets(Insets.NONE) + algorithm.setPipTemporaryDecorInsets(tempInsets) + testInsetsForAllPositions(tempInsets) + + algorithm.setPipPermanentDecorInsets(permInsets) + algorithm.setPipTemporaryDecorInsets(tempInsets) + testInsetsForAllPositions(Insets.add(permInsets, tempInsets)) + } + + private fun testInsetsForAllPositions(insets: Insets) { + gravity = Gravity.BOTTOM or Gravity.RIGHT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.BOTTOM or Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP or Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP or Gravity.RIGHT + testAnchorPositionWithInsets(insets) + + pipSize = EXPANDED_WIDE_PIP_SIZE + + gravity = Gravity.BOTTOM + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP + testAnchorPositionWithInsets(insets) + + pipSize = Size(pipSize.height, pipSize.width) + + gravity = Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.RIGHT + testAnchorPositionWithInsets(insets) + } + + private fun testAnchorPositionWithInsets(insets: Insets) { + var pipRect = Rect(0, 0, pipSize.width, pipSize.height) + pipRect.inset(insets) + var expectedBounds = Rect() + Gravity.apply(gravity, pipRect.width(), pipRect.height(), movementBounds, expectedBounds) + val reverseInsets = Insets.subtract(Insets.NONE, insets) + expectedBounds.inset(reverseInsets) + + var placement = getActualPlacement() + assertEquals(expectedBounds, placement.bounds) + } + private fun makeSideBar(width: Int, @Gravity.GravityFlags side: Int): Rect { val sidebar = Rect(0, 0, width, SCREEN_SIZE.height) if (side == Gravity.RIGHT) { diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index ab00dd5a487c..dc72aead4873 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -61,6 +61,17 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } + // canvas may be an AlphaFilterCanvas, which is intended to draw with a + // modified alpha. We do not have a way to do this without drawing into an + // extra layer, which would have a performance cost. Draw directly into the + // underlying gpu canvas. This matches prior behavior and the behavior in + // Vulkan. + { + auto* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas); + LOG_ALWAYS_FATAL_IF(!gpuCanvas, "GLFunctorDrawable::onDraw is using an invalid canvas!"); + canvas = gpuCanvas; + } + // flush will create a GrRenderTarget if not already present. canvas->flush(); |