One thread to rule them all
Moving all quickstep event handling to main thread and removing multi-thread logic. Using system input dispatcher for handling event queue to that velocity tracker works properly on different threads. Bug: 123833655 Change-Id: I7c5004c32411da4144103112905ff6b40ed700ab
This commit is contained in:
parent
8fa51172f6
commit
451987c5e6
Binary file not shown.
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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 android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.view.Choreographer;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
|
||||
/**
|
||||
* A TouchConsumer which defers all events on the UIThread until the consumer is created.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public class DeferredTouchConsumer implements TouchConsumer {
|
||||
|
||||
private final VelocityTracker mVelocityTracker;
|
||||
private final DeferredTouchProvider mTouchProvider;
|
||||
|
||||
private MotionEventQueue mMyQueue;
|
||||
private TouchConsumer mTarget;
|
||||
|
||||
public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mTouchProvider = touchProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(MotionEvent event) {
|
||||
mTarget.accept(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
mTarget.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickScrubStart() {
|
||||
mTarget.onQuickScrubStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickScrubEnd() {
|
||||
mTarget.onQuickScrubEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickScrubProgress(float progress) {
|
||||
mTarget.onQuickScrubProgress(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickStep(MotionEvent ev) {
|
||||
mTarget.onQuickStep(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(int command) {
|
||||
mTarget.onCommand(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preProcessMotionEvent(MotionEvent ev) {
|
||||
mVelocityTracker.addMovement(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
|
||||
mMyQueue = queue;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deferInit() {
|
||||
mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
|
||||
mTarget.getIntrimChoreographer(mMyQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceToLauncherConsumer() {
|
||||
return mTarget.forceToLauncherConsumer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OtherActivityTouchConsumer.RecentsAnimationState getRecentsAnimationStateToReuse() {
|
||||
return mTarget.getRecentsAnimationStateToReuse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deferNextEventToMainThread() {
|
||||
// If our target is still null, defer the next target as well
|
||||
TouchConsumer target = mTarget;
|
||||
return target == null ? true : target.deferNextEventToMainThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowOverviewFromAltTab() {
|
||||
mTarget.onShowOverviewFromAltTab();
|
||||
}
|
||||
|
||||
public interface DeferredTouchProvider {
|
||||
|
||||
TouchConsumer createTouchConsumer(VelocityTracker tracker);
|
||||
}
|
||||
}
|
|
@ -15,21 +15,23 @@
|
|||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_MASK;
|
||||
import static android.view.MotionEvent.ACTION_MOVE;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Choreographer;
|
||||
import android.view.InputEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.systemui.shared.system.ChoreographerCompat;
|
||||
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.ArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Helper class for batching input events
|
||||
|
@ -49,153 +51,88 @@ public class MotionEventQueue {
|
|||
ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_RESET =
|
||||
ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_DEFER_INIT =
|
||||
ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
|
||||
ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
|
||||
ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
|
||||
private static final int ACTION_QUICK_STEP =
|
||||
ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
|
||||
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 EventArray mEmptyArray = new EventArray();
|
||||
private final Object mExecutionLock = new Object();
|
||||
private final InputEventDispatcher mDispatcher;
|
||||
private final InputEventReceiver mReceiver;
|
||||
|
||||
// We use two arrays and swap the current index when one array is being consumed
|
||||
private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
|
||||
private int mCurrentIndex = 0;
|
||||
private final Object mConsumerParamsLock = new Object();
|
||||
private Supplier[] mConsumerParams = new Supplier[2];
|
||||
|
||||
private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer;
|
||||
private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer;
|
||||
private TouchConsumer mConsumer;
|
||||
|
||||
private final Choreographer mMainChoreographer;
|
||||
public MotionEventQueue(Looper looper, Choreographer choreographer) {
|
||||
Pair<InputEventDispatcher, InputEventReceiver> pair = InputChannelCompat.createPair(
|
||||
"sysui-callbacks", looper, choreographer, this::onInputEvent);
|
||||
|
||||
private final TouchConsumer mConsumer;
|
||||
|
||||
private Choreographer mInterimChoreographer;
|
||||
private Choreographer mCurrentChoreographer;
|
||||
|
||||
private Runnable mCurrentRunnable;
|
||||
|
||||
public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
|
||||
mMainChoreographer = choreographer;
|
||||
mConsumer = consumer;
|
||||
mCurrentChoreographer = mMainChoreographer;
|
||||
mCurrentRunnable = mMainFrameCallback;
|
||||
|
||||
setInterimChoreographer(consumer.getIntrimChoreographer(this));
|
||||
mConsumer = TouchConsumer.NO_OP;
|
||||
mDispatcher = pair.first;
|
||||
mReceiver = pair.second;
|
||||
}
|
||||
|
||||
public void setInterimChoreographer(Choreographer choreographer) {
|
||||
synchronized (mExecutionLock) {
|
||||
synchronized (mArrays) {
|
||||
setInterimChoreographerLocked(choreographer);
|
||||
ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
|
||||
}
|
||||
private void onInputEvent(InputEvent ev) {
|
||||
if (!(ev instanceof MotionEvent)) {
|
||||
throw new IllegalStateException("Unknown event " + ev);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInterimChoreographerLocked(Choreographer choreographer) {
|
||||
mInterimChoreographer = choreographer;
|
||||
if (choreographer == null) {
|
||||
mCurrentChoreographer = mMainChoreographer;
|
||||
mCurrentRunnable = mMainFrameCallback;
|
||||
MotionEvent event = (MotionEvent) ev;
|
||||
if (event.getActionMasked() == ACTION_VIRTUAL) {
|
||||
switch (event.getAction()) {
|
||||
case ACTION_QUICK_SCRUB_START:
|
||||
mConsumer.onQuickScrubStart();
|
||||
break;
|
||||
case ACTION_QUICK_SCRUB_PROGRESS:
|
||||
mConsumer.onQuickScrubProgress(event.getX());
|
||||
break;
|
||||
case ACTION_QUICK_SCRUB_END:
|
||||
mConsumer.onQuickScrubEnd();
|
||||
break;
|
||||
case ACTION_RESET:
|
||||
mConsumer.reset();
|
||||
break;
|
||||
case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
|
||||
mConsumer.onShowOverviewFromAltTab();
|
||||
mConsumer.onQuickScrubStart();
|
||||
break;
|
||||
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());
|
||||
}
|
||||
} else {
|
||||
mCurrentChoreographer = mInterimChoreographer;
|
||||
mCurrentRunnable = mInterimFrameCallback;
|
||||
mConsumer.accept(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void queue(MotionEvent event) {
|
||||
mConsumer.preProcessMotionEvent(event);
|
||||
queueNoPreProcess(event);
|
||||
mDispatcher.dispatch(event);
|
||||
}
|
||||
|
||||
private void queueNoPreProcess(MotionEvent event) {
|
||||
synchronized (mArrays) {
|
||||
EventArray array = mArrays[mCurrentIndex];
|
||||
if (array.isEmpty()) {
|
||||
ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
|
||||
}
|
||||
|
||||
int eventAction = event.getAction();
|
||||
if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
|
||||
// Replace and recycle the last event
|
||||
array.set(array.size() - 1, event).recycle();
|
||||
} else {
|
||||
array.add(event);
|
||||
array.lastEventAction = eventAction;
|
||||
}
|
||||
}
|
||||
private void queueVirtualAction(int action, float param) {
|
||||
queue(MotionEvent.obtain(0, 0, action, param, 0, 0));
|
||||
}
|
||||
|
||||
private void frameCallbackForMainChoreographer() {
|
||||
runFor(mMainChoreographer);
|
||||
}
|
||||
|
||||
private void frameCallbackForInterimChoreographer() {
|
||||
runFor(mInterimChoreographer);
|
||||
}
|
||||
|
||||
private void runFor(Choreographer caller) {
|
||||
synchronized (mExecutionLock) {
|
||||
EventArray array = swapAndGetCurrentArray(caller);
|
||||
int size = array.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MotionEvent event = array.get(i);
|
||||
if (event.getActionMasked() == ACTION_VIRTUAL) {
|
||||
switch (event.getAction()) {
|
||||
case ACTION_QUICK_SCRUB_START:
|
||||
mConsumer.onQuickScrubStart();
|
||||
break;
|
||||
case ACTION_QUICK_SCRUB_PROGRESS:
|
||||
mConsumer.onQuickScrubProgress(event.getX());
|
||||
break;
|
||||
case ACTION_QUICK_SCRUB_END:
|
||||
mConsumer.onQuickScrubEnd();
|
||||
break;
|
||||
case ACTION_RESET:
|
||||
mConsumer.reset();
|
||||
break;
|
||||
case ACTION_DEFER_INIT:
|
||||
mConsumer.deferInit();
|
||||
break;
|
||||
case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
|
||||
mConsumer.onShowOverviewFromAltTab();
|
||||
mConsumer.onQuickScrubStart();
|
||||
break;
|
||||
case ACTION_QUICK_STEP:
|
||||
mConsumer.onQuickStep(event);
|
||||
break;
|
||||
case ACTION_COMMAND:
|
||||
mConsumer.onCommand(event.getSource());
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid virtual event: " + event.getAction());
|
||||
}
|
||||
} else {
|
||||
mConsumer.accept(event);
|
||||
}
|
||||
event.recycle();
|
||||
}
|
||||
array.clear();
|
||||
array.lastEventAction = ACTION_CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
private EventArray swapAndGetCurrentArray(Choreographer caller) {
|
||||
synchronized (mArrays) {
|
||||
if (caller != mCurrentChoreographer) {
|
||||
return mEmptyArray;
|
||||
}
|
||||
EventArray current = mArrays[mCurrentIndex];
|
||||
mCurrentIndex = mCurrentIndex ^ 1;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
private void queueVirtualAction(int action, float progress) {
|
||||
queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
|
||||
private void queueVirtualAction(int action, int param) {
|
||||
MotionEvent ev = MotionEvent.obtain(0, 0, action, 0, 0, 0);
|
||||
ev.setSource(param);
|
||||
queue(ev);
|
||||
}
|
||||
|
||||
public void onQuickScrubStart() {
|
||||
|
@ -216,33 +153,44 @@ public class MotionEventQueue {
|
|||
|
||||
public void onQuickStep(MotionEvent event) {
|
||||
event.setAction(ACTION_QUICK_STEP);
|
||||
queueNoPreProcess(event);
|
||||
queue(event);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
queueVirtualAction(ACTION_RESET, 0);
|
||||
}
|
||||
|
||||
public void deferInit() {
|
||||
queueVirtualAction(ACTION_DEFER_INIT, 0);
|
||||
public void onCommand(int command) {
|
||||
queueVirtualAction(ACTION_COMMAND, command);
|
||||
}
|
||||
|
||||
public void onCommand(int command) {
|
||||
MotionEvent ev = MotionEvent.obtain(0, 0, ACTION_COMMAND, 0, 0, 0);
|
||||
ev.setSource(command);
|
||||
queueNoPreProcess(ev);
|
||||
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;
|
||||
}
|
||||
queueVirtualAction(ACTION_SWITCH_CONSUMER, index);
|
||||
}
|
||||
|
||||
public TouchConsumer getConsumer() {
|
||||
return mConsumer;
|
||||
}
|
||||
|
||||
private static class EventArray extends ArrayList<MotionEvent> {
|
||||
|
||||
public int lastEventAction = ACTION_CANCEL;
|
||||
|
||||
public EventArray() {
|
||||
super(4);
|
||||
}
|
||||
public void dispose() {
|
||||
mDispatcher.dispose();
|
||||
mReceiver.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.STATES;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
|
@ -24,6 +28,9 @@ import java.util.function.Consumer;
|
|||
*/
|
||||
public class MultiStateCallback {
|
||||
|
||||
private static final String TAG = "MultiStateCallback";
|
||||
private static final boolean DEBUG_STATES = false;
|
||||
|
||||
private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
|
||||
private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
|
||||
|
||||
|
@ -97,4 +104,25 @@ public class MultiStateCallback {
|
|||
public boolean hasStates(int stateMask) {
|
||||
return (mState & stateMask) == stateMask;
|
||||
}
|
||||
|
||||
private void debugNewState(int stateFlag) {
|
||||
if (!DEBUG_STATES) {
|
||||
return;
|
||||
}
|
||||
|
||||
int state = getState();
|
||||
StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
|
||||
String stateFlagStr = "Unknown-" + stateFlag;
|
||||
for (int i = 0; i < STATES.length; i++) {
|
||||
if ((state & (i << i)) != 0) {
|
||||
currentStateStr.add(STATES[i]);
|
||||
}
|
||||
if (stateFlag == (1 << i)) {
|
||||
stateFlagStr = STATES[i] + " (" + stateFlag + ")";
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
|
||||
+ currentStateStr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
|
|||
import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
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;
|
||||
|
@ -35,10 +36,8 @@ import android.graphics.PointF;
|
|||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Choreographer;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
|
@ -63,9 +62,6 @@ import com.android.systemui.shared.system.RecentsAnimationListener;
|
|||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -74,7 +70,6 @@ import androidx.annotation.Nullable;
|
|||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
|
||||
|
||||
private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
|
||||
public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
|
||||
private static final String UP_EVT = "OtherActivityTouchConsumer.UP";
|
||||
|
||||
|
@ -84,12 +79,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
private final Intent mHomeIntent;
|
||||
private final ActivityControlHelper mActivityControlHelper;
|
||||
private final MainThreadExecutor mMainThreadExecutor;
|
||||
private final Choreographer mBackgroundThreadChoreographer;
|
||||
private final OverviewCallbacks mOverviewCallbacks;
|
||||
private final TaskOverlayFactory mTaskOverlayFactory;
|
||||
private final TouchInteractionLog mTouchInteractionLog;
|
||||
private final InputConsumerController mInputConsumer;
|
||||
|
||||
private final MotionEventQueue mEventQueue;
|
||||
private final MotionPauseDetector mMotionPauseDetector;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
private final boolean mIsDeferredDownTarget;
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
|
@ -103,29 +101,28 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
private Rect mStableInsets = new Rect();
|
||||
private boolean mCanGestureBeContinued;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private MotionPauseDetector mMotionPauseDetector;
|
||||
private MotionEventQueue mEventQueue;
|
||||
private boolean mIsGoingToLauncher;
|
||||
private RecentsAnimationState mRecentsAnimationState;
|
||||
|
||||
public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
|
||||
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
|
||||
MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
|
||||
MainThreadExecutor mainThreadExecutor,
|
||||
@HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
|
||||
TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
|
||||
VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog,
|
||||
TouchInteractionLog touchInteractionLog, MotionEventQueue eventQueue,
|
||||
@Nullable RecentsAnimationState recentsAnimationStateToReuse) {
|
||||
super(base);
|
||||
|
||||
mRunningTask = runningTaskInfo;
|
||||
mRecentsModel = recentsModel;
|
||||
mHomeIntent = homeIntent;
|
||||
mVelocityTracker = velocityTracker;
|
||||
|
||||
mMotionPauseDetector = new MotionPauseDetector(base);
|
||||
mEventQueue = eventQueue;
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
|
||||
mActivityControlHelper = activityControl;
|
||||
mMainThreadExecutor = mainThreadExecutor;
|
||||
mBackgroundThreadChoreographer = backgroundThreadChoreographer;
|
||||
mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
|
||||
mOverviewCallbacks = overviewCallbacks;
|
||||
mTaskOverlayFactory = taskOverlayFactory;
|
||||
|
@ -145,6 +142,12 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
if (mVelocityTracker == null) {
|
||||
return;
|
||||
}
|
||||
mVelocityTracker.addMovement(ev);
|
||||
if (ev.getActionMasked() == ACTION_POINTER_UP) {
|
||||
mVelocityTracker.clear();
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
|
||||
mTouchInteractionLog.addMotionEvent(ev);
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
|
@ -275,56 +278,33 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
mInteractionHandler = handler;
|
||||
handler.setGestureEndCallback(mEventQueue::reset);
|
||||
mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
|
||||
|
||||
CountDownLatch drawWaitLock = new CountDownLatch(1);
|
||||
handler.setLauncherOnDrawCallback(() -> {
|
||||
drawWaitLock.countDown();
|
||||
if (handler == mInteractionHandler) {
|
||||
switchToMainChoreographer();
|
||||
}
|
||||
});
|
||||
handler.initWhenReady();
|
||||
|
||||
TraceHelper.beginSection("RecentsController");
|
||||
|
||||
AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
|
||||
new AssistDataReceiver() {
|
||||
@Override
|
||||
public void onHandleAssistData(Bundle bundle) {
|
||||
if (mInteractionHandler == null) {
|
||||
// Interaction is probably complete
|
||||
mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
|
||||
} else if (handler == mInteractionHandler) {
|
||||
handler.onAssistDataReceived(bundle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Runnable startActivity;
|
||||
if (reuseOldAnimState) {
|
||||
startActivity = () -> {
|
||||
handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
|
||||
mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
|
||||
mRecentsAnimationState.mMinimizedHomeBounds);
|
||||
};
|
||||
handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
|
||||
mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
|
||||
mRecentsAnimationState.mMinimizedHomeBounds);
|
||||
} else {
|
||||
startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
|
||||
mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null);
|
||||
AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
|
||||
new AssistDataReceiver() {
|
||||
@Override
|
||||
public void onHandleAssistData(Bundle bundle) {
|
||||
if (mInteractionHandler == null) {
|
||||
// Interaction is probably complete
|
||||
mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
|
||||
} else if (handler == mInteractionHandler) {
|
||||
handler.onAssistDataReceived(bundle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundExecutor.get().submit(
|
||||
() -> ActivityManagerWrapper.getInstance().startRecentsActivity(
|
||||
mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null));
|
||||
}
|
||||
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
startActivity.run();
|
||||
try {
|
||||
drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
// We have waited long enough for launcher to draw
|
||||
}
|
||||
} else {
|
||||
// We should almost always get touch-town on background thread. This is an edge case
|
||||
// when the background Choreographer has not yet initialized.
|
||||
BackgroundExecutor.get().submit(startActivity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -379,12 +359,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
|
||||
mEventQueue = queue;
|
||||
return mBackgroundThreadChoreographer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickScrubStart() {
|
||||
if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
|
||||
|
@ -444,21 +418,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
return displacement;
|
||||
}
|
||||
|
||||
public void switchToMainChoreographer() {
|
||||
mEventQueue.setInterimChoreographer(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preProcessMotionEvent(MotionEvent ev) {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.addMovement(ev);
|
||||
if (ev.getActionMasked() == ACTION_POINTER_UP) {
|
||||
mVelocityTracker.clear();
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceToLauncherConsumer() {
|
||||
return mIsGoingToLauncher;
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.view.MotionEvent;
|
|||
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.util.UiThreadHelper;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
|
@ -33,6 +34,8 @@ import java.util.ArrayList;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
/**
|
||||
* Wrapper around RecentsAnimationController to help with some synchronization
|
||||
*/
|
||||
|
@ -67,8 +70,10 @@ public class RecentsAnimationWrapper {
|
|||
mTouchProxySupplier = touchProxySupplier;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public synchronized void setController(
|
||||
RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) {
|
||||
Preconditions.assertUIThread();
|
||||
TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
|
||||
this.mController = controller;
|
||||
this.targetSet = targetSet;
|
||||
|
|
|
@ -17,7 +17,6 @@ package com.android.quickstep;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.view.Choreographer;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
@ -56,18 +55,6 @@ public interface TouchConsumer extends Consumer<MotionEvent> {
|
|||
|
||||
default void onCommand(int command) { }
|
||||
|
||||
/**
|
||||
* Called on the binder thread to allow the consumer to process the motion event before it is
|
||||
* posted on a handler thread.
|
||||
*/
|
||||
default void preProcessMotionEvent(MotionEvent ev) { }
|
||||
|
||||
default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default void deferInit() { }
|
||||
|
||||
default boolean deferNextEventToMainThread() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
|||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper
|
||||
.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
@ -33,14 +32,12 @@ import android.app.Service;
|
|||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Choreographer;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
|
@ -52,7 +49,6 @@ import com.android.quickstep.views.RecentsView;
|
|||
import com.android.systemui.shared.recents.IOverviewProxy;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.ChoreographerCompat;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
|
||||
|
||||
|
@ -78,11 +74,6 @@ public class TouchInteractionService extends Service {
|
|||
|
||||
private static final String TAG = "TouchInteractionService";
|
||||
|
||||
/**
|
||||
* A background thread used for handling UI for another window.
|
||||
*/
|
||||
private static HandlerThread sRemoteUiThread;
|
||||
|
||||
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
|
||||
|
||||
@Override
|
||||
|
@ -191,9 +182,6 @@ public class TouchInteractionService extends Service {
|
|||
private TouchInteractionLog mTouchInteractionLog;
|
||||
private InputConsumerController mInputConsumer;
|
||||
|
||||
private Choreographer mMainThreadChoreographer;
|
||||
private Choreographer mBackgroundThreadChoreographer;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
@ -202,8 +190,7 @@ public class TouchInteractionService extends Service {
|
|||
mMainThreadExecutor = new MainThreadExecutor();
|
||||
mOverviewComponentObserver = new OverviewComponentObserver(this);
|
||||
mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
|
||||
mMainThreadChoreographer = Choreographer.getInstance();
|
||||
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
|
||||
mEventQueue = new MotionEventQueue(Looper.myLooper(), Choreographer.getInstance());
|
||||
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
|
||||
mOverviewCallbacks = OverviewCallbacks.get(this);
|
||||
mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
|
||||
|
@ -215,13 +202,13 @@ public class TouchInteractionService extends Service {
|
|||
|
||||
// Temporarily disable model preload
|
||||
// new ModelPreload().start(this);
|
||||
initBackgroundChoreographer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mInputConsumer.unregisterInputConsumer();
|
||||
mOverviewComponentObserver.onDestroy();
|
||||
mEventQueue.dispose();
|
||||
sConnected = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
@ -236,20 +223,18 @@ public class TouchInteractionService extends Service {
|
|||
mEventQueue.reset();
|
||||
TouchConsumer oldConsumer = mEventQueue.getConsumer();
|
||||
if (oldConsumer.deferNextEventToMainThread()) {
|
||||
mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
|
||||
new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
|
||||
oldConsumer.forceToLauncherConsumer(),
|
||||
oldConsumer.getRecentsAnimationStateToReuse(), v)));
|
||||
mEventQueue.deferInit();
|
||||
mEventQueue.switchConsumer(() -> getCurrentTouchConsumer(downHitTarget,
|
||||
oldConsumer.forceToLauncherConsumer(),
|
||||
oldConsumer.getRecentsAnimationStateToReuse()));
|
||||
|
||||
} else {
|
||||
mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
|
||||
getCurrentTouchConsumer(downHitTarget, false, null, null));
|
||||
TouchConsumer consumer = getCurrentTouchConsumer(downHitTarget, false, null);
|
||||
mEventQueue.switchConsumer(() -> consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private TouchConsumer getCurrentTouchConsumer(@HitTarget int downHitTarget,
|
||||
boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse,
|
||||
VelocityTracker tracker) {
|
||||
boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse) {
|
||||
RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
|
||||
|
||||
if (runningTaskInfo == null && !forceToLauncher) {
|
||||
|
@ -265,27 +250,15 @@ public class TouchInteractionService extends Service {
|
|||
mOverviewComponentObserver.getActivityControlHelper(), false,
|
||||
mTouchInteractionLog, false /* waitForWindowAvailable */);
|
||||
} else {
|
||||
if (tracker == null) {
|
||||
tracker = VelocityTracker.obtain();
|
||||
}
|
||||
return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
|
||||
mOverviewComponentObserver.getOverviewIntent(),
|
||||
mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor,
|
||||
mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
|
||||
mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog,
|
||||
downHitTarget, mOverviewCallbacks,
|
||||
mTaskOverlayFactory, mInputConsumer, mTouchInteractionLog, mEventQueue,
|
||||
recentsAnimationStateToReuse);
|
||||
}
|
||||
}
|
||||
|
||||
private void initBackgroundChoreographer() {
|
||||
if (sRemoteUiThread == null) {
|
||||
sRemoteUiThread = new HandlerThread("remote-ui");
|
||||
sRemoteUiThread.start();
|
||||
}
|
||||
new Handler(sRemoteUiThread.getLooper()).post(() ->
|
||||
mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
mTouchInteractionLog.dump(pw);
|
||||
|
|
|
@ -60,7 +60,6 @@ import android.os.Handler;
|
|||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -68,11 +67,9 @@ import android.view.ViewTreeObserver.OnDrawListener;
|
|||
import android.view.WindowManager;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
|
@ -113,13 +110,11 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
|||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.WindowCallbacksCompat;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
|
||||
private static final boolean DEBUG_STATES = false;
|
||||
|
||||
// Launcher UI related states
|
||||
private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
|
||||
|
@ -171,7 +166,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
| STATE_QUICK_SCRUB_START | STATE_APP_CONTROLLER_RECEIVED;
|
||||
|
||||
// For debugging, keep in sync with above states
|
||||
private static final String[] STATES = new String[] {
|
||||
public static final String[] STATES = new String[] {
|
||||
"STATE_LAUNCHER_PRESENT",
|
||||
"STATE_LAUNCHER_STARTED",
|
||||
"STATE_LAUNCHER_DRAWN",
|
||||
|
@ -280,8 +275,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
private QuickScrubController mQuickScrubController;
|
||||
private AnimationFactory mAnimationFactory = (t, i) -> { };
|
||||
|
||||
private Runnable mLauncherDrawnCallback;
|
||||
|
||||
private boolean mWasLauncherAlreadyVisible;
|
||||
|
||||
private boolean mPassedOverviewThreshold;
|
||||
|
@ -297,8 +290,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
private final long mTouchTimeMs;
|
||||
private long mLauncherFrameDrawnTime;
|
||||
|
||||
private boolean mBgLongSwipeMode = false;
|
||||
private boolean mUiLongSwipeMode = false;
|
||||
private boolean mLongSwipeMode = false;
|
||||
private float mLongSwipeDisplacement = 0;
|
||||
private LongSwipeHelper mLongSwipeController;
|
||||
|
||||
|
@ -326,13 +318,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
private void initStateCallbacks() {
|
||||
mStateCallback = new MultiStateCallback() {
|
||||
@Override
|
||||
public void setState(int stateFlag) {
|
||||
debugNewState(stateFlag);
|
||||
super.setState(stateFlag);
|
||||
}
|
||||
};
|
||||
mStateCallback = new MultiStateCallback();
|
||||
|
||||
// Re-setup the recents UI when gesture starts, as the state could have been changed during
|
||||
// that time by a previous window transition.
|
||||
|
@ -416,14 +402,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
private void executeOnUiThread(Runnable action) {
|
||||
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
|
||||
action.run();
|
||||
} else {
|
||||
postAsyncCallback(mMainThreadHandler, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStateOnUiThread(int stateFlag) {
|
||||
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
|
||||
mStateCallback.setState(stateFlag);
|
||||
|
@ -484,7 +462,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
});
|
||||
mRecentsView.setEnableFreeScroll(false);
|
||||
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||
if (!mBgLongSwipeMode && mGestureEndTarget != HOME) {
|
||||
if (!mLongSwipeMode && mGestureEndTarget != HOME) {
|
||||
updateFinalShift();
|
||||
}
|
||||
});
|
||||
|
@ -551,10 +529,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mRecentsView.setRunningTaskIconScaledDown(true);
|
||||
}
|
||||
|
||||
public void setLauncherOnDrawCallback(Runnable callback) {
|
||||
mLauncherDrawnCallback = callback;
|
||||
}
|
||||
|
||||
private void launcherFrameDrawn() {
|
||||
AlphaProperty property = mActivityControlHelper.getAlphaProperty(mActivity);
|
||||
if (property.getValue() < 1) {
|
||||
|
@ -575,9 +549,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
|
||||
}
|
||||
}
|
||||
if (mLauncherDrawnCallback != null) {
|
||||
mLauncherDrawnCallback.run();
|
||||
}
|
||||
mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
|
@ -632,25 +603,11 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
return TaskView.getCurveScaleForInterpolation(interpolation);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@SuppressWarnings("WrongThread")
|
||||
@UiThread
|
||||
public void dispatchMotionEventToRecentsView(MotionEvent event, @Nullable Float velocityX) {
|
||||
if (mRecentsView == null) {
|
||||
return;
|
||||
}
|
||||
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
|
||||
dispatchMotionEventToRecentsViewUi(event, velocityX);
|
||||
} else {
|
||||
MotionEvent ev = MotionEvent.obtain(event);
|
||||
postAsyncCallback(mMainThreadHandler, () -> {
|
||||
dispatchMotionEventToRecentsViewUi(ev, velocityX);
|
||||
ev.recycle();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void dispatchMotionEventToRecentsViewUi(MotionEvent event, @Nullable Float velocityX) {
|
||||
// Pass the motion events to RecentsView to allow scrolling during swipe up.
|
||||
if (!mDispatchedDownEvent) {
|
||||
// The first event we dispatch should be ACTION_DOWN.
|
||||
|
@ -666,23 +623,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mRecentsView.simulateTouchEvent(event, velocityX);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@UiThread
|
||||
public void updateDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
displacement = -displacement;
|
||||
if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
|
||||
mCurrentShift.updateValue(1);
|
||||
|
||||
if (!mBgLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
|
||||
mBgLongSwipeMode = true;
|
||||
executeOnUiThread(this::onLongSwipeEnabledUi);
|
||||
if (!mLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
|
||||
onLongSwipeEnabled();
|
||||
}
|
||||
mLongSwipeDisplacement = displacement - mTransitionDragLength;
|
||||
executeOnUiThread(this::onLongSwipeDisplacementUpdated);
|
||||
onLongSwipeDisplacementUpdated();
|
||||
} else {
|
||||
if (mBgLongSwipeMode) {
|
||||
mBgLongSwipeMode = false;
|
||||
executeOnUiThread(this::onLongSwipeDisabledUi);
|
||||
if (mLongSwipeMode) {
|
||||
onLongSwipeDisabled();
|
||||
}
|
||||
float translation = Math.max(displacement, 0);
|
||||
float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
|
||||
|
@ -694,16 +649,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
setShelfState(isPaused ? PEEK : HIDE, FAST_OUT_SLOW_IN, SHELF_ANIM_DURATION);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
|
||||
if (mInteractionType == INTERACTION_NORMAL) {
|
||||
executeOnUiThread(() -> {
|
||||
mAnimationFactory.setShelfState(shelfState, interpolator, duration);
|
||||
mIsShelfPeeking = shelfState == PEEK;
|
||||
if (mRecentsView != null && shelfState.shouldPreformHaptic) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
});
|
||||
mAnimationFactory.setShelfState(shelfState, interpolator, duration);
|
||||
mIsShelfPeeking = shelfState == PEEK;
|
||||
if (mRecentsView != null && shelfState.shouldPreformHaptic) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,7 +675,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
updateLauncherTransitionProgress();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@UiThread
|
||||
private void updateFinalShift() {
|
||||
float shift = mCurrentShift.value;
|
||||
|
||||
|
@ -736,12 +690,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
|
||||
mClipAnimationHelper.getTargetRect().width());
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
|
||||
= Looper.myLooper() == mMainThreadHandler.getLooper()
|
||||
? mSyncTransactionApplier
|
||||
: null;
|
||||
mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
|
||||
.setSyncTransactionApplier(syncTransactionApplier);
|
||||
.setSyncTransactionApplier(mSyncTransactionApplier);
|
||||
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
|
||||
mTransformParams);
|
||||
|
||||
|
@ -752,14 +702,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
executeOnUiThread(this::updateFinalShiftUi);
|
||||
}
|
||||
|
||||
private void updateFinalShiftUi() {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
|
||||
mLayoutListener.open();
|
||||
mLayoutListener.update(mCurrentShift.value > 1, mUiLongSwipeMode,
|
||||
mLayoutListener.update(mCurrentShift.value > 1, mLongSwipeMode,
|
||||
mClipAnimationHelper.getCurrentRectWithInsets(),
|
||||
mClipAnimationHelper.getCurrentCornerRadius());
|
||||
}
|
||||
|
@ -801,6 +747,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
|
||||
RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
|
||||
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
|
||||
|
@ -842,6 +789,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mPassedOverviewThreshold = false;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onRecentsAnimationCanceled() {
|
||||
mRecentsAnimationWrapper.setController(null, null);
|
||||
mActivityInitListener.unregister();
|
||||
|
@ -849,6 +797,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mTouchInteractionLog.cancelRecentsAnimation();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onGestureStarted() {
|
||||
notifyGestureStartedAsync();
|
||||
mShiftAtGestureStart = mCurrentShift.value;
|
||||
|
@ -860,10 +809,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Notifies the launcher that the swipe gesture has started. This can be called multiple times
|
||||
* on both background and UI threads
|
||||
* Notifies the launcher that the swipe gesture has started. This can be called multiple times.
|
||||
*/
|
||||
@AnyThread
|
||||
@UiThread
|
||||
private void notifyGestureStartedAsync() {
|
||||
final T curActivity = mActivity;
|
||||
if (curActivity != null) {
|
||||
|
@ -873,7 +821,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@UiThread
|
||||
public void onGestureEnded(float endVelocity, float velocityX) {
|
||||
float flingThreshold = mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
|
||||
|
@ -882,8 +830,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
|
||||
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
|
||||
|
||||
if (mBgLongSwipeMode) {
|
||||
executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX));
|
||||
if (mLongSwipeMode) {
|
||||
onLongSwipeGestureFinish(endVelocity, isFling, velocityX);
|
||||
} else {
|
||||
handleNormalGestureEnd(endVelocity, isFling, velocityX);
|
||||
}
|
||||
|
@ -904,6 +852,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mTouchInteractionLog);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) {
|
||||
float velocityPxPerMs = endVelocity / 1000;
|
||||
float velocityXPxPerMs = velocityX / 1000;
|
||||
|
@ -1033,12 +982,14 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
|
||||
@UiThread
|
||||
private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
|
||||
GestureEndTarget target, float velocityPxPerMs) {
|
||||
mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
|
||||
interpolator, target, velocityPxPerMs));
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void animateToProgressInternal(float start, float end, long duration,
|
||||
Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
|
||||
mGestureEndTarget = target;
|
||||
|
@ -1080,44 +1031,37 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
});
|
||||
windowAnim.start();
|
||||
long startMillis = SystemClock.uptimeMillis();
|
||||
// Always play the entire launcher animation when going home, since it is separate from
|
||||
// the animation that has been controlled thus far.
|
||||
final float finalStart = mGestureEndTarget == HOME ? 0 : start;
|
||||
executeOnUiThread(() -> {
|
||||
// Animate the launcher components at the same time as the window, always on UI thread.
|
||||
// Adjust start progress and duration in case we are on a different thread.
|
||||
long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
|
||||
elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
|
||||
float elapsedProgress = (float) elapsedMillis / duration;
|
||||
float adjustedStart = Utilities.mapRange(elapsedProgress, finalStart, end);
|
||||
long adjustedDuration = duration - elapsedMillis;
|
||||
// We want to use the same interpolator as the window, but need to adjust it to
|
||||
// interpolate over the remaining progress (end - start).
|
||||
TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
|
||||
interpolator, adjustedStart, end);
|
||||
if (homeAnimFactory != null) {
|
||||
Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
|
||||
homeAnim.setDuration(adjustedDuration).setInterpolator(adjustedInterpolator);
|
||||
homeAnim.start();
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
if (mLauncherTransitionController == null) {
|
||||
return;
|
||||
}
|
||||
if (finalStart == end || duration <= 0) {
|
||||
mLauncherTransitionController.dispatchSetInterpolator(t -> end);
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
} else {
|
||||
mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
|
||||
mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
|
||||
if (mGestureEndTarget == HOME) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (QUICKSTEP_SPRINGS.get()) {
|
||||
mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
|
||||
}
|
||||
mLauncherTransitionController.getAnimationPlayer().start();
|
||||
// We want to use the same interpolator as the window, but need to adjust it to
|
||||
// interpolate over the remaining progress (end - start).
|
||||
TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
|
||||
interpolator, start, end);
|
||||
if (homeAnimFactory != null) {
|
||||
Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
|
||||
homeAnim.setDuration(duration).setInterpolator(adjustedInterpolator);
|
||||
homeAnim.start();
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
if (mLauncherTransitionController == null) {
|
||||
return;
|
||||
}
|
||||
if (start == end || duration <= 0) {
|
||||
mLauncherTransitionController.dispatchSetInterpolator(t -> end);
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
} else {
|
||||
mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
|
||||
mLauncherTransitionController.getAnimationPlayer().setDuration(duration);
|
||||
|
||||
if (QUICKSTEP_SPRINGS.get()) {
|
||||
mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
|
||||
}
|
||||
});
|
||||
mLauncherTransitionController.getAnimationPlayer().start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1148,12 +1092,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
originalTarget, finalTarget));
|
||||
currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
|
||||
float alpha = 1 - interpolatedProgress;
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
|
||||
= Looper.myLooper() == mMainThreadHandler.getLooper()
|
||||
? mSyncTransactionApplier
|
||||
: null;
|
||||
mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
|
||||
.setSyncTransactionApplier(syncTransactionApplier);
|
||||
.setSyncTransactionApplier(mSyncTransactionApplier);
|
||||
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
|
||||
});
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
|
@ -1404,8 +1344,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
|
||||
public void onQuickScrubProgress(float progress) {
|
||||
mCurrentQuickScrubProgress = progress;
|
||||
if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null
|
||||
|| mQuickScrubBlocked || !mStateCallback.hasStates(QUICK_SCRUB_START_UI_STATE)) {
|
||||
if (mQuickScrubController == null || mQuickScrubBlocked ||
|
||||
!mStateCallback.hasStates(QUICK_SCRUB_START_UI_STATE)) {
|
||||
return;
|
||||
}
|
||||
mQuickScrubController.onQuickScrubProgress(progress);
|
||||
|
@ -1427,39 +1367,19 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
setStateOnUiThread(STATE_HANDLER_INVALIDATED);
|
||||
}
|
||||
|
||||
private void debugNewState(int stateFlag) {
|
||||
if (!DEBUG_STATES) {
|
||||
return;
|
||||
}
|
||||
|
||||
int state = mStateCallback.getState();
|
||||
StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
|
||||
String stateFlagStr = "Unknown-" + stateFlag;
|
||||
for (int i = 0; i < STATES.length; i++) {
|
||||
if ((state & (i << i)) != 0) {
|
||||
currentStateStr.add(STATES[i]);
|
||||
}
|
||||
if (stateFlag == (1 << i)) {
|
||||
stateFlagStr = STATES[i] + " (" + stateFlag + ")";
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
|
||||
+ currentStateStr);
|
||||
}
|
||||
|
||||
public void setGestureEndCallback(Runnable gestureEndCallback) {
|
||||
mGestureEndCallback = gestureEndCallback;
|
||||
}
|
||||
|
||||
// Handling long swipe
|
||||
private void onLongSwipeEnabledUi() {
|
||||
mUiLongSwipeMode = true;
|
||||
private void onLongSwipeEnabled() {
|
||||
mLongSwipeMode = true;
|
||||
checkLongSwipeCanEnter();
|
||||
checkLongSwipeCanStart();
|
||||
}
|
||||
|
||||
private void onLongSwipeDisabledUi() {
|
||||
mUiLongSwipeMode = false;
|
||||
private void onLongSwipeDisabled() {
|
||||
mLongSwipeMode = false;
|
||||
mStateCallback.clearState(STATE_SCREENSHOT_VIEW_SHOWN);
|
||||
|
||||
if (mLongSwipeController != null) {
|
||||
|
@ -1472,7 +1392,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
private void onLongSwipeDisplacementUpdated() {
|
||||
if (!mUiLongSwipeMode || mLongSwipeController == null) {
|
||||
if (!mLongSwipeMode || mLongSwipeController == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1480,7 +1400,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
private void checkLongSwipeCanEnter() {
|
||||
if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
|
||||
if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
|
||||
|| !mActivityControlHelper.supportsLongSwipe(mActivity)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1491,7 +1411,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
private void checkLongSwipeCanStart() {
|
||||
if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
|
||||
if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
|
||||
|| !mActivityControlHelper.supportsLongSwipe(mActivity)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1511,13 +1431,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) {
|
||||
if (!mUiLongSwipeMode || mLongSwipeController == null) {
|
||||
mUiLongSwipeMode = false;
|
||||
private void onLongSwipeGestureFinish(float velocity, boolean isFling, float velocityX) {
|
||||
if (!mLongSwipeMode || mLongSwipeController == null) {
|
||||
mLongSwipeMode = false;
|
||||
handleNormalGestureEnd(velocity, isFling, velocityX);
|
||||
return;
|
||||
}
|
||||
mUiLongSwipeMode = false;
|
||||
mLongSwipeMode = false;
|
||||
finishCurrentTransitionToRecents();
|
||||
mLongSwipeController.end(velocity, isFling,
|
||||
() -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
|
||||
|
|
Loading…
Reference in New Issue