Merge "Splitting TaplTests into a pure AOSP and QS parts" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index d0c255c..7381574 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -24,27 +24,39 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.OverviewInteractionState;
+
+import java.util.ArrayList;
/**
* Provides recents-related {@link UiFactory} logic and classes.
*/
-public final class RecentsUiFactory {
+public abstract class RecentsUiFactory {
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
- private RecentsUiFactory() {}
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
- /**
- * Creates and returns a touch controller for swiping recents tasks.
- *
- * @param launcher the launcher activity
- * @return the touch controller for recents tasks
- */
- public static TouchController createTaskSwipeController(Launcher launcher) {
- // We leave all input handling to the view itself.
- return null;
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ } else {
+ boolean allowDragToOverview = OverviewInteractionState.INSTANCE.get(launcher)
+ .isSwipeUpGestureEnabled();
+ list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
+ }
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+ return list.toArray(new TouchController[list.size()]);
}
/**
@@ -62,7 +74,7 @@
*
* @param launcher the launcher activity
*/
- public static void prepareToShowRecents(Launcher launcher) {
+ public static void prepareToShowOverview(Launcher launcher) {
View overview = launcher.getOverviewPanel();
if (overview.getVisibility() != VISIBLE) {
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
@@ -74,7 +86,7 @@
*
* @param launcher the launcher activity
*/
- public static void resetRecents(Launcher launcher) {}
+ public static void resetOverview(Launcher launcher) {}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
new file mode 100644
index 0000000..fb83cd3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
+
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Touch controller which handles swipe and hold to go to Overview
+ */
+public class FlingAndHoldTouchController extends PortraitStatesTouchController {
+
+ private final MotionPauseDetector mMotionPauseDetector;
+
+ public FlingAndHoldTouchController(Launcher l) {
+ super(l, false /* allowDragToOverview */);
+ mMotionPauseDetector = new MotionPauseDetector(l);
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ mMotionPauseDetector.clear();
+
+ super.onDragStart(start);
+
+ if (mStartState == NORMAL) {
+ mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.setOverviewStateEnabled(isPaused);
+ maybeUpdateAtomicAnim(NORMAL, OVERVIEW, isPaused ? 1 : 0);
+ });
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement) {
+ mMotionPauseDetector.addPosition(displacement);
+ return super.onDrag(displacement);
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ if (mMotionPauseDetector.isPaused() && mStartState == NORMAL) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+
+ // Let the state manager know that the animation didn't go to the target state,
+ // but don't cancel ourselves (we already clean up when the animation completes).
+ Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
+ mCurrentAnimation.setOnCancelRunnable(null);
+ mCurrentAnimation.dispatchOnCancel();
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(OVERVIEW, new AnimatorSetBuilder(), maxAccuracy,
+ onCancel, NON_ATOMIC_COMPONENT);
+
+ final int logAction = fling ? Touch.FLING : Touch.SWIPE;
+ mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(OVERVIEW, logAction));
+
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ maybeUpdateAtomicAnim(NORMAL, OVERVIEW, 1f);
+ mCurrentAnimation.dispatchOnStartWithVelocity(1, velocity);
+
+ // TODO: Find a better duration
+ anim.setDuration(100);
+ anim.start();
+ settleAtomicAnimation(1f, anim.getDuration());
+ } else {
+ super.onDragEnd(velocity, fling);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index f18f43c..51e9495 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -21,32 +21,65 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.ArrayList;
/**
* Provides recents-related {@link UiFactory} logic and classes.
*/
-public final class RecentsUiFactory {
+public abstract class RecentsUiFactory {
+
+ private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
+ WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
- private RecentsUiFactory() {}
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
+ .isSwipeUpGestureEnabled();
+ boolean swipeUpToHome = swipeUpEnabled && SWIPE_HOME.get();
- /**
- * Creates and returns a touch controller for swiping recents tasks.
- *
- * @param launcher the launcher activity
- * @return the touch controller for recents tasks
- */
- public static TouchController createTaskSwipeController(Launcher launcher) {
- return new LauncherTaskViewController(launcher);
+
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
+
+ if (swipeUpToHome) {
+ list.add(new FlingAndHoldTouchController(launcher));
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ } else {
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ } else {
+ list.add(new PortraitStatesTouchController(launcher,
+ swipeUpEnabled /* allowDragToOverview */));
+ }
+ }
+
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+
+ list.add(new LauncherTaskViewController(launcher));
+ return list.toArray(new TouchController[list.size()]);
}
/**
@@ -64,7 +97,7 @@
*
* @param launcher the launcher activity
*/
- public static void prepareToShowRecents(Launcher launcher) {
+ public static void prepareToShowOverview(Launcher launcher) {
RecentsView overview = launcher.getOverviewPanel();
if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
@@ -76,9 +109,8 @@
*
* @param launcher the launcher activity
*/
- public static void resetRecents(Launcher launcher) {
- RecentsView recents = launcher.getOverviewPanel();
- recents.reset();
+ public static void resetOverview(Launcher launcher) {
+ launcher.<RecentsView>getOverviewPanel().reset();
}
/**
@@ -88,6 +120,14 @@
*/
public static void onLauncherStateOrResumeChanged(Launcher launcher) {
LauncherState state = launcher.getStateManager().getState();
+ if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
+ DeviceProfile profile = launcher.getDeviceProfile();
+ boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+ && !profile.isVerticalBarLayout();
+ UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+ visible ? 1 : 0, profile.hotseatBarSizePx);
+ }
+
if (state == NORMAL) {
launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
index 0f9b57f..a069ed5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
@@ -36,7 +36,7 @@
public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
public OverviewToAllAppsTouchController(Launcher l) {
- super(l);
+ super(l, true /* allowDragToOverview */);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index ea0e552..d20ffbb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -25,6 +25,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -67,14 +68,17 @@
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
- private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
+ private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
+
+ private final boolean mAllowDragToOverview;
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
- public PortraitStatesTouchController(Launcher l) {
+ public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
super(l, SwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
+ mAllowDragToOverview = allowDragToOverview;
}
@Override
@@ -82,7 +86,7 @@
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
// TODO: Animate to finish instead.
- mCurrentAnimation.getAnimationPlayer().end();
+ mCurrentAnimation.skipToEnd();
}
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
@@ -128,7 +132,8 @@
} else if (fromState == OVERVIEW) {
return isDragTowardPositive ? ALL_APPS : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
- return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS;
+ return mAllowDragToOverview && TouchInteractionService.isConnected()
+ ? OVERVIEW : ALL_APPS;
}
return fromState;
}
@@ -241,7 +246,10 @@
private void handleFirstSwipeToOverview(final ValueAnimator animator,
final long expectedDuration, final LauncherState targetState, final float velocity,
final boolean isFling) {
- if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
+ if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+ && targetState == OVERVIEW) {
+ mFinishFastOnSecondTouch = true;
+ } else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
if (isFling && expectedDuration != 0) {
// Update all apps interpolator to add a bit of overshoot starting from currFraction
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index d0a9e3c..70aae13 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -35,68 +35,32 @@
import android.util.Base64;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.zip.Deflater;
-public class UiFactory {
-
- private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
- WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
- .isSwipeUpGestureEnabled();
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
-
- if (!swipeUpEnabled || launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(launcher));
- }
-
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new LandscapeEdgeSwipeController(launcher));
- } else {
- list.add(new PortraitStatesTouchController(launcher));
- }
- if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
- && !launcher.getDeviceProfile().isMultiWindowMode
- && !launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new StatusBarTouchController(launcher));
- }
- TouchController taskSwipeController =
- RecentsUiFactory.createTaskSwipeController(launcher);
- if (taskSwipeController != null) {
- list.add(taskSwipeController);
- }
- return list.toArray(new TouchController[list.size()]);
- }
+public class UiFactory extends RecentsUiFactory {
public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
OverviewInteractionState.INSTANCE.get(context).setOnSwipeUpSettingChangedListener(listener);
}
public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(),
- RecentsUiFactory.createRecentsViewStateController(launcher),
+ return new StateHandler[] {
+ launcher.getAllAppsController(),
+ launcher.getWorkspace(),
+ createRecentsViewStateController(launcher),
new BackButtonAlphaHandler(launcher)};
}
@@ -116,10 +80,6 @@
.setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
}
- public static void resetOverview(Launcher launcher) {
- RecentsUiFactory.resetRecents(launcher);
- }
-
public static void onCreate(Launcher launcher) {
if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@@ -171,19 +131,6 @@
.getHighResLoadingState().setVisible(true);
}
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {
- LauncherState state = launcher.getStateManager().getState();
- if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
- DeviceProfile profile = launcher.getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
- && !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
- visible ? 1 : 0, profile.hotseatBarSizePx);
- }
-
- RecentsUiFactory.onLauncherStateOrResumeChanged(launcher);
- }
-
public static void onTrimMemory(Context context, int level) {
RecentsModel model = RecentsModel.INSTANCE.get(context);
if (model != null) {
@@ -233,8 +180,4 @@
out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
return true;
}
-
- public static void prepareToShowOverview(Launcher launcher) {
- RecentsUiFactory.prepareToShowRecents(launcher);
- }
}
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index b152bb9..5755205 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -67,6 +67,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import androidx.annotation.Nullable;
+
/**
* Touch consumer for handling events originating from an activity other than Launcher
*/
@@ -196,7 +198,7 @@
if (mPassedInitialSlop && mInteractionHandler != null) {
// Move
- dispatchMotion(ev, displacement - mStartDisplacement);
+ dispatchMotion(ev, displacement - mStartDisplacement, null);
if (FeatureFlags.SWIPE_HOME.get()) {
mMotionPauseDetector.addPosition(displacement);
@@ -217,11 +219,11 @@
}
}
- private void dispatchMotion(MotionEvent ev, float displacement) {
+ private void dispatchMotion(MotionEvent ev, float displacement, @Nullable Float velocityX) {
mInteractionHandler.updateDisplacement(displacement);
boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
if (!isLandscape) {
- mInteractionHandler.dispatchMotionEventToRecentsView(ev);
+ mInteractionHandler.dispatchMotionEventToRecentsView(ev, velocityX);
}
}
@@ -316,15 +318,16 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mPassedInitialSlop && mInteractionHandler != null) {
- dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement);
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocity = isNavBarOnRight() ? velocityX
: isNavBarOnLeft() ? -velocityX
: mVelocityTracker.getYVelocity(mActivePointerId);
+
+ dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement, velocityX);
+
mInteractionHandler.onGestureEnded(velocity, velocityX);
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index d118e99..86a8081 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -70,6 +70,7 @@
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -624,23 +625,37 @@
}
@WorkerThread
- public void dispatchMotionEventToRecentsView(MotionEvent event) {
+ @SuppressWarnings("WrongThread")
+ public void dispatchMotionEventToRecentsView(MotionEvent event, @Nullable Float velocityX) {
if (mRecentsView == null) {
return;
}
- // Pass the motion events to RecentsView to allow scrolling during swipe up.
- if (mDispatchedDownEvent) {
- mRecentsView.dispatchTouchEvent(event);
+ 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.
mDispatchedDownEvent = true;
MotionEvent downEvent = MotionEvent.obtain(event);
downEvent.setAction(MotionEvent.ACTION_DOWN);
int flags = downEvent.getEdgeFlags();
downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
- mRecentsView.dispatchTouchEvent(downEvent);
+ mRecentsView.simulateTouchEvent(downEvent, velocityX);
downEvent.recycle();
}
+
+ mRecentsView.simulateTouchEvent(event, velocityX);
}
@WorkerThread
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 258e922..7969eec 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -135,6 +135,10 @@
mIsPaused = mHasEverBeenPaused = false;
}
+ public boolean isPaused() {
+ return mIsPaused;
+ }
+
public interface OnMotionPauseListener {
void onMotionPauseChanged(boolean isPaused);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 840d2bd..923da7b 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -159,6 +159,8 @@
private final ViewPool<TaskView> mTaskViewPool;
+ @Nullable Float mSimulatedVelocityX = null;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -1610,4 +1612,18 @@
}
}
}
+
+ public void simulateTouchEvent(MotionEvent event, @Nullable Float velocityX) {
+ mSimulatedVelocityX = velocityX;
+ dispatchTouchEvent(event);
+ mSimulatedVelocityX = null;
+ }
+
+ @Override
+ protected int computeXVelocity() {
+ if (mSimulatedVelocityX != null) {
+ return mSimulatedVelocityX.intValue();
+ }
+ return super.computeXVelocity();
+ }
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a6b3a19..8f9e7c8 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1134,9 +1134,7 @@
final int activePointerId = mActivePointerId;
final int pointerIndex = ev.findPointerIndex(activePointerId);
final float x = ev.getX(pointerIndex);
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+ int velocityX = computeXVelocity();
final int deltaX = (int) (x - mDownMotionX);
final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth();
boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
@@ -1240,6 +1238,12 @@
return true;
}
+ protected int computeXVelocity() {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ return (int) velocityTracker.getXVelocity(mActivePointerId);
+ }
+
protected boolean shouldFlingForVelocity(int velocityX) {
return Math.abs(velocityX) > mFlingThresholdVelocity;
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 62f59e4..cf070c5 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -80,6 +80,9 @@
private OnAnimationEndDispatcher mEndListener;
private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
+ // We need this variable to ensure the end listener is called immediately, otherwise we run into
+ // issues where the callback interferes with the states of the swipe detector.
+ private boolean mSkipToEnd = false;
protected AnimatorPlaybackController(AnimatorSet anim, long duration,
Runnable onCancelRunnable) {
@@ -232,7 +235,11 @@
}
private void dispatchOnStartRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
+ ? nonNullList(((SpringObjectAnimator) animator).getSuperListeners())
+ : nonNullList(animator.getListeners());
+
+ for (AnimatorListener l : listeners) {
l.onAnimationStart(animator);
}
@@ -280,6 +287,17 @@
return mOnCancelRunnable;
}
+ public void skipToEnd() {
+ mSkipToEnd = true;
+ for (SpringAnimation spring : mSprings) {
+ if (spring.canSkipToEnd()) {
+ spring.skipToEnd();
+ }
+ }
+ mAnimationPlayer.end();
+ mSkipToEnd = false;
+ }
+
public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
private final ValueAnimator[] mChildAnimations;
@@ -343,19 +361,34 @@
*/
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
+ boolean mAnimatorDone = false;
+ boolean mSpringsDone = false;
+ boolean mDispatched = false;
+
@Override
public void onAnimationStart(Animator animation) {
mCancelled = false;
+ mDispatched = false;
}
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mSprings.isEmpty()) {
+ mSpringsDone = mAnimatorDone = true;
+ }
+ if (isAnySpringRunning()) {
+ mAnimatorDone = true;
+ } else {
+ mSpringsDone = true;
+ }
+
// We wait for the spring (if any) to finish running before completing the end callback.
- if (mSprings.isEmpty() || !isAnySpringRunning()) {
+ if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
dispatchOnEndRecursively(mAnim);
if (mEndAction != null) {
mEndAction.run();
}
+ mDispatched = true;
}
}
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 4ece909..e4aec10 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -73,7 +73,9 @@
mListeners = new ArrayList<>();
setFloatValues(values);
- mObjectAnimator.addListener(new AnimatorListenerAdapter() {
+ // We use this listener and track mListeners so that we can sync the animator and spring
+ // listeners.
+ super.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mAnimatorEnded = false;
@@ -94,7 +96,7 @@
for (AnimatorListener l : mListeners) {
l.onAnimationCancel(animation);
}
- mSpring.animateToFinalPosition(mObject.getProgress());
+ mSpring.cancel();
}
});
@@ -145,6 +147,10 @@
mListeners.add(listener);
}
+ public ArrayList<AnimatorListener> getSuperListeners() {
+ return super.getListeners();
+ }
+
@Override
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
@@ -167,8 +173,8 @@
@Override
public void cancel() {
- mSpring.animateToFinalPosition(mObject.getProgress());
mObjectAnimator.cancel();
+ mSpring.cancel();
}
@Override
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 6ad69d7..fa4ebaf 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -17,13 +17,16 @@
package com.android.launcher3.config;
import static androidx.core.util.Preconditions.checkNotNull;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
+
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.Utilities;
import java.util.ArrayList;
@@ -95,8 +98,9 @@
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
- public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag(
- "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches");
+ public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
+ = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
+ "Stable task list across fast task switches");
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");
@@ -249,11 +253,17 @@
@Override
void updateStorage(Context context, boolean value) {
+ if (contentResolver == null) {
+ return;
+ }
Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
}
@Override
boolean getFromStorage(Context context, boolean defaultValue) {
+ if (contentResolver == null) {
+ return defaultValue;
+ }
return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index bb14328..0e2ed6c 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -46,7 +46,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -298,7 +297,7 @@
* When going between normal and overview states, see if we passed the overview threshold and
* play the appropriate atomic animation if so.
*/
- private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
+ protected void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
float progress) {
if (!goingBetweenNormalAndOverview(fromState, toState)) {
return;
@@ -435,7 +434,11 @@
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
anim.start();
- mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
+ settleAtomicAnimation(endProgress, anim.getDuration());
+ }
+
+ protected void settleAtomicAnimation(float endProgress, long duration) {
+ mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, duration);
maybeAutoPlayAtomicComponentsAnim();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3ffd30c..d39a38e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import android.graphics.Point;
+import android.view.MotionEvent;
import android.widget.TextView;
import androidx.test.uiautomator.By;
@@ -40,9 +41,10 @@
*/
public AppIconMenu openMenu() {
final Point iconCenter = mObject.getVisibleCenter();
- mLauncher.longTap(iconCenter.x, iconCenter.y);
+ mLauncher.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
"deep_shortcuts_container");
+ mLauncher.sendPointer(MotionEvent.ACTION_UP, iconCenter);
return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 49bd73a..444f3bd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,10 +21,13 @@
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.graphics.Point;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
@@ -403,11 +406,6 @@
return mDevice;
}
- void longTap(int x, int y) {
- mDevice.drag(x, y, x, y, 0);
- }
-
-
void swipe(int startX, int startY, int endX, int endY) {
executeAndWaitForEvent(
() -> mDevice.swipe(startX, startY, endX, endY, 60),
@@ -419,4 +417,11 @@
void waitForIdle() {
mDevice.waitForIdle();
}
+
+ void sendPointer(int action, Point point) {
+ final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+ mInstrumentation.sendPointerSync(event);
+ event.recycle();
+ }
}
\ No newline at end of file