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:
Sunny Goyal 2019-02-06 13:44:17 -08:00
parent 8fa51172f6
commit 451987c5e6
9 changed files with 248 additions and 547 deletions

Binary file not shown.

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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));