Merge "Scrolling folder during drag-drop" into ub-launcher3-burnaby

This commit is contained in:
Sunny Goyal
2015-03-13 16:29:52 +00:00
committed by Android (Google) Code Review
3 changed files with 261 additions and 35 deletions
@@ -49,8 +49,8 @@ public class DragController {
/** Indicates the drag is a copy. */
public static int DRAG_ACTION_COPY = 1;
private static final int SCROLL_DELAY = 500;
private static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
public static final int SCROLL_DELAY = 500;
public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+188 -4
View File
@@ -48,6 +48,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.Workspace.ItemOperator;
@@ -74,6 +75,21 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
static final int STATE_ANIMATING = 1;
static final int STATE_OPEN = 2;
/**
* Fraction of the width to scroll when showing the next page hint.
*/
private static final float SCROLL_HINT_FRACTION = 0.07f;
/**
* Time for which the scroll hint is shown before automatically changing page.
*/
public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY;
/**
* Fraction of icon width which behave as scroll region.
*/
private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f;
private static final int REORDER_DELAY = 250;
private static final int ON_EXIT_CLOSE_DELAY = 400;
private static final Rect sTempRect = new Rect();
@@ -129,6 +145,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private boolean mDeferDropAfterUninstall;
private boolean mUninstallSuccessful;
// Folder scrolling
private int mScrollAreaOffset;
private Alarm mOnScrollHintAlarm;
private Alarm mScrollPauseAlarm;
// TODO: Use {@link #mContent} once {@link #ALLOW_FOLDER_SCROLL} is removed.
private FolderPagedView mPagedView;
private int mScrollHintDir = DragController.SCROLL_NONE;
private int mCurrentScrollDir = DragController.SCROLL_NONE;
/**
* Used to inflate the Workspace from XML.
*
@@ -157,6 +184,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
if (ALLOW_FOLDER_SCROLL) {
mOnScrollHintAlarm = new Alarm();
mScrollPauseAlarm = new Alarm();
}
}
@Override
@@ -183,6 +215,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
int measureSpec = MeasureSpec.UNSPECIFIED;
mFooter.measure(measureSpec, measureSpec);
mFooterHeight = mFooter.getMeasuredHeight();
if (ALLOW_FOLDER_SCROLL) {
mPagedView = (FolderPagedView) mContent;
}
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -386,6 +422,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void animateOpen() {
if (!(getParent() instanceof DragLayer)) return;
if (ALLOW_FOLDER_SCROLL) {
// Always open on the first page.
mPagedView.snapToPageImmediately(0);
}
Animator openFolderAnim = null;
final Runnable onCompleteRunnable;
if (!Utilities.isLmpOrAbove()) {
@@ -544,6 +585,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onDragEnter(DragObject d) {
mPrevTargetRank = -1;
mOnExitAlarm.cancelAlarm();
if (ALLOW_FOLDER_SCROLL) {
// Get the area offset such that the folder only closes if half the drag icon width
// is outside the folder area
mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset;
}
}
OnAlarmListener mReorderAlarmListener = new OnAlarmListener() {
@@ -558,18 +604,80 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
@Override
public void onDragOver(DragObject d) {
final float[] r = d.getVisualCenter(null);
r[0] -= getPaddingLeft();
r[1] -= getPaddingTop();
onDragOver(d, REORDER_DELAY);
}
private int getTargetRank(DragObject d, float[] recycle) {
recycle = d.getVisualCenter(recycle);
return mContent.findNearestArea(
(int) recycle[0] - getPaddingLeft(), (int) recycle[1] - getPaddingTop());
}
private void onDragOver(DragObject d, int reorderDelay) {
if (ALLOW_FOLDER_SCROLL && mScrollPauseAlarm.alarmPending()) {
return;
}
final float[] r = new float[2];
mTargetRank = getTargetRank(d, r);
mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1]);
if (mTargetRank != mPrevTargetRank) {
mReorderAlarm.cancelAlarm();
mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);
mReorderAlarm.setAlarm(REORDER_DELAY);
mPrevTargetRank = mTargetRank;
}
if (!ALLOW_FOLDER_SCROLL) {
return;
}
float x = r[0];
int currentPage = mPagedView.getNextPage();
int cellWidth = mPagedView.getCurrentCellLayout().getCellWidth();
if (currentPage > 0 && x < cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR) {
// Show scroll hint on the left
if (mScrollHintDir != DragController.SCROLL_LEFT) {
mPagedView.showScrollHint(-SCROLL_HINT_FRACTION);
mScrollHintDir = DragController.SCROLL_LEFT;
}
// Set alarm for when the hint is complete
if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_LEFT) {
mCurrentScrollDir = DragController.SCROLL_LEFT;
mOnScrollHintAlarm.cancelAlarm();
mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d));
mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION);
mReorderAlarm.cancelAlarm();
mTargetRank = mEmptyCellRank;
}
} else if (currentPage < (mPagedView.getPageCount() - 1) &&
(x > (getWidth() - cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR))) {
// Show scroll hint on the right
if (mScrollHintDir != DragController.SCROLL_RIGHT) {
mPagedView.showScrollHint(SCROLL_HINT_FRACTION);
mScrollHintDir = DragController.SCROLL_RIGHT;
}
// Set alarm for when the hint is complete
if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_RIGHT) {
mCurrentScrollDir = DragController.SCROLL_RIGHT;
mOnScrollHintAlarm.cancelAlarm();
mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d));
mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION);
mReorderAlarm.cancelAlarm();
mTargetRank = mEmptyCellRank;
}
} else {
mOnScrollHintAlarm.cancelAlarm();
if (mScrollHintDir != DragController.SCROLL_NONE) {
mPagedView.clearScrollHint();
mScrollHintDir = DragController.SCROLL_NONE;
}
}
}
OnAlarmListener mOnExitAlarmListener = new OnAlarmListener() {
@@ -595,6 +703,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY);
}
mReorderAlarm.cancelAlarm();
if (ALLOW_FOLDER_SCROLL) {
mOnScrollHintAlarm.cancelAlarm();
mScrollPauseAlarm.cancelAlarm();
if (mScrollHintDir != DragController.SCROLL_NONE) {
mPagedView.clearScrollHint();
mScrollHintDir = DragController.SCROLL_NONE;
}
}
}
public void onDropCompleted(final View target, final DragObject d,
@@ -960,6 +1077,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
};
}
if (ALLOW_FOLDER_SCROLL) {
// If the icon was dropped while the page was being scrolled, we need to compute
// the target location again such that the icon is placed of the final page.
if (!mPagedView.rankOnCurrentPage(mEmptyCellRank)) {
// Reorder again.
mTargetRank = getTargetRank(d, null);
// Rearrange items immediately.
mReorderAlarmListener.onAlarm(mReorderAlarm);
mOnScrollHintAlarm.cancelAlarm();
mScrollPauseAlarm.cancelAlarm();
}
mPagedView.completePendingPageChanges();
}
View currentDragView;
ShortcutInfo si = mCurrentDragInfo;
if (mIsExternalDrag) {
@@ -1090,6 +1223,57 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void getHitRectRelativeToDragLayer(Rect outRect) {
getHitRect(outRect);
outRect.left -= mScrollAreaOffset;
outRect.right += mScrollAreaOffset;
}
private class OnScrollHintListener implements OnAlarmListener {
private final DragObject mDragObject;
OnScrollHintListener(DragObject object) {
mDragObject = object;
}
/**
* Scroll hint has been shown long enough. Now scroll to appropriate page.
*/
@Override
public void onAlarm(Alarm alarm) {
if (mCurrentScrollDir == DragController.SCROLL_LEFT) {
mPagedView.scrollLeft();
mScrollHintDir = DragController.SCROLL_NONE;
} else if (mCurrentScrollDir == DragController.SCROLL_RIGHT) {
mPagedView.scrollRight();
mScrollHintDir = DragController.SCROLL_NONE;
} else {
// This should not happen
return;
}
mCurrentScrollDir = DragController.SCROLL_NONE;
// Pause drag event until the scrolling is finished
mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject));
mScrollPauseAlarm.setAlarm(DragController.RESCROLL_DELAY);
}
}
private class OnScrollFinishedListener implements OnAlarmListener {
private final DragObject mDragObject;
OnScrollFinishedListener(DragObject object) {
mDragObject = object;
}
/**
* Page scroll is complete.
*/
@Override
public void onAlarm(Alarm alarm) {
// Reorder immediately on page change.
onDragOver(mDragObject, 1);
}
}
public static interface FolderContent {
+71 -29
View File
@@ -22,6 +22,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
@@ -30,12 +31,15 @@ import com.android.launcher3.Workspace.ItemOperator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class FolderPagedView extends PagedView implements Folder.FolderContent {
private static final String TAG = "FolderPagedView";
private static final int REORDER_ANIMATION_DURATION = 230;
private static final int START_VIEW_REORDER_DELAY = 30;
private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
private static final int[] sTempPosArray = new int[2];
// TODO: Remove this restriction
@@ -137,18 +141,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
public int allocateNewLastItemRank() {
int rank = getItemCount();
int total = rank + 1;
if (rank < mMaxItemsPerPage) {
// Rearrange the items as the grid size might change.
mFolder.rearrangeChildren(total);
} else {
setupContentDimensions(total);
}
// Rearrange the items as the grid size might change.
mFolder.rearrangeChildren(total);
// Add a new page if last page is full
if (getPageAt(getChildCount() - 1).getShortcutsAndWidgets().getChildCount()
>= mMaxItemsPerPage) {
createAndAddNewPage();
}
setCurrentPage(getChildCount() - 1);
return rank;
}
@@ -265,7 +260,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
int newX, newY, rank;
rank = 0;
for (View v : list) {
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
if (currentPage == null || position >= mMaxItemsPerPage) {
// Next page
if (pageItr.hasNext()) {
@@ -276,25 +272,28 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
position = 0;
}
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
newX = position % mGridCountX;
newY = position / mGridCountX;
ItemInfo info = (ItemInfo) v.getTag();
if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
info.cellX = newX;
info.cellY = newY;
info.rank = rank;
if (saveChanges) {
LauncherModel.addOrMoveItemInDatabase(getContext(), info,
mFolder.mInfo.id, 0, info.cellX, info.cellY);
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
newX = position % mGridCountX;
newY = position / mGridCountX;
ItemInfo info = (ItemInfo) v.getTag();
if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
info.cellX = newX;
info.cellY = newY;
info.rank = rank;
if (saveChanges) {
LauncherModel.addOrMoveItemInDatabase(getContext(), info,
mFolder.mInfo.id, 0, info.cellX, info.cellY);
}
}
lp.cellX = info.cellX;
lp.cellY = info.cellY;
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
}
lp.cellX = info.cellX;
lp.cellY = info.cellY;
rank ++;
position++;
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
}
// Remove extra views.
@@ -328,6 +327,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
@Override
public int getItemCount() {
int lastPage = getChildCount() - 1;
if (lastPage < 0) {
// If there are no pages, there must be only one icon in the folder.
return 1;
}
return getPageAt(lastPage).getShortcutsAndWidgets().getChildCount()
+ lastPage * mMaxItemsPerPage;
}
@@ -406,10 +409,49 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
}
}
/**
* Scrolls the current view by a fraction
*/
public void showScrollHint(float fraction) {
int hint = (int) (fraction * getWidth());
int scroll = getScrollForPage(getNextPage()) + hint;
int delta = scroll - mUnboundedScrollX;
if (delta != 0) {
mScroller.setInterpolator(new DecelerateInterpolator());
mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, Folder.SCROLL_HINT_DURATION);
invalidate();
}
}
public void clearScrollHint() {
if (mUnboundedScrollX != getScrollForPage(getNextPage())) {
snapToPage(getNextPage());
}
}
/**
* Finish animation all the views which are animating across pages
*/
public void completePendingPageChanges() {
if (!mPageChangingViews.isEmpty()) {
HashMap<View, Runnable> pendingViews = new HashMap<>(mPageChangingViews);
for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
e.getKey().animate().cancel();
e.getValue().run();
}
}
}
public boolean rankOnCurrentPage(int rank) {
int p = rank / mMaxItemsPerPage;
return p == getNextPage();
}
@Override
public void realTimeReorder(int empty, int target) {
completePendingPageChanges();
int delay = 0;
float delayAmount = 30;
float delayAmount = START_VIEW_REORDER_DELAY;
// Animation only happens on the current page.
int pageToAnimate = getNextPage();
@@ -523,7 +565,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX,
REORDER_ANIMATION_DURATION, delay, true, true)) {
delay += delayAmount;
delayAmount *= 0.9;
delayAmount *= VIEW_REORDER_DELAY_FACTOR;
}
}
}