Always initializing the TouchConsumer on the main thread
> Creating a separate shared state class instead of storing the full TouchConsumer > Moving all RecentsAnimationCallback on the main thread, instead of routing it through the event queue Bug: 124255113 Change-Id: I7891dfa4ad4a8d81152b7b36991682535fd54f46
This commit is contained in:
@@ -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<InputEventDispatcher, InputEventReceiver> 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<TouchConsumer> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RecentsAnimationState> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<MotionEvent> {
|
||||
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<MotionEvent> {
|
||||
|
||||
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() { }
|
||||
}
|
||||
|
||||
@@ -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<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T extends BaseDraggingActivity> {
|
||||
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
implements SwipeAnimationListener {
|
||||
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
|
||||
|
||||
// Launcher UI related states
|
||||
@@ -248,14 +252,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
// 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<T> mActivityControlHelper;
|
||||
private final ActivityInitListener mActivityInitListener;
|
||||
@@ -296,10 +294,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
|
||||
private Bundle mAssistData;
|
||||
|
||||
WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
|
||||
WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
|
||||
long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
|
||||
InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog) {
|
||||
this.id = id;
|
||||
mContext = context;
|
||||
mRunningTaskInfo = runningTaskInfo;
|
||||
mRunningTaskId = runningTaskInfo.id;
|
||||
@@ -747,19 +744,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
? 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<T extends BaseDraggingActivity> {
|
||||
}
|
||||
// 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<T extends BaseDraggingActivity> {
|
||||
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();
|
||||
|
||||
@@ -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<SwipeAnimationListener> 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user