Fix flickering issues with divider during split animation

This CL makes changes to the "split divider placeholder view", which was intended to cover up the backdrop a little during the split confirm animation. The placeholder view is now larger (fullscreen) and fades in with the animation movement, so there is no longer a period of time when it looks like an awkward rectangle.

New timings and interpolators confirmed with UX. Also renamed some variables and added comments for clarity.

Fixes: 344573331
Test: Manually verified that the visual bug no longer happens on large and small screen, and from desktop and Overview.
Flag: EXEMPT bugfix
Change-Id: I3b37f2b0478035d7a3181ae7c23962fe75a13b2c
This commit is contained in:
Jeremy Sim
2024-06-13 12:41:39 -07:00
parent 6e02de9c2b
commit 91fb2f2e5e
6 changed files with 46 additions and 49 deletions
@@ -27,6 +27,8 @@ public class PhoneSplitToConfirmTimings
public int getPlaceholderIconFadeInEnd() { return 133; } public int getPlaceholderIconFadeInEnd() { return 133; }
public int getStagedRectSlideStart() { return 0; } public int getStagedRectSlideStart() { return 0; }
public int getStagedRectSlideEnd() { return 333; } public int getStagedRectSlideEnd() { return 333; }
public int getBackingScrimFadeInStart() { return 0; }
public int getBackingScrimFadeInEnd() { return 266; }
public int getDuration() { return PHONE_CONFIRM_DURATION; } public int getDuration() { return PHONE_CONFIRM_DURATION; }
} }
@@ -281,64 +281,45 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
} }
/** /**
* Creates and returns a view to fade in at .4 animation progress and adds it to the provided * Creates and returns a fullscreen scrim to fade in behind the split confirm animation, and
* [pendingAnimation]. Assumes that animation will be the final split placeholder launch anim. * adds it to the provided [pendingAnimation].
*
* [secondPlaceholderEndingBounds] refers to the second placeholder view that gets added on
* screen, not the logical second app. For landscape it's the left app and for portrait the top
* one.
*/ */
fun addDividerPlaceholderViewToAnim( fun addScrimBehindAnim(
pendingAnimation: PendingAnimation, pendingAnimation: PendingAnimation,
container: RecentsViewContainer, container: RecentsViewContainer,
secondPlaceholderEndingBounds: Rect,
context: Context context: Context
): View { ): View {
val mSplitDividerPlaceholderView = View(context) val scrim = View(context)
val recentsView = container.getOverviewPanel<RecentsView<*, *>>() val recentsView = container.getOverviewPanel<RecentsView<*, *>>()
val dp: com.android.launcher3.DeviceProfile = container.getDeviceProfile() val dp: DeviceProfile = container.getDeviceProfile()
// Add it before/under the most recently added first floating taskView // Add it before/under the most recently added first floating taskView
val firstAddedSplitViewIndex: Int = val firstAddedSplitViewIndex: Int =
container container
.getDragLayer() .getDragLayer()
.indexOfChild(recentsView.splitSelectController.firstFloatingTaskView) .indexOfChild(recentsView.splitSelectController.firstFloatingTaskView)
container.getDragLayer().addView(mSplitDividerPlaceholderView, firstAddedSplitViewIndex) container.getDragLayer().addView(scrim, firstAddedSplitViewIndex)
val lp = mSplitDividerPlaceholderView.layoutParams as InsettableFrameLayout.LayoutParams // Make the scrim fullscreen
val lp = scrim.layoutParams as InsettableFrameLayout.LayoutParams
lp.topMargin = 0 lp.topMargin = 0
lp.height = dp.heightPx
lp.width = dp.widthPx
if (dp.isLeftRightSplit) { scrim.alpha = 0f
lp.height = secondPlaceholderEndingBounds.height() scrim.setBackgroundColor(
lp.width =
container
.asContext()
.resources
.getDimensionPixelSize(R.dimen.split_divider_handle_region_height)
mSplitDividerPlaceholderView.translationX =
secondPlaceholderEndingBounds.right - lp.width / 2f
mSplitDividerPlaceholderView.translationY = 0f
} else {
lp.height =
container
.asContext()
.resources
.getDimensionPixelSize(R.dimen.split_divider_handle_region_height)
lp.width = secondPlaceholderEndingBounds.width()
mSplitDividerPlaceholderView.translationY =
secondPlaceholderEndingBounds.top - lp.height / 2f
mSplitDividerPlaceholderView.translationX = 0f
}
mSplitDividerPlaceholderView.alpha = 0f
mSplitDividerPlaceholderView.setBackgroundColor(
container.asContext().resources.getColor(R.color.taskbar_background_dark) container.asContext().resources.getColor(R.color.taskbar_background_dark)
) )
val timings = AnimUtils.getDeviceSplitToConfirmTimings(dp.isTablet) val timings = AnimUtils.getDeviceSplitToConfirmTimings(dp.isTablet) as SplitToConfirmTimings
pendingAnimation.setViewAlpha( pendingAnimation.setViewAlpha(
mSplitDividerPlaceholderView, scrim,
1f, 1f,
Interpolators.clampToProgress(timings.stagedRectScaleXInterpolator, 0.4f, 1f) Interpolators.clampToProgress(
timings.backingScrimFadeInterpolator,
timings.backingScrimFadeInStartOffset,
timings.backingScrimFadeInEndOffset
)
) )
return mSplitDividerPlaceholderView
return scrim
} }
/** Does not play any animation if user is not currently in split selection state. */ /** Does not play any animation if user is not currently in split selection state. */
@@ -17,6 +17,7 @@
package com.android.quickstep.util; package com.android.quickstep.util;
import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.LINEAR;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
@@ -31,6 +32,8 @@ abstract class SplitToConfirmTimings implements SplitAnimationTimings {
abstract public int getPlaceholderIconFadeInEnd(); abstract public int getPlaceholderIconFadeInEnd();
abstract public int getStagedRectSlideStart(); abstract public int getStagedRectSlideStart();
abstract public int getStagedRectSlideEnd(); abstract public int getStagedRectSlideEnd();
abstract public int getBackingScrimFadeInStart();
abstract public int getBackingScrimFadeInEnd();
// Common timings // Common timings
public int getInstructionsFadeStart() { return 0; } public int getInstructionsFadeStart() { return 0; }
@@ -39,6 +42,7 @@ abstract class SplitToConfirmTimings implements SplitAnimationTimings {
public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; } public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; } public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; } public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
public Interpolator getBackingScrimFadeInterpolator() { return LINEAR; }
abstract public int getDuration(); abstract public int getDuration();
@@ -48,4 +52,10 @@ abstract class SplitToConfirmTimings implements SplitAnimationTimings {
public float getInstructionsFadeEndOffset() { public float getInstructionsFadeEndOffset() {
return (float) getInstructionsFadeEnd() / getDuration(); return (float) getInstructionsFadeEnd() / getDuration();
} }
public float getBackingScrimFadeInStartOffset() {
return (float) getBackingScrimFadeInStart() / getDuration();
}
public float getBackingScrimFadeInEndOffset() {
return (float) getBackingScrimFadeInEnd() / getDuration();
}
} }
@@ -163,9 +163,8 @@ public class SplitToWorkspaceController {
new RectF(firstTaskStartingBounds), firstTaskEndingBounds, new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
false /* fadeWithThumbnail */, true /* isStagedTask */); false /* fadeWithThumbnail */, true /* isStagedTask */);
View mSplitDividerPlaceholderView = recentsView.getSplitSelectController() View backingScrim = recentsView.getSplitSelectController().getSplitAnimationController()
.getSplitAnimationController().addDividerPlaceholderViewToAnim(pendingAnimation, .addScrimBehindAnim(pendingAnimation, mLauncher, view.getContext());
mLauncher, secondTaskEndingBounds, view.getContext());
FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher, FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
view, bitmap, icon, secondTaskStartingBounds); view, bitmap, icon, secondTaskStartingBounds);
@@ -197,7 +196,7 @@ public class SplitToWorkspaceController {
private void cleanUp() { private void cleanUp() {
mLauncher.getDragLayer().removeView(firstFloatingTaskView); mLauncher.getDragLayer().removeView(firstFloatingTaskView);
mLauncher.getDragLayer().removeView(secondFloatingTaskView); mLauncher.getDragLayer().removeView(secondFloatingTaskView);
mLauncher.getDragLayer().removeView(mSplitDividerPlaceholderView); mLauncher.getDragLayer().removeView(backingScrim);
mController.getSplitAnimationController().removeSplitInstructionsView(mLauncher); mController.getSplitAnimationController().removeSplitInstructionsView(mLauncher);
mController.resetState(); mController.resetState();
} }
@@ -27,6 +27,8 @@ public class TabletSplitToConfirmTimings
public int getPlaceholderIconFadeInEnd() { return 250; } public int getPlaceholderIconFadeInEnd() { return 250; }
public int getStagedRectSlideStart() { return 0; } public int getStagedRectSlideStart() { return 0; }
public int getStagedRectSlideEnd() { return 500; } public int getStagedRectSlideEnd() { return 500; }
public int getBackingScrimFadeInStart() { return 0; }
public int getBackingScrimFadeInEnd() { return 400; }
public int getDuration() { return TABLET_CONFIRM_DURATION; } public int getDuration() { return TABLET_CONFIRM_DURATION; }
} }
@@ -738,7 +738,11 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
private int mSplitHiddenTaskViewIndex = -1; private int mSplitHiddenTaskViewIndex = -1;
@Nullable @Nullable
private FloatingTaskView mSecondFloatingTaskView; private FloatingTaskView mSecondFloatingTaskView;
private View mSplitDividerPlaceholderView; /**
* A fullscreen scrim that goes behind the splitscreen animation to hide color conflicts and
* possible flickers. Removed after tasks + divider finish animating in.
*/
private View mSplitScrim;
/** /**
* The task to be removed and immediately re-added. Should not be added to task pool. * The task to be removed and immediately re-added. Should not be added to task pool.
@@ -4926,9 +4930,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds, mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds,
secondTaskEndingBounds); secondTaskEndingBounds);
mSplitDividerPlaceholderView = mSplitSelectStateController mSplitScrim = mSplitSelectStateController.getSplitAnimationController()
.getSplitAnimationController().addDividerPlaceholderViewToAnim(pendingAnimation, .addScrimBehindAnim(pendingAnimation, mContainer, getContext());
mContainer, secondTaskEndingBounds, getContext());
FloatingTaskView firstFloatingTaskView = FloatingTaskView firstFloatingTaskView =
mSplitSelectStateController.getFirstFloatingTaskView(); mSplitSelectStateController.getFirstFloatingTaskView();
firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds); firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
@@ -4983,7 +4986,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView()); safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
safeRemoveDragLayerView(mSecondFloatingTaskView); safeRemoveDragLayerView(mSecondFloatingTaskView);
safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView()); safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView());
safeRemoveDragLayerView(mSplitDividerPlaceholderView); safeRemoveDragLayerView(mSplitScrim);
mSecondFloatingTaskView = null; mSecondFloatingTaskView = null;
mSplitSelectSource = null; mSplitSelectSource = null;
mSplitSelectStateController.getSplitAnimationController() mSplitSelectStateController.getSplitAnimationController()