From a406f727ad966d73dd616eb8747cee8606c738e6 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 19 Dec 2019 11:50:55 -0800 Subject: [PATCH] Add vertical layout support for Overview in portrait WM is making changes which allows apps to maintain their orientation independent of the orientation of the foreground app. This allows recents to always start in portrait even when the app currently running is in landscape. This means we have to give the illusion of a landscape oriented overview when user swipes up in gesterual nav when launcher is started in portrait configuration. PagedOrientationHandler abstracts all coordinate specific logic from Paged/RecentsView primarily, but also all other dynamic calculations throughout launcher. PagedViewOrientationState is the single point of exposure to other classes that depend on those changes. The goal is to also minimize holding state to allow for default implementations of PagedOrientationHandler for all the 3p/Fallback classes. PagedViewOrientationState also holds other data around rotation that isn't specifically tied to view logic. The fake landscape overview can be toggled with: adb shell settings put global forced_rotation [0/1] Fixes: 146176182 Change-Id: I65d8d4e9f92b93931cbe0053ccaf0cda8d2ffd6c --- .../LauncherAppTransitionManagerImpl.java | 12 +- .../FlingAndHoldTouchController.java | 4 +- .../NavBarToHomeTouchController.java | 2 +- ...ButtonNavbarToOverviewTouchController.java | 4 +- .../NoButtonQuickSwitchTouchController.java | 5 +- .../QuickSwitchTouchController.java | 4 +- .../TaskViewTouchController.java | 28 +- .../AppToOverviewAnimationProvider.java | 3 +- .../android/quickstep/BaseSwipeUpHandler.java | 60 ++- .../quickstep/FallbackActivityInterface.java | 3 +- .../quickstep/FallbackSwipeHandler.java | 3 +- .../quickstep/LauncherActivityInterface.java | 19 +- .../quickstep/LauncherSwipeHandler.java | 2 + .../android/quickstep/RecentsActivity.java | 3 +- .../com/android/quickstep/TaskViewUtils.java | 1 + .../quickstep/TouchInteractionService.java | 19 + .../fallback/FallbackRecentsView.java | 5 + .../OtherActivityInputConsumer.java | 3 +- .../util/AppWindowAnimationHelper.java | 60 ++- .../quickstep/views/ClearAllButton.java | 42 +- .../quickstep/views/LauncherRecentsView.java | 28 +- .../android/quickstep/views/RecentsView.java | 179 +++++--- .../quickstep/views/TaskThumbnailView.java | 1 + .../com/android/quickstep/views/TaskView.java | 86 +++- quickstep/res/layout/task.xml | 1 - .../launcher3/BaseQuickstepLauncher.java | 9 +- .../quickstep/BaseActivityInterface.java | 1 + .../OrientationTouchTransformer.java | 47 ++- .../quickstep/RecentsAnimationController.java | 2 +- .../RecentsAnimationDeviceState.java | 27 +- .../com/android/quickstep/SystemUiProxy.java | 27 +- .../android/quickstep/util/LayoutUtils.java | 3 + src/com/android/launcher3/PagedView.java | 388 ++++++++++-------- src/com/android/launcher3/Utilities.java | 6 + src/com/android/launcher3/Workspace.java | 6 +- .../launcher3/allapps/AllAppsPagedView.java | 2 +- .../launcher3/folder/FolderPagedView.java | 2 +- .../model/PagedViewOrientedState.java | 100 +++++ .../notification/NotificationMainView.java | 2 +- .../launcher3/states/RotationHelper.java | 137 +++++++ .../AbstractStateChangeTouchController.java | 2 +- .../launcher3/touch/BaseSwipeDetector.java | 2 +- .../touch/LandscapePagedViewHandler.java | 263 ++++++++++++ .../touch/PagedOrientationHandler.java | 120 ++++++ .../touch/PortraitPagedViewHandler.java | 257 ++++++++++++ .../touch/SeascapePagedViewHandler.java | 71 ++++ .../touch/SingleAxisSwipeDetector.java | 12 +- .../launcher3/views/AbstractSlideInView.java | 3 +- .../touch/SingleAxisSwipeDetectorTest.java | 14 +- 49 files changed, 1682 insertions(+), 398 deletions(-) create mode 100644 src/com/android/launcher3/model/PagedViewOrientedState.java create mode 100644 src/com/android/launcher3/touch/LandscapePagedViewHandler.java create mode 100644 src/com/android/launcher3/touch/PagedOrientationHandler.java create mode 100644 src/com/android/launcher3/touch/PortraitPagedViewHandler.java create mode 100644 src/com/android/launcher3/touch/SeascapePagedViewHandler.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 51ee216139..772820719b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; @@ -39,6 +38,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; +import android.util.FloatProperty; import android.view.View; import androidx.annotation.NonNull; @@ -50,6 +50,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationBuilder; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -88,7 +89,8 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets); - AppWindowAnimationHelper helper = new AppWindowAnimationHelper(mLauncher); + AppWindowAnimationHelper helper = + new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher); anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION)); @@ -197,7 +199,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), RecentsView.CONTENT_ALPHA, values); case INDEX_RECENTS_TRANSLATE_X_ANIM: - return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X) + PagedOrientationHandler orientationHandler = + ((RecentsView)mLauncher.getOverviewPanel()).getPagedViewOrientedState() + .getOrientationHandler(); + FloatProperty translate = orientationHandler.getPrimaryViewTranslate(); + return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), translate) .setDampingRatio(0.8f) .setStiffness(250) .setValues(values) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index 519939e8d0..9cbe11acc3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -80,10 +80,10 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { mMotionPauseDetector.clear(); - super.onDragStart(start); + super.onDragStart(start, startDisplacement); if (handlingOverviewAnim()) { mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index ad4a343cb4..19a2bae467 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -126,7 +126,7 @@ public class NavBarToHomeTouchController implements TouchController, } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { initCurrentAnimation(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 34fc3e492c..ab634a4b12 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -94,8 +94,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo } @Override - public void onDragStart(boolean start) { - super.onDragStart(start); + public void onDragStart(boolean start, float startDisplacement) { + super.onDragStart(start, startDisplacement); mReachedOverview = false; } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 799f1ad182..715529e355 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -116,12 +116,13 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mLauncher = launcher; mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this); mShelfPeekAnim = mLauncher.getShelfPeekAnim(); + mRecentsView = mLauncher.getOverviewPanel(); mXRange = mLauncher.getDeviceProfile().widthPx / 2f; - mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile()); + mYRange = LayoutUtils.getShelfTrackingDistance( + mLauncher, mLauncher.getDeviceProfile()); mMotionPauseDetector = new MotionPauseDetector(mLauncher); mMotionPauseMinDisplacement = mLauncher.getResources().getDimension( R.dimen.motion_pause_detector_min_displacement_from_app); - mRecentsView = mLauncher.getOverviewPanel(); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index 912be983f7..d5b221db06 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -92,8 +92,8 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll } @Override - public void onDragStart(boolean start) { - super.onDragStart(start); + public void onDragStart(boolean start, float startDisplacement) { + super.onDragStart(start, startDisplacement); mStartContainerType = LauncherLogProto.ContainerType.NAVBAR; mTaskToLaunch = mLauncher.getOverviewPanel().getTaskViewAt(0); ActivityManagerWrapper.getInstance() diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index f79ad25571..e0532ac4cf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -36,6 +36,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.touch.BaseSwipeDetector; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.FlingBlockCheck; @@ -77,7 +78,9 @@ public abstract class TaskViewTouchController public TaskViewTouchController(T activity) { mActivity = activity; mRecentsView = activity.getOverviewPanel(); - mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL); + SingleAxisSwipeDetector.Direction dir = + mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection(); + mDetector = new SingleAxisSwipeDetector(activity, this, dir); } private boolean canInterceptTouch() { @@ -190,15 +193,18 @@ public abstract class TaskViewTouchController mPendingAnimation = null; } + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); mCurrentAnimationIsGoingUp = goingUp; BaseDragLayer dl = mActivity.getDragLayer(); - long maxDuration = (long) (2 * dl.getHeight()); - + final int secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl); + long maxDuration = (long) (2 * secondaryLayerDimension); + int verticalFactor = -orientationHandler.getTaskDismissDirectionFactor(); + int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged); if (goingUp) { mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, true /* animateTaskView */, true /* removeTask */, maxDuration); - mEndDisplacement = -mTaskBeingDragged.getHeight(); + mEndDisplacement = -secondaryTaskDimension; } else { mPendingAnimation = mRecentsView.createTaskLaunchAnimation( mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN); @@ -207,6 +213,7 @@ public abstract class TaskViewTouchController dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); mEndDisplacement = dl.getHeight() - mTempCords[1]; } + mEndDisplacement *= verticalFactor; if (mCurrentAnimation != null) { mCurrentAnimation.setOnCancelRunnable(null); @@ -220,9 +227,10 @@ public abstract class TaskViewTouchController } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); if (mCurrentAnimation == null) { - reInitAnimationController(mDetector.wasInitialTouchPositive()); + reInitAnimationController(orientationHandler.isGoingUp(startDisplacement)); mDisplacementShift = 0; } else { mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier; @@ -233,9 +241,10 @@ public abstract class TaskViewTouchController @Override public boolean onDrag(float displacement) { + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); float totalDisplacement = displacement + mDisplacementShift; - boolean isGoingUp = - totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0; + boolean isGoingUp = totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : + orientationHandler.isGoingUp(totalDisplacement); if (isGoingUp != mCurrentAnimationIsGoingUp) { reInitAnimationController(isGoingUp); mFlingBlockCheck.blockFling(); @@ -262,11 +271,12 @@ public abstract class TaskViewTouchController if (blockedFling) { fling = false; } + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); float progress = mCurrentAnimation.getProgressFraction(); float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress(); if (fling) { logAction = Touch.FLING; - boolean goingUp = velocity < 0; + boolean goingUp = orientationHandler.isGoingUp(velocity); goingToEnd = goingUp == mCurrentAnimationIsGoingUp; } else { logAction = Touch.SWIPE; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 59b117f2e8..375f16013f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -125,7 +125,8 @@ final class AppToOverviewAnimationProvider imple return anim; } - final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(mActivity); + final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper( + mRecentsView.getPagedViewOrientedState(), mActivity); // At this point, the activity is already started and laid-out. Get the home-bounds // relative to the screen using the rootView of the activity. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 3601af2f32..8957b0d3e9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -45,7 +45,9 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.model.PagedViewOrientedState; +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory; @@ -94,7 +96,7 @@ public abstract class BaseSwipeUpHandler mActivityInterface; protected final InputConsumerController mInputConsumer; - protected final AppWindowAnimationHelper mAppWindowAnimationHelper; + protected AppWindowAnimationHelper mAppWindowAnimationHelper; protected final TransformParams mTransformParams = new TransformParams(); // Shift in the range of [0, 1]. @@ -123,6 +125,8 @@ public abstract class BaseSwipeUpHandler getRecentsViewDispatcher(RotationMode rotationMode) { - return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null; + public Consumer getRecentsViewDispatcher() { + return mRecentsView != null ? mRecentsView.getEventDispatcher() : null; } @UiThread @@ -326,10 +328,19 @@ public abstract class BaseSwipeUpHandler @Override protected boolean onActivityInit(Boolean alreadyOnHome) { + super.onActivityInit(alreadyOnHome); final T activity = mActivityInterface.getCreatedActivity(); if (mActivity == activity) { return true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java index 3f5179f76c..94b0051a51 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -183,7 +183,8 @@ public final class RecentsActivity extends BaseRecentsActivity { RemoteAnimationTargetCompat[] wallpaperTargets) { AnimatorSet target = new AnimatorSet(); boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING); - AppWindowAnimationHelper helper = new AppWindowAnimationHelper(this); + AppWindowAnimationHelper helper = new AppWindowAnimationHelper( + mFallbackRecentsView.getPagedViewOrientedState(), this); target.play(getRecentsWindowAnimator(taskView, !activityClosing, appTargets, wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index 8d735915d9..aedb756101 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -130,6 +130,7 @@ public final class TaskViewUtils { .setLauncherOnTop(true); final RecentsView recentsView = v.getRecentsView(); + params.setPagedOrientedState(recentsView.getPagedViewOrientedState()); final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); appAnimator.addUpdateListener(new MultiValueUpdateListener() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index e1b5df0bca..7617ffe5bc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -56,6 +56,8 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.config.FeatureFlags; @@ -80,6 +82,7 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; +import com.android.quickstep.views.RecentsView; import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.IOverviewProxy; @@ -537,6 +540,22 @@ public class TouchInteractionService extends Service implements PluginListener { } super.applyLoadPlan(tasks); } + + @Override + protected boolean supportsVerticalLandscape() { + return false; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 2f8682f9a0..bd9f330082 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -176,8 +176,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Proxy events to recents view if (mPassedWindowMoveSlop && mInteractionHandler != null && !mRecentsViewDispatcher.hasConsumer()) { - mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher( - mNavBarPosition.getRotationMode())); + mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher()); } int edgeFlags = ev.getEdgeFlags(); ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java index 5a9c2fe594..6923ca25ff 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java @@ -15,12 +15,6 @@ */ package com.android.quickstep.util; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; -import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; - import android.annotation.TargetApi; import android.content.Context; import android.graphics.Matrix; @@ -37,6 +31,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.SystemUiProxy; @@ -50,6 +45,12 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. import com.android.systemui.shared.system.TransactionCompat; import com.android.systemui.shared.system.WindowManagerWrapper; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; +import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; + /** * Utility class to handle window clip animation */ @@ -82,6 +83,7 @@ public class AppWindowAnimationHelper { private final Rect mTmpRect = new Rect(); private final RectF mTmpRectF = new RectF(); private final RectF mCurrentRectWithInsets = new RectF(); + private PagedViewOrientedState mOrientedState; // Corner radius of windows, in pixels private final float mWindowCornerRadius; // Corner radius of windows when they're in overview mode. @@ -100,20 +102,24 @@ public class AppWindowAnimationHelper { private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a; private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1; - public AppWindowAnimationHelper(Context context) { + public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) { + mOrientedState = orientedState; mWindowCornerRadius = getWindowCornerRadius(context.getResources()); mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources()); mTaskCornerRadius = TaskCornerRadius.get(context); mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows; } + public AppWindowAnimationHelper(Context context) { + this(null, context); + } + private void updateSourceStack(RemoteAnimationTargetCompat target) { mSourceInsets.set(target.contentInsets); mSourceStackBounds.set(target.sourceContainerBounds); // TODO: Should sourceContainerBounds already have this offset? mSourceStackBounds.offsetTo(target.position.x, target.position.y); - } public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) { @@ -138,8 +144,9 @@ public class AppWindowAnimationHelper { // from the source rect. The difference between the target rect (scaled to the // source rect) is the amount to clip on each edge. RectF scaledTargetRect = new RectF(mTargetRect); - Utilities.scaleRectFAboutCenter(scaledTargetRect, - mSourceRect.width() / mTargetRect.width()); + float scale = getSrcToTargetScale(); + Utilities.scaleRectFAboutCenter(scaledTargetRect, scale); + scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top); mSourceWindowClipInsets.set( Math.max(scaledTargetRect.left, 0), @@ -149,6 +156,15 @@ public class AppWindowAnimationHelper { mSourceRect.set(scaledTargetRect); } + private float getSrcToTargetScale() { + if (mOrientedState == null) { + return mSourceRect.width() / mTargetRect.width(); + } else { + return mOrientedState.getOrientationHandler() + .getCurrentAppAnimationScale(mSourceRect, mTargetRect); + } + } + public void prepareAnimation(DeviceProfile dp, boolean isOpening) { mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING; mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode; @@ -221,7 +237,6 @@ public class AppWindowAnimationHelper { layer = Integer.MAX_VALUE; } } - // Since radius is in Surface space, but we draw the rounded corners in screen space, we // have to undo the scale. surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer, @@ -237,11 +252,16 @@ public class AppWindowAnimationHelper { mTmpRectF.set(mTargetRect); Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale); mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF)); - mCurrentRect.offset(params.mOffsetX, 0); + if (mOrientedState == null || mOrientedState.areMultipleLayoutOrientationsDisabled()) { + mCurrentRect.offset(params.mOffset, 0); + } else { + int displayRotation = mOrientedState.getDisplayRotation(); + mOrientedState.getOrientationHandler().offsetTaskRect(mCurrentRect, + params.mOffset, displayRotation); + } } updateClipRect(params); - return mCurrentRect; } @@ -340,7 +360,7 @@ public class AppWindowAnimationHelper { * @return The source rect's scale and translation relative to the target rect. */ public LauncherState.ScaleAndTranslation getScaleAndTranslation() { - float scale = mSourceRect.width() / mTargetRect.width(); + float scale = getSrcToTargetScale(); float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY(); return new LauncherState.ScaleAndTranslation(scale, 0, translationY); } @@ -390,7 +410,7 @@ public class AppWindowAnimationHelper { public static class TransformParams { private float mProgress; - private float mOffsetX; + private float mOffset; private float mOffsetScale; private @Nullable RectF mCurrentRect; private float mTargetAlpha; @@ -401,7 +421,7 @@ public class AppWindowAnimationHelper { public TransformParams() { mProgress = 0; - mOffsetX = 0; + mOffset = 0; mOffsetScale = 1; mCurrentRect = null; mTargetAlpha = 1; @@ -453,8 +473,8 @@ public class AppWindowAnimationHelper { * the default), then offset the current rect by this amount after computing the rect based * on {@link #mProgress}. */ - public TransformParams setOffsetX(float offsetX) { - mOffsetX = offsetX; + public TransformParams setOffset(float offset) { + mOffset = offset; return this; } @@ -504,8 +524,8 @@ public class AppWindowAnimationHelper { return mProgress; } - public float getOffsetX() { - return mOffsetX; + public float getOffset() { + return mOffset; } public float getOffsetScale() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java index 9db0c09ae2..d0819c187b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java @@ -21,7 +21,7 @@ import android.util.AttributeSet; import android.util.Property; import android.widget.Button; -import com.android.launcher3.Utilities; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.views.RecentsView.PageCallbacks; import com.android.quickstep.views.RecentsView.ScrollState; @@ -44,21 +44,26 @@ public class ClearAllButton extends Button implements PageCallbacks { private float mContentAlpha = 1; private float mVisibilityAlpha = 1; - private final boolean mIsRtl; + private boolean mIsRtl; private int mScrollOffset; + private RecentsView mParent; public ClearAllButton(Context context, AttributeSet attrs) { super(context, attrs); - mIsRtl = Utilities.isRtl(context.getResources()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); + mScrollOffset = mIsRtl ? mParent.getPaddingRight() / 2 : - mParent.getPaddingLeft() / 2; + } - RecentsView parent = (RecentsView) getParent(); - mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2; + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mParent = (RecentsView) getParent(); + mIsRtl = !mParent.getPagedOrientationHandler().getRecentsRtlSetting(getResources()); } @Override @@ -73,6 +78,21 @@ public class ClearAllButton extends Button implements PageCallbacks { } } + public void onLayoutChanged() { + if (mParent == null) { + return; + } + setRotation(mParent.getPagedOrientationHandler().getDegreesRotated()); + } + + public void setRtl(boolean rtl) { + if (mIsRtl == rtl) { + return; + } + mIsRtl = rtl; + invalidate(); + } + public void setVisibilityAlpha(float alpha) { if (mVisibilityAlpha != alpha) { mVisibilityAlpha = alpha; @@ -82,14 +102,16 @@ public class ClearAllButton extends Button implements PageCallbacks { @Override public void onPageScroll(ScrollState scrollState) { - float width = getWidth(); - if (width == 0) { + PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler(); + float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight()); + if (orientationSize == 0) { return; } - float shift = Math.min(scrollState.scrollFromEdge, width); - setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift)); - mScrollAlpha = 1 - shift / width; + float shift = Math.min(scrollState.scrollFromEdge, orientationSize); + float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift); + orientationHandler.setPrimaryAndResetSecondaryTranslate(this, translation); + mScrollAlpha = 1 - shift / orientationSize; updateAlpha(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 3e106aa463..b2d182b331 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -42,6 +42,7 @@ import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; +import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; @@ -180,19 +181,21 @@ public class LauncherRecentsView extends RecentsView implements StateL * @return The translationX to apply to this view so that the first task is just offscreen. */ public float getOffscreenTranslationX(float recentsScale) { - float offscreenX = NORMAL.getOverviewScaleAndTranslation(mActivity).translationX; + LauncherState.ScaleAndTranslation overviewScaleAndTranslation = + NORMAL.getOverviewScaleAndTranslation(mActivity); + float offscreen = mOrientationHandler.getTranslationValue(overviewScaleAndTranslation); // Offset since scale pushes tasks outwards. getTaskSize(sTempRect); - int taskWidth = sTempRect.width(); - offscreenX += taskWidth * (recentsScale - 1) / 2; + int taskSize = mOrientationHandler.getPrimarySize(sTempRect); + offscreen += taskSize * (recentsScale - 1) / 2; if (mRunningTaskTileHidden) { // The first task is hidden, so offset by its width. - offscreenX -= (taskWidth + getPageSpacing()) * recentsScale; + offscreen -= (taskSize + getPageSpacing()) * recentsScale; } if (isRtl()) { - offscreenX = -offscreenX; + offscreen = -offscreen; } - return offscreenX; + return offscreen; } @Override @@ -276,6 +279,11 @@ public class LauncherRecentsView extends RecentsView implements StateL return mTransformParams; } + @Override + protected boolean supportsVerticalLandscape() { + return PagedView.sFlagForcedRotation; + } + @Override public void reset() { super.reset(); @@ -339,19 +347,19 @@ public class LauncherRecentsView extends RecentsView implements StateL } @Override - protected int computeMinScrollX() { + protected int computeMinScroll() { if (canComputeScrollX() && !mIsRtl) { return computeScrollX(); } - return super.computeMinScrollX(); + return super.computeMinScroll(); } @Override - protected int computeMaxScrollX() { + protected int computeMaxScroll() { if (canComputeScrollX() && mIsRtl) { return computeScrollX(); } - return super.computeMaxScrollX(); + return super.computeMaxScroll(); } private boolean canComputeScrollX() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 27ef93c614..50a66299b4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -22,7 +22,6 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; @@ -52,7 +51,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; @@ -66,11 +64,13 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Property; import android.util.SparseBooleanArray; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -90,12 +90,15 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; 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; @@ -162,6 +165,8 @@ public abstract class RecentsView extends PagedView impl } }; + private final OrientationEventListener mOrientationListener; + private int mPreviousRotation; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; protected AppWindowAnimationHelper mAppWindowAnimationHelper; @@ -340,7 +345,8 @@ public abstract class RecentsView extends PagedView impl mActivity = (T) BaseActivity.fromContext(context); mModel = RecentsModel.INSTANCE.get(context); mIdp = InvariantDeviceProfile.INSTANCE.get(context); - mTempAppWindowAnimationHelper = new AppWindowAnimationHelper(context); + mTempAppWindowAnimationHelper = + new AppWindowAnimationHelper(getPagedViewOrientedState(), context); mClearAllButton = (ClearAllButton) LayoutInflater.from(context) .inflate(R.layout.overview_clear_all_button, this, false); @@ -348,7 +354,7 @@ public abstract class RecentsView extends PagedView impl mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */, 10 /* initial size */); - mIsRtl = !Utilities.isRtl(getResources()); + mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); mTaskTopMargin = getResources() .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); @@ -368,9 +374,21 @@ public abstract class RecentsView extends PagedView impl .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding); setWillNotDraw(false); updateEmptyMessage(); + disableMultipleLayoutRotations(!supportsVerticalLandscape()); // Initialize quickstep specific cache params here, as this is constructed only once mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5); + mOrientationListener = new OrientationEventListener(getContext()) { + @Override + public void onOrientationChanged(int i) { + int rotation = RotationHelper.getRotationFromDegrees(i, mPreviousRotation); + if (mPreviousRotation != rotation) { + animateRecentsRotationInPlace(rotation); + mPreviousRotation = rotation; + } + } + }; + } public OverScroller getScroller() { @@ -496,6 +514,13 @@ public abstract class RecentsView extends PagedView impl } public void setOverviewStateEnabled(boolean enabled) { + if (supportsVerticalLandscape() && mOrientationListener.canDetectOrientation()) { + if (enabled) { + mOrientationListener.enable(); + } else { + mOrientationListener.disable(); + } + } mOverviewStateEnabled = enabled; updateTaskStackListenerState(); if (!enabled) { @@ -623,7 +648,7 @@ public abstract class RecentsView extends PagedView impl final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex; final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); - taskView.bind(task); + taskView.bind(task, mLayoutRotation); } if (mNextPage == INVALID_PAGE) { @@ -755,19 +780,21 @@ public abstract class RecentsView extends PagedView impl if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { return; } - int scrollX = getScrollX(); - final int halfPageWidth = getNormalChildWidth() / 2; - final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth; - final int halfScreenWidth = getMeasuredWidth() / 2; + CurveProperties curveProperties = mOrientationHandler + .getCurveProperties(this, mInsets); + int scroll = curveProperties.scroll; + final int halfPageSize = curveProperties.halfPageSize; + final int screenCenter = curveProperties.screenCenter; + final int halfScreenSize = curveProperties.halfScreenSize; final int pageSpacing = mPageSpacing; - mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX); + mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll); final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth; + float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing; + float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; mScrollState.linearInterpolation = Math.min(1, Math.abs(distanceFromScreenCenter) / distanceToReachEdge); ((PageCallbacks) page).onPageScroll(mScrollState); @@ -915,6 +942,47 @@ public abstract class RecentsView extends PagedView impl setSwipeDownShouldLaunchApp(true); } + private void animateRecentsRotationInPlace(int newRotation) { + if (!supportsVerticalLandscape()) { + return; + } + + AnimatorSet pa = setRecentsChangedOrientation(true); + pa.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + updateLayoutRotation(newRotation); + ((DragLayer)mActivity.getDragLayer()).recreateControllers(); + rotateAllChildTasks(); + setRecentsChangedOrientation(false).start(); + } + }); + pa.start(); + } + + public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) { + getRunningTaskIndex(); + int runningIndex = getCurrentPage(); + AnimatorSet as = new AnimatorSet(); + for (int i = 0; i < getTaskViewCount(); i++) { + if (runningIndex == i) { + continue; + } + View taskView = getTaskViewAt(i); + as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1)); + } + return as; + } + + abstract protected boolean supportsVerticalLandscape(); + + private void rotateAllChildTasks() { + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView taskView = getTaskViewAt(i); + taskView.setOverviewRotation(mLayoutRotation); + } + } + /** * Called when a gesture from an app has finished. */ @@ -950,7 +1018,7 @@ public abstract class RecentsView extends PagedView impl new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0, false, true, false, false, new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false); - taskView.bind(mTmpRunningTask); + taskView.bind(mTmpRunningTask, mLayoutRotation); } boolean runningTaskTileHidden = mRunningTaskTileHidden; @@ -1126,15 +1194,18 @@ public abstract class RecentsView extends PagedView impl private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) { addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); + FloatProperty secondaryViewTranslate = + mOrientationHandler.getSecondaryViewTranslate(); + int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); + int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor(); if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) { - addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y, - MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, - SpringForce.STIFFNESS_MEDIUM, - 0, -taskView.getHeight()), - duration, LINEAR, anim); + addAnim(new SpringObjectAnimator<>(taskView, secondaryViewTranslate, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, 0, verticalFactor * secondaryTaskDimension), + duration, LINEAR, anim); } else { - addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), - duration, LINEAR, anim); + addAnim(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate, + verticalFactor * secondaryTaskDimension), duration, LINEAR, anim); } } @@ -1165,11 +1236,9 @@ public abstract class RecentsView extends PagedView impl } int[] oldScroll = new int[count]; - getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); - int[] newScroll = new int[count]; + getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView); - int taskCount = getTaskViewCount(); int scrollDiffPerPage = 0; if (count > 1) { @@ -1212,8 +1281,9 @@ public abstract class RecentsView extends PagedView impl SpringForce.STIFFNESS_MEDIUM, 0, scrollDiff), duration, ACCEL, anim); } else { - addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration, - ACCEL, anim); + Property translationProperty = mOrientationHandler.getPrimaryViewTranslate(); + addAnim(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff), + duration, ACCEL, anim); } needsCurveUpdates = true; @@ -1429,6 +1499,18 @@ public abstract class RecentsView extends PagedView impl } } + @Override + public void setLayoutRotation(int touchRotation, int displayRotation) { + if (!sFlagForcedRotation) { + return; + } + + super.setLayoutRotation(touchRotation, displayRotation); + mClearAllButton.onLayoutChanged(); + mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); + setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); + } + @Override public void onViewAdded(View child) { super.onViewAdded(child); @@ -1649,7 +1731,8 @@ public abstract class RecentsView extends PagedView impl } }); - AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(mActivity); + AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper( + getPagedViewOrientedState(), mActivity); appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this); appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */); AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper); @@ -1806,7 +1889,7 @@ public abstract class RecentsView extends PagedView impl } @Override - protected int computeMinScrollX() { + protected int computeMinScroll() { if (getTaskViewCount() > 0) { if (mDisallowScrollToClearAll) { // We aren't showing the clear all button, @@ -1821,11 +1904,11 @@ public abstract class RecentsView extends PagedView impl } return getScrollForPage(mTaskViewStartIndex); } - return super.computeMinScrollX(); + return super.computeMinScroll(); } @Override - protected int computeMaxScrollX() { + protected int computeMaxScroll() { if (getTaskViewCount() > 0) { if (mDisallowScrollToClearAll) { // We aren't showing the clear all button, @@ -1840,7 +1923,7 @@ public abstract class RecentsView extends PagedView impl } return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); } - return super.computeMaxScrollX(); + return super.computeMaxScroll(); } public ClearAllButton getClearAllButton() { @@ -1855,31 +1938,25 @@ public abstract class RecentsView extends PagedView impl return 0; } int startScroll = getScrollForPage(getRunningTaskIndex()); - int offsetX = startScroll - getScrollX(); - offsetX *= getScaleX(); + int offsetX = startScroll - mOrientationHandler.getPrimaryScroll(this); + offsetX *= mOrientationHandler.getPrimaryScale(this); return offsetX; } - public Consumer getEventDispatcher(RotationMode rotationMode) { - if (rotationMode.isTransposed) { - Matrix transform = new Matrix(); - transform.setRotate(-rotationMode.surfaceRotation); - - if (getWidth() > 0 && getHeight() > 0) { - float scale = ((float) getWidth()) / getHeight(); - transform.postScale(scale, 1 / scale); - } - - Matrix inverse = new Matrix(); - transform.invert(inverse); - return e -> { - e.transform(transform); - super.onTouchEvent(e); - e.transform(inverse); - }; - } else { + public Consumer getEventDispatcher() { + int degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation); + if (degreesRotated == 0) { return super::onTouchEvent; } + + // At this point the event coordinates have already been transformed, so we need to + // undo that transformation since PagedView also accommodates for the transformation via + // PagedOrientationHandler + return e -> { + RotationHelper.transformEvent(-degreesRotated, e, true); + super.onTouchEvent(e); + RotationHelper.transformEvent(-degreesRotated, e, false); + }; } public AppWindowAnimationHelper getClipAnimationHelper() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 8ed139269a..178ff32b4a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -36,6 +36,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Log; import android.util.Property; import android.view.Surface; import android.view.View; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 79b9a9d833..9150cc76d0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -16,15 +16,6 @@ package com.android.quickstep.views; -import static android.widget.Toast.LENGTH_SHORT; - -import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -41,7 +32,7 @@ import android.os.Handler; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; -import android.view.Gravity; +import android.view.Surface; import android.view.View; import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; @@ -58,8 +49,10 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.touch.PagedOrientationHandler; 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; @@ -83,6 +76,20 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import static android.view.Gravity.BOTTOM; +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.CENTER_VERTICAL; +import static android.view.Gravity.END; +import static android.view.Gravity.START; +import static android.view.Gravity.TOP; +import static android.widget.Toast.LENGTH_SHORT; +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; + /** * A task in the Recents view. */ @@ -186,6 +193,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mFooterVerticalOffset = 0; private float mFooterAlpha = 1; private int mStackHeight; + private boolean mHideActionsView; + private PagedOrientationHandler mOrientationHandler; public TaskView(Context context) { this(context, null); @@ -244,7 +253,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { if (mActionsView != null) { TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.overview_actions_height), - Gravity.BOTTOM); + BOTTOM); addView(mActionsView, params); mActionsView.setAlpha(0); } @@ -266,10 +275,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { /** * Updates this task view to the given {@param task}. */ - public void bind(Task task) { + public void bind(Task task, int recentsRotation) { cancelPendingLoadTasks(); mTask = task; mSnapshotView.bind(task); + setOverviewRotation(recentsRotation); } public Task getTask() { @@ -439,6 +449,45 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } + void setOverviewRotation(int iconRotation) { + PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler(); + boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources()); + LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams(); + snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext()); + int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); + LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); + int rotation = RotationHelper.getDegreesFromRotation(iconRotation); + mHideActionsView = true; + switch (iconRotation) { + case Surface.ROTATION_90: + iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; + iconParams.rightMargin = -thumbnailPadding; + iconParams.leftMargin = iconParams.topMargin = iconParams.bottomMargin = 0; + break; + case Surface.ROTATION_180: + iconParams.gravity = BOTTOM | CENTER_HORIZONTAL; + iconParams.bottomMargin = -thumbnailPadding; + iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0; + break; + case Surface.ROTATION_270: + iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; + iconParams.leftMargin = -thumbnailPadding; + iconParams.rightMargin = iconParams.topMargin = iconParams.bottomMargin = 0; + break; + case Surface.ROTATION_0: + default: + iconParams.gravity = TOP | CENTER_HORIZONTAL; + iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = + iconParams.bottomMargin = 0; + mHideActionsView = false; + break; + } + mSnapshotView.setLayoutParams(snapshotParams); + mIconView.setLayoutParams(iconParams); + mIconView.setRotation(rotation); + updateActionsViewVisibility(!mHideActionsView); + } + private void setIconAndDimTransitionProgress(float progress, boolean invert) { if (invert) { progress = 1 - progress; @@ -601,8 +650,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { addView(view, indexToAdd); LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.gravity = - Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL; layoutParams.bottomMargin = ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin; view.setAlpha(mFooterAlpha); @@ -855,9 +903,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mFullscreenProgress = progress; boolean isFullscreen = mFullscreenProgress > 0; mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); - if (mActionsView != null) { - mActionsView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); - } + updateActionsViewVisibility(progress < 1 && !mHideActionsView); setClipChildren(!isFullscreen); setClipToPadding(!isFullscreen); @@ -891,6 +937,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { invalidateOutline(); } + private void updateActionsViewVisibility(boolean isVisible) { + if (mActionsView != null) { + mActionsView.setVisibility(isVisible ? VISIBLE : GONE); + } + } + public boolean isRunningTask() { if (getRecentsView() == null) { return false; diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 60cfa0cbaa..f9bb2f2b14 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -31,7 +31,6 @@ android:id="@+id/icon" android:layout_width="@dimen/task_thumbnail_icon_size" android:layout_height="@dimen/task_thumbnail_icon_size" - android:layout_gravity="top|center_horizontal" android:focusable="false" android:importantForAccessibility="no"/> \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 07d2381aba..fa0e840b20 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -41,6 +41,7 @@ import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; import com.android.launcher3.proxy.StartActivityParams; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.uioverrides.BackButtonAlphaHandler; import com.android.launcher3.uioverrides.RecentsViewStateController; import com.android.launcher3.util.UiThreadHelper; @@ -51,6 +52,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.views.RecentsView; import java.util.stream.Stream; @@ -210,9 +212,10 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() { if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) { - float offscreenTranslationX = getDeviceProfile().widthPx - - getOverviewPanel().getPaddingStart(); - return new ScaleAndTranslation(1f, offscreenTranslationX, 0f); + PagedOrientationHandler layoutVertical = + ((RecentsView)getOverviewPanel()).getPagedViewOrientedState().getOrientationHandler(); + return layoutVertical.getScaleAndTranslation(getDeviceProfile(), + getOverviewPanel()); } return super.getOverviewScaleAndTranslationForNormalState(); } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index fd55e077fd..58d6ee7998 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -31,6 +31,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.ShelfPeekAnim; import com.android.systemui.shared.recents.model.ThumbnailData; diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 3dae510d4a..f78ac57cd7 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -33,8 +33,11 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.DefaultDisplay; +import java.io.PrintWriter; + /** * Maintains state for supporting nav bars and tracking their gestures in multiple orientations. * See {@link OrientationRectF#applyTransform(MotionEvent, boolean)} for transformation of @@ -149,8 +152,7 @@ class OrientationTouchTransformer { Point size = display.realSize; int rotation = display.rotation; OrientationRectF orientationRectF = - new OrientationRectF(0, 0, size.x, size.y, rotation, - size.y, size.x); + new OrientationRectF(0, 0, size.x, size.y, rotation); if (mMode == SysUINavigationMode.Mode.NO_BUTTON) { int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); orientationRectF.top = orientationRectF.bottom - touchHeight; @@ -206,6 +208,14 @@ class OrientationTouchTransformer { return false; } + int getCurrentActiveRotation() { + if (mLastRectTouched == null) { + return 0; + } else { + return mLastRectTouched.mRotation; + } + } + public void transform(MotionEvent event) { int eventAction = event.getActionMasked(); switch (eventAction) { @@ -249,6 +259,19 @@ class OrientationTouchTransformer { } } + public void dump(PrintWriter pw) { + pw.println("OrientationTouchTransformerState: "); + pw.println(" currentActiveRotation=" + getCurrentActiveRotation()); + pw.println(" lastTouchedRegion=" + mLastRectTouched); + pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions); + StringBuilder regions = new StringBuilder(" currentTouchableRotations="); + for(int i = 0; i < mSwipeTouchRegions.size(); i++) { + OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i)); + regions.append(rectF.mRotation).append(" "); + } + pw.println(regions.toString()); + } + private class OrientationRectF extends RectF { /** @@ -262,12 +285,11 @@ class OrientationTouchTransformer { private float mHeight; private float mWidth; - OrientationRectF(float left, float top, float right, float bottom, int rotation, - float height, float width) { + OrientationRectF(float left, float top, float right, float bottom, int rotation) { super(left, top, right, bottom); this.mRotation = rotation; - mHeight = height - maxDelta; - mWidth = width - maxDelta; + mHeight = bottom - maxDelta; + mWidth = right - maxDelta; } @Override @@ -280,7 +302,7 @@ class OrientationTouchTransformer { boolean applyTransform(MotionEvent event, boolean forceTransform) { MotionEvent tmp = MotionEvent.obtain(event); Matrix outMatrix = new Matrix(); - int delta = deltaRotation(mCurrentRotation, mRotation); + int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation); switch (delta) { case Surface.ROTATION_0: outMatrix.reset(); @@ -314,16 +336,5 @@ class OrientationTouchTransformer { } return false; } - - /** - * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. - * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... - * A value of 0 means no rotation has been applied - */ - private int deltaRotation(int oldRotation, int newRotation) { - int delta = newRotation - oldRotation; - if (delta < 0) delta += 4; - return delta; - } } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 21a4918b1b..8dd4aa4cab 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -39,7 +39,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; /** - * Wrapper around RecentsAnimationController to help with some synchronization + * Wrapper around RecentsAnimationControllerCompat to help with some synchronization */ public class RecentsAnimationController { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 259d1ddddb..d845650637 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,8 +17,6 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; -import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE; -import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; @@ -40,13 +38,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.RectF; import android.graphics.Region; import android.os.Process; import android.os.UserManager; -import android.graphics.Region; -import android.os.Process; import android.text.TextUtils; import android.util.Log; import android.view.MotionEvent; @@ -82,6 +76,7 @@ public class RecentsAnimationDeviceState implements private final SysUINavigationMode mSysUiNavMode; private final DefaultDisplay mDefaultDisplay; private final int mDisplayId; + private int mDisplayRotation; private final ArrayList mOnDestroyActions = new ArrayList<>(); @@ -234,6 +229,7 @@ public class RecentsAnimationDeviceState implements return; } + mDisplayRotation = info.rotation; mNavBarPosition = new NavBarPosition(mMode, info); updateGestureTouchRegions(); mOrientationTouchTransformer.createOrAddTouchRegion(info); @@ -499,6 +495,18 @@ public class RecentsAnimationDeviceState implements mOrientationTouchTransformer.transform(event); } + public void enableMultipleRegions(boolean enable) { + mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + } + + public int getCurrentActiveRotation() { + return mOrientationTouchTransformer.getCurrentActiveRotation(); + } + + public int getDisplayRotation() { + return mDisplayRotation; + } + public void dump(PrintWriter pw) { pw.println("DeviceState:"); pw.println(" canStartSystemGesture=" + canStartSystemGesture()); @@ -508,9 +516,8 @@ public class RecentsAnimationDeviceState implements pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); - } - - public void enableMultipleRegions(boolean enable) { - mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + pw.println(" currentActiveRotation=" + getCurrentActiveRotation()); + pw.println(" displayRotation=" + getDisplayRotation()); + mOrientationTouchTransformer.dump(pw); } } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 908747c6b5..0210a814cf 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -334,25 +334,14 @@ public class SystemUiProxy implements ISystemUiProxy { } } - @Override - public void setSplitScreenMinimized(boolean minimized) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setSplitScreenMinimized(minimized); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopScreenPinning", e); - } - } - } - - @Override public void onQuickSwitchToNewTask() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onQuickSwitchToNewTask(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onQuickstepStarted", e); - } - } + //TODO(b/150250451) add back in after big CL goes through +// if (mSystemUiProxy != null) { +// try { +// mSystemUiProxy.onQuickSwitchToNewTask(); +// } catch (RemoteException e) { +// Log.w(TAG, "Failed call onQuickstepStarted", e); +// } +// } } } diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index 1d8a79f683..ba99016561 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -33,6 +33,9 @@ import com.android.quickstep.SysUINavigationMode; import java.lang.annotation.Retention; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; +import static java.lang.annotation.RetentionPolicy.SOURCE; + public class LayoutUtils { private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1; diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 01893e92b1..ef6bd3d019 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -16,11 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; -import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; -import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; - import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; @@ -35,6 +30,7 @@ import android.util.Log; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -49,13 +45,25 @@ import android.widget.ScrollView; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.pageindicators.PageIndicator; +import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.touch.OverScroll; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.Thunk; import java.util.ArrayList; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; +import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO; + /** * An abstraction of the original Workspace which supports browsing through a * sequential list of "pages" @@ -64,6 +72,8 @@ public abstract class PagedView extends ViewGrou private static final String TAG = "PagedView"; private static final boolean DEBUG = false; + public static boolean sFlagForcedRotation = false; + public static final int INVALID_PAGE = -1; protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE; @@ -97,8 +107,8 @@ public abstract class PagedView extends ViewGrou @ViewDebug.ExportedProperty(category = "launcher") protected int mNextPage = INVALID_PAGE; - protected int mMinScrollX; - protected int mMaxScrollX; + protected int mMaxScroll; + protected int mMinScroll; protected OverScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; @@ -106,9 +116,12 @@ public abstract class PagedView extends ViewGrou private float mDownMotionX; private float mDownMotionY; - private float mLastMotionX; - private float mLastMotionXRemainder; - private float mTotalMotionX; + private float mDownMotionPrimary; + private float mLastMotion; + private float mLastMotionRemainder; + private float mTotalMotion; + protected PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler(); + protected final PagedViewOrientedState mOrientationState = new PagedViewOrientedState(); protected int[] mPageScrolls; private boolean mIsBeingDragged; @@ -123,11 +136,14 @@ public abstract class PagedView extends ViewGrou protected boolean mIsPageInTransition = false; - protected float mSpringOverScrollX; + protected float mSpringOverScroll; protected boolean mWasInOverscroll = false; - protected int mUnboundedScrollX; + protected int mUnboundedScroll; + + protected int mLayoutRotation = Surface.ROTATION_0; + protected int mDisplayRotation = Surface.ROTATION_0; // Page Indicator @Thunk int mPageIndicatorViewId; @@ -166,11 +182,12 @@ public abstract class PagedView extends ViewGrou * Initializes various states for this workspace. */ protected void init() { - mScroller = new OverScroller(getContext()); + Context context = getContext(); + mScroller = new OverScroller(context); setDefaultInterpolator(Interpolators.SCROLL); mCurrentPage = 0; - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); @@ -182,6 +199,8 @@ public abstract class PagedView extends ViewGrou if (Utilities.ATLEAST_OREO) { setDefaultFocusHighlightEnabled(false); } + + sFlagForcedRotation = Utilities.isForcedRotation(context); } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -236,12 +255,12 @@ public abstract class PagedView extends ViewGrou */ protected void updateCurrentPageScroll() { // If the current page is invalid, just reset the scroll position to zero - int newX = 0; + int newPosition = 0; if (0 <= mCurrentPage && mCurrentPage < getPageCount()) { - newX = getScrollForPage(mCurrentPage); + newPosition = getScrollForPage(mCurrentPage); } - scrollTo(newX, 0); - mScroller.startScroll(mScroller.getCurrPos(), newX - mScroller.getCurrPos()); + mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition); + mOrientationHandler.scrollerStartScroll(mScroller, newPosition); forceFinishScroller(true); } @@ -285,7 +304,7 @@ public abstract class PagedView extends ViewGrou int dir = !mIsRtl ? 1 : - 1; int currScroll = getScrollForPage(page); int prevScroll; - while (currScroll < mMinScrollX) { + while (currScroll < mMinScroll) { page += dir; prevScroll = currScroll; currScroll = getScrollForPage(page); @@ -294,7 +313,7 @@ public abstract class PagedView extends ViewGrou break; } } - while (currScroll > mMaxScrollX) { + while (currScroll > mMaxScroll) { page -= dir; prevScroll = currScroll; currScroll = getScrollForPage(page); @@ -378,45 +397,73 @@ public abstract class PagedView extends ViewGrou AccessibilityEvent.TYPE_VIEW_FOCUSED, null); } - protected int getUnboundedScrollX() { - return mUnboundedScrollX; + protected int getUnboundedScroll() { + return mUnboundedScroll; + } + + protected void updateLayoutRotation(int touchRotation) { + setLayoutRotation(touchRotation, mDisplayRotation); + } + + /** @param touchRotation Must be one of {@link android.view.Surface.ROTATION_0/90/180/270} */ + public void setLayoutRotation(int touchRotation, int displayRotation) { + if (mLayoutRotation == touchRotation && mDisplayRotation == displayRotation) { + return; + } + + mOrientationState.update(touchRotation, displayRotation); + mOrientationHandler = mOrientationState.getOrientationHandler(); + mLayoutRotation = touchRotation; + mDisplayRotation = displayRotation; + requestLayout(); + } + + public PagedViewOrientedState getPagedViewOrientedState() { + return mOrientationState; + } + + public PagedOrientationHandler getPagedOrientationHandler() { + return getPagedViewOrientedState().getOrientationHandler(); + } + + public void disableMultipleLayoutRotations(boolean disable) { + mOrientationState.disableMultipleOrientations(disable); + mOrientationHandler = mOrientationState.getOrientationHandler(); + requestLayout(); } @Override public void scrollBy(int x, int y) { - scrollTo(getUnboundedScrollX() + x, getScrollY() + y); + mOrientationHandler.delegateScrollBy(this, getUnboundedScroll(), x, y); } @Override public void scrollTo(int x, int y) { - mUnboundedScrollX = x; + int primaryScroll = mOrientationHandler.getPrimaryValue(x, y); + int secondaryScroll = mOrientationHandler.getSecondaryValue(x, y); + mUnboundedScroll = primaryScroll; - boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX); - boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX); - - if (!isXBeforeFirstPage && !isXAfterLastPage) { - mSpringOverScrollX = 0; + boolean isBeforeFirstPage = mIsRtl ? + (primaryScroll > mMaxScroll) : (primaryScroll < mMinScroll); + boolean isAfterLastPage = mIsRtl ? + (primaryScroll < mMinScroll) : (primaryScroll > mMaxScroll); + if (!isBeforeFirstPage && !isAfterLastPage) { + mSpringOverScroll = 0; } - if (isXBeforeFirstPage) { - super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y); + if (isBeforeFirstPage) { + mOrientationHandler.delegateScrollTo(this, + secondaryScroll, mIsRtl ? mMaxScroll : mMinScroll); if (mAllowOverScroll) { mWasInOverscroll = true; - if (mIsRtl) { - overScroll(x - mMaxScrollX); - } else { - overScroll(x - mMinScrollX); - } + overScroll(primaryScroll - (mIsRtl ? mMaxScroll : mMinScroll)); } - } else if (isXAfterLastPage) { - super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y); + } else if (isAfterLastPage) { + mOrientationHandler.delegateScrollTo(this, + secondaryScroll, mIsRtl ? mMinScroll : mMaxScroll); if (mAllowOverScroll) { mWasInOverscroll = true; - if (mIsRtl) { - overScroll(x - mMinScrollX); - } else { - overScroll(x - mMaxScrollX); - } + overScroll(primaryScroll - (mIsRtl ? mMinScroll : mMaxScroll)); } } else { if (mWasInOverscroll) { @@ -425,7 +472,13 @@ public abstract class PagedView extends ViewGrou } super.scrollTo(x, y); } + } + /** + * Helper for {@link PagedOrientationHandler} to be able to call parent's scrollTo method + */ + public void superScrollTo(int x, int y) { + super.scrollTo(x, y); } private void sendScrollAccessibilityEvent() { @@ -436,9 +489,7 @@ public abstract class PagedView extends ViewGrou ev.setScrollable(true); ev.setScrollX(getScrollX()); ev.setScrollY(getScrollY()); - ev.setMaxScrollX(mMaxScrollX); - ev.setMaxScrollY(0); - + mOrientationHandler.setMaxScroll(ev, mMaxScroll); sendAccessibilityEventUnchecked(ev); } } @@ -459,9 +510,10 @@ public abstract class PagedView extends ViewGrou protected boolean computeScrollHelper(boolean shouldInvalidate) { if (mScroller.computeScrollOffset()) { // Don't bother scrolling if the page does not need to be moved - if (getUnboundedScrollX() != mScroller.getCurrPos() - || getScrollX() != mScroller.getCurrPos()) { - scrollTo(mScroller.getCurrPos(), 0); + int currentScroll = mOrientationHandler.getPrimaryScroll(this); + if (mUnboundedScroll != mScroller.getCurrPos() + || currentScroll != mScroller.getCurrPos()) { + mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrPos()); } if (shouldInvalidate) { invalidate(); @@ -580,7 +632,8 @@ public abstract class PagedView extends ViewGrou if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); - if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) { + boolean isScrollChanged = getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC); + if (isScrollChanged) { pageScrollChanged = true; } @@ -621,7 +674,6 @@ public abstract class PagedView extends ViewGrou /** * Initializes {@code outPageScrolls} with scroll positions for view at that index. The length * of {@code outPageScrolls} should be same as the the childCount - * */ protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren, ComputePageScrollsLogic scrollLogic) { @@ -631,36 +683,30 @@ public abstract class PagedView extends ViewGrou final int endIndex = mIsRtl ? -1 : childCount; final int delta = mIsRtl ? -1 : 1; - final int verticalCenter = (getPaddingTop() + getMeasuredHeight() + mInsets.top - - mInsets.bottom - getPaddingBottom()) / 2; + final int pageCenter = mOrientationHandler.getCenterForPage(this, mInsets); - final int scrollOffsetLeft = mInsets.left + getPaddingLeft(); - final int scrollOffsetRight = getWidth() - getPaddingRight() - mInsets.right; + final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets); + final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets); boolean pageScrollChanged = false; - for (int i = startIndex, childLeft = scrollOffsetLeft; i != endIndex; i += delta) { + for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) { final View child = getPageAt(i); if (scrollLogic.shouldIncludeView(child)) { - final int childWidth = child.getMeasuredWidth(); - final int childRight = childLeft + childWidth; - - if (layoutChildren) { - final int childHeight = child.getMeasuredHeight(); - final int childTop = verticalCenter - childHeight / 2; - child.layout(childLeft, childTop, childRight, childTop + childHeight); - } + ChildBounds bounds = mOrientationHandler.getChildBounds(child, childStart, + pageCenter, layoutChildren); + final int primaryDimension = bounds.primaryDimension; + final int childPrimaryEnd = bounds.childPrimaryEnd; // In case the pages are of different width, align the page to left or right edge // based on the orientation. final int pageScroll = mIsRtl - ? (childLeft - scrollOffsetLeft) - : Math.max(0, childRight - scrollOffsetRight); + ? (childStart - scrollOffsetStart) + : Math.max(0, childPrimaryEnd - scrollOffsetEnd); if (outPageScrolls[i] != pageScroll) { pageScrollChanged = true; outPageScrolls[i] = pageScroll; } - - childLeft += childWidth + mPageSpacing + getChildGap(); + childStart += primaryDimension + mPageSpacing + getChildGap(); } } return pageScrollChanged; @@ -671,15 +717,15 @@ public abstract class PagedView extends ViewGrou } protected void updateMinAndMaxScrollX() { - mMinScrollX = computeMinScrollX(); - mMaxScrollX = computeMaxScrollX(); + mMinScroll = computeMinScroll(); + mMaxScroll = computeMaxScroll(); } - protected int computeMinScrollX() { + protected int computeMinScroll() { return 0; } - protected int computeMaxScrollX() { + protected int computeMaxScroll() { int childCount = getChildCount(); if (childCount > 0) { final int index = mIsRtl ? 0 : childCount - 1; @@ -722,7 +768,8 @@ public abstract class PagedView extends ViewGrou protected int getChildOffset(int index) { if (index < 0 || index > getChildCount() - 1) return 0; - return getPageAt(index).getLeft(); + View pageAtIndex = getPageAt(index); + return mOrientationHandler.getChildStart(pageAtIndex); } @Override @@ -873,13 +920,13 @@ public abstract class PagedView extends ViewGrou case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. + * whether the user has moved far enough from their original down touch. */ if (mActivePointerId != INVALID_POINTER) { determineScrollingStart(ev); } // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN - // event. in that case, treat the first occurence of a move event as a ACTION_DOWN + // event. in that case, treat the first occurrence of a move event as a ACTION_DOWN // i.e. fall through to the next case (don't break) // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events // while it's small- this was causing a crash before we checked for INVALID_POINTER) @@ -892,9 +939,9 @@ public abstract class PagedView extends ViewGrou // Remember location of down touch mDownMotionX = x; mDownMotionY = y; - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTotalMotionX = 0; + mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0); + mLastMotionRemainder = 0; + mTotalMotion = 0; mActivePointerId = ev.getPointerId(0); updateIsBeingDraggedOnTouchDown(); @@ -956,17 +1003,17 @@ public abstract class PagedView extends ViewGrou final int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) return; - final float x = ev.getX(pointerIndex); - final int xDiff = (int) Math.abs(x - mLastMotionX); + final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, pointerIndex); + final int diff = (int) Math.abs(primaryDirection - mLastMotion); final int touchSlop = Math.round(touchSlopScale * mTouchSlop); - boolean xMoved = xDiff > touchSlop; + boolean moved = diff > touchSlop; - if (xMoved) { + if (moved) { // Scroll if the user moved far enough along the X axis mIsBeingDragged = true; - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; + mTotalMotion += Math.abs(mLastMotion - primaryDirection); + mLastMotion = primaryDirection; + mLastMotionRemainder = 0; onScrollInteractionBegin(); pageBeginTransition(); // Stop listening for things like pinches. @@ -1033,10 +1080,9 @@ public abstract class PagedView extends ViewGrou @Override protected void dispatchDraw(Canvas canvas) { - if (mScroller.isSpringing() && mSpringOverScrollX != 0) { + if (mScroller.isSpringing() && mSpringOverScroll != 0) { int saveCount = canvas.save(); - - canvas.translate(-mSpringOverScrollX, 0); + mOrientationHandler.set(canvas, CANVAS_TRANSLATE, -mSpringOverScroll); super.dispatchDraw(canvas); canvas.restoreToCount(saveCount); @@ -1046,25 +1092,27 @@ public abstract class PagedView extends ViewGrou } protected void dampedOverScroll(int amount) { - mSpringOverScrollX = amount; + mSpringOverScroll = amount; if (amount == 0) { return; } - int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth()); - mSpringOverScrollX = overScrollAmount; + int size = mOrientationHandler.getMeasuredSize(this); + int overScrollAmount = OverScroll.dampedScroll(amount, size); + mSpringOverScroll = overScrollAmount; if (mScroller.isSpringing()) { invalidate(); return; } - int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX); - super.scrollTo(x + overScrollAmount, getScrollY()); + int primaryScroll = mOrientationHandler.getPrimaryScroll(this); + int boundedScroll = Utilities.boundToRange(primaryScroll, mMinScroll, mMaxScroll); + mOrientationHandler.delegateScrollTo(this, boundedScroll + overScrollAmount); invalidate(); } protected void overScroll(int amount) { - mSpringOverScrollX = amount; + mSpringOverScroll = amount; if (mScroller.isSpringing()) { invalidate(); return; @@ -1073,11 +1121,8 @@ public abstract class PagedView extends ViewGrou if (amount == 0) return; if (mFreeScroll && !mScroller.isFinished()) { - if (amount < 0) { - super.scrollTo(mMinScrollX + amount, getScrollY()); - } else { - super.scrollTo(mMaxScrollX + amount, getScrollY()); - } + int scrollAmount = amount < 0 ? mMinScroll + amount : mMaxScroll + amount; + mOrientationHandler.delegateScrollTo(this, scrollAmount); } else { dampedOverScroll(amount); } @@ -1127,37 +1172,37 @@ public abstract class PagedView extends ViewGrou } // Remember where the motion event started - mDownMotionX = mLastMotionX = ev.getX(); + mDownMotionX = ev.getX(); mDownMotionY = ev.getY(); - mLastMotionXRemainder = 0; - mTotalMotionX = 0; + mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0); + mLastMotionRemainder = 0; + mTotalMotion = 0; mActivePointerId = ev.getPointerId(0); - if (mIsBeingDragged) { onScrollInteractionBegin(); pageBeginTransition(); } break; - case MotionEvent.ACTION_MOVE: - if (mIsBeingDragged) { + case MotionEvent.ACTION_MOVE: + if (mIsBeingDragged) { // Scroll to follow the motion event final int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) return true; - final float x = ev.getX(pointerIndex); - final float deltaX = mLastMotionX + mLastMotionXRemainder - x; - - mTotalMotionX += Math.abs(deltaX); + float direction = mOrientationHandler.getPrimaryDirection(ev, pointerIndex); + float delta = mLastMotion + mLastMotionRemainder - direction; + mTotalMotion += Math.abs(delta); // Only scroll and update mLastMotionX if we have moved some discrete amount. We // keep the remainder because we are actually testing if we've moved from the last // scrolled position (which is discrete). - if (Math.abs(deltaX) >= 1.0f) { - scrollBy((int) deltaX, 0); - mLastMotionX = x; - mLastMotionXRemainder = deltaX - (int) deltaX; + if (Math.abs(delta) >= 1.0f) { + mLastMotion = direction; + mLastMotionRemainder = delta - (int) delta; + + mOrientationHandler.set(this, VIEW_SCROLL_BY, (int) delta); } else { awakenScrollBars(); } @@ -1170,27 +1215,31 @@ public abstract class PagedView extends ViewGrou if (mIsBeingDragged) { final int activePointerId = mActivePointerId; final int pointerIndex = ev.findPointerIndex(activePointerId); - final float x = ev.getX(pointerIndex); + final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, + pointerIndex); final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); - final int deltaX = (int) (x - mDownMotionX); - final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth(); - boolean isSignificantMove = Math.abs(deltaX) > pageWidth * - SIGNIFICANT_MOVE_THRESHOLD; - mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); - boolean isFling = mTotalMotionX > mTouchSlop && shouldFlingForVelocity(velocityX); - boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0; - boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0; + int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker, + mActivePointerId); + int delta = (int) (primaryDirection - mDownMotionPrimary); + int pageOrientedSize = mOrientationHandler.getMeasuredSize(getPageAt(mCurrentPage)); + + boolean isSignificantMove = Math.abs(delta) > pageOrientedSize * + SIGNIFICANT_MOVE_THRESHOLD; + + mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection); + boolean isFling = mTotalMotion > mTouchSlop && shouldFlingForVelocity(velocity); + boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0; + boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0; if (!mFreeScroll) { // In the case that the page is moved far to one direction and then is flung // in the opposite direction, we use a threshold to determine whether we should // just return to the starting page, or if we should skip one further. boolean returnToOriginalPage = false; - if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && - Math.signum(velocityX) != Math.signum(deltaX) && isFling) { + if (Math.abs(delta) > pageOrientedSize * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && + Math.signum(velocity) != Math.signum(delta) && isFling) { returnToOriginalPage = true; } @@ -1199,15 +1248,15 @@ public abstract class PagedView extends ViewGrou // test for a large move if a fling has been registered. That is, a large // move to the left and fling to the right will register as a fling to the right. - if (((isSignificantMove && !isDeltaXLeft && !isFling) || - (isFling && !isVelocityXLeft)) && mCurrentPage > 0) { + if (((isSignificantMove && !isDeltaLeft && !isFling) || + (isFling && !isVelocityLeft)) && mCurrentPage > 0) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; - snapToPageWithVelocity(finalPage, velocityX); - } else if (((isSignificantMove && isDeltaXLeft && !isFling) || - (isFling && isVelocityXLeft)) && + snapToPageWithVelocity(finalPage, velocity); + } else if (((isSignificantMove && isDeltaLeft && !isFling) || + (isFling && isVelocityLeft)) && mCurrentPage < getChildCount() - 1) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; - snapToPageWithVelocity(finalPage, velocityX); + snapToPageWithVelocity(finalPage, velocity); } else { snapToDestination(); } @@ -1216,38 +1265,40 @@ public abstract class PagedView extends ViewGrou abortScrollerAnimation(true); } - int initialScrollX = getScrollX(); + int initialScroll = mOrientationHandler.getPrimaryScroll(this); + int maxScroll = mMaxScroll; + int minScroll = mMinScroll; - if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) || - ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) { - mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX); + if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) || + ((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) { + mScroller.springBack(initialScroll, minScroll, maxScroll); mNextPage = getPageNearestToCenterOfScreen(); } else { mScroller.setInterpolator(mDefaultInterpolator); - mScroller.fling(initialScrollX, -velocityX, - mMinScrollX, mMaxScrollX, - Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR)); + mScroller.fling(initialScroll, -velocity, + minScroll, maxScroll, + Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR)); - int finalX = mScroller.getFinalPos(); - mNextPage = getPageNearestToCenterOfScreen(finalX); + int finalPos = mScroller.getFinalPos(); + mNextPage = getPageNearestToCenterOfScreen(finalPos); int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1); int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0); - if (finalX > mMinScrollX && finalX < mMaxScrollX) { + if (finalPos > minScroll && finalPos < maxScroll) { // If scrolling ends in the half of the added space that is closer to // the end, settle to the end. Otherwise snap to the nearest page. // If flinging past one of the ends, don't change the velocity as it // will get stopped at the end anyway. - int pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2 - ? mMinScrollX - : finalX > (lastPageScroll + mMaxScrollX) / 2 - ? mMaxScrollX - : getScrollForPage(mNextPage); + int pageSnapped = finalPos < (firstPageScroll + minScroll) / 2 + ? minScroll + : finalPos > (lastPageScroll + maxScroll) / 2 + ? maxScroll + : getScrollForPage(mNextPage); - mScroller.setFinalPos(pageSnappedX); + mScroller.setFinalPos(pageSnapped); // Ensure the scroll/snap doesn't happen too fast; int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION - - mScroller.getDuration(); + - mScroller.getDuration(); if (extraScrollDuration > 0) { mScroller.extendDuration(extraScrollDuration); } @@ -1279,8 +1330,8 @@ public abstract class PagedView extends ViewGrou return true; } - protected boolean shouldFlingForVelocity(int velocityX) { - return Math.abs(velocityX) > mFlingThresholdVelocity; + protected boolean shouldFlingForVelocity(int velocity) { + return Math.abs(velocity) > mFlingThresholdVelocity; } private void resetTouchState() { @@ -1364,8 +1415,9 @@ public abstract class PagedView extends ViewGrou // active pointer and adjust accordingly. // TODO: Make this decision more intelligent. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); - mLastMotionXRemainder = 0; + mLastMotion = mDownMotionPrimary = mOrientationHandler.getPrimaryDirection(ev, + newPointerIndex); + mLastMotionRemainder = 0; mActivePointerId = ev.getPointerId(newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); @@ -1383,19 +1435,20 @@ public abstract class PagedView extends ViewGrou } public int getPageNearestToCenterOfScreen() { - return getPageNearestToCenterOfScreen(getScrollX()); + return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this)); } - private int getPageNearestToCenterOfScreen(int scaledScrollX) { - int screenCenter = scaledScrollX + (getMeasuredWidth() / 2); + private int getPageNearestToCenterOfScreen(int scaledScroll) { + int pageOrientationSize = mOrientationHandler.getMeasuredSize(this); + int screenCenter = scaledScroll + (pageOrientationSize / 2); int minDistanceFromScreenCenter = Integer.MAX_VALUE; int minDistanceFromScreenCenterIndex = -1; final int childCount = getChildCount(); for (int i = 0; i < childCount; ++i) { View layout = getPageAt(i); - int childWidth = layout.getMeasuredWidth(); - int halfChildWidth = (childWidth / 2); - int childCenter = getChildOffset(i) + halfChildWidth; + int childSize = mOrientationHandler.getMeasuredSize(layout); + int halfChildSize = (childSize / 2); + int childCenter = getChildOffset(i) + halfChildSize; int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); if (distanceFromScreenCenter < minDistanceFromScreenCenter) { minDistanceFromScreenCenter = distanceFromScreenCenter; @@ -1410,7 +1463,8 @@ public abstract class PagedView extends ViewGrou } protected boolean isInOverScroll() { - return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX); + int scroll = mOrientationHandler.getPrimaryScroll(this); + return scroll > mMaxScroll || scroll < mMinScroll; } protected int getPageSnapDuration() { @@ -1432,10 +1486,10 @@ public abstract class PagedView extends ViewGrou protected boolean snapToPageWithVelocity(int whichPage, int velocity) { whichPage = validateNewPage(whichPage); - int halfScreenSize = getMeasuredWidth() / 2; + int halfScreenSize = mOrientationHandler.getMeasuredSize(this) / 2; - final int newX = getScrollForPage(whichPage); - int delta = newX - getUnboundedScrollX(); + final int newLoc = getScrollForPage(whichPage); + int delta = newLoc - getUnboundedScroll(); int duration = 0; if (Math.abs(velocity) < mMinFlingVelocity) { @@ -1462,7 +1516,7 @@ public abstract class PagedView extends ViewGrou if (QUICKSTEP_SPRINGS.get()) { return snapToPage(whichPage, delta, duration, false, null, - velocity * Math.signum(newX - getUnboundedScrollX()), true); + velocity * Math.signum(delta), true); } else { return snapToPage(whichPage, delta, duration); } @@ -1488,8 +1542,8 @@ public abstract class PagedView extends ViewGrou TimeInterpolator interpolator) { whichPage = validateNewPage(whichPage); - int newX = getScrollForPage(whichPage); - final int delta = newX - getUnboundedScrollX(); + int newLoc = getScrollForPage(whichPage); + final int delta = newLoc - getUnboundedScroll(); return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false); } @@ -1535,9 +1589,9 @@ public abstract class PagedView extends ViewGrou } if (spring && QUICKSTEP_SPRINGS.get()) { - mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity); + mScroller.startScrollSpring(getUnboundedScroll(), delta, duration, velocity); } else { - mScroller.startScroll(getUnboundedScrollX(), delta, duration); + mScroller.startScroll(getUnboundedScroll(), delta, duration); } updatePageIndicator(); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index e0e4cc0ce4..9780630b78 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; +import static com.android.launcher3.states.RotationHelper.FIXED_ROTATION_TRANSFORM_SETTING_NAME; import android.animation.ValueAnimator; import android.annotation.TargetApi; @@ -127,6 +128,11 @@ public final class Utilities { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; } + public static boolean isForcedRotation(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0; + } + // An intent extra to indicate the horizontal scroll of the wallpaper. public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET"; public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR"; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a8f492f623..590c620020 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -966,8 +966,8 @@ public class Workspace extends PagedView private boolean isScrollingOverlay() { return mLauncherOverlay != null && - ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) - || (!mIsRtl && getUnboundedScrollX() < mMinScrollX)); + ((mIsRtl && getUnboundedScroll() > mMaxScroll) + || (!mIsRtl && getUnboundedScroll() < mMinScroll)); } @Override @@ -1003,7 +1003,7 @@ public class Workspace extends PagedView public void showPageIndicatorAtCurrentScroll() { if (mPageIndicator != null) { - mPageIndicator.setScroll(getScrollX(), computeMaxScrollX()); + mPageIndicator.setScroll(getScrollX(), computeMaxScroll()); } } diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java index 5b73940488..ab4cb6b8db 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -48,7 +48,7 @@ public class AllAppsPagedView extends PagedView { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - mPageIndicator.setScroll(l, mMaxScrollX); + mPageIndicator.setScroll(l, mMaxScroll); } @Override diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 3b5fd5902c..c6d62f8bc7 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -258,7 +258,7 @@ public class FolderPagedView extends PagedView { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - mPageIndicator.setScroll(l, mMaxScrollX); + mPageIndicator.setScroll(l, mMaxScroll); } /** diff --git a/src/com/android/launcher3/model/PagedViewOrientedState.java b/src/com/android/launcher3/model/PagedViewOrientedState.java new file mode 100644 index 0000000000..fd1154c9fa --- /dev/null +++ b/src/com/android/launcher3/model/PagedViewOrientedState.java @@ -0,0 +1,100 @@ +/* + * + * * Copyright (C) 2020 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.model; + +import android.view.Surface; + +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PortraitPagedViewHandler; +import com.android.launcher3.touch.LandscapePagedViewHandler; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.SeascapePagedViewHandler; + +/** + * Container to hold orientation/rotation related information for Launcher. + * This is not meant to be an abstraction layer for applying different functionality between + * the different orientation/rotations. For that see {@link PagedOrientationHandler} + * + * This class has initial default state assuming the device and foreground app have + * no ({@link Surface.ROTATION_0} rotation. + * + * Currently this class resides in {@link com.android.launcher3.PagedView}, but there's a ticket + * to disassociate it from Launcher since it's needed before Launcher is instantiated + * See TODO(b/150300347) + */ +public final class PagedViewOrientedState { + + private PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler(); + + private int mTouchRotation = Surface.ROTATION_0; + private int mDisplayRotation = Surface.ROTATION_0; + /** + * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake + * launcher orientations. + */ + private boolean mDisableMultipleOrientations; + + public void update(int touchRotation, int displayRotation) { + mDisplayRotation = displayRotation; + mTouchRotation = touchRotation; + if (mTouchRotation == Surface.ROTATION_90) { + mOrientationHandler = new LandscapePagedViewHandler(); + } else if (mTouchRotation == Surface.ROTATION_270) { + mOrientationHandler = new SeascapePagedViewHandler(); + } else { + mOrientationHandler = new PortraitPagedViewHandler(); + } + } + + /** + * @return {@code true} if the area where the user touched the nav bar is the expected + * location for the given display rotation. Ex. bottom of phone in portrait, or left side of + * phone in landscape, right side in seascape, etc. + * False otherwise + */ + public boolean isTouchRegionNaturalForDisplay() { + return mTouchRotation == mDisplayRotation; + } + + public boolean areMultipleLayoutOrientationsDisabled() { + return mDisableMultipleOrientations; + } + + public void disableMultipleOrientations(boolean disable) { + mDisableMultipleOrientations = disable; + if (disable) { + mOrientationHandler = new PortraitPagedViewHandler(); + } + } + + /** + * Gets the difference between the rotation of the device/display and which region the + * user is currently interacting with in factors of 90 degree clockwise rotations. + * Ex. Display is in portrait -> 0, user touches landscape region -> 1, this + * method would return 3 because it takes 3 clockwise 90 degree rotations from normal to + * landscape (portrait -> seascape -> reverse portrait -> landscape) + */ + public int getTouchDisplayDelta() { + return RotationHelper.deltaRotation(mTouchRotation, mDisplayRotation); + } + + public PagedOrientationHandler getOrientationHandler() { + return mOrientationHandler; + } +} diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 812268a0f4..b193ffd8cb 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -176,7 +176,7 @@ public class NotificationMainView extends FrameLayout implements SingleAxisSwipe // SingleAxisSwipeDetector.Listener's @Override - public void onDragStart(boolean start) { } + public void onDragStart(boolean start, float startDisplacement) { } @Override diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 852928b416..95b13b448a 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,15 +17,26 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.provider.Settings; +import android.view.MotionEvent; +import android.view.Surface; import android.view.WindowManager; import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -38,6 +49,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; + public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; + public static boolean getAllowRotationDefaultValue() { // If the device was scaled, used the original dimensions to determine if rotation // is allowed of not. @@ -92,6 +105,18 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } else { mPrefs = null; } + + // TODO(b/150260456) Add this in home settings as well + final ContentResolver resolver = launcher.getContentResolver(); + final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { + @Override + public void onChange(boolean selfChange) { + PagedView.sFlagForcedRotation = Utilities.isForcedRotation(mLauncher); + } + }; + resolver.registerContentObserver(Settings.Global.getUriFor( + FIXED_ROTATION_TRANSFORM_SETTING_NAME), + false, observer); } public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { @@ -204,6 +229,118 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } } + public static int getDegreesFromRotation(int rotation) { + int degrees; + switch (rotation) { + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + case Surface.ROTATION_0: + default: + degrees = 0; + break; + } + return degrees; + } + + public static int getRotationFromDegrees(int degrees, int currentRotation) { + int threshold = 70; + if (degrees >= (360 - threshold) || degrees < (threshold)) { + return Surface.ROTATION_0; + } else if (degrees < (90 + threshold)) { + return Surface.ROTATION_270; + } else if (degrees < 180 + threshold) { + return Surface.ROTATION_180; + } else { + return Surface.ROTATION_90; + } + } + + /** + * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. + * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... + * A value of 0 means no rotation has been applied + */ + public static int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } + + /** + * Creates a matrix to transform the given motion event specified by degrees. + * If {@param inverse} is {@code true}, the inverse of that matrix will be applied + */ + public static void transformEvent(int degrees, MotionEvent ev, boolean inverse) { + Matrix transform = new Matrix(); + transform.setRotate(degrees); + if (inverse) { + Matrix inv = new Matrix(); + transform.invert(inv); + ev.transform(inv); + } else { + ev.transform(transform); + } + // TODO: Add scaling back in based on degrees +// if (getWidth() > 0 && getHeight() > 0) { +// float scale = ((float) getWidth()) / getHeight(); +// transform.postScale(scale, 1 / scale); +// } + } + + /** + * TODO(b/149658423): Have {@link com.android.quickstep.OrientationTouchTransformer + * also use this} + */ + public static Matrix getRotationMatrix(int screenWidth, int screenHeight, int displayRotation) { + Matrix m = new Matrix(); + switch (displayRotation) { + case Surface.ROTATION_0: + return m; + case Surface.ROTATION_90: + m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation)); + m.postTranslate(0, screenWidth); + break; + case Surface.ROTATION_270: + m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation)); + m.postTranslate(screenHeight, 0); + break; + } + return m; + } + + public static void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight, + int displayRotation) { + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + m.mapRect(src); + } + + public static void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, + int screenHeight, int displayRotation) { + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + Matrix inverse = new Matrix(); + m.invert(inverse); + inverse.mapRect(src); + } + + public static void getTargetRectForRotation(Rect srcOut, int screenWidth, int screenHeight, + int displayRotation) { + RectF wrapped = new RectF(srcOut); + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + m.mapRect(wrapped); + wrapped.round(srcOut); + } + + public static boolean isRotationLandscape(int rotation) { + return rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90; + } + @Override public String toString() { return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 9df6241a5b..34d69e9eec 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -230,7 +230,7 @@ public abstract class AbstractStateChangeTouchController } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { mStartState = mLauncher.getStateManager().getState(); mIsLogContainerSet = false; if (mCurrentAnimation == null) { diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java index 30283dab8d..1276ece7e2 100644 --- a/src/com/android/launcher3/touch/BaseSwipeDetector.java +++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java @@ -236,7 +236,7 @@ public abstract class BaseSwipeDetector { } else { mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop; mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop; - } + } } protected abstract boolean shouldScrollStart(PointF displacement); diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java new file mode 100644 index 0000000000..3090d97fe0 --- /dev/null +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -0,0 +1,263 @@ +/* + * 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.touch; + +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.PagedView; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.OverScroller; + +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; + +public class LandscapePagedViewHandler implements PagedOrientationHandler { + @Override + public float getCurrentAppAnimationScale(RectF src, RectF target) { + return src.height() / target.height(); + } + + @Override + public int getPrimaryValue(int x, int y) { + return y; + } + + @Override + public int getSecondaryValue(int x, int y) { + return x; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int minMaxScroll) { + pagedView.superScrollTo(secondaryScroll, minMaxScroll); + } + + @Override + public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) { + pagedView.scrollTo(pagedView.getScrollX() + x, unboundedScroll + y); + } + + @Override + public void scrollerStartScroll(OverScroller scroller, int newPosition) { + scroller.startScroll(scroller.getCurrPos(), newPosition - scroller.getCurrPos()); + } + + @Override + public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { + int scroll = pagedView.getScrollY(); + final int halfPageSize = pagedView.getNormalChildHeight() / 2; + final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize; + final int halfScreenSize = pagedView.getMeasuredHeight() / 2; + return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + } + + @Override + public float getDragLengthFactor(int dimension, int transitionDragLength) { + return Math.min(1.0f, (float) dimension / transitionDragLength); + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement > 0; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int primaryScroll) { + pagedView.superScrollTo(pagedView.getScrollX(), primaryScroll); + } + + @Override + public void set(T target, Int2DAction action, int param) { + action.call(target, 0, param); + } + + @Override + public void set(T target, Float2DAction action, float param) { + action.call(target, 0, param); + } + + @Override + public float getPrimaryDirection(MotionEvent event, int pointerIndex) { + return event.getY(pointerIndex); + } + + @Override + public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { + return velocityTracker.getYVelocity(pointerId); + } + + @Override + public int getMeasuredSize(View view) { + return view.getMeasuredHeight(); + } + + @Override + public int getPrimarySize(Rect rect) { + return rect.height(); + } + + @Override + public float getPrimarySize(RectF rect) { + return rect.height(); + } + + @Override + public int getSecondaryDimension(View view) { + return view.getWidth(); + } + + @Override + public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) { + float offscreenTranslationY = dp.heightPx - view.getPaddingTop(); + return new ScaleAndTranslation(1f, 0f, offscreenTranslationY); + } + + @Override + public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) { + return scaleAndTranslation.translationY; + } + + @Override + public FloatProperty getPrimaryViewTranslate() { + return VIEW_TRANSLATE_Y; + } + + @Override + public FloatProperty getSecondaryViewTranslate() { + return VIEW_TRANSLATE_X; + } + + @Override + public void setPrimaryAndResetSecondaryTranslate(View view, float translation) { + view.setTranslationX(0); + view.setTranslationY(translation); + } + + @Override + public float getViewCenterPosition(View view) { + return view.getTop() + view.getTranslationY(); + } + + @Override + public int getPrimaryScroll(View view) { + return view.getScrollY(); + } + + @Override + public float getPrimaryScale(View view) { + return view.getScaleY(); + } + + @Override + public void setMaxScroll(AccessibilityEvent event, int maxScroll) { + event.setMaxScrollY(maxScroll); + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return !Utilities.isRtl(resources); + } + + @Override + public float getDegreesRotated() { + return 90; + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(value, 0); + } else if (delta == 1) { + rect.offset(0, -value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + Matrix m = new Matrix(); + m.setRotate(270); + m.postTranslate(0, screenWidth); + RectF newTarget = new RectF(); + RectF oldTarget = new RectF(src); + m.mapRect(newTarget, oldTarget); + src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); + } + + @Override + public int getChildStart(View view) { + return view.getTop(); + } + + @Override + public int getCenterForPage(View view, Rect insets) { + return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left + - insets.right - view.getPaddingRight()) / 2; + } + + @Override + public int getScrollOffsetStart(View view, Rect insets) { + return insets.top + view.getPaddingTop(); + } + + @Override + public int getScrollOffsetEnd(View view, Rect insets) { + return view.getHeight() - view.getPaddingBottom() - insets.bottom; + } + + @Override + public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() { + return HORIZONTAL; + } + + @Override + public int getShortEdgeLength(DeviceProfile dp) { + return dp.heightPx; + } + + @Override + public int getTaskDismissDirectionFactor() { + return 1; + } + + @Override + public ChildBounds getChildBounds(View child, int childStart, int pageCenter, + boolean layoutChild) { + final int childHeight = child.getMeasuredHeight(); + final int childBottom = childStart + childHeight; + final int childWidth = child.getMeasuredWidth(); + final int childLeft = pageCenter - childWidth/ 2; + if (layoutChild) { + child.layout(childLeft, childStart, childLeft + childWidth, childBottom); + } + return new ChildBounds(childHeight, childWidth, childBottom, childLeft); + } +} diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java new file mode 100644 index 0000000000..2f02076f8e --- /dev/null +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 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.touch; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState; +import com.android.launcher3.PagedView; +import com.android.launcher3.util.OverScroller; + +/** + * Abstraction layer to separate horizontal and vertical specific implementations + * for {@link com.android.launcher3.PagedView}. Majority of these implementations are (should be) as + * simple as choosing the correct X and Y analogous methods. + */ +public interface PagedOrientationHandler { + + interface Int2DAction { + void call(T target, int x, int y); + } + interface Float2DAction { + void call(T target, float x, float y); + } + Int2DAction VIEW_SCROLL_BY = View::scrollBy; + Int2DAction VIEW_SCROLL_TO = View::scrollTo; + Float2DAction CANVAS_TRANSLATE = Canvas::translate; + void set(T target, Int2DAction action, int param); + void set(T target, Float2DAction action, float param); + float getPrimaryDirection(MotionEvent event, int pointerIndex); + float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId); + int getMeasuredSize(View view); + int getPrimarySize(Rect rect); + float getPrimarySize(RectF rect); + int getSecondaryDimension(View view); + LauncherState.ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view); + float getTranslationValue(LauncherState.ScaleAndTranslation scaleAndTranslation); + FloatProperty getPrimaryViewTranslate(); + FloatProperty getSecondaryViewTranslate(); + void setPrimaryAndResetSecondaryTranslate(View view, float translation); + float getViewCenterPosition(View view); + int getPrimaryScroll(View view); + float getPrimaryScale(View view); + int getChildStart(View view); + int getCenterForPage(View view, Rect insets); + int getScrollOffsetStart(View view, Rect insets); + int getScrollOffsetEnd(View view, Rect insets); + SingleAxisSwipeDetector.Direction getOppositeSwipeDirection(); + int getShortEdgeLength(DeviceProfile dp); + int getTaskDismissDirectionFactor(); + ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild); + void setMaxScroll(AccessibilityEvent event, int maxScroll); + boolean getRecentsRtlSetting(Resources resources); + float getDegreesRotated(); + void offsetTaskRect(RectF rect, float value, int delta); + void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight); + float getCurrentAppAnimationScale(RectF src, RectF target); + int getPrimaryValue(int x, int y); + int getSecondaryValue(int x, int y); + void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll); + /** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/ + void delegateScrollTo(PagedView pagedView, int primaryScroll); + void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y); + void scrollerStartScroll(OverScroller scroller, int newPosition); + CurveProperties getCurveProperties(PagedView pagedView, Rect insets); + float getDragLengthFactor(int dimension, int transitionDragLength); + boolean isGoingUp(float displacement); + + class CurveProperties { + public final int scroll; + public final int halfPageSize; + public final int screenCenter; + public final int halfScreenSize; + + public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) { + this.scroll = scroll; + this.halfPageSize = halfPageSize; + this.screenCenter = screenCenter; + this.halfScreenSize = halfScreenSize; + } + } + + class ChildBounds { + + public final int primaryDimension; + public final int secondaryDimension; + public final int childPrimaryEnd; + public final int childSecondaryEnd; + + ChildBounds(int primaryDimension, int secondaryDimension, int childPrimaryEnd, + int childSecondaryEnd) { + this.primaryDimension = primaryDimension; + this.secondaryDimension = secondaryDimension; + this.childPrimaryEnd = childPrimaryEnd; + this.childSecondaryEnd = childSecondaryEnd; + } + } +} diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java new file mode 100644 index 0000000000..fbd80bbfd1 --- /dev/null +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -0,0 +1,257 @@ +/* + * 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.touch; + +import android.content.res.Resources; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.PagedView; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.OverScroller; + +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; + +public class PortraitPagedViewHandler implements PagedOrientationHandler { + + @Override + public float getCurrentAppAnimationScale(RectF src, RectF target) { + return src.width() / target.width(); + } + + @Override + public int getPrimaryValue(int x, int y) { + return x; + } + + @Override + public int getSecondaryValue(int x, int y) { + return y; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll) { + pagedView.superScrollTo(primaryScroll, secondaryScroll); + } + + @Override + public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) { + pagedView.scrollTo(unboundedScroll + x, pagedView.getScrollY() + y); + } + + @Override + public void scrollerStartScroll(OverScroller scroller, int newPosition) { + scroller.startScroll(newPosition - scroller.getCurrPos(), scroller.getCurrPos()); + } + + @Override + public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { + int scroll = pagedView.getScrollX(); + final int halfPageSize = pagedView.getNormalChildWidth() / 2; + final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize; + final int halfScreenSize = pagedView.getMeasuredWidth() / 2; + return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + } + + @Override + public float getDragLengthFactor(int dimension, int transitionDragLength) { + return (float) dimension / transitionDragLength; + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement < 0; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int primaryScroll) { + pagedView.superScrollTo(primaryScroll, pagedView.getScrollY()); + } + + @Override + public void set(T target, Int2DAction action, int param) { + action.call(target, param, 0); + } + + @Override + public void set(T target, Float2DAction action, float param) { + action.call(target, param, 0); + } + + @Override + public float getPrimaryDirection(MotionEvent event, int pointerIndex) { + return event.getX(pointerIndex); + } + + @Override + public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { + return velocityTracker.getXVelocity(pointerId); + } + + @Override + public int getMeasuredSize(View view) { + return view.getMeasuredWidth(); + } + + @Override + public int getPrimarySize(Rect rect) { + return rect.width(); + } + + @Override + public float getPrimarySize(RectF rect) { + return rect.width(); + } + + @Override + public int getSecondaryDimension(View view) { + return view.getHeight(); + } + + @Override + public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) { + float offscreenTranslationX = dp.widthPx - view.getPaddingStart(); + return new ScaleAndTranslation(1f, offscreenTranslationX, 0f); + } + + @Override + public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) { + return scaleAndTranslation.translationX; + } + + @Override + public FloatProperty getPrimaryViewTranslate() { + return VIEW_TRANSLATE_X; + } + + @Override + public FloatProperty getSecondaryViewTranslate() { + return VIEW_TRANSLATE_Y; + } + + @Override + public void setPrimaryAndResetSecondaryTranslate(View view, float translation) { + view.setTranslationX(translation); + view.setTranslationY(0); + } + + @Override + public float getViewCenterPosition(View view) { + return view.getLeft() + view.getTranslationX(); + } + + @Override + public int getPrimaryScroll(View view) { + return view.getScrollX(); + } + + @Override + public float getPrimaryScale(View view) { + return view.getScaleX(); + } + + @Override + public void setMaxScroll(AccessibilityEvent event, int maxScroll) { + event.setMaxScrollX(maxScroll); + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return !Utilities.isRtl(resources); + } + + @Override + public float getDegreesRotated() { + return 0; + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(value, 0); + } else if (delta == 1) { + rect.offset(0, -value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + //no-op + } + + @Override + public int getChildStart(View view) { + return view.getLeft(); + } + + @Override + public int getCenterForPage(View view, Rect insets) { + return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top + - insets.bottom - view.getPaddingBottom()) / 2; + } + + @Override + public int getScrollOffsetStart(View view, Rect insets) { + return insets.left + view.getPaddingLeft(); + } + + @Override + public int getScrollOffsetEnd(View view, Rect insets) { + return view.getWidth() - view.getPaddingRight() - insets.right; + } + + @Override + public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() { + return VERTICAL; + } + + @Override + public int getShortEdgeLength(DeviceProfile dp) { + return dp.widthPx; + } + + @Override + public int getTaskDismissDirectionFactor() { + return -1; + } + + @Override + public ChildBounds getChildBounds(View child, int childStart, int pageCenter, + boolean layoutChild) { + final int childWidth = child.getMeasuredWidth(); + final int childRight = childStart + childWidth; + final int childHeight = child.getMeasuredHeight(); + final int childTop = pageCenter - childHeight / 2; + if (layoutChild) { + child.layout(childStart, childTop, childRight, childTop + childHeight); + } + return new ChildBounds(childWidth, childHeight, childRight, childTop); + } +} diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java new file mode 100644 index 0000000000..f1875cc32f --- /dev/null +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -0,0 +1,71 @@ +/* + * 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.touch; + +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; + +import com.android.launcher3.Utilities; + +public class SeascapePagedViewHandler extends LandscapePagedViewHandler { + + @Override + public int getTaskDismissDirectionFactor() { + return -1; + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return Utilities.isRtl(resources); + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(-value, 0); + } else if (delta == 1) { + rect.offset(0, value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, -value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + Matrix m = new Matrix(); + m.setRotate(90); + m.postTranslate(screenHeight, 0); + RectF newTarget = new RectF(); + RectF oldTarget = new RectF(src); + m.mapRect(newTarget, oldTarget); + src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); + } + + @Override + public float getDegreesRotated() { + return 270; + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement < 0; + } +} diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java index 9d406f3bef..d72548619f 100644 --- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java +++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java @@ -148,7 +148,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { @Override protected void reportDragStartInternal(boolean recatch) { - mListener.onDragStart(!recatch); + float startDisplacement = mDir.extractDirection(mSubtractDisplacement); + mListener.onDragStart(!recatch, startDisplacement); } @Override @@ -165,8 +166,13 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { /** Listener to receive updates on the swipe. */ public interface Listener { - /** @param start whether this was the original drag start, as opposed to a recatch. */ - void onDragStart(boolean start); + /** + * TODO(b/150256055) consolidate all the different onDrag() methods into one + * @param start whether this was the original drag start, as opposed to a recatch. + * @param startDisplacement the initial touch displacement for the primary direction as + * given by by {@link Direction#extractDirection(PointF)} + */ + void onDragStart(boolean start, float startDisplacement); boolean onDrag(float displacement); diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index bdba39c371..11c10293e4 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -149,8 +149,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView /* SingleAxisSwipeDetector.Listener */ @Override - public void onDragStart(boolean start) { - } + public void onDragStart(boolean start, float startDisplacement) { } @Override public boolean onDrag(float displacement) { diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java index b0ece77c9b..472e1a1c91 100644 --- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java +++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java @@ -89,7 +89,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100, 100 - mTouchSlop); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -99,7 +99,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100, 100 + mTouchSlop); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -107,7 +107,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener, never()).onDragStart(anyBoolean()); + verify(mMockListener, never()).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -118,7 +118,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -129,7 +129,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 - mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -140,7 +140,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 - mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -151,7 +151,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test