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