Reland slow gesture callback handling
Now that b/329324086 has been fixed, we can be more certain that launcher always gets a signal to clean up from WM. - Relanding original fix for b/285636175 with some additional error checking - We will now check whether the recents animation start is pending on ACTION_UP - We will now block entire swipes to prevent sending additional inputs an input consumer while the recents animation start is pending - We will now only stop blocking inputs on ACTION_DOWN Flag: LEGACY ENABLE_HANDLE_DELAYED_GESTURE_CALLBACKS TEAMFOOD Bug: 329324927 Fixes: 285636175 Test: added a delay in RecentsAnimationCallbacks.onAnimationStart and attempted several rapid gestures Change-Id: I9805114da34bf44e6b28c2a8a665e4cca88904c2
This commit is contained in:
@@ -229,3 +229,13 @@ flag {
|
||||
description: "Enables an add button in the widget picker"
|
||||
bug: "323886237"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "enable_handle_delayed_gesture_callbacks"
|
||||
namespace: "launcher"
|
||||
description: "Enables additional handling for delayed mid-gesture callbacks"
|
||||
bug: "285636175"
|
||||
metadata {
|
||||
purpose: PURPOSE_BUGFIX
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,13 +516,14 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
return mSwipeUpStartTimeMs;
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("GestureState:");
|
||||
pw.println(" gestureID=" + mGestureId);
|
||||
pw.println(" runningTask=" + mRunningTask);
|
||||
pw.println(" endTarget=" + mEndTarget);
|
||||
pw.println(" lastAppearedTaskTargetId=" + Arrays.toString(mLastAppearedTaskTargets));
|
||||
pw.println(" lastStartedTaskId=" + Arrays.toString(mLastStartedTaskId));
|
||||
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "GestureState:");
|
||||
pw.println(prefix + "\tgestureID=" + mGestureId);
|
||||
pw.println(prefix + "\trunningTask=" + mRunningTask);
|
||||
pw.println(prefix + "\tendTarget=" + mEndTarget);
|
||||
pw.println(prefix + "\tlastAppearedTaskTargetId="
|
||||
+ Arrays.toString(mLastAppearedTaskTargets));
|
||||
pw.println(prefix + "\tlastStartedTaskId=" + Arrays.toString(mLastStartedTaskId));
|
||||
pw.println(prefix + "\tisRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -219,6 +220,13 @@ public class RecentsAnimationCallbacks implements
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "RecentsAnimationCallbacks:");
|
||||
|
||||
pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen);
|
||||
pw.println(prefix + "\tmCancelled=" + mCancelled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the recents animation callbacks.
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -267,4 +268,14 @@ public class RecentsAnimationController {
|
||||
public boolean getFinishTargetIsLauncher() {
|
||||
return mFinishTargetIsLauncher;
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "RecentsAnimationController:");
|
||||
|
||||
pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen);
|
||||
pw.println(prefix + "\tmUseLauncherSysBarFlags=" + mUseLauncherSysBarFlags);
|
||||
pw.println(prefix + "\tmSplitScreenMinimized=" + mSplitScreenMinimized);
|
||||
pw.println(prefix + "\tmFinishRequested=" + mFinishRequested);
|
||||
pw.println(prefix + "\tmFinishTargetIsLauncher=" + mFinishTargetIsLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Extension of {@link RemoteAnimationTargets} with additional information about swipe
|
||||
* up animation
|
||||
@@ -63,4 +65,14 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
super.dump(prefix, pw);
|
||||
prefix += '\t';
|
||||
pw.println(prefix + "RecentsAnimationTargets:");
|
||||
|
||||
pw.println(prefix + "\thomeContentInsets=" + homeContentInsets);
|
||||
pw.println(prefix + "\tminimizedHomeBounds=" + minimizedHomeBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import android.os.Bundle;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@@ -149,6 +150,14 @@ public class RemoteAnimationTargets {
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "RemoteAnimationTargets:");
|
||||
|
||||
pw.println(prefix + "\ttargetMode=" + targetMode);
|
||||
pw.println(prefix + "\thasRecents=" + hasRecents);
|
||||
pw.println(prefix + "\tmReleased=" + mReleased);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for intercepting surface release method
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.quickstep;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
|
||||
import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
|
||||
@@ -49,6 +50,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
@@ -67,6 +69,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
private Context mCtx;
|
||||
|
||||
private boolean mRecentsAnimationStartPending = false;
|
||||
private boolean mShouldIgnoreMotionEvents = false;
|
||||
|
||||
private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
|
||||
@Override
|
||||
@@ -103,8 +106,16 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
.startRecentsActivity(intent, 0, null, null, null));
|
||||
}
|
||||
|
||||
public boolean isRecentsAnimationStartPending() {
|
||||
return mRecentsAnimationStartPending;
|
||||
boolean shouldIgnoreMotionEvents() {
|
||||
return mShouldIgnoreMotionEvents;
|
||||
}
|
||||
|
||||
void notifyNewGestureStart() {
|
||||
// If mRecentsAnimationStartPending is true at the beginning of a gesture, block all motion
|
||||
// events for this new gesture so that this new gesture does not interfere with the
|
||||
// previously-requested recents animation. Otherwise, clean up mShouldIgnoreMotionEvents.
|
||||
// NOTE: this can lead to misleading logs
|
||||
mShouldIgnoreMotionEvents = mRecentsAnimationStartPending;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +156,12 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
@Override
|
||||
public void onRecentsAnimationStart(RecentsAnimationController controller,
|
||||
RecentsAnimationTargets targets) {
|
||||
mRecentsAnimationStartPending = false;
|
||||
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
|
||||
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
|
||||
"TaskAnimationManager.startRecentsAnimation(onRecentsAnimationStart): ")
|
||||
.append("Setting mRecentsAnimationStartPending = false"));
|
||||
mRecentsAnimationStartPending = false;
|
||||
}
|
||||
if (mCallbacks == null) {
|
||||
// It's possible for the recents animation to have finished and be cleaned up
|
||||
// by the time we process the start callback, and in that case, just we can skip
|
||||
@@ -186,13 +202,25 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
||||
mRecentsAnimationStartPending = false;
|
||||
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
|
||||
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
|
||||
"TaskAnimationManager.startRecentsAnimation")
|
||||
.append("(onRecentsAnimationCanceled): ")
|
||||
.append("Setting mRecentsAnimationStartPending = false"));
|
||||
mRecentsAnimationStartPending = false;
|
||||
}
|
||||
cleanUpRecentsAnimation(newCallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
||||
mRecentsAnimationStartPending = false;
|
||||
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
|
||||
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
|
||||
"TaskAnimationManager.startRecentsAnimation")
|
||||
.append("(onRecentsAnimationFinished): ")
|
||||
.append("Setting mRecentsAnimationStartPending = false"));
|
||||
mRecentsAnimationStartPending = false;
|
||||
}
|
||||
cleanUpRecentsAnimation(newCallbacks);
|
||||
}
|
||||
|
||||
@@ -303,13 +331,29 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
|
||||
mRecentsAnimationStartPending = SystemUiProxy.INSTANCE.getNoCreate()
|
||||
.startRecentsActivity(intent, options, mCallbacks);
|
||||
if (enableHandleDelayedGestureCallbacks()) {
|
||||
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
|
||||
"TaskAnimationManager.startRecentsAnimation(shell transition path): ")
|
||||
.append("Setting mRecentsAnimationStartPending = ")
|
||||
.append(mRecentsAnimationStartPending));
|
||||
}
|
||||
} else {
|
||||
UI_HELPER_EXECUTOR.execute(
|
||||
() -> ActivityManagerWrapper.getInstance().startRecentsActivity(
|
||||
intent,
|
||||
eventTime,
|
||||
mCallbacks,
|
||||
result -> mRecentsAnimationStartPending = result,
|
||||
result -> {
|
||||
if (enableHandleDelayedGestureCallbacks()) {
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
new ActiveGestureLog.CompoundString(
|
||||
"TaskAnimationManager.startRecentsAnimation")
|
||||
.append("(legacy path): Setting ")
|
||||
.append("mRecentsAnimationStartPending = ")
|
||||
.append(result));
|
||||
}
|
||||
mRecentsAnimationStartPending = result;
|
||||
},
|
||||
MAIN_EXECUTOR.getHandler()));
|
||||
}
|
||||
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
|
||||
@@ -439,7 +483,24 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
return mCallbacks;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
// TODO
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "TaskAnimationManager:");
|
||||
|
||||
if (enableHandleDelayedGestureCallbacks()) {
|
||||
pw.println(prefix + "\tmRecentsAnimationStartPending=" + mRecentsAnimationStartPending);
|
||||
pw.println(prefix + "\tmShouldIgnoreUpcomingGestures=" + mShouldIgnoreMotionEvents);
|
||||
}
|
||||
if (mController != null) {
|
||||
mController.dump(prefix + '\t', pw);
|
||||
}
|
||||
if (mCallbacks != null) {
|
||||
mCallbacks.dump(prefix + '\t', pw);
|
||||
}
|
||||
if (mTargets != null) {
|
||||
mTargets.dump(prefix + '\t', pw);
|
||||
}
|
||||
if (mLastGestureState != null) {
|
||||
mLastGestureState.dump(prefix + '\t', pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.Flags.enableCursorHoverStates;
|
||||
import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
|
||||
import static com.android.launcher3.Flags.useActivityOverlay;
|
||||
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
|
||||
import static com.android.launcher3.LauncherPrefs.backedUpItem;
|
||||
@@ -41,6 +42,7 @@ import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
|
||||
@@ -716,16 +718,22 @@ public class TouchInteractionService extends Service {
|
||||
boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
|
||||
&& isHoverActionWithoutConsumer(event);
|
||||
|
||||
// TODO(b/285636175): Uncomment this once WM can properly guarantee all animation callbacks
|
||||
// if (mTaskAnimationManager.isRecentsAnimationStartPending()
|
||||
// && (action == ACTION_DOWN || isHoverActionWithoutConsumer)) {
|
||||
// ActiveGestureLog.INSTANCE.addLog(
|
||||
// new CompoundString("TIS.onInputEvent: ")
|
||||
// .append("Cannot process input event: a recents animation has been ")
|
||||
// .append("requested, but hasn't started."),
|
||||
// RECENTS_ANIMATION_START_PENDING);
|
||||
// return;
|
||||
// }
|
||||
if (enableHandleDelayedGestureCallbacks()) {
|
||||
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
|
||||
mTaskAnimationManager.notifyNewGestureStart();
|
||||
}
|
||||
if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
|
||||
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
new CompoundString("TIS.onMotionEvent: A new gesture has been ")
|
||||
.append("started, but a previously-requested recents ")
|
||||
.append("animation hasn't started. Ignoring all following ")
|
||||
.append("motion events."),
|
||||
RECENTS_ANIMATION_START_PENDING);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
|
||||
|
||||
@@ -1431,28 +1439,31 @@ public class TouchInteractionService extends Service {
|
||||
mOverviewCommandHelper.dump(pw);
|
||||
}
|
||||
if (mGestureState != null) {
|
||||
mGestureState.dump(pw);
|
||||
mGestureState.dump("", pw);
|
||||
}
|
||||
pw.println("Input state:");
|
||||
pw.println(" mInputMonitorCompat=" + mInputMonitorCompat);
|
||||
pw.println(" mInputEventReceiver=" + mInputEventReceiver);
|
||||
pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat);
|
||||
pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
|
||||
DisplayController.INSTANCE.get(this).dump(pw);
|
||||
pw.println("TouchState:");
|
||||
BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
|
||||
: mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
|
||||
boolean resumed = mOverviewComponentObserver != null
|
||||
&& mOverviewComponentObserver.getActivityInterface().isResumed();
|
||||
pw.println(" createdOverviewActivity=" + createdOverviewActivity);
|
||||
pw.println(" resumed=" + resumed);
|
||||
pw.println(" mConsumer=" + mConsumer.getName());
|
||||
pw.println("\tcreatedOverviewActivity=" + createdOverviewActivity);
|
||||
pw.println("\tresumed=" + resumed);
|
||||
pw.println("\tmConsumer=" + mConsumer.getName());
|
||||
ActiveGestureLog.INSTANCE.dump("", pw);
|
||||
RecentsModel.INSTANCE.get(this).dump("", pw);
|
||||
if (mTaskAnimationManager != null) {
|
||||
mTaskAnimationManager.dump("", pw);
|
||||
}
|
||||
if (createdOverviewActivity != null) {
|
||||
createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
|
||||
}
|
||||
mTaskbarManager.dumpLogs("", pw);
|
||||
pw.println("AssistStateManager:");
|
||||
AssistStateManager.INSTANCE.get(this).dump(" ", pw);
|
||||
AssistStateManager.INSTANCE.get(this).dump("\t", pw);
|
||||
SystemUiProxy.INSTANCE.get(this).dump(pw);
|
||||
DeviceConfigWrapper.get().dump(" ", pw);
|
||||
}
|
||||
|
||||
@@ -277,7 +277,8 @@ public class ActiveGestureErrorDetector {
|
||||
errorDetected |= printErrorIfTrue(
|
||||
true,
|
||||
prefix,
|
||||
/* errorMessage= */ "new gesture attempted while a requested recents"
|
||||
/* errorMessage= */ (eventEntry.getDuplicateCount() + 1)
|
||||
+ " gesture(s) attempted while a requested recents"
|
||||
+ " animation is still pending.",
|
||||
writer);
|
||||
break;
|
||||
|
||||
@@ -216,6 +216,10 @@ public class ActiveGestureLog {
|
||||
return gestureEvent;
|
||||
}
|
||||
|
||||
public int getDuplicateCount() {
|
||||
return duplicateCount;
|
||||
}
|
||||
|
||||
private void update(
|
||||
@NonNull CompoundString compoundString,
|
||||
ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
|
||||
Reference in New Issue
Block a user