Add vertical layout support for Overview in portrait
WM is making changes which allows apps to maintain
their orientation independent of the orientation of
the foreground app. This allows recents to always start
in portrait even when the app currently running is in
landscape. This means we have to give the illusion of
a landscape oriented overview when user swipes up in
gesterual nav when launcher is started in portrait
configuration.
PagedOrientationHandler abstracts all coordinate specific
logic from Paged/RecentsView primarily, but also all
other dynamic calculations throughout launcher.
PagedViewOrientationState is the single point of exposure
to other classes that depend on those changes. The goal
is to also minimize holding state to allow for default
implementations of PagedOrientationHandler for all the
3p/Fallback classes. PagedViewOrientationState also
holds other data around rotation that isn't
specifically tied to view logic.
The fake landscape overview can be toggled with:
adb shell settings put global forced_rotation [0/1]
Fixes: 146176182
Change-Id: I65d8d4e9f92b93931cbe0053ccaf0cda8d2ffd6c
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 01893e9..ef6bd3d 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.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 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 @@
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 @@
@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 @@
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 @@
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 @@
* 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 @@
if (Utilities.ATLEAST_OREO) {
setDefaultFocusHighlightEnabled(false);
}
+
+ sFlagForcedRotation = Utilities.isForcedRotation(context);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -236,12 +255,12 @@
*/
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 @@
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 @@
break;
}
}
- while (currScroll > mMaxScrollX) {
+ while (currScroll > mMaxScroll) {
page -= dir;
prevScroll = currScroll;
currScroll = getScrollForPage(page);
@@ -378,45 +397,73 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
@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 @@
}
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 @@
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 @@
}
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 e0e4cc0..9780630 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 @@
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 a8f492f..590c620 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -966,8 +966,8 @@
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
- ((mIsRtl && getUnboundedScrollX() > mMaxScrollX)
- || (!mIsRtl && getUnboundedScrollX() < mMinScrollX));
+ ((mIsRtl && getUnboundedScroll() > mMaxScroll)
+ || (!mIsRtl && getUnboundedScroll() < mMinScroll));
}
@Override
@@ -1003,7 +1003,7 @@
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 5b73940..ab4cb6b 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -48,7 +48,7 @@
@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 3b5fd59..c6d62f8 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -258,7 +258,7 @@
@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 0000000..fd1154c
--- /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 812268a..b193ffd 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -176,7 +176,7 @@
// 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 852928b..95b13b4 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -17,15 +17,26 @@
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 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 @@
} 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 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 9df6241..34d69e9 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -230,7 +230,7 @@
}
@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 30283da..1276ece 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -236,7 +236,7 @@
} 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 0000000..3090d97
--- /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 <T> void set(T target, Int2DAction<T> action, int param) {
+ action.call(target, 0, param);
+ }
+
+ @Override
+ public <T> void set(T target, Float2DAction<T> 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<View> getPrimaryViewTranslate() {
+ return VIEW_TRANSLATE_Y;
+ }
+
+ @Override
+ public FloatProperty<View> 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 0000000..2f02076
--- /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<T> {
+ void call(T target, int x, int y);
+ }
+ interface Float2DAction<T> {
+ void call(T target, float x, float y);
+ }
+ Int2DAction<View> VIEW_SCROLL_BY = View::scrollBy;
+ Int2DAction<View> VIEW_SCROLL_TO = View::scrollTo;
+ Float2DAction<Canvas> CANVAS_TRANSLATE = Canvas::translate;
+ <T> void set(T target, Int2DAction<T> action, int param);
+ <T> void set(T target, Float2DAction<T> 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<View> getPrimaryViewTranslate();
+ FloatProperty<View> 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 0000000..fbd80bb
--- /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 <T> void set(T target, Int2DAction<T> action, int param) {
+ action.call(target, param, 0);
+ }
+
+ @Override
+ public <T> void set(T target, Float2DAction<T> 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<View> getPrimaryViewTranslate() {
+ return VIEW_TRANSLATE_X;
+ }
+
+ @Override
+ public FloatProperty<View> 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 0000000..f1875cc
--- /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 9d406f3..d725486 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -148,7 +148,8 @@
@Override
protected void reportDragStartInternal(boolean recatch) {
- mListener.onDragStart(!recatch);
+ float startDisplacement = mDir.extractDirection(mSubtractDisplacement);
+ mListener.onDragStart(!recatch, startDisplacement);
}
@Override
@@ -165,8 +166,13 @@
/** 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 bdba39c..11c1029 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -149,8 +149,7 @@
/* SingleAxisSwipeDetector.Listener */
@Override
- public void onDragStart(boolean start) {
- }
+ public void onDragStart(boolean start, float startDisplacement) { }
@Override
public boolean onDrag(float displacement) {