Merge "Fixes on updates on the PredictionRowView" into ub-launcher3-master
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 9069698..b3025ff 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 20ee547..6395473 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -85,4 +85,9 @@
public float getHoseatAlpha(Launcher launcher) {
return launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 1;
}
+
+ @Override
+ public LauncherState getHistoryForState(LauncherState previousState) {
+ return previousState == OVERVIEW ? OVERVIEW : NORMAL;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
new file mode 100644
index 0000000..c1b26d4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -0,0 +1,95 @@
+/*
+ * 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.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SwipeDetector.VERTICAL;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+
+import android.graphics.Rect;
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.VerticalSwipeController;
+import com.android.quickstep.RecentsView;
+
+/**
+ * Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW.
+ */
+public class EdgeSwipeController extends VerticalSwipeController {
+
+ private final Rect mTempRect = new Rect();
+
+ public EdgeSwipeController(Launcher l) {
+ super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout()
+ ? HORIZONTAL : VERTICAL);
+ }
+
+ @Override
+ protected boolean shouldInterceptTouch(MotionEvent ev) {
+ return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ }
+
+ @Override
+ protected int getSwipeDirection(MotionEvent ev) {
+ return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
+ }
+
+ @Override
+ protected boolean isTransitionFlipped() {
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ Rect insets = mLauncher.getDragLayer().getInsets();
+ return insets.left > insets.right;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
+ // TODO: Log something
+ }
+
+ @Override
+ protected void initSprings() {
+ mSpringHandlers = new SpringAnimationHandler[0];
+ }
+
+ @Override
+ protected float getShiftRange() {
+ RecentsView.getPageRect(mLauncher, mTempRect);
+ DragLayer dl = mLauncher.getDragLayer();
+ Rect insets = dl.getInsets();
+
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ if (insets.left > insets.right) {
+ return insets.left + mTempRect.left;
+ } else {
+ return dl.getWidth() - mTempRect.right + insets.right;
+ }
+ } else {
+ return dl.getHeight() - mTempRect.bottom + insets.bottom;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 989803a..2e5e75e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import android.content.Context;
import android.graphics.Rect;
import android.view.View;
@@ -74,15 +75,7 @@
@Override
public float getVerticalProgress(Launcher launcher) {
- DeviceProfile grid = launcher.getDeviceProfile();
- if (!grid.isVerticalBarLayout()) {
- return 1f;
- }
-
- float total = grid.heightPx;
- float searchHeight = total - grid.availableHeightPx +
- launcher.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
- return 1 - (searchHeight / total);
+ return getVerticalProgress(launcher.getDeviceProfile(), launcher);
}
@Override
@@ -125,4 +118,15 @@
return new float[] {scale, translationX, translationY};
}
+
+ public static float getVerticalProgress(DeviceProfile grid, Context context) {
+ if (!grid.isVerticalBarLayout()) {
+ return 1f;
+ }
+
+ float total = grid.heightPx;
+ float searchHeight = total - grid.availableHeightPx +
+ context.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
+ return 1 - (searchHeight / total);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index d657e4e..1465822 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.uioverrides;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -31,9 +34,6 @@
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsView;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
public class RecentsViewStateController implements StateHandler {
private final Launcher mLauncher;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index f59f0de..2481c32 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -42,6 +43,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -90,6 +92,8 @@
private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
+ private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5;
+ private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6;
private final Launcher mLauncher;
private final SwipeDetector mDetector;
@@ -120,18 +124,25 @@
}
private boolean canInterceptTouch(MotionEvent ev) {
- if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
- // Don't listen for the swipe gesture if we are already in some other state.
- return false;
- }
- if (mAnimatingToOverview) {
- return false;
- }
if (mCurrentAnimation != null) {
// If we are already animating from a previous state, we can intercept.
return true;
}
- if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ if (mLauncher.isInState(NORMAL)) {
+ if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 &&
+ !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ // On normal swipes ignore edge swipes
+ return false;
+ }
+ } else if (mLauncher.isInState(ALL_APPS)) {
+ if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ return false;
+ }
+ } else {
+ // Don't listen for the swipe gesture if we are already in some other state.
+ return false;
+ }
+ if (mAnimatingToOverview) {
return false;
}
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
@@ -237,6 +248,10 @@
mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected);
mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
+ if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+ mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS);
+ }
+
mOverviewProgressRange = new FloatRange();
mOverviewProgressRange.start = mLauncher.isInState(NORMAL)
? MIN_PROGRESS_TO_OVERVIEW
@@ -247,6 +262,12 @@
// Build current animation
mFromState = mLauncher.getStateManager().getState();
mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
+
+ if (mToState == NORMAL && mLauncher.getStateManager().getLastState() == OVERVIEW) {
+ mToState = OVERVIEW;
+ mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_BACK_TARGET);
+ }
+
mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
@@ -258,10 +279,8 @@
onRecentsPlanLoaded(recentsModel.getLastLoadPlan());
} else {
mDragPauseDetector.addDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+ recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
}
- // Reload again so that we get the latest list
- // TODO: Use callback instead of polling everytime
- recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
} else {
mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
}
@@ -324,7 +343,7 @@
if (fling) {
logAction = Touch.FLING;
- targetState = velocity < 0 ? ALL_APPS : NORMAL;
+ targetState = velocity < 0 ? ALL_APPS : mLauncher.getStateManager().getLastState();
// snap to top or bottom using the release velocity
} else {
logAction = Touch.SWIPE;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 05bd171..bd443aa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -37,9 +37,16 @@
public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
public static TouchController[] createTouchControllers(Launcher launcher) {
- return new TouchController[] {
- new TwoStepSwipeController(launcher),
- new OverviewSwipeUpController(launcher)};
+ if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+ return new TouchController[]{
+ new EdgeSwipeController(launcher),
+ new TwoStepSwipeController(launcher),
+ new OverviewSwipeUpController(launcher)};
+ } else {
+ return new TouchController[]{
+ new TwoStepSwipeController(launcher),
+ new OverviewSwipeUpController(launcher)};
+ }
}
public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
index ed60b84..4816e2a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
@@ -77,6 +77,7 @@
mWorkspaceClickTarget.setOnClickListener(this);
mWidgetsButton.setOnClickListener(this);
+ setOnClickListener(this);
}
@Override
@@ -168,7 +169,7 @@
@Override
public void onClick(View view) {
- if (view == mWorkspaceClickTarget) {
+ if (view == mWorkspaceClickTarget || view == this) {
mLauncher.getStateManager().goToState(NORMAL);
} else if (view == mWidgetsButton) {
WidgetsFullSheet.show(mLauncher, true);
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index 3ca1865..ce528e3 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -33,14 +33,15 @@
import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.TraceHelper;
@@ -66,6 +67,7 @@
private static final long RECENTS_VIEW_VISIBILITY_DURATION = 150;
private static final long MAX_SWIPE_DURATION = 200;
private static final long MIN_SWIPE_DURATION = 80;
+ private static final int QUICK_SWITCH_SNAP_DURATION = 120;
// Ideal velocity for a smooth transition
private static final float PIXEL_PER_MS = 2f;
@@ -97,6 +99,7 @@
private SnapshotDragView mDragView;
private RecentsView mRecentsView;
private RecentsViewStateController mStateController;
+ private QuickScrubController mQuickScrubController;
private Hotseat mHotseat;
private AllAppsScrim mAllAppsScrim;
private RecentsTaskLoadPlan mLoadPlan;
@@ -104,14 +107,27 @@
private boolean mLauncherReady;
private boolean mTouchEndHandled;
private float mCurrentDisplacement;
+ private @TouchInteractionService.InteractionType int mInteractionType;
+ private boolean mStartedQuickScrubFromHome;
private Bitmap mTaskSnapshot;
- NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context) {
+ NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context,
+ @TouchInteractionService.InteractionType int interactionType) {
mRunningTaskId = runningTaskInfo.id;
mContext = context;
+ mInteractionType = interactionType;
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ // TODO: If in multi window mode, dp = dp.getMultiWindowProfile()
+ dp = dp.copy(mContext);
+ // TODO: Use different insets for multi-window mode
+ dp.updateInsets(mStableInsets);
+ RecentsView.getPageRect(dp, mContext, mTargetRect);
+ mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right,
+ dp.heightPx - mStableInsets.top - mStableInsets.bottom);
+
// Build the state callback
mStateCallback = new MultiStateCallback();
mStateCallback.addCallback(STATE_LAUNCHER_READY, this::onLauncherReady);
@@ -181,11 +197,24 @@
mLauncher.getDragLayer().addView(mDragView);
mDragView.setPivotX(0);
mDragView.setPivotY(0);
- mRecentsView = mLauncher.getOverviewPanel();
+ mRecentsView = launcher.getOverviewPanel();
mStateController = mRecentsView.getStateController();
mHotseat = mLauncher.getHotseat();
mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
+ boolean interactionIsQuick
+ = mInteractionType == TouchInteractionService.INTERACTION_QUICK_SCRUB
+ || mInteractionType == TouchInteractionService.INTERACTION_QUICK_SWITCH;
+ mStartedQuickScrubFromHome = alreadyOnHome && interactionIsQuick;
+ if (interactionIsQuick) {
+ mQuickScrubController = mRecentsView.getQuickScrubController();
+ mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome);
+ animateToProgress(1f, MAX_SWIPE_DURATION);
+ if (mStartedQuickScrubFromHome) {
+ mDragView.setVisibility(View.INVISIBLE);
+ }
+ }
+
// Optimization
if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
// All-apps search box is visible in vertical bar layout.
@@ -214,17 +243,10 @@
@UiThread
private void updateFinalShift() {
- if (!mLauncherReady) {
+ if (!mLauncherReady || mStartedQuickScrubFromHome) {
return;
}
- if (mTargetRect.isEmpty()) {
- RecentsView.getPageRect(mLauncher, mTargetRect);
- DragLayer dl = mLauncher.getDragLayer();
- mSourceRect.set(0, 0, dl.getWidth() - mStableInsets.left - mStableInsets.right,
- dl.getHeight() - mStableInsets.top - mStableInsets.bottom);
- }
-
float shift = mCurrentShift.value * mActivityMultiplier.value;
int hotseatSize = getHotseatSize();
@@ -258,7 +280,7 @@
private void setTaskPlanToUi() {
mRecentsView.update(mLoadPlan);
- mRecentsView.initToPage(mRecentsView.getFirstTaskIndex());
+ mRecentsView.initToPage(mStartedQuickScrubFromHome ? 0 : mRecentsView.getFirstTaskIndex());
ObjectAnimator anim = mStateController.animateVisibility(true /* isVisible */)
.setDuration(RECENTS_VIEW_VISIBILITY_DURATION);
anim.addListener(new AnimationSuccessListener() {
@@ -298,7 +320,12 @@
}
}
- ObjectAnimator anim = mCurrentShift.animateToValue(endShift).setDuration(duration);
+ animateToProgress(endShift, duration);
+ }
+
+ /** Animates to the given progress, where 0 is the current app and 1 is overview. */
+ private void animateToProgress(float progress, long duration) {
+ ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
anim.setInterpolator(Interpolators.SCROLL);
anim.addListener(new AnimationSuccessListener() {
@Override
@@ -342,5 +369,32 @@
if (currentRecentsPage instanceof TaskView) {
((TaskView) currentRecentsPage).animateIconToScale(1f);
}
+ if (mInteractionType == TouchInteractionService.INTERACTION_QUICK_SWITCH) {
+ for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
+ TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
+ if (taskView.getTask().key.id != mRunningTaskId) {
+ mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION);
+ taskView.postDelayed(() -> {taskView.launchTask(true);},
+ QUICK_SWITCH_SNAP_DURATION);
+ break;
+ }
+ }
+ } else if (mInteractionType == TouchInteractionService.INTERACTION_QUICK_SCRUB) {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.snapToPageForCurrentQuickScrubSection();
+ }
+ }
+ }
+
+ public void onQuickScrubEnd() {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.onQuickScrubEnd();
+ }
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.onQuickScrubProgress(progress);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
new file mode 100644
index 0000000..f4c2055
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -0,0 +1,111 @@
+/*
+ * 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.view.HapticFeedbackConstants;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.OnAlarmListener;
+
+/**
+ * Responds to quick scrub callbacks to page through and launch recent tasks.
+ *
+ * The behavior is to evenly divide the progress into sections, each of which scrolls one page.
+ * The first and last section set an alarm to auto-advance backwards or forwards, respectively.
+ */
+public class QuickScrubController implements OnAlarmListener {
+
+ private static final int NUM_QUICK_SCRUB_SECTIONS = 5;
+ private static final long AUTO_ADVANCE_DELAY = 500;
+ private static final int QUICKSCRUB_END_SNAP_DURATION_PER_PAGE = 60;
+
+ private Launcher mLauncher;
+ private Alarm mAutoAdvanceAlarm;
+ private RecentsView mRecentsView;
+
+ private int mQuickScrubSection;
+ private int mStartPage;
+
+ public QuickScrubController(Launcher launcher) {
+ mLauncher = launcher;
+ mAutoAdvanceAlarm = new Alarm();
+ mAutoAdvanceAlarm.setOnAlarmListener(this);
+ }
+
+ public void onQuickScrubStart(boolean startingFromHome) {
+ mRecentsView = mLauncher.getOverviewPanel();
+ mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex();
+ mQuickScrubSection = 0;
+ }
+
+ public void onQuickScrubEnd() {
+ mAutoAdvanceAlarm.cancelAlarm();
+ if (mRecentsView != null) {
+ int page = mRecentsView.getNextPage();
+ // Settle on the page then launch it.
+ int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
+ * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
+ mRecentsView.snapToPage(page, snapDuration);
+ mRecentsView.postDelayed(() -> {
+ if (page < mRecentsView.getFirstTaskIndex()) {
+ mRecentsView.getPageAt(page).performClick();
+ } else {
+ ((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
+ }
+ }, snapDuration);
+ }
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ int quickScrubSection = Math.round(progress * NUM_QUICK_SCRUB_SECTIONS);
+ if (quickScrubSection != mQuickScrubSection) {
+ int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
+ goToPageWithHaptic(pageToGoTo);
+ if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) {
+ mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ } else {
+ mAutoAdvanceAlarm.cancelAlarm();
+ }
+ mQuickScrubSection = quickScrubSection;
+ }
+ }
+
+ public void snapToPageForCurrentQuickScrubSection() {
+ goToPageWithHaptic(mRecentsView.getCurrentPage() + mQuickScrubSection);
+ }
+
+ private void goToPageWithHaptic(int pageToGoTo) {
+ if (pageToGoTo != mRecentsView.getNextPage()) {
+ mRecentsView.snapToPage(pageToGoTo);
+ mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+ }
+
+ @Override
+ public void onAlarm(Alarm alarm) {
+ int currPage = mRecentsView.getNextPage();
+ if (mQuickScrubSection == NUM_QUICK_SCRUB_SECTIONS
+ && currPage < mRecentsView.getPageCount() - 1) {
+ goToPageWithHaptic(currPage + 1);
+ } else if (mQuickScrubSection == 0 && currPage > mStartPage) {
+ goToPageWithHaptic(currPage - 1);
+ }
+ mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index e7e794f..09003b2 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Rect;
@@ -29,6 +31,7 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.uioverrides.OverviewState;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
@@ -41,8 +44,6 @@
import java.util.ArrayList;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
/**
* A list of recent tasks.
*/
@@ -52,6 +53,8 @@
public static final int SCROLL_TYPE_TASK = 1;
public static final int SCROLL_TYPE_WORKSPACE = 2;
+ private final Launcher mLauncher;
+ private QuickScrubController mQuickScrubController;
private final ScrollState mScrollState = new ScrollState();
private boolean mOverviewStateEnabled;
private boolean mTaskStackListenerRegistered;
@@ -88,6 +91,9 @@
setClipChildren(true);
setupLayoutTransition();
+ mLauncher = Launcher.getLauncher(context);
+ mQuickScrubController = new QuickScrubController(mLauncher);
+
mScrollState.isRtl = mIsRtl;
}
@@ -106,7 +112,8 @@
protected void onFinishInflate() {
super.onFinishInflate();
- Rect padding = getPadding(Launcher.getLauncher(getContext()));
+ Rect padding =
+ getPadding(Launcher.getLauncher(getContext()).getDeviceProfile(), getContext());
setPadding(padding.left, padding.top, padding.right, padding.bottom);
mFirstTaskIndex = getPageCount();
@@ -213,8 +220,7 @@
}
}
- private static Rect getPadding(Launcher launcher) {
- DeviceProfile profile = launcher.getDeviceProfile();
+ private static Rect getPadding(DeviceProfile profile, Context context) {
Rect stableInsets = new Rect();
WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
Rect padding = new Rect(profile.workspacePadding);
@@ -228,7 +234,7 @@
float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
- stableInsets.top
- - profile.heightPx * (1 - OVERVIEW.getVerticalProgress(launcher));
+ - profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context));
float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
overviewHeight = taskHeight * scaledRatio;
@@ -247,15 +253,18 @@
}
public static void getPageRect(Launcher launcher, Rect outRect) {
- DragLayer dl = launcher.getDragLayer();
- Rect targetPadding = getPadding(launcher);
- Rect insets = dl.getInsets();
+ getPageRect(launcher.getDeviceProfile(), launcher, outRect);
+ }
+
+ public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
+ Rect targetPadding = getPadding(grid, context);
+ Rect insets = grid.getInsets();
outRect.set(
targetPadding.left + insets.left,
targetPadding.top + insets.top,
- dl.getWidth() - targetPadding.right - insets.right,
- dl.getHeight() - targetPadding.bottom - insets.bottom);
- outRect.top += launcher.getResources()
+ grid.widthPx - targetPadding.right - insets.right,
+ grid.heightPx - targetPadding.bottom - insets.bottom);
+ outRect.top += context.getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
}
@@ -294,10 +303,14 @@
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
removeView(taskView);
if (getChildCount() == mFirstTaskIndex) {
- Launcher.getLauncher(getContext()).getStateManager().goToState(LauncherState.NORMAL);
+ mLauncher.getStateManager().goToState(LauncherState.NORMAL);
}
}
+ public QuickScrubController getQuickScrubController() {
+ return mQuickScrubController;
+ }
+
public interface PageCallbacks {
/**
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4321791..56f0e89 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -37,6 +37,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
+import android.support.annotation.IntDef;
import android.util.Log;
import android.view.Choreographer;
import android.view.Display;
@@ -56,6 +57,8 @@
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
/**
@@ -64,8 +67,21 @@
@TargetApi(Build.VERSION_CODES.O)
public class TouchInteractionService extends Service {
+ public static final int EDGE_NAV_BAR = 1 << 8;
+
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;
+
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
@@ -77,6 +93,30 @@
public void onBind(ISystemUiProxy iSystemUiProxy) throws RemoteException {
mISystemUiProxy = iSystemUiProxy;
}
+
+ @Override
+ public void onQuickSwitch() {
+ startTouchTracking(INTERACTION_QUICK_SWITCH);
+ }
+
+ @Override
+ public void onQuickScrubStart() {
+ startTouchTracking(INTERACTION_QUICK_SCRUB);
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubEnd();
+ }
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubProgress(progress);
+ }
+ }
};
private final Consumer<MotionEvent> mOtherActivityTouchConsumer
@@ -214,7 +254,7 @@
if (mInteractionHandler == null) {
if (Math.abs(displacement) >= mTouchSlop) {
mStartDisplacement = Math.signum(displacement) * mTouchSlop;
- startTouchTracking();
+ startTouchTracking(INTERACTION_NORMAL);
}
} else {
// Move
@@ -242,10 +282,10 @@
return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
}
- private void startTouchTracking() {
+ private void startTouchTracking(@InteractionType int interactionType) {
// Create the shared handler
final NavBarSwipeInteractionHandler handler =
- new NavBarSwipeInteractionHandler(mRunningTask, this);
+ new NavBarSwipeInteractionHandler(mRunningTask, this, interactionType);
TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
@@ -381,9 +421,12 @@
}
private void sendEvent(MotionEvent ev) {
+ int flags = ev.getEdgeFlags();
+ ev.setEdgeFlags(flags | EDGE_NAV_BAR);
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
mTarget.dispatchTouchEvent(ev);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+ ev.setEdgeFlags(flags);
}
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 75e2f70..7a6a244 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -214,6 +214,11 @@
mBadgeRenderer = new BadgeRenderer(iconSizePx);
}
+ public DeviceProfile copy(Context context) {
+ Point size = new Point(availableWidthPx, availableHeightPx);
+ return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape);
+ }
+
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
// the system decor is always excluded.
@@ -376,6 +381,10 @@
updateWorkspacePadding();
}
+ public Rect getInsets() {
+ return mInsets;
+ }
+
public void updateAppsViewNumCols() {
allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
}
@@ -509,6 +518,5 @@
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
return c.createConfigurationContext(context);
-
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4b2e432..2a5f453 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1608,8 +1608,9 @@
if (topView != null) {
topView.onBackPressed();
} else if (!isInState(NORMAL)) {
+ LauncherState lastState = mStateManager.getLastState();
ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType);
- mStateManager.goToState(NORMAL);
+ mStateManager.goToState(lastState);
} else {
// Back button is a no-op here, but give at least some feedback for the button press
mWorkspace.showOutlinesTemporarily();
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4f65d19..670f579 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -170,6 +170,11 @@
};
}
+ public LauncherState getHistoryForState(LauncherState previousState) {
+ // No history is supported
+ return NORMAL;
+ }
+
protected static void dispatchWindowStateChanged(Launcher launcher) {
launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index bc4ac8b..3a660dc 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -66,7 +66,7 @@
* - Go back with back key TODO: make this not go to workspace
* - From all apps
* - From workspace
- * - Enter and exit car mode (becuase it causes an extra configuration changed)
+ * - Enter and exit car mode (becase it causes an extra configuration changed)
* - From all apps
* - From the center workspace
* - From another workspace
@@ -82,6 +82,9 @@
private StateHandler[] mStateHandlers;
private LauncherState mState = NORMAL;
+ private LauncherState mLastStableState = NORMAL;
+ private LauncherState mCurrentStableState = NORMAL;
+
private StateListener mStateListener;
public LauncherStateManager(Launcher l) {
@@ -146,7 +149,6 @@
private void goToState(LauncherState state, boolean animated, long delay,
Runnable onCompleteRunnable) {
if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) {
-
// Run any queued runnable
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
@@ -262,11 +264,21 @@
}
private void onStateTransitionEnd(LauncherState state) {
+ // Only change the stable states after the transitions have finished
+ if (state != mCurrentStableState) {
+ mLastStableState = state.getHistoryForState(mCurrentStableState);
+ mCurrentStableState = state;
+ }
+
mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
mLauncher.finishAutoCancelActionMode();
}
+ public LauncherState getLastState() {
+ return mLastStableState;
+ }
+
/**
* Cancels the current animation.
*/
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 18797a4..2d0e630 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -57,4 +57,6 @@
public static final boolean ALL_APPS_TABS_ENABLED = true;
// When enabled prediction row is rendered as it's own custom view
public static final boolean ALL_APPS_PREDICTION_ROW_VIEW = true;
+
+ public static final boolean ENABLE_TWO_SWIPE_TARGETS = true;
}
diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
index 7b1632c..5d47cd2 100644
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SwipeDetector.Direction;
import java.util.ArrayList;
@@ -56,6 +57,7 @@
protected final Launcher mLauncher;
private final SwipeDetector mDetector;
private final LauncherState mBaseState;
+ private final LauncherState mTargetState;
private boolean mNoIntercept;
@@ -66,12 +68,18 @@
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- private SpringAnimationHandler[] mSpringHandlers;
+ protected SpringAnimationHandler[] mSpringHandlers;
public VerticalSwipeController(Launcher l, LauncherState baseState) {
+ this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
+ }
+
+ public VerticalSwipeController(
+ Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) {
mLauncher = l;
- mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
+ mDetector = new SwipeDetector(l, this, dir);
mBaseState = baseState;
+ mTargetState = targetState;
}
private boolean canInterceptTouch(MotionEvent ev) {
@@ -96,7 +104,7 @@
}
}
- private void initSprings() {
+ protected void initSprings() {
AllAppsContainerView appsView = mLauncher.getAppsView();
SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
@@ -178,12 +186,13 @@
long maxAccuracy = (long) (2 * range);
// Build current animation
- mToState = mLauncher.isInState(ALL_APPS) ? mBaseState : ALL_APPS;
+ mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState;
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, maxAccuracy);
mCurrentAnimation.getTarget().addListener(this);
mStartProgress = 0;
- mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
+ mProgressMultiplier =
+ (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range;
mCurrentAnimation.dispatchOnStart();
} else {
mCurrentAnimation.pause();
@@ -195,7 +204,11 @@
}
}
- private float getShiftRange() {
+ protected boolean isTransitionFlipped() {
+ return false;
+ }
+
+ protected float getShiftRange() {
return mLauncher.getAllAppsController().getShiftRange();
}
@@ -213,27 +226,25 @@
final float progress = mCurrentAnimation.getProgressFraction();
if (fling) {
- if (velocity < 0) {
- targetState = ALL_APPS;
- animationDuration = SwipeDetector.calculateDuration(velocity,
- mToState == ALL_APPS ? (1 - progress) : progress);
+ if (velocity < 0 ^ isTransitionFlipped()) {
+ targetState = mTargetState;
} else {
targetState = mBaseState;
- animationDuration = SwipeDetector.calculateDuration(velocity,
- mToState == ALL_APPS ? progress : (1 - progress));
}
+ animationDuration = SwipeDetector.calculateDuration(velocity,
+ mToState == targetState ? (1 - progress) : progress);
// snap to top or bottom using the release velocity
} else {
if (progress > SUCCESS_TRANSITION_PROGRESS) {
targetState = mToState;
animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
} else {
- targetState = mToState == ALL_APPS ? mBaseState : ALL_APPS;
+ targetState = mToState == mTargetState ? mBaseState : mTargetState;
animationDuration = SwipeDetector.calculateDuration(velocity, progress);
}
}
- if (fling && targetState == ALL_APPS) {
+ if (fling && targetState == mTargetState) {
for (SpringAnimationHandler h : mSpringHandlers) {
// The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);