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:
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user