diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 462d947a24..ecd7e8610c 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -206,3 +206,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_handle_delayed_gesture_callbacks" + namespace: "launcher" + description: "Enables additional handling for delayed mid-gesture callbacks" + bug: "285636175" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index d02909c527..63833c2b19 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -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()); } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 5d26ec0bb0..da7a98f330 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -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. */ diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 06a442b6dc..1b05e284be 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -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); + } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java index 556dd7e81e..b10fba47be 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -24,6 +24,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 @@ -62,4 +64,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); + } } diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index e0c7403181..57edd82326 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -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 */ diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 4e62d60d2c..53275a87d0 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -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 @@ -102,8 +105,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; } /** @@ -144,7 +155,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 @@ -185,13 +201,25 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @Override public void onRecentsAnimationCanceled(HashMap 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); } @@ -301,13 +329,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); @@ -437,7 +481,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); + } } } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 719c4f7f70..e2daa6f461 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -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; @@ -40,6 +41,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; @@ -734,16 +736,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"); @@ -1449,28 +1457,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().getCreatedActivity(); 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); } diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index e3772bd0ec..cfa6b9891f 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -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; diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java index 1e05a69425..c54862aba3 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java @@ -216,6 +216,10 @@ public class ActiveGestureLog { return gestureEvent; } + public int getDuplicateCount() { + return duplicateCount; + } + private void update( @NonNull CompoundString compoundString, ActiveGestureErrorDetector.GestureEvent gestureEvent) {