Refactoring the touch handling into a different class.
This makes it easier to maintain states for a single touch session (from down to up/cancel)
Change-Id: I27da239376337cd02c104fea54e39b2e30b53fab
diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
index aa210b8..21b032b 100644
--- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
@@ -18,7 +18,7 @@
import android.support.annotation.WorkerThread;
import com.android.launcher3.states.InternalStateHandler;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index e2abd59..40246e2 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -15,10 +15,11 @@
*/
package com.android.quickstep;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchInteractionService.isInteractionQuick;
+
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -29,10 +30,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build;
-import android.os.UserHandle;
import android.support.annotation.UiThread;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -51,10 +50,9 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
new file mode 100644
index 0000000..df012df
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -0,0 +1,363 @@
+/*
+ * 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 static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+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.quickstep.RemoteRunnable.executeSafely;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TraceHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.AssistDataReceiver;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+/**
+ * Touch consumer for handling events originating from an activity other than Launcher
+ */
+public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
+ private static final String TAG = "ActivityTouchConsumer";
+
+ private final RunningTaskInfo mRunningTask;
+ private final RecentsModel mRecentsModel;
+ private final Intent mHomeIntent;
+ private final ISystemUiProxy mISystemUiProxy;
+ private final MainThreadExecutor mMainThreadExecutor;
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private int mActivePointerId = INVALID_POINTER_ID;
+ private boolean mTouchThresholdCrossed;
+ private int mTouchSlop;
+ private float mStartDisplacement;
+ private BaseSwipeInteractionHandler mInteractionHandler;
+ private int mDisplayRotation;
+ private Rect mStableInsets = new Rect();
+
+ private VelocityTracker mVelocityTracker;
+
+ public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
+ RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
+ MainThreadExecutor mainThreadExecutor) {
+ super(base);
+ mRunningTask = runningTaskInfo;
+ mRecentsModel = recentsModel;
+ mHomeIntent = homeIntent;
+ mVelocityTracker = VelocityTracker.obtain();
+ mISystemUiProxy = systemUiProxy;
+ mMainThreadExecutor = mainThreadExecutor;
+ }
+
+ @Override
+ public void accept(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ return;
+ }
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ TraceHelper.beginSection("TouchInt");
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+ mTouchThresholdCrossed = false;
+
+ // Start the window animation on down to give more time for launcher to draw
+ if (!isUsingScreenShot()) {
+ startTouchTrackingForWindowAnimation();
+ }
+
+ mVelocityTracker.addMovement(ev);
+ Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+ mDisplayRotation = display.getRotation();
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ break;
+ }
+ case ACTION_POINTER_UP: {
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ mVelocityTracker.clear();
+ }
+ break;
+ }
+ case ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mVelocityTracker.addMovement(ev);
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ float displacement = ev.getY(pointerIndex) - mDownPos.y;
+ if (isNavBarOnRight()) {
+ displacement = ev.getX(pointerIndex) - mDownPos.x;
+ } else if (isNavBarOnLeft()) {
+ displacement = mDownPos.x - ev.getX(pointerIndex);
+ }
+ if (!mTouchThresholdCrossed) {
+ mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
+ if (mTouchThresholdCrossed) {
+ mStartDisplacement = Math.signum(displacement) * mTouchSlop;
+
+ if (isUsingScreenShot()) {
+ startTouchTrackingForScreenshotAnimation();
+ }
+
+ // Notify the handler that the gesture has actually started
+ mInteractionHandler.onGestureStarted();
+
+ // Notify the system that we have started tracking the event
+ if (mISystemUiProxy != null) {
+ executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
+ }
+ }
+ } else {
+ // Move
+ mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+ }
+ break;
+ }
+ case ACTION_CANCEL:
+ // TODO: Should be different than ACTION_UP
+ case ACTION_UP: {
+ TraceHelper.endSection("TouchInt");
+
+ finishTouchTracking();
+ break;
+ }
+ }
+ }
+
+ private boolean isNavBarOnRight() {
+ return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
+ }
+
+ private boolean isNavBarOnLeft() {
+ return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
+ }
+
+ private boolean isUsingScreenShot() {
+ return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
+ }
+
+ /**
+ * Called when the gesture has started.
+ */
+ private void startTouchTrackingForScreenshotAnimation() {
+ // Create the shared handler
+ final NavBarSwipeInteractionHandler handler =
+ new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
+
+ TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
+
+ // Start the recents activity on a background thread
+ BackgroundExecutor.get().submit(() -> {
+ // Get the snap shot before
+ handler.setTaskSnapshot(getCurrentTaskSnapshot());
+
+ // Start the launcher activity with our custom handler
+ Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
+ startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
+ TraceHelper.partitionSection("TouchInt", "Home started");
+ });
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, null);
+ mInteractionHandler = handler;
+ mInteractionHandler.setGestureEndCallback(this::onFinish);
+ }
+
+ private Bitmap getCurrentTaskSnapshot() {
+ TraceHelper.beginSection("TaskSnapshot");
+ // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
+ Point displaySize = new Point();
+ Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+ display.getRealSize(displaySize);
+ int rotation = display.getRotation();
+ // The rotation is backwards in landscape, so flip it.
+ if (rotation == Surface.ROTATION_270) {
+ rotation = Surface.ROTATION_90;
+ } else if (rotation == Surface.ROTATION_90) {
+ rotation = Surface.ROTATION_270;
+ }
+ try {
+ return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
+ false, rotation).toBitmap();
+ } catch (Exception e) {
+ Log.e(TAG, "Error capturing snapshot", e);
+
+ // Return a dummy bitmap
+ Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
+ bitmap.eraseColor(Color.WHITE);
+ return bitmap;
+ } finally {
+ TraceHelper.endSection("TaskSnapshot");
+ }
+ }
+
+ private void startTouchTrackingForWindowAnimation() {
+ // Create the shared handler
+ final WindowTransformSwipeHandler handler =
+ new WindowTransformSwipeHandler(mRunningTask, this);
+ BackgroundExecutor.get().submit(() -> {
+ ActivityManagerWrapper.getInstance().startRecentsActivity(mHomeIntent,
+ new AssistDataReceiver() {
+ @Override
+ public void onHandleAssistData(Bundle bundle) {
+ // Pass to AIAI
+ }
+ },
+ new RecentsAnimationListener() {
+ public void onAnimationStart(
+ RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps) {
+ if (mInteractionHandler == handler) {
+ handler.setRecentsAnimation(controller, apps);
+
+ } else {
+ controller.finish(false /* toHome */);
+ }
+ }
+
+ public void onAnimationCanceled() {
+ if (mInteractionHandler == handler) {
+ handler.setRecentsAnimation(null, null);
+ }
+ }
+ }, null, null);
+ });
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, null);
+ mInteractionHandler = handler;
+ handler.setGestureEndCallback(this::onFinish);
+ handler.setLauncherOnDrawCallback(() -> {
+ if (handler == mInteractionHandler) {
+ switchToMainConsumer();
+ }
+ });
+ mMainThreadExecutor.execute(handler::initWhenReady);
+ }
+
+ /**
+ * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+ * the animation can still be running.
+ */
+ private void finishTouchTracking() {
+ if (mTouchThresholdCrossed) {
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+
+ float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
+ : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
+ : mVelocityTracker.getYVelocity(mActivePointerId);
+ mInteractionHandler.onGestureEnded(velocity);
+ } else if (!isUsingScreenShot()) {
+ // Since we start touch tracking on DOWN, we may reach this state without actually
+ // starting the gesture. In that case, just cleanup immediately.
+ final BaseSwipeInteractionHandler handler = mInteractionHandler;
+ mMainThreadExecutor.execute(handler::reset);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+
+ onTouchTrackingComplete();
+ }
+
+ @Override
+ public void reset() {
+ // Clean up the old interaction handler
+ if (mInteractionHandler != null) {
+ final BaseSwipeInteractionHandler handler = mInteractionHandler;
+ mMainThreadExecutor.execute(handler::reset);
+ mInteractionHandler = null;
+ }
+ }
+
+ @Override
+ public void updateTouchTracking(int interactionType) {
+ mMainThreadExecutor.execute(() -> {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.updateInteractionType(interactionType);
+ }
+ });
+ }
+
+ @Override
+ public boolean shouldUseBackgroundConsumer() {
+ return !isUsingScreenShot();
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubEnd();
+ }
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubProgress(progress);
+ }
+ }
+
+ private void onFinish() {
+ mInteractionHandler = null;
+ }
+
+ public void onTouchTrackingComplete() { }
+
+ public void switchToMainConsumer() { }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
new file mode 100644
index 0000000..d4d3a3f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.support.annotation.IntDef;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.function.Consumer;
+
+@TargetApi(Build.VERSION_CODES.O)
+@FunctionalInterface
+public interface TouchConsumer extends Consumer<MotionEvent> {
+
+ static boolean isInteractionQuick(@InteractionType int interactionType) {
+ return interactionType == INTERACTION_QUICK_SCRUB ||
+ interactionType == INTERACTION_QUICK_SWITCH;
+ }
+
+ @IntDef(flag = true, value = {
+ INTERACTION_NORMAL,
+ INTERACTION_QUICK_SWITCH,
+ INTERACTION_QUICK_SCRUB
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InteractionType {}
+ int INTERACTION_NORMAL = 0;
+ int INTERACTION_QUICK_SWITCH = 1;
+ int INTERACTION_QUICK_SCRUB = 2;
+
+ default void reset() { }
+
+ default boolean shouldUseBackgroundConsumer() {
+ return false;
+ }
+
+ default void updateTouchTracking(@InteractionType int interactionType) { }
+
+ default void onQuickScrubEnd() { }
+
+ default void onQuickScrubProgress(float progress) { }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 67146ff..ab496ad 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,58 +21,34 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
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.quickstep.RemoteRunnable.executeSafely;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityOptions;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.support.annotation.IntDef;
import android.util.Log;
import android.view.Choreographer;
-import android.view.Display;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.WindowManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.Utilities;
import com.android.launcher3.model.ModelPreload;
-import com.android.launcher3.util.TraceHelper;
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.AssistDataReceiver;
-import com.android.systemui.shared.system.BackgroundExecutor;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.function.Consumer;
/**
* Service connected by system-UI for handling touch interaction.
@@ -84,17 +60,6 @@
private static final String TAG = "TouchInteractionService";
- @IntDef(flag = true, value = {
- INTERACTION_NORMAL,
- INTERACTION_QUICK_SWITCH,
- INTERACTION_QUICK_SCRUB
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface InteractionType {}
- public static final int INTERACTION_NORMAL = 0;
- public static final int INTERACTION_QUICK_SWITCH = 1;
- public static final int INTERACTION_QUICK_SCRUB = 2;
-
/**
* A background thread used for handling UI for another window.
*/
@@ -115,34 +80,29 @@
@Override
public void onQuickSwitch() {
- updateTouchTracking(INTERACTION_QUICK_SWITCH);
+ mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH);
}
@Override
public void onQuickScrubStart() {
- updateTouchTracking(INTERACTION_QUICK_SCRUB);
+ mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
sQuickScrubEnabled = true;
}
@Override
public void onQuickScrubEnd() {
- if (mInteractionHandler != null) {
- mInteractionHandler.onQuickScrubEnd();
- }
+ mCurrentConsumer.onQuickScrubEnd();
sQuickScrubEnabled = false;
}
@Override
public void onQuickScrubProgress(float progress) {
- if (mInteractionHandler != null) {
- mInteractionHandler.onQuickScrubProgress(progress);
- }
+ mCurrentConsumer.onQuickScrubProgress(progress);
}
};
- private final Consumer<MotionEvent> mOtherActivityTouchConsumer
- = this::handleTouchDownOnOtherActivity;
- private final Consumer<MotionEvent> mNoOpTouchConsumer = (ev) -> {};
+ private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
+ private TouchConsumer mCurrentConsumer = mNoOpTouchConsumer;
private static boolean sConnected = false;
private static boolean sQuickScrubEnabled = false;
@@ -162,20 +122,7 @@
private ComponentName mLauncher;
private MotionEventQueue mEventQueue;
private MainThreadExecutor mMainThreadExecutor;
-
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private int mActivePointerId = INVALID_POINTER_ID;
- private VelocityTracker mVelocityTracker;
- private boolean mTouchThresholdCrossed;
- private int mTouchSlop;
- private float mStartDisplacement;
- private BaseSwipeInteractionHandler mInteractionHandler;
- private int mDisplayRotation;
- private Rect mStableInsets = new Rect();
-
private ISystemUiProxy mISystemUiProxy;
-
private Choreographer mBackgroundThreadChoreographer;
@Override
@@ -218,265 +165,45 @@
if (ev.getActionMasked() == ACTION_DOWN) {
mRunningTask = mAM.getRunningTask();
+ mCurrentConsumer.reset();
if (mRunningTask == null) {
- mEventQueue.setConsumer(mNoOpTouchConsumer);
- mEventQueue.setInterimChoreographer(null);
+ mCurrentConsumer = mNoOpTouchConsumer;
} else if (mRunningTask.topActivity.equals(mLauncher)) {
- mEventQueue.setConsumer(getLauncherConsumer());
- mEventQueue.setInterimChoreographer(null);
+ mCurrentConsumer = getLauncherConsumer();
} else {
- mEventQueue.setConsumer(mOtherActivityTouchConsumer);
- mEventQueue.setInterimChoreographer(
- isUsingScreenShot() ? null : mBackgroundThreadChoreographer);
+ mCurrentConsumer = getOtherActivityConsumer();
}
+
+ mEventQueue.setConsumer(mCurrentConsumer);
+ mEventQueue.setInterimChoreographer(mCurrentConsumer.shouldUseBackgroundConsumer()
+ ? mBackgroundThreadChoreographer : null);
}
mEventQueue.queue(ev);
}
- private void handleTouchDownOnOtherActivity(MotionEvent ev) {
- if (ev.getActionMasked() != ACTION_DOWN && mVelocityTracker == null) {
- return;
- }
- switch (ev.getActionMasked()) {
- case ACTION_DOWN: {
- TraceHelper.beginSection("TouchInt");
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
- mTouchThresholdCrossed = false;
+ private TouchConsumer getOtherActivityConsumer() {
+ TouchConsumer consumer = new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel,
+ mHomeIntent, mISystemUiProxy, mMainThreadExecutor) {
- // Clean up the old interaction handler
- if (mInteractionHandler != null) {
- final BaseSwipeInteractionHandler handler = mInteractionHandler;
- mMainThreadExecutor.execute(handler::reset);
- mInteractionHandler = null;
+ @Override
+ public void switchToMainConsumer() {
+ if (mCurrentConsumer == this) {
+ mEventQueue.setInterimChoreographer(null);
}
-
- // Start the window animation on down to give more time for launcher to draw
- if (!isUsingScreenShot()) {
- startTouchTrackingForWindowAnimation();
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- mVelocityTracker.addMovement(ev);
-
- Display display = getSystemService(WindowManager.class).getDefaultDisplay();
- mDisplayRotation = display.getRotation();
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- break;
}
- case ACTION_POINTER_UP: {
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- mVelocityTracker.clear();
+
+ @Override
+ public void onTouchTrackingComplete() {
+ if (mCurrentConsumer == this) {
+ mCurrentConsumer = mNoOpTouchConsumer;
+ mEventQueue.setConsumer(mCurrentConsumer);
}
- break;
}
- case ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mVelocityTracker.addMovement(ev);
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
- float displacement = ev.getY(pointerIndex) - mDownPos.y;
- if (isNavBarOnRight()) {
- displacement = ev.getX(pointerIndex) - mDownPos.x;
- } else if (isNavBarOnLeft()) {
- displacement = mDownPos.x - ev.getX(pointerIndex);
- }
- if (!mTouchThresholdCrossed) {
- mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
- if (mTouchThresholdCrossed) {
- mStartDisplacement = Math.signum(displacement) * mTouchSlop;
-
- if (isUsingScreenShot()) {
- startTouchTrackingForScreenshotAnimation();
- }
-
- // Notify the handler that the gesture has actually started
- mInteractionHandler.onGestureStarted();
-
- // Notify the system that we have started tracking the event
- if (mISystemUiProxy != null) {
- executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
- }
- }
- } else {
- // Move
- mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
- }
- break;
- }
- case ACTION_CANCEL:
- // TODO: Should be different than ACTION_UP
- case ACTION_UP: {
- TraceHelper.endSection("TouchInt");
-
- finishTouchTracking();
- mEventQueue.setConsumer(mNoOpTouchConsumer);
- break;
- }
- }
+ };
+ return consumer;
}
- private boolean isNavBarOnRight() {
- return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
- }
-
- private boolean isNavBarOnLeft() {
- return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
- }
-
- private boolean isUsingScreenShot() {
- return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
- }
-
- /**
- * Called when the gesture has started.
- */
- private void startTouchTrackingForScreenshotAnimation() {
- // Create the shared handler
- final NavBarSwipeInteractionHandler handler =
- new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
-
- TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
-
- // Start the recents activity on a background thread
- BackgroundExecutor.get().submit(() -> {
- // Get the snap shot before
- handler.setTaskSnapshot(getCurrentTaskSnapshot());
-
- // Start the launcher activity with our custom handler
- Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
- startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
- TraceHelper.partitionSection("TouchInt", "Home started");
- });
-
- // Preload the plan
- mRecentsModel.loadTasks(mRunningTask.id, null);
- mInteractionHandler = handler;
- mInteractionHandler.setGestureEndCallback(() -> mInteractionHandler = null);
- }
-
- private void startTouchTrackingForWindowAnimation() {
- // Create the shared handler
- final WindowTransformSwipeHandler handler =
- new WindowTransformSwipeHandler(mRunningTask, this);
- BackgroundExecutor.get().submit(() -> {
- ActivityManagerWrapper.getInstance().startRecentsActivity(mHomeIntent,
- new AssistDataReceiver() {
- @Override
- public void onHandleAssistData(Bundle bundle) {
- // Pass to AIAI
- }
- },
- new RecentsAnimationListener() {
- public void onAnimationStart(
- RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps) {
- if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(controller, apps);
-
- } else {
- controller.finish(false /* toHome */);
- }
- }
-
- public void onAnimationCanceled() {
- if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(null, null);
- }
- }
- }, null, null);
- });
-
- // Preload the plan
- mRecentsModel.loadTasks(mRunningTask.id, null);
- mInteractionHandler = handler;
- handler.setGestureEndCallback(() -> {
- if (handler == mInteractionHandler) {
- mInteractionHandler = null;
- }
- });
- handler.setLauncherOnDrawCallback(() -> {
- if (handler == mInteractionHandler) {
- mEventQueue.setInterimChoreographer(null);
- }
- });
- mMainThreadExecutor.execute(handler::initWhenReady);
- }
-
- private void updateTouchTracking(@InteractionType int interactionType) {
- final BaseSwipeInteractionHandler handler = mInteractionHandler;
- mMainThreadExecutor.execute(() -> handler.updateInteractionType(interactionType));
- }
-
- /**
- * Called when the gesture has ended. Does not correlate to the completion of the interaction as
- * the animation can still be running.
- */
- private void finishTouchTracking() {
- if (mTouchThresholdCrossed) {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
- float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
- : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
- : mVelocityTracker.getYVelocity(mActivePointerId);
- mInteractionHandler.onGestureEnded(velocity);
- } else if (!isUsingScreenShot()) {
- // Since we start touch tracking on DOWN, we may reach this state without actually
- // starting the gesture. In that case, just cleanup immediately.
- final BaseSwipeInteractionHandler handler = mInteractionHandler;
- mMainThreadExecutor.execute(handler::reset);
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- private Bitmap getCurrentTaskSnapshot() {
- TraceHelper.beginSection("TaskSnapshot");
- // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
- Point displaySize = new Point();
- Display display = getSystemService(WindowManager.class).getDefaultDisplay();
- display.getRealSize(displaySize);
- int rotation = display.getRotation();
- // The rotation is backwards in landscape, so flip it.
- if (rotation == Surface.ROTATION_270) {
- rotation = Surface.ROTATION_90;
- } else if (rotation == Surface.ROTATION_90) {
- rotation = Surface.ROTATION_270;
- }
- try {
- return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
- false, rotation).toBitmap();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing snapshot", e);
-
- // Return a dummy bitmap
- Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
- bitmap.eraseColor(Color.WHITE);
- return bitmap;
- } finally {
- TraceHelper.endSection("TaskSnapshot");
- }
- }
-
- private Consumer<MotionEvent> getLauncherConsumer() {
+ private TouchConsumer getLauncherConsumer() {
Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
if (launcher == null) {
@@ -490,15 +217,18 @@
return new LauncherTouchConsumer(target);
}
- private class LauncherTouchConsumer implements Consumer<MotionEvent> {
+ private class LauncherTouchConsumer implements TouchConsumer {
private final View mTarget;
private final int[] mLocationOnScreen = new int[2];
+ private final PointF mDownPos = new PointF();
+ private final int mTouchSlop;
private boolean mTrackingStarted = false;
LauncherTouchConsumer(View target) {
mTarget = target;
+ mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
}
@Override
@@ -507,7 +237,6 @@
if (action == ACTION_DOWN) {
mTrackingStarted = false;
mDownPos.set(ev.getX(), ev.getY());
- mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
} else if (!mTrackingStarted) {
switch (action) {
case ACTION_POINTER_UP:
@@ -559,9 +288,4 @@
new Handler(sRemoteUiThread.getLooper()).post(() ->
mBackgroundThreadChoreographer = Choreographer.getInstance());
}
-
- public static boolean isInteractionQuick(@InteractionType int interactionType) {
- return interactionType == INTERACTION_QUICK_SCRUB ||
- interactionType == INTERACTION_QUICK_SWITCH;
- }
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 6416dc1..3364c36 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,10 +17,10 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchInteractionService.isInteractionQuick;
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
@@ -54,7 +54,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;