Fix bug with Launcher animation canceling, esp. around OverviewSplitSelect
This commit fixes a bug where the user could cancel animations when transitioning between Launcher states, potentially resulting in a state where Overview elements (task thumbnails etc.) were wrongly hidden or invisible.
The bug occurred because functions such as createInitialSplitSelectAnimation() and createAtomicAnimation() did not carry out any cleanup upon animation failure. This resulted in RecentsView potentially being in a polluted state for the next launch.
Bug was fixed by adding cleanup routines to two onAnimationEnd listeners.
Fixes: 242715097
Test: Manual
Change-Id: I05415ecf515e247aa535e3ca8371e540c3189b01
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 8782ee6..2e4e739 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -28,11 +28,10 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
-import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.SPLIT_INSTRUCTIONS_FADE;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
@@ -112,6 +111,7 @@
// TODO (b/238651489): Refactor state management to avoid need for double check
FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
if (floatingTask != null) {
+ // We are in split selection state currently, transitioning to another state
DragLayer dragLayer = mLauncher.getDragLayer();
RectF onScreenRectF = new RectF();
Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask,
@@ -127,8 +127,8 @@
);
setter.setFloat(
- mRecentsView,
- FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+ mRecentsView.getFirstFloatingTaskView(),
+ PRIMARY_TRANSLATE_OFFSCREEN,
mRecentsView.getPagedOrientationHandler()
.getFloatingTaskOffscreenTranslationTarget(
floatingTask,
@@ -140,14 +140,14 @@
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
LINEAR
));
- setter.setFloat(
- mRecentsView,
- SPLIT_INSTRUCTIONS_FADE,
- 1,
+ setter.setViewAlpha(
+ mRecentsView.getSplitInstructionsView(),
+ 0,
config.getInterpolator(
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
LINEAR
- ));
+ )
+ );
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index eb739a6..7c96bf8 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -227,6 +227,11 @@
}
@Override
+ public void onStateTransitionFailed(RecentsState toState) {
+ reset();
+ }
+
+ @Override
public void onStateTransitionComplete(RecentsState finalState) {
if (finalState == HOME) {
// Clean-up logic that occurs when recents is no longer in use/visible.
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 7a66ea0..c3bf041 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -14,6 +14,7 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -49,6 +50,29 @@
*/
public class FloatingTaskView extends FrameLayout {
+ public static final FloatProperty<FloatingTaskView> PRIMARY_TRANSLATE_OFFSCREEN =
+ new FloatProperty<FloatingTaskView>("floatingTaskPrimaryTranslateOffscreen") {
+ @Override
+ public void setValue(FloatingTaskView view, float translation) {
+ ((RecentsView) view.mActivity.getOverviewPanel()).getPagedOrientationHandler()
+ .setFloatingTaskPrimaryTranslation(
+ view,
+ translation,
+ view.mActivity.getDeviceProfile()
+ );
+ }
+
+ @Override
+ public Float get(FloatingTaskView view) {
+ return ((RecentsView) view.mActivity.getOverviewPanel())
+ .getPagedOrientationHandler()
+ .getFloatingTaskPrimaryTranslation(
+ view,
+ view.mActivity.getDeviceProfile()
+ );
+ }
+ };
+
private FloatingTaskThumbnailView mThumbnailView;
private SplitPlaceholderView mSplitPlaceholderView;
private RectF mStartingPosition;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index a870f9c..2539ed6 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -319,4 +319,10 @@
super.applyThumbnailSplashAlpha();
mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
}
+
+ @Override
+ void setThumbnailVisibility(int visibility) {
+ super.setThumbnailVisibility(visibility);
+ mSnapshotView2.setVisibility(visibility);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 6a33d36..de7ccad 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -120,6 +120,11 @@
}
@Override
+ public void onStateTransitionFailed(LauncherState toState) {
+ reset();
+ }
+
+ @Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
// Clean-up logic that occurs when recents is no longer in use/visible.
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 69557a8..efaa644 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -397,39 +397,6 @@
}
};
- public static final FloatProperty<RecentsView> FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN =
- new FloatProperty<RecentsView>("firstFloatingTaskTranslateOffscreen") {
- @Override
- public void setValue(RecentsView view, float translation) {
- view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation(
- view.mFirstFloatingTaskView,
- translation,
- view.mActivity.getDeviceProfile()
- );
- }
-
- @Override
- public Float get(RecentsView view) {
- return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation(
- view.mFirstFloatingTaskView,
- view.mActivity.getDeviceProfile()
- );
- }
- };
-
- public static final FloatProperty<RecentsView> SPLIT_INSTRUCTIONS_FADE =
- new FloatProperty<RecentsView>("splitInstructionsFade") {
- @Override
- public void setValue(RecentsView view, float fade) {
- view.mSplitInstructionsView.setAlpha(1 - fade);
- }
-
- @Override
- public Float get(RecentsView view) {
- return 1 - view.mSplitInstructionsView.getAlpha();
- }
- };
-
// OverScroll constants
private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -2827,7 +2794,11 @@
RectF startingTaskRect = new RectF();
if (mSplitHiddenTaskView != null) {
- mSplitHiddenTaskView.setVisibility(INVISIBLE);
+ // Split staging is initiated, hide the original TaskView thumbnail.
+ // Toggled back on in resetFromSplitSelectionState().
+ mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
+ anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
+ clampToProgress(LINEAR, 0, 0.167f));
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitHiddenTaskView.getThumbnail(),
mSplitHiddenTaskView.getThumbnail().getThumbnail(),
@@ -2855,6 +2826,8 @@
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
+ // If transition to split select was interrupted, clean up to prevent glitches
+ resetFromSplitSelectionState();
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
}
@@ -4222,7 +4195,9 @@
resetTaskVisuals();
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
- mSplitHiddenTaskView.setVisibility(VISIBLE);
+ // Toggle thumbnail visibility back on (turned off in
+ // createInitialSplitSelectAnimation()).
+ mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
}
@@ -5334,6 +5309,11 @@
return mFirstFloatingTaskView;
}
+ @Nullable
+ public SplitInstructionsView getSplitInstructionsView() {
+ return mSplitInstructionsView;
+ }
+
/** Update the current activity locus id to show the enabled state of Overview */
public void updateLocusId() {
String locusId = "Overview";
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 34c2021..a84ea63 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -323,6 +323,19 @@
}
};
+ public static final FloatProperty<TaskView> ICON_ALPHA =
+ new FloatProperty<TaskView>("iconAlpha") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.mIconView.setAlpha(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mIconView.getAlpha();
+ }
+ };
+
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
@@ -1488,6 +1501,10 @@
return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
}
+ void setThumbnailVisibility(int visibility) {
+ mSnapshotView.setVisibility(visibility);
+ }
+
/**
* We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
*/
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2aa9dde..c44e1e1 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -342,6 +342,11 @@
public void onAnimationSuccess(Animator animator) {
onStateTransitionEnd(state);
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ onStateTransitionFailed(state);
+ }
};
}
@@ -354,6 +359,12 @@
}
}
+ private void onStateTransitionFailed(STATE_TYPE state) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateTransitionFailed(state);
+ }
+ }
+
private void onStateTransitionEnd(STATE_TYPE state) {
// Only change the stable states after the transitions have finished
if (state != mCurrentStableState) {
@@ -588,6 +599,11 @@
default void onStateTransitionStart(STATE_TYPE toState) { }
+ /**
+ * If the state transition animation fails (e.g. is canceled by the user), this fires.
+ */
+ default void onStateTransitionFailed(STATE_TYPE toState) { }
+
default void onStateTransitionComplete(STATE_TYPE finalState) { }
}