From e16edc943f2e2e32f658dc2e01c416382ea71c26 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 21 Sep 2023 16:15:17 -0700 Subject: [PATCH] Account for tasks over splitscreen in RemoteTargetGluer * Previously we defaulted to 2 handles, but we didn't take into account the posibility of having assistant showing a translucent task on top of 2 apps. Or other tasks such as Home controls. * We can scale down the number of handles we have from initialization (as done in assignTargetsForSplitscreen), but it's a bit harder to scale up. This is a separate, larger problem in that the handles are created and needed before onAnimationStart(), so Launcher doesn't know exactly how many handles are needed to be created for the recents animation, that's determined by the actual onAnimationStart() call itself. * There's a small known bug only in the split case where one of the tasks shows up over the assistant task, but can live with that to minimize changes. (peanutbutter@ to clarify?) Demo: https://drive.google.com/file/d/1hqipfdym_rRXFsAW932es7UdxTx66Zu4/view?usp=drive_link&resourcekey=0-Ri_Kl0xCNN3E81StTh4vxw As you can see, Assistant translucent overlay seems to work well, but there are still some glitches with Home controls. Specifically, there is a flicker, sometimes followed by a black screen, when returning to the split tasks with Home controls open on top. The flicker also happens with Assistant, but I haven't seen the black screen for that case yet. In any case, the crash is avoided, so... progress? (Also these flickers/black screen happen for Home controls even without splitscreen). Original change by peanutbutter@: ag/24830632 Test: Tested assistant result tasks with fullscreen and splitscreen, and by itself. Seems to animate fine. Also tested with Home controls, which animates fine and avoids the crash, but still has some issues when re-opening the tasks as described above. Flag: NA Bug: 321328009 Change-Id: I0aa04a6a14cf723b34431855483662039c96e553 --- .../android/quickstep/RemoteTargetGluer.java | 88 +++++++++++++------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java index 98d0ece5a0..2e70703b61 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -20,11 +20,13 @@ import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBound import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; +import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Rect; import android.util.Log; import android.view.RemoteAnimationTarget; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.util.SplitConfigurationOptions; @@ -35,6 +37,8 @@ import com.android.wm.shell.util.SplitBounds; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Glues together the necessary components to animate a remote target using a @@ -43,7 +47,9 @@ import java.util.Arrays; public class RemoteTargetGluer { private static final String TAG = "RemoteTargetGluer"; - private static final int DEFAULT_NUM_HANDLES = 2; + // This is the default number of handles to create when we don't know how many tasks are running + // (e.g. if we're in split screen). Allocate extra for potential tasks overlaid, like volume. + private static final int DEFAULT_NUM_HANDLES = 4; private RemoteTargetHandle[] mRemoteTargetHandles; private SplitConfigurationOptions.SplitBounds mSplitBounds; @@ -107,7 +113,7 @@ public class RemoteTargetGluer { for (int i = 0; i < mRemoteTargetHandles.length; i++) { RemoteAnimationTarget primaryTaskTarget = targets.apps[i]; mRemoteTargetHandles[i].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, null)); + createRemoteAnimationTargetsForTarget(targets, Collections.emptyList())); mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null); } return mRemoteTargetHandles; @@ -177,18 +183,42 @@ public class RemoteTargetGluer { RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId); RemoteAnimationTarget bottomRightTarget = targets.findTask( mSplitBounds.rightBottomTaskId); + List overlayTargets = Arrays.stream(targets.apps).filter( + target -> target.windowConfiguration.getWindowingMode() + != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).toList(); // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude, // vice versa mRemoteTargetHandles[0].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, bottomRightTarget)); - mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, - mSplitBounds); + createRemoteAnimationTargetsForTarget(targets, + Collections.singletonList(bottomRightTarget))); + mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, mSplitBounds); mRemoteTargetHandles[1].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, topLeftTarget)); - mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, - mSplitBounds); + createRemoteAnimationTargetsForTarget(targets, + Collections.singletonList(topLeftTarget))); + mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, mSplitBounds); + + // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks + if (!overlayTargets.isEmpty()) { + ArrayList targetsToExclude = new ArrayList<>(); + targetsToExclude.add(topLeftTarget); + targetsToExclude.add(bottomRightTarget); + // Start i at 2 to account for top/left and bottom/right split handles already made + for (int i = 2; i < targets.apps.length; i++) { + if (i >= mRemoteTargetHandles.length) { + Log.e(TAG, String.format("Attempting to animate an untracked target" + + " (%d handles allocated, but %d want to animate)", + mRemoteTargetHandles.length, targets.apps.length)); + break; + } + mRemoteTargetHandles[i].mTransformParams.setTargetSet( + createRemoteAnimationTargetsForTarget(targets, targetsToExclude)); + mRemoteTargetHandles[i].mTaskViewSimulator.setPreview( + overlayTargets.get(i - 2)); + } + + } } return mRemoteTargetHandles; } @@ -214,32 +244,38 @@ public class RemoteTargetGluer { /** * Ensures that we aren't excluding ancillary targets such as home/recents * - * @param targetToExclude Will be excluded from the resulting return value. - * Pass in {@code null} to not exclude anything + * @param targetsToExclude Will be excluded from the resulting return value. + * Pass in an empty list to not exclude anything * @return RemoteAnimationTargets where all the app targets from the passed in - * {@param targets} are included except {@param targetToExclude} + * {@code targets} are included except {@code targetsToExclude} */ private RemoteAnimationTargets createRemoteAnimationTargetsForTarget( - RemoteAnimationTargets targets, - RemoteAnimationTarget targetToExclude) { - ArrayList targetsWithoutExcluded = new ArrayList<>(); + @NonNull RemoteAnimationTargets targets, + @NonNull List targetsToExclude) { + ArrayList targetsToInclude = new ArrayList<>(); for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) { - if (targetCompat == targetToExclude) { + boolean skipTarget = false; + for (RemoteAnimationTarget excludingTarget : targetsToExclude) { + if (targetCompat == excludingTarget) { + skipTarget = true; + break; + } + if (excludingTarget != null + && excludingTarget.taskInfo != null + && targetCompat.taskInfo != null + && excludingTarget.taskInfo.parentTaskId == targetCompat.taskInfo.taskId) { + // Also exclude corresponding parent task + skipTarget = true; + } + } + if (skipTarget) { continue; } - if (targetToExclude != null - && targetToExclude.taskInfo != null - && targetCompat.taskInfo != null - && targetToExclude.taskInfo.parentTaskId == targetCompat.taskInfo.taskId) { - // Also exclude corresponding parent task - continue; - } - - targetsWithoutExcluded.add(targetCompat); + targetsToInclude.add(targetCompat); } - final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray( - new RemoteAnimationTarget[targetsWithoutExcluded.size()]); + final RemoteAnimationTarget[] filteredApps = targetsToInclude.toArray( + new RemoteAnimationTarget[0]); return new RemoteAnimationTargets( filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode); }