diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java index b61268be40..3664c97ff0 100644 --- a/quickstep/src/com/android/quickstep/MotionEventQueue.java +++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java @@ -30,8 +30,7 @@ import android.view.MotionEvent; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher; import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; - -import java.util.function.Supplier; +import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; /** * Helper class for batching input events @@ -49,29 +48,25 @@ public class MotionEventQueue { ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_QUICK_SCRUB_END = ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT); - private static final int ACTION_RESET = + private static final int ACTION_NEW_GESTURE = ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB = ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT); private static final int ACTION_QUICK_STEP = ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT); - private static final int ACTION_COMMAND = - ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT); - private static final int ACTION_SWITCH_CONSUMER = - ACTION_VIRTUAL | (8 << ACTION_POINTER_INDEX_SHIFT); private final InputEventDispatcher mDispatcher; private final InputEventReceiver mReceiver; - - private final Object mConsumerParamsLock = new Object(); - private Supplier[] mConsumerParams = new Supplier[2]; + private final ConsumerFactory mConsumerFactory; private TouchConsumer mConsumer; - public MotionEventQueue(Looper looper, Choreographer choreographer) { + public MotionEventQueue(Looper looper, Choreographer choreographer, + ConsumerFactory consumerFactory) { Pair pair = InputChannelCompat.createPair( "sysui-callbacks", looper, choreographer, this::onInputEvent); + mConsumerFactory = consumerFactory; mConsumer = TouchConsumer.NO_OP; mDispatcher = pair.first; mReceiver = pair.second; @@ -93,9 +88,12 @@ public class MotionEventQueue { case ACTION_QUICK_SCRUB_END: mConsumer.onQuickScrubEnd(); break; - case ACTION_RESET: - mConsumer.reset(); + case ACTION_NEW_GESTURE: { + boolean useSharedState = mConsumer.isActive(); + mConsumer.onConsumerAboutToBeSwitched(); + mConsumer = mConsumerFactory.newConsumer(event.getSource(), useSharedState); break; + } case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB: mConsumer.onShowOverviewFromAltTab(); mConsumer.onQuickScrubStart(); @@ -103,16 +101,6 @@ public class MotionEventQueue { case ACTION_QUICK_STEP: mConsumer.onQuickStep(event); break; - case ACTION_COMMAND: - mConsumer.onCommand(event.getSource()); - break; - case ACTION_SWITCH_CONSUMER: - synchronized (mConsumerParamsLock) { - int index = event.getSource(); - mConsumer = (TouchConsumer) mConsumerParams[index].get(); - mConsumerParams[index] = null; - } - break; default: Log.e(TAG, "Invalid virtual event: " + event.getAction()); } @@ -156,41 +144,26 @@ public class MotionEventQueue { queue(event); } - public void reset() { - queueVirtualAction(ACTION_RESET, 0); + public void onNewGesture(@HitTarget int downHitTarget) { + queueVirtualAction(ACTION_NEW_GESTURE, downHitTarget); } - public void onCommand(int command) { - queueVirtualAction(ACTION_COMMAND, command); - } - - public void switchConsumer(Supplier consumer) { - int index = -1; - synchronized (mConsumerParamsLock) { - // Find a null index - for (int i = 0; i < mConsumerParams.length; i++) { - if (mConsumerParams[i] == null) { - index = i; - break; - } - } - if (index < 0) { - index = mConsumerParams.length; - final Supplier[] newValues = new Supplier[index + 1]; - System.arraycopy(mConsumerParams, 0, newValues, 0, index); - mConsumerParams = newValues; - } - mConsumerParams[index] = consumer; + /** + * To be called by the consumer when it's no longer active. + */ + public void onConsumerInactive(TouchConsumer caller) { + if (mConsumer == caller) { + mConsumer = TouchConsumer.NO_OP; } - queueVirtualAction(ACTION_SWITCH_CONSUMER, index); - } - - public TouchConsumer getConsumer() { - return mConsumer; } public void dispose() { mDispatcher.dispose(); mReceiver.dispose(); } + + public interface ConsumerFactory { + + TouchConsumer newConsumer(@HitTarget int downHitTarget, boolean useSharedState); + } } diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 5801c105e1..84f16cb258 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -25,7 +25,6 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; @@ -37,7 +36,6 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; -import android.util.SparseArray; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; @@ -45,24 +43,23 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowManager; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget; import com.android.quickstep.util.MotionPauseDetector; -import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.AssistDataReceiver; import com.android.systemui.shared.system.BackgroundExecutor; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; -import com.android.systemui.shared.system.RecentsAnimationControllerCompat; -import com.android.systemui.shared.system.RecentsAnimationListener; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.WindowManagerWrapper; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; /** * Touch consumer for handling events originating from an activity other than Launcher @@ -73,16 +70,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN"; private static final String UP_EVT = "OtherActivityTouchConsumer.UP"; - private final SparseArray mAnimationStates = new SparseArray<>(); private final RunningTaskInfo mRunningTask; private final RecentsModel mRecentsModel; private final Intent mHomeIntent; private final ActivityControlHelper mActivityControlHelper; - private final MainThreadExecutor mMainThreadExecutor; private final OverviewCallbacks mOverviewCallbacks; private final TaskOverlayFactory mTaskOverlayFactory; private final TouchInteractionLog mTouchInteractionLog; private final InputConsumerController mInputConsumer; + private final SwipeSharedState mSwipeSharedState; private final MotionEventQueue mEventQueue; private final MotionPauseDetector mMotionPauseDetector; @@ -99,18 +95,13 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private WindowTransformSwipeHandler mInteractionHandler; private int mDisplayRotation; private Rect mStableInsets = new Rect(); - private boolean mCanGestureBeContinued; - - private boolean mIsGoingToLauncher; - private RecentsAnimationState mRecentsAnimationState; public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo, RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, - MainThreadExecutor mainThreadExecutor, @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks, TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog, MotionEventQueue eventQueue, - @Nullable RecentsAnimationState recentsAnimationStateToReuse) { + SwipeSharedState swipeSharedState) { super(base); mRunningTask = runningTaskInfo; @@ -122,14 +113,13 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC mVelocityTracker = VelocityTracker.obtain(); mActivityControlHelper = activityControl; - mMainThreadExecutor = mainThreadExecutor; mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget); mOverviewCallbacks = overviewCallbacks; mTaskOverlayFactory = taskOverlayFactory; mTouchInteractionLog = touchInteractionLog; mTouchInteractionLog.setTouchConsumer(this); mInputConsumer = inputConsumer; - mRecentsAnimationState = recentsAnimationStateToReuse; + mSwipeSharedState = swipeSharedState; } @Override @@ -156,8 +146,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC mActivePointerId = ev.getPointerId(0); mDownPos.set(ev.getX(), ev.getY()); mLastPos.set(mDownPos); - // If mRecentsAnimationState != null, we are continuing the previous gesture. - mPassedInitialSlop = mRecentsAnimationState != null; + // If active listener isn't null, we are continuing the previous gesture. + mPassedInitialSlop = mSwipeSharedState.getActiveListener() != null; mQuickStepDragSlop = NavigationBarCompat.getQuickStepDragSlopPx(); // Start the window animation on down to give more time for launcher to draw if the @@ -262,30 +252,23 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private void startTouchTrackingForWindowAnimation(long touchTimeMs) { mTouchInteractionLog.startRecentsAnimation(); - // Create the shared handler - boolean reuseOldAnimState = mRecentsAnimationState != null; - if (reuseOldAnimState) { - mRecentsAnimationState.changeParent(this); - } else { - mRecentsAnimationState = new RecentsAnimationState(this); - } + RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener(); final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler( - mRecentsAnimationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper, - reuseOldAnimState, mInputConsumer, mTouchInteractionLog); + mRunningTask, this, touchTimeMs, mActivityControlHelper, + listenerSet != null, mInputConsumer, mTouchInteractionLog); // Preload the plan mRecentsModel.getTasks(null); mInteractionHandler = handler; - handler.setGestureEndCallback(mEventQueue::reset); + handler.setGestureEndCallback(this::onInteractionGestureFinished); mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged); handler.initWhenReady(); TraceHelper.beginSection("RecentsController"); - if (reuseOldAnimState) { - handler.onRecentsAnimationStart(mRecentsAnimationState.mController, - mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets, - mRecentsAnimationState.mMinimizedHomeBounds); + if (listenerSet != null) { + listenerSet.addListener(handler); + mSwipeSharedState.applyActiveRecentsAnimationState(handler); } else { AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null : new AssistDataReceiver() { @@ -300,18 +283,13 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } }; + RecentsAnimationListenerSet newListenerSet = + mSwipeSharedState.newRecentsAnimationListenerSet(); + newListenerSet.addListener(handler); BackgroundExecutor.get().submit( () -> ActivityManagerWrapper.getInstance().startRecentsActivity( - mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null)); - } - - } - - @Override - public void onCommand(int command) { - RecentsAnimationState state = mAnimationStates.get(command); - if (state != null) { - state.execute(); + mHomeIntent, assistDataReceiver, newListenerSet, + null, null)); } } @@ -335,7 +313,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } else { // Since we start touch tracking on DOWN, we may reach this state without actually // starting the gesture. In that case, just cleanup immediately. - reset(); + onConsumerAboutToBeSwitched(); + onInteractionGestureFinished(); // Also clean up in case the system has handled the UP and canceled the animation before // we had a chance to start the recents animation. In such a case, we will not receive @@ -347,15 +326,35 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } @Override - public void reset() { - // Clean up the old interaction handler + public void onConsumerAboutToBeSwitched() { + Preconditions.assertUIThread(); if (mInteractionHandler != null) { - final WindowTransformSwipeHandler handler = mInteractionHandler; - mInteractionHandler = null; - WindowTransformSwipeHandler.GestureEndTarget endTarget = handler.mGestureEndTarget; - mIsGoingToLauncher = endTarget != null && endTarget.isLauncher; - mCanGestureBeContinued = endTarget != null && endTarget.canBeContinued; - mMainThreadExecutor.execute(mCanGestureBeContinued ? handler::cancel : handler::reset); + // The consumer is being switched while we are active. Set up the shared state to be + // used by the next animation + removeListener(); + GestureEndTarget endTarget = mInteractionHandler.mGestureEndTarget; + mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued; + mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher; + if (mSwipeSharedState.canGestureBeContinued) { + mInteractionHandler.cancel(); + } else { + mInteractionHandler.reset(); + } + } + } + + @UiThread + private void onInteractionGestureFinished() { + Preconditions.assertUIThread(); + removeListener(); + mInteractionHandler = null; + mEventQueue.onConsumerInactive(this); + } + + private void removeListener() { + RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener(); + if (listenerSet != null) { + listenerSet.removeListener(mInteractionHandler); } } @@ -419,81 +418,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } @Override - public boolean forceToLauncherConsumer() { - return mIsGoingToLauncher; - } - - @Override - public @Nullable RecentsAnimationState getRecentsAnimationStateToReuse() { - return mCanGestureBeContinued ? mRecentsAnimationState : null; - } - - @Override - public boolean deferNextEventToMainThread() { - // TODO: Consider also check if the eventQueue is using mainThread of not. + public boolean isActive() { return mInteractionHandler != null; } - - public static class RecentsAnimationState implements RecentsAnimationListener { - - private static final String ANIMATION_START_EVT = "RecentsAnimationState.onAnimationStart"; - private final int id; - - private OtherActivityTouchConsumer mParent; - - private RecentsAnimationControllerCompat mController; - private RemoteAnimationTargetSet mTargets; - private Rect mHomeContentInsets; - private Rect mMinimizedHomeBounds; - private boolean mCancelled; - - public RecentsAnimationState(OtherActivityTouchConsumer parent) { - mParent = parent; - id = mParent.mAnimationStates.size(); - mParent.mAnimationStates.put(id, this); - } - - @Override - public void onAnimationStart( - RecentsAnimationControllerCompat controller, - RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, - Rect minimizedHomeBounds) { - RaceConditionTracker.onEvent(ANIMATION_START_EVT, ENTER); - mController = controller; - mTargets = new RemoteAnimationTargetSet(apps, MODE_CLOSING); - mHomeContentInsets = homeContentInsets; - mMinimizedHomeBounds = minimizedHomeBounds; - mParent.mEventQueue.onCommand(id); - RaceConditionTracker.onEvent(ANIMATION_START_EVT, EXIT); - } - - @Override - public void onAnimationCanceled() { - mCancelled = true; - mParent.mEventQueue.onCommand(id); - } - - public void execute() { - WindowTransformSwipeHandler handler = mParent.mInteractionHandler; - if (handler == null || handler.id != id) { - if (!mCancelled && mController != null) { - TraceHelper.endSection("RecentsController", "Finishing no handler"); - mController.finish(false /* toHome */); - } - } else if (mCancelled) { - TraceHelper.endSection("RecentsController", - "Cancelled: " + handler); - handler.onRecentsAnimationCanceled(); - } else { - TraceHelper.partitionSection("RecentsController", "Received"); - handler.onRecentsAnimationStart(mController, mTargets, - mHomeContentInsets, mMinimizedHomeBounds); - } - } - - public void changeParent(OtherActivityTouchConsumer newParent) { - mParent = newParent; - mParent.mAnimationStates.put(id, this); - } - } } diff --git a/quickstep/src/com/android/quickstep/SwipeSharedState.java b/quickstep/src/com/android/quickstep/SwipeSharedState.java new file mode 100644 index 0000000000..15914ba698 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SwipeSharedState.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import com.android.launcher3.util.Preconditions; +import com.android.quickstep.util.RecentsAnimationListenerSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; + +/** + * Utility class used to store state information shared across multiple transitions. + */ +public class SwipeSharedState implements SwipeAnimationListener { + + private RecentsAnimationListenerSet mRecentsAnimationListener; + private SwipeAnimationTargetSet mLastAnimationTarget; + private boolean mLastAnimationCancelled = false; + + public boolean canGestureBeContinued; + public boolean goingToLauncher; + + @Override + public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { + mLastAnimationTarget = targetSet; + mLastAnimationCancelled = false; + } + + @Override + public final void onRecentsAnimationCanceled() { + mLastAnimationTarget = null; + mLastAnimationCancelled = true; + } + + private void clearListenerState() { + if (mRecentsAnimationListener != null) { + mRecentsAnimationListener.removeListener(this); + } + mRecentsAnimationListener = null; + mLastAnimationTarget = null; + mLastAnimationCancelled = false; + } + + public RecentsAnimationListenerSet newRecentsAnimationListenerSet() { + Preconditions.assertUIThread(); + clearListenerState(); + mRecentsAnimationListener = new RecentsAnimationListenerSet(); + mRecentsAnimationListener.addListener(this); + return mRecentsAnimationListener; + } + + public RecentsAnimationListenerSet getActiveListener() { + return mRecentsAnimationListener; + } + + public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) { + if (mLastAnimationTarget != null) { + listener.onRecentsAnimationStart(mLastAnimationTarget); + } else if (mLastAnimationCancelled) { + listener.onRecentsAnimationCanceled(); + } + } + + public void clearAllState() { + clearListenerState(); + canGestureBeContinued = false; + goingToLauncher = false; + } +} diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java index ef3759573a..026f715851 100644 --- a/quickstep/src/com/android/quickstep/TouchConsumer.java +++ b/quickstep/src/com/android/quickstep/TouchConsumer.java @@ -20,9 +20,6 @@ import android.os.Build; import android.view.MotionEvent; import androidx.annotation.IntDef; -import androidx.annotation.Nullable; - -import com.android.quickstep.OtherActivityTouchConsumer.RecentsAnimationState; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -43,8 +40,6 @@ public interface TouchConsumer extends Consumer { int INTERACTION_NORMAL = 0; int INTERACTION_QUICK_SCRUB = 1; - default void reset() { } - default void onQuickScrubStart() { } default void onQuickScrubEnd() { } @@ -53,22 +48,14 @@ public interface TouchConsumer extends Consumer { default void onQuickStep(MotionEvent ev) { } - default void onCommand(int command) { } + default void onShowOverviewFromAltTab() {} - default boolean deferNextEventToMainThread() { - return false; - } - - default boolean forceToLauncherConsumer() { + default boolean isActive() { return false; } /** - * When continuing a gesture, return the current non-null animation state that hasn't finished. + * Called by the event queue when the consumer is about to be switched to a new consumer. */ - default @Nullable RecentsAnimationState getRecentsAnimationStateToReuse() { - return null; - } - - default void onShowOverviewFromAltTab() {} + default void onConsumerAboutToBeSwitched() { } } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 0f52debed9..97e797bfa2 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -44,7 +44,6 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.views.BaseDragLayer; -import com.android.quickstep.OtherActivityTouchConsumer.RecentsAnimationState; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -61,6 +60,8 @@ import java.io.PrintWriter; @TargetApi(Build.VERSION_CODES.O) public class TouchInteractionService extends Service { + public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor(); + private static final SparseArray sMotionEventNames; static { @@ -81,7 +82,7 @@ public class TouchInteractionService extends Service { mTouchInteractionLog.prepareForNewGesture(); TraceHelper.beginSection("SysUiBinder"); - setupTouchConsumer(downHitTarget); + mEventQueue.onNewGesture(downHitTarget); TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget); } @@ -135,7 +136,7 @@ public class TouchInteractionService extends Service { @Override public void onOverviewShown(boolean triggeredFromAltTab) { if (triggeredFromAltTab) { - setupTouchConsumer(HIT_TARGET_NONE); + mEventQueue.onNewGesture(HIT_TARGET_NONE); mEventQueue.onOverviewShownFromAltTab(); } else { mOverviewCommandHelper.onOverviewShown(); @@ -172,7 +173,6 @@ public class TouchInteractionService extends Service { private ActivityManagerWrapper mAM; private RecentsModel mRecentsModel; private MotionEventQueue mEventQueue; - private MainThreadExecutor mMainThreadExecutor; private ISystemUiProxy mISystemUiProxy; private OverviewCommandHelper mOverviewCommandHelper; private OverviewComponentObserver mOverviewComponentObserver; @@ -181,20 +181,22 @@ public class TouchInteractionService extends Service { private TaskOverlayFactory mTaskOverlayFactory; private TouchInteractionLog mTouchInteractionLog; private InputConsumerController mInputConsumer; + private SwipeSharedState mSwipeSharedState; @Override public void onCreate() { super.onCreate(); mAM = ActivityManagerWrapper.getInstance(); mRecentsModel = RecentsModel.INSTANCE.get(this); - mMainThreadExecutor = new MainThreadExecutor(); mOverviewComponentObserver = new OverviewComponentObserver(this); mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver); - mEventQueue = new MotionEventQueue(Looper.myLooper(), Choreographer.getInstance()); + mEventQueue = new MotionEventQueue(Looper.myLooper(), Choreographer.getInstance(), + this::newConsumer); mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this); mOverviewCallbacks = OverviewCallbacks.get(this); mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this); mTouchInteractionLog = new TouchInteractionLog(); + mSwipeSharedState = new SwipeSharedState(); mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mInputConsumer.registerInputConsumer(); @@ -219,27 +221,15 @@ public class TouchInteractionService extends Service { return mMyBinder; } - private void setupTouchConsumer(@HitTarget int downHitTarget) { - mEventQueue.reset(); - TouchConsumer oldConsumer = mEventQueue.getConsumer(); - if (oldConsumer.deferNextEventToMainThread()) { - mEventQueue.switchConsumer(() -> getCurrentTouchConsumer(downHitTarget, - oldConsumer.forceToLauncherConsumer(), - oldConsumer.getRecentsAnimationStateToReuse())); - - } else { - TouchConsumer consumer = getCurrentTouchConsumer(downHitTarget, false, null); - mEventQueue.switchConsumer(() -> consumer); - } - } - - private TouchConsumer getCurrentTouchConsumer(@HitTarget int downHitTarget, - boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse) { + private TouchConsumer newConsumer(@HitTarget int downHitTarget, boolean useSharedState) { RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); + if (!useSharedState) { + mSwipeSharedState.clearAllState(); + } - if (runningTaskInfo == null && !forceToLauncher) { + if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) { return TouchConsumer.NO_OP; - } else if (forceToLauncher || + } else if (mSwipeSharedState.goingToLauncher || mOverviewComponentObserver.getActivityControlHelper().isResumed()) { return OverviewTouchConsumer.newInstance( mOverviewComponentObserver.getActivityControlHelper(), false, @@ -252,10 +242,10 @@ public class TouchInteractionService extends Service { } else { return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel, mOverviewComponentObserver.getOverviewIntent(), - mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor, + mOverviewComponentObserver.getActivityControlHelper(), downHitTarget, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer, mTouchInteractionLog, mEventQueue, - recentsAnimationStateToReuse); + mSwipeSharedState); } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 0e9ddee53b..bc4e094296 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -34,6 +34,7 @@ import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_ST import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION; import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB; +import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK; @@ -97,6 +98,8 @@ import com.android.quickstep.TouchConsumer.InteractionType; import com.android.quickstep.TouchInteractionService.OverviewTouchConsumer; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; import com.android.quickstep.util.TransformedRect; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -113,7 +116,8 @@ import com.android.systemui.shared.system.WindowCallbacksCompat; import java.util.function.BiFunction; @TargetApi(Build.VERSION_CODES.O) -public class WindowTransformSwipeHandler { +public class WindowTransformSwipeHandler + implements SwipeAnimationListener { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); // Launcher UI related states @@ -248,14 +252,8 @@ public class WindowTransformSwipeHandler { // To avoid UI jump when gesture is started, we offset the animation by the threshold. private float mShiftAtGestureStart = 0; - private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); + private final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler(); - // An increasing identifier per single instance of OtherActivityTouchConsumer. Generally one - // instance of OtherActivityTouchConsumer will only have one swipe handle, but sometimes we can - // end up with multiple handlers if we get recents command in the middle of a swipe gesture. - // This is used to match the corresponding activity manager callbacks in - // OtherActivityTouchConsumer - public final int id; private final Context mContext; private final ActivityControlHelper mActivityControlHelper; private final ActivityInitListener mActivityInitListener; @@ -296,10 +294,9 @@ public class WindowTransformSwipeHandler { private Bundle mAssistData; - WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context, + WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, ActivityControlHelper controller, boolean continuingLastGesture, InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog) { - this.id = id; mContext = context; mRunningTaskInfo = runningTaskInfo; mRunningTaskId = runningTaskInfo.id; @@ -747,19 +744,18 @@ public class WindowTransformSwipeHandler { ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart)); } - @UiThread - public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller, - RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) { + @Override + public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); final Rect overviewStackBounds; - RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId); + RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); - if (minimizedHomeBounds != null && runningTaskTarget != null) { + if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) { overviewStackBounds = mActivityControlHelper - .getOverviewWindowBounds(minimizedHomeBounds, runningTaskTarget); - dp = dp.getMultiWindowProfile(mContext, - new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height())); - dp.updateInsets(homeContentInsets); + .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget); + dp = dp.getMultiWindowProfile(mContext, new Point( + targetSet.minimizedHomeBounds.width(), targetSet.minimizedHomeBounds.height())); + dp.updateInsets(targetSet.homeContentInsets); } else { if (mActivity != null) { int loc[] = new int[2]; @@ -772,7 +768,7 @@ public class WindowTransformSwipeHandler { } // If we are not in multi-window mode, home insets should be same as system insets. dp = dp.copy(mContext); - dp.updateInsets(homeContentInsets); + dp.updateInsets(targetSet.homeContentInsets); } dp.updateIsSeascape(mContext.getSystemService(WindowManager.class)); @@ -782,14 +778,14 @@ public class WindowTransformSwipeHandler { mClipAnimationHelper.prepareAnimation(false /* isOpening */); initTransitionEndpoints(dp); - mRecentsAnimationWrapper.setController(controller, targets); - mTouchInteractionLog.startRecentsAnimationCallback(targets.apps.length); + mRecentsAnimationWrapper.setController(targetSet.controller, targetSet); + mTouchInteractionLog.startRecentsAnimationCallback(targetSet.apps.length); setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); mPassedOverviewThreshold = false; } - @UiThread + @Override public void onRecentsAnimationCanceled() { mRecentsAnimationWrapper.setController(null, null); mActivityInitListener.unregister(); diff --git a/quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java new file mode 100644 index 0000000000..3e49568331 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; + +import android.graphics.Rect; +import android.util.ArraySet; + +import com.android.launcher3.Utilities; +import com.android.launcher3.util.Preconditions; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +import java.util.Set; + +import androidx.annotation.UiThread; + +/** + * Wrapper around {@link RecentsAnimationListener} which delegates callbacks to multiple listeners + * on the main thread + */ +public class RecentsAnimationListenerSet implements RecentsAnimationListener { + + private final Set mListeners = new ArraySet<>(); + + @UiThread + public void addListener(SwipeAnimationListener listener) { + Preconditions.assertUIThread(); + mListeners.add(listener); + } + + @UiThread + public void removeListener(SwipeAnimationListener listener) { + Preconditions.assertUIThread(); + mListeners.remove(listener); + } + + @Override + public final void onAnimationStart(RecentsAnimationControllerCompat controller, + RemoteAnimationTargetCompat[] targets, Rect homeContentInsets, + Rect minimizedHomeBounds) { + SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets, + homeContentInsets, minimizedHomeBounds); + Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + for (SwipeAnimationListener listener : mListeners) { + listener.onRecentsAnimationStart(targetSet); + } + }); + } + + @Override + public final void onAnimationCanceled() { + Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + for (SwipeAnimationListener listener : mListeners) { + listener.onRecentsAnimationCanceled(); + } + }); + } +} diff --git a/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java new file mode 100644 index 0000000000..4f02acfa2b --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; + +import android.graphics.Rect; + +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +/** + * Extension of {@link RemoteAnimationTargetSet} with additional information about swipe + * up animation + */ +public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { + + public final RecentsAnimationControllerCompat controller; + public final Rect homeContentInsets; + public final Rect minimizedHomeBounds; + + public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller, + RemoteAnimationTargetCompat[] targets, Rect homeContentInsets, + Rect minimizedHomeBounds) { + super(targets, MODE_CLOSING); + this.controller = controller; + this.homeContentInsets = homeContentInsets; + this.minimizedHomeBounds = minimizedHomeBounds; + } + + + public interface SwipeAnimationListener { + + void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet); + + void onRecentsAnimationCanceled(); + } +}