summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout-land/launcher.xml1
-rw-r--r--res/layout-port/launcher.xml1
-rw-r--r--res/layout/apps_customize_pane.xml1
-rw-r--r--src/com/android/launcher3/AppsCustomizePagedView.java1
-rw-r--r--src/com/android/launcher3/ButtonDropTarget.java7
-rw-r--r--src/com/android/launcher3/DragController.java21
-rw-r--r--src/com/android/launcher3/DragLayer.java92
-rw-r--r--src/com/android/launcher3/DropTarget.java21
-rw-r--r--src/com/android/launcher3/Folder.java9
-rw-r--r--src/com/android/launcher3/FolderIcon.java4
-rw-r--r--src/com/android/launcher3/Launcher.java3
-rw-r--r--src/com/android/launcher3/PagedView.java1288
-rw-r--r--src/com/android/launcher3/Workspace.java261
13 files changed, 1188 insertions, 522 deletions
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 5fe05dd67..6dfbf21a5 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -35,6 +35,7 @@
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center"
android:paddingStart="@dimen/workspace_left_padding"
android:paddingEnd="@dimen/workspace_right_padding"
android:paddingTop="@dimen/workspace_top_padding"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 2aec4e8c2..30eba1c7d 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -53,6 +53,7 @@
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center"
android:paddingStart="@dimen/workspace_left_padding"
android:paddingEnd="@dimen/workspace_right_padding"
android:paddingTop="@dimen/workspace_top_padding"
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index a67cf919a..7efe48638 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -55,6 +55,7 @@
android:id="@+id/apps_customize_pane_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center"
launcher:maxAppCellCountX="@integer/apps_customize_maxCellCountX"
launcher:maxAppCellCountY="@integer/apps_customize_maxCellCountY"
launcher:pageLayoutWidthGap="@dimen/apps_customize_pageLayoutWidthGap"
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 9bc1e745a..aa2d531cf 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -1696,7 +1696,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
return windowMaxIndex;
}
- @Override
protected String getCurrentPageDescription() {
int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
int stringId = R.string.default_scroll_format;
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index a7486a8e3..3fcc2b904 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -112,7 +112,7 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
}
@Override
- public void getHitRect(android.graphics.Rect outRect) {
+ public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
super.getHitRect(outRect);
outRect.bottom += mBottomDragPadding;
}
@@ -155,11 +155,6 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
return to;
}
- @Override
- public DropTarget getDropTargetDelegate(DragObject d) {
- return null;
- }
-
public void getLocationInDragLayer(int[] loc) {
mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 970ce2611..c59f34c25 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -509,10 +509,6 @@ public class DragController {
private void checkTouchMove(DropTarget dropTarget) {
if (dropTarget != null) {
- DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);
- if (delegate != null) {
- dropTarget = delegate;
- }
if (mLastDropTarget != dropTarget) {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
@@ -701,24 +697,15 @@ public class DragController {
if (!target.isDropEnabled())
continue;
- target.getHitRect(r);
-
- // Convert the hit rect to DragLayer coordinates
- target.getLocationInDragLayer(dropCoordinates);
- r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
+ target.getHitRectRelativeToDragLayer(r);
mDragObject.x = x;
mDragObject.y = y;
if (r.contains(x, y)) {
- DropTarget delegate = target.getDropTargetDelegate(mDragObject);
- if (delegate != null) {
- target = delegate;
- target.getLocationInDragLayer(dropCoordinates);
- }
- // Make dropCoordinates relative to the DropTarget
- dropCoordinates[0] = x - dropCoordinates[0];
- dropCoordinates[1] = y - dropCoordinates[1];
+ dropCoordinates[0] = x;
+ dropCoordinates[1] = y;
+ mLauncher.getDragLayer().mapCoordInSelfToDescendent((View) target, dropCoordinates);
return target;
}
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index b02f803d6..11539e99d 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -24,6 +24,7 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -295,8 +296,10 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
mTmpXY[0] = 0;
mTmpXY[1] = 0;
float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+
r.set(mTmpXY[0], mTmpXY[1],
- mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight());
+ (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
+ (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
return scale;
}
@@ -306,32 +309,93 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
return getDescendantCoordRelativeToSelf(child, loc);
}
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+ return getDescendantCoordRelativeToSelf(descendant, coord, false);
+ }
+
/**
* Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
* coordinates.
*
* @param descendant The descendant to which the passed coordinate is relative.
* @param coord The coordinate that we want mapped.
+ * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
+ * sometimes this is relevant as in a child's coordinates within the root descendant.
* @return The factor by which this descendant is scaled relative to this DragLayer. Caution
* this scale factor is assumed to be equal in X and Y, and so if at any point this
* assumption fails, we will need to return a pair of scale factors.
*/
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
+ boolean includeRootScroll) {
+ ArrayList<View> ancestorChain = new ArrayList<View>();
+
+ float[] pt = {coord[0], coord[1]};
+
+ View v = descendant;
+ while(v != this && v != null) {
+ ancestorChain.add(v);
+ v = (View) v.getParent();
+ }
+ ancestorChain.add(this);
+
float scale = 1.0f;
+ int count = ancestorChain.size();
+ for (int i = 0; i < count; i++) {
+ View v0 = ancestorChain.get(i);
+ View v1 = i < count -1 ? ancestorChain.get(i + 1) : null;
+
+ // For TextViews, scroll has a meaning which relates to the text position
+ // which is very strange... ignore the scroll.
+ if (v0 != descendant || includeRootScroll) {
+ pt[0] -= v0.getScrollX();
+ pt[1] -= v0.getScrollY();
+ }
+
+ v0.getMatrix().mapPoints(pt);
+ pt[0] += v0.getLeft();
+ pt[1] += v0.getTop();
+ scale *= v0.getScaleX();
+ }
+
+ coord[0] = (int) Math.round(pt[0]);
+ coord[1] = (int) Math.round(pt[1]);
+ return scale;
+ }
+
+ /**
+ * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
+ */
+ public float mapCoordInSelfToDescendent(View descendant, int[] coord) {
+ ArrayList<View> ancestorChain = new ArrayList<View>();
+
float[] pt = {coord[0], coord[1]};
- descendant.getMatrix().mapPoints(pt);
- scale *= descendant.getScaleX();
- pt[0] += descendant.getLeft();
- pt[1] += descendant.getTop();
- ViewParent viewParent = descendant.getParent();
- while (viewParent instanceof View && viewParent != this) {
- final View view = (View)viewParent;
- view.getMatrix().mapPoints(pt);
- scale *= view.getScaleX();
- pt[0] += view.getLeft() - view.getScrollX();
- pt[1] += view.getTop() - view.getScrollY();
- viewParent = view.getParent();
+
+ View v = descendant;
+ while(v != this) {
+ ancestorChain.add(v);
+ v = (View) v.getParent();
}
+ ancestorChain.add(this);
+
+ float scale = 1.0f;
+ Matrix inverse = new Matrix();
+ int count = ancestorChain.size();
+ for (int i = count - 1; i >= 0; i--) {
+ View ancestor = ancestorChain.get(i);
+ View next = i > 0 ? ancestorChain.get(i-1) : null;
+
+ pt[0] += ancestor.getScrollX();
+ pt[1] += ancestor.getScrollY();
+
+ if (next != null) {
+ pt[0] -= next.getLeft();
+ pt[1] -= next.getTop();
+ next.getMatrix().invert(inverse);
+ inverse.mapPoints(pt);
+ scale *= next.getScaleX();
+ }
+ }
+
coord[0] = (int) Math.round(pt[0]);
coord[1] = (int) Math.round(pt[1]);
return scale;
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index fa364fadf..64f0ac867 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -141,25 +141,6 @@ public interface DropTarget {
void onFlingToDelete(DragObject dragObject, int x, int y, PointF vec);
/**
- * Allows a DropTarget to delegate drag and drop events to another object.
- *
- * Most subclasses will should just return null from this method.
- *
- * @param source DragSource where the drag started
- * @param x X coordinate of the drop location
- * @param y Y coordinate of the drop location
- * @param xOffset Horizontal offset with the object being dragged where the original
- * touch happened
- * @param yOffset Vertical offset with the object being dragged where the original
- * touch happened
- * @param dragView The DragView that's being dragged around on screen.
- * @param dragInfo Data associated with the object being dragged
- *
- * @return The DropTarget to delegate to, or null to not delegate to another object.
- */
- DropTarget getDropTargetDelegate(DragObject dragObject);
-
- /**
* Check if a drop action can occur at, or near, the requested location.
* This will be called just before onDrop.
*
@@ -177,7 +158,7 @@ public interface DropTarget {
boolean acceptDrop(DragObject dragObject);
// These methods are implemented in Views
- void getHitRect(Rect outRect);
+ void getHitRectRelativeToDragLayer(Rect outRect);
void getLocationInDragLayer(int[] loc);
int getLeft();
int getTop();
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index cf1a43225..4de929725 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -832,10 +832,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return true;
}
- public DropTarget getDropTargetDelegate(DragObject d) {
- return null;
- }
-
private void setupContentDimensions(int count) {
ArrayList<View> list = getItemsInReadingOrder();
@@ -1208,4 +1204,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
startEditingFolderName();
}
}
+
+ @Override
+ public void getHitRectRelativeToDragLayer(Rect outRect) {
+ getHitRect(outRect);
+ }
}
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index e11d7d18a..92f126c3f 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -427,10 +427,6 @@ public class FolderIcon extends LinearLayout implements FolderListener {
onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
}
- public DropTarget getDropTargetDelegate(DragObject d) {
- return null;
- }
-
private void computePreviewDrawingParams(int drawableSize, int totalSize) {
if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
mIntrinsicIconSize = drawableSize;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 47fa66b77..b8fce6def 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2435,6 +2435,9 @@ public class Launcher extends Activity
// User long pressed on empty space
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ // Disabling reordering until we sort out some issues.
+ //mWorkspace.startReordering();
+ // TODO: need to have a new way to set wallpaper or start reordering
startWallpaper();
} else {
if (!(itemUnderLongClick instanceof Folder)) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 842dc2034..20d7e5e00 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -18,15 +18,23 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -39,11 +47,12 @@ import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
import android.widget.Scroller;
-import com.android.launcher3.R;
-
import java.util.ArrayList;
/**
@@ -58,8 +67,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// the min drag distance for a fling to register, to prevent random page shifts
private static final int MIN_LENGTH_FOR_FLING = 25;
- protected static final int PAGE_SNAP_ANIMATION_DURATION = 550;
- protected static final int MAX_PAGE_SNAP_DURATION = 750;
+ protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
protected static final float NANOTIME_DIV = 1000000000.0f;
@@ -76,6 +84,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private static final int MIN_SNAP_VELOCITY = 1500;
private static final int MIN_FLING_VELOCITY = 250;
+ // We are disabling touch interaction of the widget region for factory ROM.
+ private static final boolean DISABLE_TOUCH_INTERACTION = false;
+ private static final boolean DISABLE_TOUCH_SIDE_PAGES = false;
+ private static final boolean DISABLE_FLING_TO_DELETE = false;
+
static final int AUTOMATIC_PAGE_SPACING = -1;
protected int mFlingThresholdVelocity;
@@ -96,7 +109,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected Scroller mScroller;
private VelocityTracker mVelocityTracker;
+ private float mParentDownMotionX;
+ private float mParentDownMotionY;
private float mDownMotionX;
+ private float mDownMotionY;
+ private float mDownScrollX;
protected float mLastMotionX;
protected float mLastMotionXRemainder;
protected float mLastMotionY;
@@ -110,6 +127,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected final static int TOUCH_STATE_SCROLLING = 1;
protected final static int TOUCH_STATE_PREV_PAGE = 2;
protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+ protected final static int TOUCH_STATE_REORDERING = 4;
+
protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
protected int mTouchState = TOUCH_STATE_REST;
@@ -117,8 +136,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected OnLongClickListener mLongClickListener;
- protected boolean mAllowLongPress = true;
-
protected int mTouchSlop;
private int mPagingTouchSlop;
private int mMaximumVelocity;
@@ -158,7 +175,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected boolean mContentIsRefreshable = true;
// If true, modify alpha of neighboring pages as user scrolls left/right
- protected boolean mFadeInAdjacentScreens = true;
+ protected boolean mFadeInAdjacentScreens = false;
// It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
// to switch to a new page
@@ -173,6 +190,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// All syncs and layout passes are deferred until data is ready.
protected boolean mIsDataReady = false;
+ protected boolean mAllowLongPress = true;
+
// Scrolling indicator
private ValueAnimator mScrollIndicatorAnimator;
private View mScrollIndicator;
@@ -186,8 +205,64 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected static final int sScrollIndicatorFlashDuration = 650;
private boolean mScrollingPaused = false;
- // If set, will defer loading associated pages until the scrolling settles
- private boolean mDeferLoadAssociatedPagesUntilScrollCompletes;
+ // The viewport whether the pages are to be contained (the actual view may be larger than the
+ // viewport)
+ private Rect mViewport = new Rect();
+
+ // Reordering
+ // We use the min scale to determine how much to expand the actually PagedView measured
+ // dimensions such that when we are zoomed out, the view is not clipped
+ private int REORDERING_DROP_REPOSITION_DURATION = 200;
+ protected int REORDERING_REORDER_REPOSITION_DURATION = 300;
+ protected int REORDERING_ZOOM_IN_OUT_DURATION = 250;
+ private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300;
+ private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
+ private long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150;
+ private float mMinScale = 1f;
+ protected View mDragView;
+ protected AnimatorSet mZoomInOutAnim;
+ private Runnable mSidePageHoverRunnable;
+ private int mSidePageHoverIndex = -1;
+ // This variable's scope is only for the duration of startReordering() and endReordering()
+ private boolean mReorderingStarted = false;
+ // This variable's scope is for the duration of startReordering() and after the zoomIn()
+ // animation after endReordering()
+ private boolean mIsReordering;
+ // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
+ private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
+ private int mPostReorderingPreZoomInRemainingAnimationCount;
+ private Runnable mPostReorderingPreZoomInRunnable;
+
+ // Edge swiping
+ private boolean mOnlyAllowEdgeSwipes = false;
+ private boolean mDownEventOnEdge = false;
+ private int mEdgeSwipeRegionSize = 0;
+
+ // Convenience/caching
+ private Matrix mTmpInvMatrix = new Matrix();
+ private float[] mTmpPoint = new float[2];
+ private Rect mTmpRect = new Rect();
+ private Rect mAltTmpRect = new Rect();
+
+ // Fling to delete
+ private int FLING_TO_DELETE_FADE_OUT_DURATION = 350;
+ private float FLING_TO_DELETE_FRICTION = 0.035f;
+ // The degrees specifies how much deviation from the up vector to still consider a fling "up"
+ private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f;
+ protected int mFlingToDeleteThresholdVelocity = -1400;
+ // Drag to delete
+ private boolean mDeferringForDelete = false;
+ private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
+ private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350;
+
+ // Drop to delete
+ private View mDeleteDropTarget;
+
+ private boolean mAutoComputePageSpacing = false;
+ private boolean mRecomputePageSpacing = false;
+
+ // Bouncer
+ private boolean mTopAlignPageWhenShrinkingForBouncer = false;
public interface PageSwitchListener {
void onPageSwitch(View newPage, int newPageIndex);
@@ -203,10 +278,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
public PagedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
+ if (mPageSpacing < 0) {
+ mAutoComputePageSpacing = mRecomputePageSpacing = true;
+ }
mPageLayoutPaddingTop = a.getDimensionPixelSize(
R.styleable.PagedView_pageLayoutPaddingTop, 0);
mPageLayoutPaddingBottom = a.getDimensionPixelSize(
@@ -240,17 +317,86 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mCenterPagesVertically = true;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();
+ mTouchSlop = configuration.getScaledPagingTouchSlop();
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mDensity = getResources().getDisplayMetrics().density;
+ // Scale the fling-to-delete threshold by the density
+ mFlingToDeleteThresholdVelocity =
+ (int) (mFlingToDeleteThresholdVelocity * mDensity);
+
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
setOnHierarchyChangeListener(this);
}
+ void setDeleteDropTarget(View v) {
+ mDeleteDropTarget = v;
+ }
+
+ // Convenience methods to map points from self to parent and vice versa
+ float[] mapPointFromViewToParent(View v, float x, float y) {
+ mTmpPoint[0] = x;
+ mTmpPoint[1] = y;
+ v.getMatrix().mapPoints(mTmpPoint);
+ mTmpPoint[0] += v.getLeft();
+ mTmpPoint[1] += v.getTop();
+ return mTmpPoint;
+ }
+ float[] mapPointFromParentToView(View v, float x, float y) {
+ mTmpPoint[0] = x - v.getLeft();
+ mTmpPoint[1] = y - v.getTop();
+ v.getMatrix().invert(mTmpInvMatrix);
+ mTmpInvMatrix.mapPoints(mTmpPoint);
+ return mTmpPoint;
+ }
+
+ void updateDragViewTranslationDuringDrag() {
+ float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
+ float y = mLastMotionY - mDownMotionY;
+ mDragView.setTranslationX(x);
+ mDragView.setTranslationY(y);
+
+ if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
+ }
+
+ public void setMinScale(float f) {
+ mMinScale = f;
+ requestLayout();
+ }
+
+ @Override
+ public void setScaleX(float scaleX) {
+ super.setScaleX(scaleX);
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
+ }
+
+ // Convenience methods to get the actual width/height of the PagedView (since it is measured
+ // to be larger to account for the minimum possible scale)
+ int getViewportWidth() {
+ return mViewport.width();
+ }
+ int getViewportHeight() {
+ return mViewport.height();
+ }
+
+ // Convenience methods to get the offset ASSUMING that we are centering the pages in the
+ // PagedView both horizontally and vertically
+ int getViewportOffsetX() {
+ return (getMeasuredWidth() - getViewportWidth()) / 2;
+ }
+
+ int getViewportOffsetY() {
+ return (getMeasuredHeight() - getViewportHeight()) / 2;
+ }
+
public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
mPageSwitchListener = pageSwitchListener;
if (mPageSwitchListener != null) {
@@ -272,6 +418,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected void setDataIsReady() {
mIsDataReady = true;
}
+
protected boolean isDataReady() {
return mIsDataReady;
}
@@ -284,6 +431,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
int getCurrentPage() {
return mCurrentPage;
}
+
int getNextPage() {
return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
}
@@ -306,13 +454,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
* the previous tab page.
*/
protected void updateCurrentPageScroll() {
- // If the current page is invalid, just reset the scroll position to zero
- int newX = 0;
- if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
- int offset = getChildOffset(mCurrentPage);
- int relOffset = getRelativeChildOffset(mCurrentPage);
- newX = offset - relOffset;
- }
+ int offset = getChildOffset(mCurrentPage);
+ int relOffset = getRelativeChildOffset(mCurrentPage);
+ int newX = offset - relOffset;
scrollTo(newX, 0);
mScroller.setFinalX(newX);
mScroller.forceFinished(true);
@@ -362,7 +506,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
}
}
-
protected void pageBeginMoving() {
if (!mIsPageMoving) {
mIsPageMoving = true;
@@ -410,28 +553,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public void scrollTo(int x, int y) {
- final boolean isRtl = isLayoutRtl();
mUnboundedScrollX = x;
- boolean isXBeforeFirstPage = isRtl ? (x > mMaxScrollX) : (x < 0);
- boolean isXAfterLastPage = isRtl ? (x < 0) : (x > mMaxScrollX);
- if (isXBeforeFirstPage) {
+ if (x < 0) {
super.scrollTo(0, y);
if (mAllowOverScroll) {
- if (isRtl) {
- overScroll(x - mMaxScrollX);
- } else {
- overScroll(x);
- }
+ overScroll(x);
}
- } else if (isXAfterLastPage) {
+ } else if (x > mMaxScrollX) {
super.scrollTo(mMaxScrollX, y);
if (mAllowOverScroll) {
- if (isRtl) {
- overScroll(x);
- } else {
- overScroll(x - mMaxScrollX);
- }
+ overScroll(x - mMaxScrollX);
}
} else {
mOverScrollX = x;
@@ -440,6 +572,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mTouchX = x;
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+ // Update the last motion events when scrolling
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
}
// we moved this functionality to a helper function so SmoothPagedView can reuse it
@@ -458,27 +598,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mNextPage = INVALID_PAGE;
notifyPageSwitchListener();
- // Load the associated pages if necessary
- if (mDeferLoadAssociatedPagesUntilScrollCompletes) {
- loadAssociatedPages(mCurrentPage);
- mDeferLoadAssociatedPagesUntilScrollCompletes = false;
- }
-
// We don't want to trigger a page end moving unless the page has settled
// and the user has stopped scrolling
if (mTouchState == TOUCH_STATE_REST) {
pageEndMoving();
}
- // Notify the user when the page changes
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent ev =
- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- ev.getText().add(getCurrentPageDescription());
- sendAccessibilityEventUnchecked(ev);
- }
+ onPostReorderingAnimationCompleted();
return true;
}
return false;
@@ -489,6 +615,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
computeScrollHelper();
}
+ protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
+ return mTopAlignPageWhenShrinkingForBouncer;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mIsDataReady || getChildCount() == 0) {
@@ -496,12 +626,26 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return;
}
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ // We measure the dimensions of the PagedView to be larger than the pages so that when we
+ // zoom out (and scale down), the view is still contained in the parent
+ View parent = (View) getParent();
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- if (widthMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
+ // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
+ // viewport, we can be at most one and a half screens offset once we scale down
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ int maxSize = Math.max(dm.widthPixels, dm.heightPixels);
+ int parentWidthSize = (int) (1.5f * maxSize);
+ int parentHeightSize = maxSize;
+ int scaledWidthSize = (int) (parentWidthSize / mMinScale);
+ int scaledHeightSize = (int) (parentHeightSize / mMinScale);
+ mViewport.set(0, 0, widthSize, heightSize);
+
+ if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
}
// Return early if we aren't given a proper dimension
@@ -515,15 +659,16 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
* is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
* each page to have the same width.
*/
- int maxChildHeight = 0;
-
final int verticalPadding = getPaddingTop() + getPaddingBottom();
final int horizontalPadding = getPaddingLeft() + getPaddingRight();
-
// The children are given the same width and height as the workspace
// unless they were set to WRAP_CONTENT
if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
+ if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// disallowing padding in paged view (just pass 0)
@@ -550,33 +695,25 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
- if (DEBUG) Log.d(TAG, "\tmeasure-child" + i + ": " + child.getMeasuredWidth() + ", "
- + child.getMeasuredHeight());
- }
-
- if (heightMode == MeasureSpec.AT_MOST) {
- heightSize = maxChildHeight + verticalPadding;
}
-
- setMeasuredDimension(widthSize, heightSize);
+ setMeasuredDimension(scaledWidthSize, scaledHeightSize);
// We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
// We also wait until we set the measured dimensions before flushing the cache as well, to
// ensure that the cache is filled with good values.
invalidateCachedOffsets();
- if (mChildCountOnLastMeasure != getChildCount()) {
+ if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) {
setCurrentPage(mCurrentPage);
}
mChildCountOnLastMeasure = getChildCount();
if (childCount > 0) {
- if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
+ getChildWidth(0));
// Calculate the variable page spacing if necessary
- if (mPageSpacing == AUTOMATIC_PAGE_SPACING) {
+ if (mAutoComputePageSpacing && mRecomputePageSpacing) {
// The gap between pages in the PagedView should be equal to the gap from the page
// to the edge of the screen (so it is not visible in the current screen). To
// account for unequal padding on each side of the paged view, we take the maximum
@@ -585,64 +722,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
int spacing = Math.max(offset, widthSize - offset -
getChildAt(0).getMeasuredWidth());
setPageSpacing(spacing);
+ mRecomputePageSpacing = false;
}
}
updateScrollingIndicatorPosition();
if (childCount > 0) {
- final int index = isLayoutRtl() ? 0 : childCount - 1;
- mMaxScrollX = getChildOffset(index) - getRelativeChildOffset(index);
+ mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
} else {
mMaxScrollX = 0;
}
}
- protected void scrollToNewPageWithoutMovingPages(int newCurrentPage) {
- int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
- int delta = newX - getScrollX();
-
- final int pageCount = getChildCount();
- for (int i = 0; i < pageCount; i++) {
- View page = (View) getPageAt(i);
- page.setX(page.getX() + delta);
- }
- setCurrentPage(newCurrentPage);
- }
-
- // A layout scale of 1.0f assumes that the pages, in their unshrunken state, have a
- // scale of 1.0f. A layout scale of 0.8f assumes the pages have a scale of 0.8f, and
- // tightens the layout accordingly
- public void setLayoutScale(float childrenScale) {
- mLayoutScale = childrenScale;
- invalidateCachedOffsets();
-
- // Now we need to do a re-layout, but preserving absolute X and Y coordinates
- int childCount = getChildCount();
- float childrenX[] = new float[childCount];
- float childrenY[] = new float[childCount];
- for (int i = 0; i < childCount; i++) {
- final View child = getPageAt(i);
- childrenX[i] = child.getX();
- childrenY[i] = child.getY();
- }
- // Trigger a full re-layout (never just call onLayout directly!)
- int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
- int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
- requestLayout();
- measure(widthSpec, heightSpec);
- layout(getLeft(), getTop(), getRight(), getBottom());
- for (int i = 0; i < childCount; i++) {
- final View child = getPageAt(i);
- child.setX(childrenX[i]);
- child.setY(childrenY[i]);
- }
-
- // Also, the page offset has changed (since the pages are now smaller);
- // update the page offset, but again preserving absolute X and Y coordinates
- scrollToNewPageWithoutMovingPages(mCurrentPage);
- }
-
public void setPageSpacing(int pageSpacing) {
mPageSpacing = pageSpacing;
invalidateCachedOffsets();
@@ -655,23 +747,25 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
- final int verticalPadding = getPaddingTop() + getPaddingBottom();
final int childCount = getChildCount();
- final boolean isRtl = isLayoutRtl();
- final int startIndex = isRtl ? childCount - 1 : 0;
- final int endIndex = isRtl ? -1 : childCount;
- final int delta = isRtl ? -1 : 1;
- int childLeft = getRelativeChildOffset(startIndex);
- for (int i = startIndex; i != endIndex; i += delta) {
+ int offsetX = getViewportOffsetX();
+ int offsetY = getViewportOffsetY();
+
+ // Update the viewport offsets
+ mViewport.offset(offsetX, offsetY);
+
+ int verticalPadding = getPaddingTop() + getPaddingBottom();
+ int childLeft = offsetX + getRelativeChildOffset(0);
+ for (int i = 0; i < childCount; i++) {
final View child = getPageAt(i);
+ int childTop = offsetY + getPaddingTop();
+ if (mCenterPagesVertically) {
+ childTop += ((getViewportHeight() - verticalPadding) - child.getMeasuredHeight()) / 2;
+ }
if (child.getVisibility() != View.GONE) {
final int childWidth = getScaledMeasuredWidth(child);
final int childHeight = child.getMeasuredHeight();
- int childTop = getPaddingTop();
- if (mCenterPagesVertically) {
- childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
- }
if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
child.layout(childLeft, childTop,
@@ -712,12 +806,16 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
mForceScreenScrolled = true;
+ mRecomputePageSpacing = true;
invalidate();
invalidateCachedOffsets();
}
@Override
public void onChildViewRemoved(View parent, View child) {
+ mForceScreenScrolled = true;
+ invalidate();
+ invalidateCachedOffsets();
}
protected void invalidateCachedOffsets() {
@@ -740,7 +838,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected int getChildOffset(int index) {
- final boolean isRtl = isLayoutRtl();
+ if (index < 0 || index > getChildCount() - 1) return 0;
+
int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
mChildOffsets : mChildOffsetsWithLayoutScale;
@@ -750,11 +849,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (getChildCount() == 0)
return 0;
- final int startIndex = isRtl ? getChildCount() - 1 : 0;
- final int endIndex = isRtl ? index : index;
- final int delta = isRtl ? -1 : 1;
- int offset = getRelativeChildOffset(startIndex);
- for (int i = startIndex; i != endIndex; i += delta) {
+ int offset = getRelativeChildOffset(0);
+ for (int i = 0; i < index; ++i) {
offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
}
if (childOffsets != null) {
@@ -765,12 +861,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected int getRelativeChildOffset(int index) {
+ if (index < 0 || index > getChildCount() - 1) return 0;
+
if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
return mChildRelativeOffsets[index];
} else {
final int padding = getPaddingLeft() + getPaddingRight();
final int offset = getPaddingLeft() +
- (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
+ (getViewportWidth() - padding - getChildWidth(index)) / 2;
if (mChildRelativeOffsets != null) {
mChildRelativeOffsets[index] = offset;
}
@@ -787,36 +885,47 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return (int) (maxWidth * mLayoutScale + 0.5f);
}
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ // Do nothing
+ }
+
+ // TODO: Fix this
protected void getVisiblePages(int[] range) {
- final boolean isRtl = isLayoutRtl();
+ range[0] = 0;
+ range[1] = getPageCount() - 1;
+
+ /*
final int pageCount = getChildCount();
if (pageCount > 0) {
- final int screenWidth = getMeasuredWidth();
- int leftScreen = isRtl ? pageCount - 1 : 0;
+ final int screenWidth = getViewportWidth();
+ int leftScreen = 0;
int rightScreen = 0;
- int endIndex = isRtl ? 0 : pageCount - 1;
- int delta = isRtl ? -1 : 1;
+ int offsetX = getViewportOffsetX() + getScrollX();
View currPage = getPageAt(leftScreen);
- while (leftScreen != endIndex &&
+ while (leftScreen < pageCount - 1 &&
currPage.getX() + currPage.getWidth() -
- currPage.getPaddingRight() < getScrollX()) {
- leftScreen += delta;
+ currPage.getPaddingRight() < offsetX) {
+ leftScreen++;
currPage = getPageAt(leftScreen);
}
rightScreen = leftScreen;
- currPage = getPageAt(rightScreen + delta);
- while (rightScreen != endIndex &&
- currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
- rightScreen += delta;
- currPage = getPageAt(rightScreen + delta);
+ currPage = getPageAt(rightScreen + 1);
+ while (rightScreen < pageCount - 1 &&
+ currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
+ rightScreen++;
+ currPage = getPageAt(rightScreen + 1);
}
- range[0] = Math.min(leftScreen, rightScreen);
- range[1] = Math.max(leftScreen, rightScreen);
+
+ // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
+ // because we don't draw them while scrolling?
+ range[0] = Math.max(0, leftScreen - 1);
+ range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
} else {
range[0] = -1;
range[1] = -1;
}
+ */
}
protected boolean shouldDrawChild(View child) {
@@ -825,7 +934,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
protected void dispatchDraw(Canvas canvas) {
- int halfScreenSize = getMeasuredWidth() / 2;
+ int halfScreenSize = getViewportWidth() / 2;
// mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
// Otherwise it is equal to the scaled overscroll position.
int screenCenter = mOverScrollX + halfScreenSize;
@@ -851,13 +960,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
getScrollY() + getBottom() - getTop());
- for (int i = getChildCount() - 1; i >= 0; i--) {
+ // Draw all the children, leaving the drag view for last
+ for (int i = pageCount - 1; i >= 0; i--) {
final View v = getPageAt(i);
+ if (v == mDragView) continue;
if (mForceDrawAllChildrenNextFrame ||
(leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
drawChild(canvas, v, drawingTime);
}
}
+ // Draw the drag view on top (if there is one)
+ if (mDragView != null) {
+ drawChild(canvas, mDragView, drawingTime);
+ }
+
mForceDrawAllChildrenNextFrame = false;
canvas.restore();
}
@@ -891,7 +1007,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- // XXX-RTL: This will be fixed in a future CL
if (direction == View.FOCUS_LEFT) {
if (getCurrentPage() > 0) {
snapToPage(getCurrentPage() - 1);
@@ -908,7 +1023,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- // XXX-RTL: This will be fixed in a future CL
if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
}
@@ -969,27 +1083,40 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
* Return true if a tap at (x, y) should trigger a flip to the previous page.
*/
protected boolean hitsPreviousPage(float x, float y) {
- if (isLayoutRtl()) {
- return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
- } else {
- return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
- }
+ return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
}
/**
* Return true if a tap at (x, y) should trigger a flip to the next page.
*/
protected boolean hitsNextPage(float x, float y) {
- if (isLayoutRtl()) {
- return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
- } else {
- return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
- }
+ return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+ }
+ /** Returns whether x and y originated within the buffered viewport */
+ private boolean isTouchPointInViewportWithBuffer(int x, int y) {
+ mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
+ mViewport.right + mViewport.width() / 2, mViewport.bottom);
+ return mTmpRect.contains(x, y);
+ }
+
+ /** Returns whether x and y originated within the current page view bounds */
+ private boolean isTouchPointInCurrentPage(int x, int y) {
+ View v = getPageAt(getCurrentPage());
+ if (v != null) {
+ mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()),
+ v.getBottom());
+ return mTmpRect.contains(x, y);
+ }
+ return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DISABLE_TOUCH_INTERACTION) {
+ return false;
+ }
+
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
@@ -1033,12 +1160,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
final float y = ev.getY();
// Remember location of down touch
mDownMotionX = x;
+ mDownMotionY = y;
+ mDownScrollX = getScrollX();
mLastMotionX = x;
mLastMotionY = y;
+ float[] p = mapPointFromViewToParent(this, x, y);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
- mAllowLongPress = true;
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
/*
* If being flinged and user touches the screen, initiate drag;
@@ -1051,17 +1189,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mTouchState = TOUCH_STATE_REST;
mScroller.abortAnimation();
} else {
- mTouchState = TOUCH_STATE_SCROLLING;
+ if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
+ mTouchState = TOUCH_STATE_SCROLLING;
+ } else {
+ mTouchState = TOUCH_STATE_REST;
+ }
}
// check if this can be the beginning of a tap on the side of the pages
// to scroll the current page
- if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
- if (getChildCount() > 0) {
- if (hitsPreviousPage(x, y)) {
- mTouchState = TOUCH_STATE_PREV_PAGE;
- } else if (hitsNextPage(x, y)) {
- mTouchState = TOUCH_STATE_NEXT_PAGE;
+ if (!DISABLE_TOUCH_SIDE_PAGES) {
+ if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
+ if (getChildCount() > 0) {
+ if (hitsPreviousPage(x, y)) {
+ mTouchState = TOUCH_STATE_PREV_PAGE;
+ } else if (hitsNextPage(x, y)) {
+ mTouchState = TOUCH_STATE_NEXT_PAGE;
+ }
}
}
}
@@ -1070,10 +1214,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST;
- mAllowLongPress = false;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
+ // Just intercept the touch event on up if we tap outside the strict viewport
+ if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) {
+ return true;
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -1098,16 +1243,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
* user moves their touch point too far.
*/
protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
- /*
- * Locally do absolute value. mLastMotionX is set to the y value
- * of the down event.
- */
+ // Disallow scrolling if we don't have a valid pointer index
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == -1) {
- return;
- }
+ if (pointerIndex == -1) return;
+
+ // Disallow scrolling if we started the gesture from outside the viewport
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
+ if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;
+
+ // If we're only allowing edge swipes, we break out early if the down event wasn't
+ // at the edge.
+ if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return;
+
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
@@ -1123,18 +1271,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mTotalMotionX += Math.abs(mLastMotionX - x);
mLastMotionX = x;
mLastMotionXRemainder = 0;
- mTouchX = getScrollX();
+ mTouchX = getViewportOffsetX() + getScrollX();
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
pageBeginMoving();
}
- // Either way, cancel any pending longpress
- cancelCurrentPageLongPress();
}
}
+ protected float getMaxScrollProgress() {
+ return 1.0f;
+ }
+
protected void cancelCurrentPageLongPress() {
if (mAllowLongPress) {
- mAllowLongPress = false;
+ //mAllowLongPress = false;
// Try canceling the long press. It could also have been scheduled
// by a distant descendant, so use the mAllowLongPress flag to block
// everything
@@ -1145,16 +1295,25 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
+ protected float getBoundedScrollProgress(int screenCenter, View v, int page) {
+ final int halfScreenSize = getViewportWidth() / 2;
+
+ screenCenter = Math.min(getScrollX() + halfScreenSize, screenCenter);
+ screenCenter = Math.max(halfScreenSize, screenCenter);
+
+ return getScrollProgress(screenCenter, v, page);
+ }
+
protected float getScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getMeasuredWidth() / 2;
+ final int halfScreenSize = getViewportWidth() / 2;
int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
int delta = screenCenter - (getChildOffset(page) -
getRelativeChildOffset(page) + halfScreenSize);
float scrollProgress = delta / (totalDistance * 1.0f);
- scrollProgress = Math.min(scrollProgress, 1.0f);
- scrollProgress = Math.max(scrollProgress, -1.0f);
+ scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
+ scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
return scrollProgress;
}
@@ -1166,7 +1325,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void acceleratedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
// We want to reach the max over scroll effect when the user has
// over scrolled half the size of the screen
@@ -1191,7 +1350,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void dampedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
float f = (amount / screenSize);
@@ -1228,6 +1387,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (DISABLE_TOUCH_INTERACTION) {
+ return false;
+ }
+
// Skip touch handling if there are no pages to swipe
if (getChildCount() <= 0) return super.onTouchEvent(ev);
@@ -1247,9 +1410,22 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
+ mDownMotionY = mLastMotionY = ev.getY();
+ mDownScrollX = getScrollX();
+ float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
+
if (mTouchState == TOUCH_STATE_SCROLLING) {
pageBeginMoving();
}
@@ -1259,6 +1435,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (mTouchState == TOUCH_STATE_SCROLLING) {
// 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;
@@ -1281,6 +1460,112 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
} else {
awakenScrollBars();
}
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ // Update the last motion position
+ mLastMotionX = ev.getX();
+ mLastMotionY = ev.getY();
+
+ // Update the parent down so that our zoom animations take this new movement into
+ // account
+ float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = pt[0];
+ mParentDownMotionY = pt[1];
+ updateDragViewTranslationDuringDrag();
+
+ // Find the closest page to the touch point
+ final int dragViewIndex = indexOfChild(mDragView);
+ int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
+ getViewportWidth());
+ int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0]
+ + bufferSize);
+ int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0]
+ - bufferSize);
+
+ // Change the drag view if we are hovering over the drop target
+ boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget(
+ (int) mParentDownMotionX, (int) mParentDownMotionY);
+ setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete);
+
+ if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
+ if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
+ if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
+ if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
+
+ float parentX = mParentDownMotionX;
+ int pageIndexToSnapTo = -1;
+ if (parentX < leftBufferEdge && dragViewIndex > 0) {
+ pageIndexToSnapTo = dragViewIndex - 1;
+ } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
+ pageIndexToSnapTo = dragViewIndex + 1;
+ }
+
+ final int pageUnderPointIndex = pageIndexToSnapTo;
+ if (pageUnderPointIndex > -1 && !isHoveringOverDelete) {
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
+ pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
+ pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
+ mSidePageHoverIndex = pageUnderPointIndex;
+ mSidePageHoverRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Update the down scroll position to account for the fact that the
+ // current page is moved
+ mDownScrollX = getChildOffset(pageUnderPointIndex)
+ - getRelativeChildOffset(pageUnderPointIndex);
+
+ // Setup the scroll to the correct page before we swap the views
+ snapToPage(pageUnderPointIndex);
+
+ // For each of the pages between the paged view and the drag view,
+ // animate them from the previous position to the new position in
+ // the layout (as a result of the drag view moving in the layout)
+ int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
+ int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
+ dragViewIndex + 1 : pageUnderPointIndex;
+ int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
+ dragViewIndex - 1 : pageUnderPointIndex;
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = getViewportOffsetX() + getChildOffset(i);
+ int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag(ANIM_TAG_KEY);
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f));
+ anim.start();
+ v.setTag(anim);
+ }
+
+ removeView(mDragView);
+ onRemoveView(mDragView, false);
+ addView(mDragView, pageUnderPointIndex);
+ onAddView(mDragView, pageUnderPointIndex);
+ mSidePageHoverIndex = -1;
+ }
+ };
+ postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
+ }
+ } else {
+ removeCallbacks(mSidePageHoverRunnable);
+ mSidePageHoverIndex = -1;
+ }
} else {
determineScrollingStart(ev);
}
@@ -1317,15 +1602,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// We give flings precedence over large moves, which is why we short-circuit our
// 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.
- final boolean isRtl = isLayoutRtl();
- boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
- boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
- if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
- (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
+ if (((isSignificantMove && deltaX > 0 && !isFling) ||
+ (isFling && velocityX > 0)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
snapToPageWithVelocity(finalPage, velocityX);
- } else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
- (isFling && isVelocityXLeft)) &&
+ } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
+ (isFling && velocityX < 0)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
snapToPageWithVelocity(finalPage, velocityX);
@@ -1352,21 +1634,45 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
} else {
snapToDestination();
}
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ // Update the last motion position
+ mLastMotionX = ev.getX();
+ mLastMotionY = ev.getY();
+
+ // Update the parent down so that our zoom animations take this new movement into
+ // account
+ float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
+ mParentDownMotionX = pt[0];
+ mParentDownMotionY = pt[1];
+ updateDragViewTranslationDuringDrag();
+ boolean handledFling = false;
+ if (!DISABLE_FLING_TO_DELETE) {
+ // Check the velocity and see if we are flinging-to-delete
+ PointF flingToDeleteVector = isFlingingToDelete();
+ if (flingToDeleteVector != null) {
+ onFlingToDelete(flingToDeleteVector);
+ handledFling = true;
+ }
+ }
+ if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX,
+ (int) mParentDownMotionY)) {
+ onDropToDelete();
+ }
} else {
onUnhandledTap(ev);
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+
+ // Remove the callback to wait for the side page hover timeout
+ removeCallbacks(mSidePageHoverRunnable);
+ // End any intermediate reordering states
+ resetTouchState();
break;
case MotionEvent.ACTION_CANCEL:
if (mTouchState == TOUCH_STATE_SCROLLING) {
snapToDestination();
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -1374,9 +1680,25 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
break;
}
+ System.out.println("onTouch, return true");
return true;
}
+ public void onFlingToDelete(View v) {}
+ public void onRemoveView(View v, boolean deletePermanently) {}
+ public void onRemoveViewAnimationCompleted() {}
+ public void onAddView(View v, int index) {}
+
+ private void resetTouchState() {
+ releaseVelocityTracker();
+ endReordering();
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ mDownEventOnEdge = false;
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {}
+
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1393,9 +1715,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
}
if (hscroll != 0 || vscroll != 0) {
- boolean isForwardScroll = isLayoutRtl() ? (hscroll < 0 || vscroll < 0)
- : (hscroll > 0 || vscroll > 0);
- if (isForwardScroll) {
+ if (hscroll > 0 || vscroll > 0) {
scrollRight();
} else {
scrollLeft();
@@ -1441,8 +1761,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
- protected void onUnhandledTap(MotionEvent ev) {}
-
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
@@ -1453,14 +1771,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected int getChildIndexForRelativeOffset(int relativeOffset) {
- final boolean isRtl = isLayoutRtl();
final int childCount = getChildCount();
int left;
int right;
- final int startIndex = isRtl ? childCount - 1 : 0;
- final int endIndex = isRtl ? -1 : childCount;
- final int delta = isRtl ? -1 : 1;
- for (int i = startIndex; i != endIndex; i += delta) {
+ for (int i = 0; i < childCount; ++i) {
left = getRelativeChildOffset(i);
right = (left + getScaledMeasuredWidth(getPageAt(i)));
if (left <= relativeOffset && relativeOffset <= right) {
@@ -1478,16 +1792,28 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return (minWidth > measuredWidth) ? minWidth : measuredWidth;
}
+ int getPageNearestToPoint(float x) {
+ int index = 0;
+ for (int i = 0; i < getChildCount(); ++i) {
+ if (x < getChildAt(i).getRight() - getScrollX()) {
+ return index;
+ } else {
+ index++;
+ }
+ }
+ return Math.min(index, getChildCount() - 1);
+ }
+
int getPageNearestToCenterOfScreen() {
int minDistanceFromScreenCenter = Integer.MAX_VALUE;
int minDistanceFromScreenCenterIndex = -1;
- int screenCenter = getScrollX() + (getMeasuredWidth() / 2);
+ int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View layout = (View) getPageAt(i);
int childWidth = getScaledMeasuredWidth(layout);
int halfChildWidth = (childWidth / 2);
- int childCenter = getChildOffset(i) + halfChildWidth;
+ int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1523,11 +1849,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected void snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
- int halfScreenSize = getMeasuredWidth() / 2;
+ int halfScreenSize = getViewportWidth() / 2;
if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
- + getMeasuredWidth() + ", " + getChildWidth(whichPage));
+ + getViewportWidth() + ", " + getChildWidth(whichPage));
final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
int delta = newX - mUnboundedScrollX;
int duration = 0;
@@ -1554,7 +1880,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// user flings, so we scale the duration by a value near to the derivative of the scroll
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- duration = Math.min(duration, MAX_PAGE_SNAP_DURATION);
snapToPage(whichPage, delta, duration);
}
@@ -1563,21 +1888,32 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
+ protected void snapToPageImmediately(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+ }
+
protected void snapToPage(int whichPage, int duration) {
+ snapToPage(whichPage, duration, false);
+ }
+
+ protected void snapToPage(int whichPage, int duration, boolean immediate) {
whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
- if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
+ getChildWidth(whichPage));
int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
final int sX = mUnboundedScrollX;
final int delta = newX - sX;
- snapToPage(whichPage, delta, duration);
+ snapToPage(whichPage, delta, duration, immediate);
}
protected void snapToPage(int whichPage, int delta, int duration) {
- mNextPage = whichPage;
+ snapToPage(whichPage, delta, duration, false);
+ }
+ protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
+ mNextPage = whichPage;
View focusedChild = getFocusedChild();
if (focusedChild != null && whichPage != mCurrentPage &&
focusedChild == getPageAt(mCurrentPage)) {
@@ -1586,21 +1922,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
pageBeginMoving();
awakenScrollBars(duration);
- if (duration == 0) {
+ if (immediate) {
+ duration = 0;
+ } else if (duration == 0) {
duration = Math.abs(delta);
}
if (!mScroller.isFinished()) mScroller.abortAnimation();
mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
- // Load associated pages immediately if someone else is handling the scroll, otherwise defer
- // loading associated pages until the scroll settles
- if (mDeferScrollUpdate) {
- loadAssociatedPages(mNextPage);
- } else {
- mDeferLoadAssociatedPagesUntilScrollCompletes = true;
- }
notifyPageSwitchListener();
+
+ // Trigger a compute() to finish switching pages if necessary
+ if (immediate) {
+ computeScroll();
+ }
+
+ mForceScreenScrolled = true;
invalidate();
}
@@ -1806,6 +2144,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
hideScrollingIndicator(false);
}
};
+
protected void flashScrollingIndicator(boolean animated) {
removeCallbacks(hideScrollingIndicatorRunnable);
showScrollingIndicator(!animated);
@@ -1825,10 +2164,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
updateScrollingIndicatorPosition();
mScrollIndicator.setVisibility(View.VISIBLE);
cancelScrollingIndicatorAnimations();
- if (immediately || mScrollingPaused) {
+ if (immediately) {
mScrollIndicator.setAlpha(1f);
} else {
- mScrollIndicatorAnimator = LauncherAnimUtils.ofFloat(mScrollIndicator, "alpha", 1f);
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
mScrollIndicatorAnimator.start();
}
@@ -1850,11 +2189,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Fade the indicator out
updateScrollingIndicatorPosition();
cancelScrollingIndicatorAnimations();
- if (immediately || mScrollingPaused) {
+ if (immediately) {
mScrollIndicator.setVisibility(View.INVISIBLE);
mScrollIndicator.setAlpha(0f);
} else {
- mScrollIndicatorAnimator = LauncherAnimUtils.ofFloat(mScrollIndicator, "alpha", 0f);
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
private boolean cancelled = false;
@@ -1896,20 +2235,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
private void updateScrollingIndicatorPosition() {
- final boolean isRtl = isLayoutRtl();
if (!isScrollingIndicatorEnabled()) return;
if (mScrollIndicator == null) return;
int numPages = getChildCount();
- int pageWidth = getMeasuredWidth();
+ int pageWidth = getViewportWidth();
+ int lastChildIndex = Math.max(0, getChildCount() - 1);
+ int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
- float scrollPos = isRtl ? mMaxScrollX - getScrollX() : getScrollX();
- float offset = Math.max(0f, Math.min(1f, (float) scrollPos / mMaxScrollX));
- if (isRtl) {
- offset = 1f - offset;
- }
+ float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
int indicatorSpace = trackWidth / numPages;
int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
if (hasElasticScrollIndicator()) {
@@ -1924,10 +2260,469 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mScrollIndicator.setTranslationX(indicatorPos);
}
- public void showScrollIndicatorTrack() {
+ // Animate the drag view back to the original position
+ void animateDragViewToOriginalPosition() {
+ if (mDragView != null) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
+ ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onPostReorderingAnimationCompleted();
+ }
+ });
+ anim.start();
+ }
+ }
+
+ // "Zooms out" the PagedView to reveal more side pages
+ protected boolean zoomOut() {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+
+ if (!(getScaleX() < 1f || getScaleY() < 1f)) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
+ ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
+ mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Show the delete drop target
+ if (mDeleteDropTarget != null) {
+ mDeleteDropTarget.setVisibility(View.VISIBLE);
+ mDeleteDropTarget.animate().alpha(1f)
+ .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mDeleteDropTarget.setAlpha(0f);
+ }
+ });
+ }
+ }
+ });
+ mZoomInOutAnim.start();
+ return true;
+ }
+ return false;
+ }
+
+ protected void onStartReordering() {
+ // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
+ mTouchState = TOUCH_STATE_REORDERING;
+ mIsReordering = true;
+
+ // Mark all the non-widget pages as invisible
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ for (int i = 0; i < getPageCount(); ++i) {
+ if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+ getPageAt(i).setAlpha(0f);
+ }
+ }
+
+ // We must invalidate to trigger a redraw to update the layers such that the drag view
+ // is always drawn on top
+ invalidate();
+ }
+
+ private void onPostReorderingAnimationCompleted() {
+ // Trigger the callback when reordering has settled
+ --mPostReorderingPreZoomInRemainingAnimationCount;
+ if (mPostReorderingPreZoomInRunnable != null &&
+ mPostReorderingPreZoomInRemainingAnimationCount == 0) {
+ mPostReorderingPreZoomInRunnable.run();
+ mPostReorderingPreZoomInRunnable = null;
+ }
+ }
+
+ protected void onEndReordering() {
+ mIsReordering = false;
+
+ // Mark all the non-widget pages as visible again
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ for (int i = 0; i < getPageCount(); ++i) {
+ if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+ getPageAt(i).setAlpha(1f);
+ }
+ }
+ }
+
+ public boolean startReordering() {
+ int dragViewIndex = getPageNearestToCenterOfScreen();
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ mReorderingStarted = true;
+
+ // Check if we are within the reordering range
+ if (mTempVisiblePagesRange[0] <= dragViewIndex &&
+ dragViewIndex <= mTempVisiblePagesRange[1]) {
+ if (zoomOut()) {
+ // Find the drag view under the pointer
+ mDragView = getChildAt(dragViewIndex);
+
+ onStartReordering();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean isReordering(boolean testTouchState) {
+ boolean state = mIsReordering;
+ if (testTouchState) {
+ state &= (mTouchState == TOUCH_STATE_REORDERING);
+ }
+ return state;
+ }
+ void endReordering() {
+ // For simplicity, we call endReordering sometimes even if reordering was never started.
+ // In that case, we don't want to do anything.
+ if (!mReorderingStarted) return;
+ mReorderingStarted = false;
+
+ // If we haven't flung-to-delete the current child, then we just animate the drag view
+ // back into position
+ final Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onEndReordering();
+ }
+ };
+ if (!mDeferringForDelete) {
+ mPostReorderingPreZoomInRunnable = new Runnable() {
+ public void run() {
+ zoomIn(onCompleteRunnable);
+ };
+ };
+
+ mPostReorderingPreZoomInRemainingAnimationCount =
+ NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
+ // Snap to the current page
+ snapToPage(indexOfChild(mDragView), 0);
+ // Animate the drag view back to the front position
+ animateDragViewToOriginalPosition();
+ } else {
+ // Handled in post-delete-animation-callbacks
+ }
+ }
+
+ // "Zooms in" the PagedView to highlight the current page
+ protected boolean zoomIn(final Runnable onCompleteRunnable) {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+ if (getScaleX() < 1f || getScaleY() < 1f) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f));
+ mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Hide the delete drop target
+ if (mDeleteDropTarget != null) {
+ mDeleteDropTarget.animate().alpha(0f)
+ .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDeleteDropTarget.setVisibility(View.GONE);
+ }
+ });
+ }
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mDragView = null;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDragView = null;
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ });
+ mZoomInOutAnim.start();
+ return true;
+ } else {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Flinging to delete - IN PROGRESS
+ */
+ private PointF isFlingingToDelete() {
+ ViewConfiguration config = ViewConfiguration.get(getContext());
+ mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+
+ if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+ // Do a quick dot product test to ensure that we are flinging upwards
+ PointF vel = new PointF(mVelocityTracker.getXVelocity(),
+ mVelocityTracker.getYVelocity());
+ PointF upVec = new PointF(0f, -1f);
+ float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
+ (vel.length() * upVec.length()));
+ if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
+ return vel;
+ }
+ }
+ return null;
}
- public void hideScrollIndicatorTrack() {
+ /**
+ * Creates an animation from the current drag view along its current velocity vector.
+ * For this animation, the alpha runs for a fixed duration and we update the position
+ * progressively.
+ */
+ private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
+ private View mDragView;
+ private PointF mVelocity;
+ private Rect mFrom;
+ private long mPrevTime;
+ private float mFriction;
+
+ private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+
+ public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from,
+ long startTime, float friction) {
+ mDragView = dragView;
+ mVelocity = vel;
+ mFrom = from;
+ mPrevTime = startTime;
+ mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = ((Float) animation.getAnimatedValue()).floatValue();
+ long curTime = AnimationUtils.currentAnimationTimeMillis();
+
+ mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
+ mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
+
+ mDragView.setTranslationX(mFrom.left);
+ mDragView.setTranslationY(mFrom.top);
+ mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+
+ mVelocity.x *= mFriction;
+ mVelocity.y *= mFriction;
+ mPrevTime = curTime;
+ }
+ };
+
+ private static final int ANIM_TAG_KEY = 100;
+
+ private Runnable createPostDeleteAnimationRunnable(final View dragView) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ int dragViewIndex = indexOfChild(dragView);
+
+ // For each of the pages around the drag view, animate them from the previous
+ // position to the new position in the layout (as a result of the drag view moving
+ // in the layout)
+ // NOTE: We can make an assumption here because we have side-bound pages that we
+ // will always have pages to animate in from the left
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
+ boolean slideFromLeft = (isLastWidgetPage ||
+ dragViewIndex > mTempVisiblePagesRange[0]);
+
+ // Setup the scroll to the correct page before we swap the views
+ if (slideFromLeft) {
+ snapToPageImmediately(dragViewIndex - 1);
+ }
+
+ int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]);
+ int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1);
+ int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 );
+ int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex);
+ ArrayList<Animator> animations = new ArrayList<Animator>();
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = 0;
+ int newX = 0;
+ if (slideFromLeft) {
+ if (i == 0) {
+ // Simulate the page being offscreen with the page spacing
+ oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i)
+ - mPageSpacing;
+ } else {
+ oldX = getViewportOffsetX() + getChildOffset(i - 1);
+ }
+ newX = getViewportOffsetX() + getChildOffset(i);
+ } else {
+ oldX = getChildOffset(i) - getChildOffset(i - 1);
+ newX = 0;
+ }
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag();
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ // Note: Hacky, but we want to skip any optimizations to not draw completely
+ // hidden views
+ v.setAlpha(Math.max(v.getAlpha(), 0.01f));
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f),
+ ObjectAnimator.ofFloat(v, "alpha", 1f));
+ animations.add(anim);
+ v.setTag(ANIM_TAG_KEY, anim);
+ }
+
+ AnimatorSet slideAnimations = new AnimatorSet();
+ slideAnimations.playTogether(animations);
+ slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
+ slideAnimations.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mDeferringForDelete = false;
+ onEndReordering();
+ onRemoveViewAnimationCompleted();
+ }
+ };
+ zoomIn(onCompleteRunnable);
+ }
+ });
+ slideAnimations.start();
+
+ removeView(dragView);
+ onRemoveView(dragView, true);
+ }
+ };
+ }
+
+ public void onFlingToDelete(PointF vel) {
+ final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+ // NOTE: Because it takes time for the first frame of animation to actually be
+ // called and we expect the animation to be a continuation of the fling, we have
+ // to account for the time that has elapsed since the fling finished. And since
+ // we don't have a startDelay, we will always get call to update when we call
+ // start() (which we want to ignore).
+ final TimeInterpolator tInterpolator = new TimeInterpolator() {
+ private int mCount = -1;
+ private long mStartTime;
+ private float mOffset;
+ /* Anonymous inner class ctor */ {
+ mStartTime = startTime;
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ if (mCount < 0) {
+ mCount++;
+ } else if (mCount == 0) {
+ mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+ mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
+ mCount++;
+ }
+ return Math.min(1f, mOffset + t);
+ }
+ };
+
+ final Rect from = new Rect();
+ final View dragView = mDragView;
+ from.left = (int) dragView.getTranslationX();
+ from.top = (int) dragView.getTranslationY();
+ AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
+ from, startTime, FLING_TO_DELETE_FRICTION);
+
+ final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
+
+ // Create and start the animation
+ ValueAnimator mDropAnim = new ValueAnimator();
+ mDropAnim.setInterpolator(tInterpolator);
+ mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
+ mDropAnim.setFloatValues(0f, 1f);
+ mDropAnim.addUpdateListener(updateCb);
+ mDropAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
+ }
+ });
+ mDropAnim.start();
+ mDeferringForDelete = true;
+ }
+
+ /* Drag to delete */
+ private boolean isHoveringOverDeleteDropTarget(int x, int y) {
+ if (mDeleteDropTarget != null) {
+ mAltTmpRect.set(0, 0, 0, 0);
+ View parent = (View) mDeleteDropTarget.getParent();
+ if (parent != null) {
+ parent.getGlobalVisibleRect(mAltTmpRect);
+ }
+ mDeleteDropTarget.getGlobalVisibleRect(mTmpRect);
+ mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top);
+ return mTmpRect.contains(x, y);
+ }
+ return false;
+ }
+
+ protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {}
+
+ private void onDropToDelete() {
+ final View dragView = mDragView;
+
+ final float toScale = 0f;
+ final float toAlpha = 0f;
+
+ // Create and start the complex animation
+ ArrayList<Animator> animations = new ArrayList<Animator>();
+ AnimatorSet motionAnim = new AnimatorSet();
+ motionAnim.setInterpolator(new DecelerateInterpolator(2));
+ motionAnim.playTogether(
+ ObjectAnimator.ofFloat(dragView, "scaleX", toScale),
+ ObjectAnimator.ofFloat(dragView, "scaleY", toScale));
+ animations.add(motionAnim);
+
+ AnimatorSet alphaAnim = new AnimatorSet();
+ alphaAnim.setInterpolator(new LinearInterpolator());
+ alphaAnim.playTogether(
+ ObjectAnimator.ofFloat(dragView, "alpha", toAlpha));
+ animations.add(alphaAnim);
+
+ final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
+
+ AnimatorSet anim = new AnimatorSet();
+ anim.playTogether(animations);
+ anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
+ }
+ });
+ anim.start();
+
+ mDeferringForDelete = true;
}
/* Accessibility */
@@ -1976,11 +2771,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return false;
}
- protected String getCurrentPageDescription() {
- return String.format(getContext().getString(R.string.default_scroll_format),
- getNextPage() + 1, getChildCount());
- }
-
@Override
public boolean onHoverEvent(android.view.MotionEvent event) {
return true;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 16c52ded5..d366a16d1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -33,7 +33,9 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -96,7 +98,6 @@ public class Workspace extends SmoothPagedView
private float mBackgroundAlpha = 0;
private float mWallpaperScrollRatio = 1.0f;
- private int mOriginalPageSpacing;
private final WallpaperManager mWallpaperManager;
private IBinder mWindowToken;
@@ -147,11 +148,10 @@ public class Workspace extends SmoothPagedView
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
private int[] mTempCell = new int[2];
+ private int[] mTempPt = new int[2];
private int[] mTempEstimate = new int[2];
private float[] mDragViewVisualCenter = new float[2];
- private float[] mTempDragCoordinates = new float[2];
private float[] mTempCellLayoutCenterCoordinates = new float[2];
- private float[] mTempDragBottomRightCoordinates = new float[2];
private Matrix mTempInverseMatrix = new Matrix();
private SpringLoadedDragController mSpringLoadedDragController;
@@ -194,7 +194,6 @@ public class Workspace extends SmoothPagedView
private Point mDisplaySize = new Point();
private boolean mIsStaticWallpaper;
private int mWallpaperTravelWidth;
- private int mSpringLoadedPageSpacing;
private int mCameraDistance;
// Variables relating to the creation of user folders by hovering shortcuts over shortcuts
@@ -239,24 +238,13 @@ public class Workspace extends SmoothPagedView
private int mSavedScrollX;
private float mSavedRotationY;
private float mSavedTranslationX;
- private float mCurrentScaleX;
- private float mCurrentScaleY;
- private float mCurrentRotationY;
- private float mCurrentTranslationX;
- private float mCurrentTranslationY;
- private float[] mOldTranslationXs;
- private float[] mOldTranslationYs;
- private float[] mOldScaleXs;
- private float[] mOldScaleYs;
+
+ private float mCurrentScale;
+ private float mNewScale;
private float[] mOldBackgroundAlphas;
private float[] mOldAlphas;
- private float[] mNewTranslationXs;
- private float[] mNewTranslationYs;
- private float[] mNewScaleXs;
- private float[] mNewScaleYs;
private float[] mNewBackgroundAlphas;
private float[] mNewAlphas;
- private float[] mNewRotationYs;
private int mLastChildCount = -1;
private float mTransitionProgress;
@@ -287,7 +275,6 @@ public class Workspace extends SmoothPagedView
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContentIsRefreshable = false;
- mOriginalPageSpacing = mPageSpacing;
mOutlineHelper = HolographicOutlineHelper.obtain(context);
@@ -334,8 +321,6 @@ public class Workspace extends SmoothPagedView
mSpringLoadedShrinkFactor =
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
- mSpringLoadedPageSpacing =
- res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
// if the value is manually specified, use that instead
@@ -422,6 +407,7 @@ public class Workspace extends SmoothPagedView
setClipChildren(false);
setClipToPadding(false);
setChildrenDrawnWithCacheEnabled(true);
+ setMinScale(0.5f);
final Resources res = getResources();
try {
@@ -499,6 +485,8 @@ public class Workspace extends SmoothPagedView
CellLayout newScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
+
+ newScreen.setOnLongClickListener(mLongClickListener);
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(screenId);
addView(newScreen, getChildCount());
@@ -711,42 +699,6 @@ public class Workspace extends SmoothPagedView
}
/**
- * Check if the point (x, y) hits a given page.
- */
- private boolean hitsPage(int index, float x, float y) {
- final View page = getChildAt(index);
- if (page != null) {
- float[] localXY = { x, y };
- mapPointFromSelfToChild(page, localXY);
- return (localXY[0] >= 0 && localXY[0] < page.getWidth()
- && localXY[1] >= 0 && localXY[1] < page.getHeight());
- }
- return false;
- }
-
- @Override
- protected boolean hitsPreviousPage(float x, float y) {
- // mNextPage is set to INVALID_PAGE whenever we are stationary.
- // Calculating "next page" this way ensures that you scroll to whatever page you tap on
- final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
-
- // Only allow tap to next page on large devices, where there's significant margin outside
- // the active workspace
- return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current - 1, x, y);
- }
-
- @Override
- protected boolean hitsNextPage(float x, float y) {
- // mNextPage is set to INVALID_PAGE whenever we are stationary.
- // Calculating "next page" this way ensures that you scroll to whatever page you tap on
- final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
-
- // Only allow tap to next page on large devices, where there's significant margin outside
- // the active workspace
- return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current + 1, x, y);
- }
-
- /**
* Called directly from a CellLayout (not by the framework), after we've been added as a
* listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
* that it should intercept touch events, which is not something that is normally supported.
@@ -1685,19 +1637,11 @@ public class Workspace extends SmoothPagedView
private void initAnimationArrays() {
final int childCount = getChildCount();
if (mLastChildCount == childCount) return;
- mOldTranslationXs = new float[childCount];
- mOldTranslationYs = new float[childCount];
- mOldScaleXs = new float[childCount];
- mOldScaleYs = new float[childCount];
+
mOldBackgroundAlphas = new float[childCount];
mOldAlphas = new float[childCount];
- mNewTranslationXs = new float[childCount];
- mNewTranslationYs = new float[childCount];
- mNewScaleXs = new float[childCount];
- mNewScaleYs = new float[childCount];
mNewBackgroundAlphas = new float[childCount];
mNewAlphas = new float[childCount];
- mNewRotationYs = new float[childCount];
}
Animator getChangeStateAnimation(final State state, boolean animated) {
@@ -1725,28 +1669,19 @@ public class Workspace extends SmoothPagedView
final boolean stateIsNormal = (state == State.NORMAL);
final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
final boolean stateIsSmall = (state == State.SMALL);
- float finalScaleFactor = 1.0f;
float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
- float translationX = 0;
- float translationY = 0;
boolean zoomIn = true;
+ mNewScale = 1.0f;
if (state != State.NORMAL) {
- finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
- setPageSpacing(mSpringLoadedPageSpacing);
+ mNewScale = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
if (oldStateIsNormal && stateIsSmall) {
zoomIn = false;
- setLayoutScale(finalScaleFactor);
updateChildrenLayersEnabled(false);
} else {
finalBackgroundAlpha = 1.0f;
- setLayoutScale(finalScaleFactor);
}
- } else {
- setPageSpacing(mOriginalPageSpacing);
- setLayoutScale(1.0f);
}
-
final int duration = zoomIn ?
getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
@@ -1774,49 +1709,30 @@ public class Workspace extends SmoothPagedView
mOldAlphas[i] = initialAlpha;
mNewAlphas[i] = finalAlpha;
if (animated) {
- mOldTranslationXs[i] = cl.getTranslationX();
- mOldTranslationYs[i] = cl.getTranslationY();
- mOldScaleXs[i] = cl.getScaleX();
- mOldScaleYs[i] = cl.getScaleY();
mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
-
- mNewTranslationXs[i] = translationX;
- mNewTranslationYs[i] = translationY;
- mNewScaleXs[i] = finalScaleFactor;
- mNewScaleYs[i] = finalScaleFactor;
mNewBackgroundAlphas[i] = finalBackgroundAlpha;
} else {
- cl.setTranslationX(translationX);
- cl.setTranslationY(translationY);
- cl.setScaleX(finalScaleFactor);
- cl.setScaleY(finalScaleFactor);
+ setScaleX(mNewScale);
+ setScaleY(mNewScale);
cl.setBackgroundAlpha(finalBackgroundAlpha);
cl.setShortcutAndWidgetAlpha(finalAlpha);
}
}
if (animated) {
+ LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
+ scale.scaleX(mNewScale)
+ .scaleY(mNewScale)
+ .setInterpolator(mZoomInInterpolator);
+ anim.play(scale);
for (int index = 0; index < getChildCount(); index++) {
final int i = index;
final CellLayout cl = (CellLayout) getChildAt(i);
float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
- cl.setTranslationX(mNewTranslationXs[i]);
- cl.setTranslationY(mNewTranslationYs[i]);
- cl.setScaleX(mNewScaleXs[i]);
- cl.setScaleY(mNewScaleYs[i]);
cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
- cl.setRotationY(mNewRotationYs[i]);
} else {
- LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl);
- a.translationX(mNewTranslationXs[i])
- .translationY(mNewTranslationYs[i])
- .scaleX(mNewScaleXs[i])
- .scaleY(mNewScaleYs[i])
- .setDuration(duration)
- .setInterpolator(mZoomInInterpolator);
- anim.play(a);
if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
LauncherViewPropertyAnimator alphaAnim =
@@ -2694,20 +2610,6 @@ public class Workspace extends SmoothPagedView
mLastReorderY = -1;
}
- public DropTarget getDropTargetDelegate(DragObject d) {
- return null;
- }
-
- /*
- *
- * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
- * coordinate space. The argument xy is modified with the return result.
- *
- */
- void mapPointFromSelfToChild(View v, float[] xy) {
- mapPointFromSelfToChild(v, xy, null);
- }
-
/*
*
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
@@ -2719,25 +2621,34 @@ public class Workspace extends SmoothPagedView
*
*/
void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
- if (cachedInverseMatrix == null) {
- v.getMatrix().invert(mTempInverseMatrix);
- cachedInverseMatrix = mTempInverseMatrix;
+ xy[0] = xy[0] - v.getLeft();
+ xy[1] = xy[1] - v.getTop();
+ }
+
+ boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
+ if (r == null) {
+ r = new Rect();
}
- int scrollX = getScrollX();
- if (mNextPage != INVALID_PAGE) {
- scrollX = mScroller.getFinalX();
+ mTempPt[0] = x;
+ mTempPt[1] = y;
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
+ mLauncher.getHotseat().getHitRect(r);
+ if (r.contains(mTempPt[0], mTempPt[1])) {
+ return true;
}
- xy[0] = xy[0] + scrollX - v.getLeft();
- xy[1] = xy[1] + getScrollY() - v.getTop();
- cachedInverseMatrix.mapPoints(xy);
+ return false;
}
-
void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
- hotseat.getLayout().getMatrix().invert(mTempInverseMatrix);
- xy[0] = xy[0] - hotseat.getLeft() - hotseat.getLayout().getLeft();
- xy[1] = xy[1] - hotseat.getTop() - hotseat.getLayout().getTop();
- mTempInverseMatrix.mapPoints(xy);
+ mTempPt[0] = (int) xy[0];
+ mTempPt[1] = (int) xy[1];
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
+
+ mTempPt[0] -= hotseat.getLeft();
+ mTempPt[1] -= hotseat.getTop();
+
+ xy[0] = mTempPt[0];
+ xy[1] = mTempPt[1];
}
/*
@@ -2747,13 +2658,8 @@ public class Workspace extends SmoothPagedView
*
*/
void mapPointFromChildToSelf(View v, float[] xy) {
- v.getMatrix().mapPoints(xy);
- int scrollX = getScrollX();
- if (mNextPage != INVALID_PAGE) {
- scrollX = mScroller.getFinalX();
- }
- xy[0] -= (scrollX - v.getLeft());
- xy[1] -= (getScrollY() - v.getTop());
+ xy[0] += v.getLeft();
+ xy[1] += v.getTop();
}
static private float squaredDistance(float[] point1, float[] point2) {
@@ -2764,45 +2670,6 @@ public class Workspace extends SmoothPagedView
/*
*
- * Returns true if the passed CellLayout cl overlaps with dragView
- *
- */
- boolean overlaps(CellLayout cl, DragView dragView,
- int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
- // Transform the coordinates of the item being dragged to the CellLayout's coordinates
- final float[] draggedItemTopLeft = mTempDragCoordinates;
- draggedItemTopLeft[0] = dragViewX;
- draggedItemTopLeft[1] = dragViewY;
- final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
- draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
- draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
-
- // Transform the dragged item's top left coordinates
- // to the CellLayout's local coordinates
- mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
- float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
- float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
-
- if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
- // Transform the dragged item's bottom right coordinates
- // to the CellLayout's local coordinates
- mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
- float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
- float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
-
- if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
- float overlap = (overlapRegionRight - overlapRegionLeft) *
- (overlapRegionBottom - overlapRegionTop);
- if (overlap > 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- /*
- *
* This method returns the CellLayout that is currently being dragged to. In order to drag
* to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
* strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
@@ -2912,8 +2779,7 @@ public class Workspace extends SmoothPagedView
// Identify whether we have dragged over a side page
if (isSmall()) {
if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
- mLauncher.getHotseat().getHitRect(r);
- if (r.contains(d.x, d.y)) {
+ if (isPointInSelfOverHotseat(d.x, d.y, r)) {
layout = mLauncher.getHotseat().getLayout();
}
}
@@ -2921,7 +2787,6 @@ public class Workspace extends SmoothPagedView
layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
}
if (layout != mDragTargetLayout) {
-
setCurrentDropLayout(layout);
setCurrentDragOverlappingLayout(layout);
@@ -2937,8 +2802,7 @@ public class Workspace extends SmoothPagedView
} else {
// Test to see if we are over the hotseat otherwise just use the current page
if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
- mLauncher.getHotseat().getHitRect(r);
- if (r.contains(d.x, d.y)) {
+ if (isPointInSelfOverHotseat(d.x, d.y, r)) {
layout = mLauncher.getHotseat().getLayout();
}
}
@@ -3117,10 +2981,10 @@ public class Workspace extends SmoothPagedView
}
@Override
- public void getHitRect(Rect outRect) {
+ public void getHitRectRelativeToDragLayer(Rect outRect) {
// We want the workspace to have the whole area of the display (it will find the correct
// cell layout to drop to in the existing drag/drop logic.
- outRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
}
/**
@@ -3355,7 +3219,7 @@ public class Workspace extends SmoothPagedView
setFinalTransitionTransform(layout);
float cellLayoutScale =
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc);
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
resetTransitionTransform(layout);
float dragViewScaleX;
@@ -3436,31 +3300,15 @@ public class Workspace extends SmoothPagedView
public void setFinalTransitionTransform(CellLayout layout) {
if (isSwitchingState()) {
- int index = indexOfChild(layout);
- mCurrentScaleX = layout.getScaleX();
- mCurrentScaleY = layout.getScaleY();
- mCurrentTranslationX = layout.getTranslationX();
- mCurrentTranslationY = layout.getTranslationY();
- mCurrentRotationY = layout.getRotationY();
- layout.setScaleX(mNewScaleXs[index]);
- layout.setScaleY(mNewScaleYs[index]);
- layout.setTranslationX(mNewTranslationXs[index]);
- layout.setTranslationY(mNewTranslationYs[index]);
- layout.setRotationY(mNewRotationYs[index]);
+ mCurrentScale = getScaleX();
+ setScaleX(mNewScale);
+ setScaleY(mNewScale);
}
}
public void resetTransitionTransform(CellLayout layout) {
if (isSwitchingState()) {
- mCurrentScaleX = layout.getScaleX();
- mCurrentScaleY = layout.getScaleY();
- mCurrentTranslationX = layout.getTranslationX();
- mCurrentTranslationY = layout.getTranslationY();
- mCurrentRotationY = layout.getRotationY();
- layout.setScaleX(mCurrentScaleX);
- layout.setScaleY(mCurrentScaleY);
- layout.setTranslationX(mCurrentTranslationX);
- layout.setTranslationY(mCurrentTranslationY);
- layout.setRotationY(mCurrentRotationY);
+ setScaleX(mCurrentScale);
+ setScaleY(mCurrentScale);
}
}
@@ -4114,7 +3962,6 @@ public class Workspace extends SmoothPagedView
public void syncPageItems(int page, boolean immediate) {
}
- @Override
protected String getCurrentPageDescription() {
int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
return String.format(getContext().getString(R.string.workspace_scroll_format),