From 59904c788eff3762d77406a4dca344a4243a08d3 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 14 Mar 2025 09:55:14 -0400 Subject: [PATCH] Fix app pair crash * With the change made in ag/31615582, we assumed that top level + stage level split roots would always be included in the transition, that isn't the case when an app pair is already showing and then we switch to another app pair (stage roots aren't included since they aren't modified in that scenario) * Now shell sends the entire tree. Also modified SplitScreenUtils to consider TRANSIT_CHANGE changes as well for the scenario where one app of an app pair is already full screen and then we launch an app pair which has that same app, the stage root there undergoes a change and not a open/close transition. * One KI is that the animation doesn't fully work when launching a second app pair from taskbar when split is already showing (though it is an improvement from current behavior/animation). This is because when the second app pair launches, the tasks' snapshot windows/leashes are passed back as leafs, however these snapshots have not been parented to be under the stage roots. So both snapshots are positioned at 0, 0 causing overlap. Will fix that in a followup Bug: 399037701 Test: Open app pairs from desktop windowing + normal taskbar Flag: EXEMPT bugfix Change-Id: I83adffd16fea98b8e877dfbe1ba1ef4a3d08fcb9 --- .../util/SplitAnimationController.kt | 69 +++++++++++-------- .../quickstep/util/SplitScreenUtils.kt | 25 ++++--- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index 8601350b8f..7dc7b9ec31 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -36,6 +36,7 @@ import android.view.RemoteAnimationTarget import android.view.SurfaceControl import android.view.SurfaceControl.Transaction import android.view.View +import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo @@ -65,6 +66,7 @@ import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource import com.android.launcher3.views.BaseDragLayer import com.android.quickstep.TaskViewUtils +import com.android.quickstep.util.SplitScreenUtils.Companion.extractTopParentAndChildren import com.android.quickstep.views.FloatingAppPairView import com.android.quickstep.views.FloatingTaskView import com.android.quickstep.views.GroupedTaskView @@ -730,7 +732,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC val launchAnimation = AnimatorSet() val splitRoots: Pair>? = - SplitScreenUtils.extractTopParentAndChildren(transitionInfo) + extractTopParentAndChildren(transitionInfo) check(splitRoots != null) { "Could not find split roots" } // Will point to change (0) in diagram above @@ -981,35 +983,19 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC progressUpdater.setDuration(QuickstepTransitionManager.APP_LAUNCH_DURATION) progressUpdater.interpolator = Interpolators.EMPHASIZED - var rootCandidate: Change? = null - - for (change in transitionInfo.changes) { - val taskInfo: RunningTaskInfo = change.taskInfo ?: continue - - // TODO (b/316490565): Replace this logic when SplitBounds is available to - // startAnimation() and we can know the precise taskIds of launching tasks. - if ( - taskInfo.windowingMode == windowingMode && - (change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT) - ) { - // Found one! - rootCandidate = change - break + val splitTree: Pair>? = extractTopParentAndChildren(transitionInfo) + check(splitTree != null) { "Could not find a split root candidate" } + val rootCandidate = splitTree.first + val stageRootTaskIds: Set = splitTree.second + .map { it.taskInfo!!.taskId } + .toSet() + val leafTasks: List = transitionInfo.changes + .filter { + (TransitionUtil.isOpeningMode(it.mode) || it.mode == TRANSIT_CHANGE) + && it.taskInfo != null + && it.taskInfo!!.parentTaskId in stageRootTaskIds } - } - - // If we could not find a proper root candidate, something went wrong. - check(rootCandidate != null) { "Could not find a split root candidate" } - - // Recurse up the tree until parent is null, then we've found our root. - var parentToken: WindowContainerToken? = rootCandidate.parent - while (parentToken != null) { - rootCandidate = transitionInfo.getChange(parentToken) ?: break - parentToken = rootCandidate.parent - } - - // Make sure nothing weird happened, like getChange() returning null. - check(rootCandidate != null) { "Failed to find a root leash" } + .toList() // Starting position is a 34% size tile centered in the middle of the screen. // Ending position is the full device screen. @@ -1037,13 +1023,38 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC t.apply() } + // When animation ends, run finishCallback progressUpdater.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { finishCallback.run() } + + override fun onAnimationStart(animation: Animator) { + // Reset leaf and stage root tasks, animation can begin from freeform windows + for (leaf in leafTasks) { + val endAbsBounds = leaf.endAbsBounds + + t.setAlpha(leaf.leash, 1f) + t.setCrop(leaf.leash, 0f, 0f, + endAbsBounds.width().toFloat(), endAbsBounds.height().toFloat()) + t.setPosition(leaf.leash, 0f, 0f) + } + + for (stageRoot in splitTree.second) { + val endAbsBounds = stageRoot.endAbsBounds + + t.setAlpha(stageRoot.leash, 1f) + t.setCrop(stageRoot.leash, 0f, 0f, + endAbsBounds.width().toFloat(), endAbsBounds.height().toFloat()) + t.setPosition(stageRoot.leash, endAbsBounds.left.toFloat(), + endAbsBounds.top.toFloat()) + } + t.apply() + } } + ) launchAnimation.play(progressUpdater) diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt index 4005c5a813..1919f68d4a 100644 --- a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt +++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt @@ -17,14 +17,13 @@ package com.android.quickstep.util import android.util.Log -import android.view.WindowManager.TRANSIT_OPEN -import android.view.WindowManager.TRANSIT_TO_FRONT +import android.view.WindowManager.TRANSIT_CHANGE import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.TransitionInfo.FLAG_FIRST_CUSTOM import com.android.launcher3.util.SplitConfigurationOptions +import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.split.SplitBounds -import java.lang.IllegalStateException class SplitScreenUtils { companion object { @@ -54,14 +53,7 @@ class SplitScreenUtils { ): Pair>? { val parentToChildren = mutableMapOf>() val hasParent = mutableSetOf() - // filter out anything that isn't opening and the divider - val taskChanges: List = - transitionInfo.changes - .filter { change -> - (change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT) && - change.flags < FLAG_FIRST_CUSTOM - } - .toList() + val taskChanges: List = getNonClosingChanges(transitionInfo) // 1. Build Parent-Child Relationships for (change in taskChanges) { @@ -90,5 +82,16 @@ class SplitScreenUtils { null } } + + + /** @return includes only opening + [TRANSIT_CHANGE] changes and the divider */ + private fun getNonClosingChanges(transitionInfo: TransitionInfo): List { + return transitionInfo.changes + .filter { change -> + (TransitionUtil.isOpeningMode(change.mode) || change.mode == TRANSIT_CHANGE) + && change.flags < FLAG_FIRST_CUSTOM + } + .toList() + } } }