feec2171f8
- Pressing Tab wraps around to the first item from the folder name,
and vice versa when pressing Shift+Tab.
- When tapping off the folder while editing the text, the folder
requests focus. We handle the following cases from that state:
- Pressing an arrow key or Tab gives focus to the first item.
- Pressing Shift+Tab gives focus to the last item.
- Fix slight corner case where moving from folder name to an item
didn't update mIsEditingName to false. So when clicking off of
the folder, it gave focus to the folder (as mentioned above)
instead of closing the folder like it usually does when icons are
focused. Not a huge deal, but still worth fixing.
Bug: 25687579
Change-Id: I1bec844c8ccd09529a11b9e3a1d92b3bdf7b2eb3
695 lines
24 KiB
Java
695 lines
24 KiB
Java
/**
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.launcher3;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.Gravity;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.animation.DecelerateInterpolator;
|
|
import android.view.animation.Interpolator;
|
|
import android.view.animation.OvershootInterpolator;
|
|
|
|
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
|
|
import com.android.launcher3.PageIndicator.PageMarkerResources;
|
|
import com.android.launcher3.Workspace.ItemOperator;
|
|
import com.android.launcher3.util.Thunk;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
|
|
public class FolderPagedView extends PagedView {
|
|
|
|
private static final String TAG = "FolderPagedView";
|
|
|
|
private static final boolean ALLOW_FOLDER_SCROLL = true;
|
|
|
|
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 PAGE_INDICATOR_ANIMATION_START_DELAY = 300;
|
|
private static final int PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY = 150;
|
|
private static final int PAGE_INDICATOR_ANIMATION_DURATION = 400;
|
|
|
|
// This value approximately overshoots to 1.5 times the original size.
|
|
private static final float PAGE_INDICATOR_OVERSHOOT_TENSION = 4.9f;
|
|
|
|
/**
|
|
* Fraction of the width to scroll when showing the next page hint.
|
|
*/
|
|
private static final float SCROLL_HINT_FRACTION = 0.07f;
|
|
|
|
private static final int[] sTempPosArray = new int[2];
|
|
|
|
public final boolean mIsRtl;
|
|
|
|
private final LayoutInflater mInflater;
|
|
private final IconCache mIconCache;
|
|
|
|
@Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
|
|
|
|
private final int mMaxCountX;
|
|
private final int mMaxCountY;
|
|
private final int mMaxItemsPerPage;
|
|
|
|
private int mAllocatedContentSize;
|
|
private int mGridCountX;
|
|
private int mGridCountY;
|
|
|
|
private Folder mFolder;
|
|
private FocusIndicatorView mFocusIndicatorView;
|
|
private PagedFolderKeyEventListener mKeyListener;
|
|
|
|
private PageIndicator mPageIndicator;
|
|
|
|
public FolderPagedView(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
LauncherAppState app = LauncherAppState.getInstance();
|
|
|
|
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
|
|
mMaxCountX = profile.numFolderColumns;
|
|
mMaxCountY = profile.numFolderRows;
|
|
|
|
mMaxItemsPerPage = mMaxCountX * mMaxCountY;
|
|
|
|
mInflater = LayoutInflater.from(context);
|
|
mIconCache = app.getIconCache();
|
|
|
|
mIsRtl = Utilities.isRtl(getResources());
|
|
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
|
|
|
setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color));
|
|
}
|
|
|
|
public void setFolder(Folder folder) {
|
|
mFolder = folder;
|
|
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
|
|
mKeyListener = new PagedFolderKeyEventListener(folder);
|
|
mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
|
|
}
|
|
|
|
/**
|
|
* Sets up the grid size such that {@param count} items can fit in the grid.
|
|
* The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
|
|
* maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
|
|
*/
|
|
private void setupContentDimensions(int count) {
|
|
mAllocatedContentSize = count;
|
|
boolean done;
|
|
if (count >= mMaxItemsPerPage) {
|
|
mGridCountX = mMaxCountX;
|
|
mGridCountY = mMaxCountY;
|
|
done = true;
|
|
} else {
|
|
done = false;
|
|
}
|
|
|
|
while (!done) {
|
|
int oldCountX = mGridCountX;
|
|
int oldCountY = mGridCountY;
|
|
if (mGridCountX * mGridCountY < count) {
|
|
// Current grid is too small, expand it
|
|
if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) {
|
|
mGridCountX++;
|
|
} else if (mGridCountY < mMaxCountY) {
|
|
mGridCountY++;
|
|
}
|
|
if (mGridCountY == 0) mGridCountY++;
|
|
} else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) {
|
|
mGridCountY = Math.max(0, mGridCountY - 1);
|
|
} else if ((mGridCountX - 1) * mGridCountY >= count) {
|
|
mGridCountX = Math.max(0, mGridCountX - 1);
|
|
}
|
|
done = mGridCountX == oldCountX && mGridCountY == oldCountY;
|
|
}
|
|
|
|
// Update grid size
|
|
for (int i = getPageCount() - 1; i >= 0; i--) {
|
|
getPageAt(i).setGridSize(mGridCountX, mGridCountY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Binds items to the layout.
|
|
* @return list of items that could not be bound, probably because we hit the max size limit.
|
|
*/
|
|
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
|
|
ArrayList<View> icons = new ArrayList<View>();
|
|
ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
|
|
|
|
for (ShortcutInfo item : items) {
|
|
if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
|
|
extra.add(item);
|
|
} else {
|
|
icons.add(createNewView(item));
|
|
}
|
|
}
|
|
arrangeChildren(icons, icons.size(), false);
|
|
return extra;
|
|
}
|
|
|
|
/**
|
|
* Create space for a new item at the end, and returns the rank for that item.
|
|
* Also sets the current page to the last page.
|
|
*/
|
|
public int allocateRankForNewItem(ShortcutInfo info) {
|
|
int rank = getItemCount();
|
|
ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
|
|
views.add(rank, null);
|
|
arrangeChildren(views, views.size(), false);
|
|
setCurrentPage(rank / mMaxItemsPerPage);
|
|
return rank;
|
|
}
|
|
|
|
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
|
|
View icon = createNewView(item);
|
|
addViewForRank(icon, item, rank);
|
|
return icon;
|
|
}
|
|
|
|
/**
|
|
* Adds the {@param view} to the layout based on {@param rank} and updated the position
|
|
* related attributes. It assumes that {@param item} is already attached to the view.
|
|
*/
|
|
public void addViewForRank(View view, ShortcutInfo item, int rank) {
|
|
int pagePos = rank % mMaxItemsPerPage;
|
|
int pageNo = rank / mMaxItemsPerPage;
|
|
|
|
item.rank = rank;
|
|
item.cellX = pagePos % mGridCountX;
|
|
item.cellY = pagePos / mGridCountX;
|
|
|
|
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
|
|
lp.cellX = item.cellX;
|
|
lp.cellY = item.cellY;
|
|
getPageAt(pageNo).addViewToCellLayout(
|
|
view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
|
|
}
|
|
|
|
@SuppressLint("InflateParams")
|
|
public View createNewView(ShortcutInfo item) {
|
|
final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
|
|
R.layout.folder_application, null, false);
|
|
textView.applyFromShortcutInfo(item, mIconCache);
|
|
textView.setOnClickListener(mFolder);
|
|
textView.setOnLongClickListener(mFolder);
|
|
textView.setOnFocusChangeListener(mFocusIndicatorView);
|
|
textView.setOnKeyListener(mKeyListener);
|
|
|
|
textView.setLayoutParams(new CellLayout.LayoutParams(
|
|
item.cellX, item.cellY, item.spanX, item.spanY));
|
|
return textView;
|
|
}
|
|
|
|
@Override
|
|
public CellLayout getPageAt(int index) {
|
|
return (CellLayout) getChildAt(index);
|
|
}
|
|
|
|
public void removeCellLayoutView(View view) {
|
|
for (int i = getChildCount() - 1; i >= 0; i --) {
|
|
getPageAt(i).removeView(view);
|
|
}
|
|
}
|
|
|
|
public CellLayout getCurrentCellLayout() {
|
|
return getPageAt(getNextPage());
|
|
}
|
|
|
|
private CellLayout createAndAddNewPage() {
|
|
DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
|
|
CellLayout page = new CellLayout(getContext());
|
|
page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
|
|
page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
|
|
page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
|
page.setInvertIfRtl(true);
|
|
page.setGridSize(mGridCountX, mGridCountY);
|
|
|
|
addView(page, -1, generateDefaultLayoutParams());
|
|
return page;
|
|
}
|
|
|
|
@Override
|
|
protected int getChildGap() {
|
|
return getPaddingLeft() + getPaddingRight();
|
|
}
|
|
|
|
public void setFixedSize(int width, int height) {
|
|
width -= (getPaddingLeft() + getPaddingRight());
|
|
height -= (getPaddingTop() + getPaddingBottom());
|
|
for (int i = getChildCount() - 1; i >= 0; i --) {
|
|
((CellLayout) getChildAt(i)).setFixedSize(width, height);
|
|
}
|
|
}
|
|
|
|
public void removeItem(View v) {
|
|
for (int i = getChildCount() - 1; i >= 0; i --) {
|
|
getPageAt(i).removeView(v);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates position and rank of all the children in the view.
|
|
* It essentially removes all views from all the pages and then adds them again in appropriate
|
|
* page.
|
|
*
|
|
* @param list the ordered list of children.
|
|
* @param itemCount if greater than the total children count, empty spaces are left
|
|
* at the end, otherwise it is ignored.
|
|
*
|
|
*/
|
|
public void arrangeChildren(ArrayList<View> list, int itemCount) {
|
|
arrangeChildren(list, itemCount, true);
|
|
}
|
|
|
|
@SuppressLint("RtlHardcoded")
|
|
private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
|
|
ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
|
|
for (int i = 0; i < getChildCount(); i++) {
|
|
CellLayout page = (CellLayout) getChildAt(i);
|
|
page.removeAllViews();
|
|
pages.add(page);
|
|
}
|
|
setupContentDimensions(itemCount);
|
|
|
|
Iterator<CellLayout> pageItr = pages.iterator();
|
|
CellLayout currentPage = null;
|
|
|
|
int position = 0;
|
|
int newX, newY, rank;
|
|
|
|
rank = 0;
|
|
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()) {
|
|
currentPage = pageItr.next();
|
|
} else {
|
|
currentPage = createAndAddNewPage();
|
|
}
|
|
position = 0;
|
|
}
|
|
|
|
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);
|
|
|
|
if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) {
|
|
((BubbleTextView) v).verifyHighRes();
|
|
}
|
|
}
|
|
|
|
rank ++;
|
|
position++;
|
|
}
|
|
|
|
// Remove extra views.
|
|
boolean removed = false;
|
|
while (pageItr.hasNext()) {
|
|
removeView(pageItr.next());
|
|
removed = true;
|
|
}
|
|
if (removed) {
|
|
setCurrentPage(0);
|
|
}
|
|
|
|
setEnableOverscroll(getPageCount() > 1);
|
|
|
|
// Update footer
|
|
mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE);
|
|
// Set the gravity as LEFT or RIGHT instead of START, as START depends on the actual text.
|
|
mFolder.mFolderName.setGravity(getPageCount() > 1 ?
|
|
(mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL);
|
|
}
|
|
|
|
public int getDesiredWidth() {
|
|
return getPageCount() > 0 ?
|
|
(getPageAt(0).getDesiredWidth() + getPaddingLeft() + getPaddingRight()) : 0;
|
|
}
|
|
|
|
public int getDesiredHeight() {
|
|
return getPageCount() > 0 ?
|
|
(getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0;
|
|
}
|
|
|
|
public int getItemCount() {
|
|
int lastPageIndex = getChildCount() - 1;
|
|
if (lastPageIndex < 0) {
|
|
// If there are no pages, nothing has yet been added to the folder.
|
|
return 0;
|
|
}
|
|
return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount()
|
|
+ lastPageIndex * mMaxItemsPerPage;
|
|
}
|
|
|
|
/**
|
|
* @return the rank of the cell nearest to the provided pixel position.
|
|
*/
|
|
public int findNearestArea(int pixelX, int pixelY) {
|
|
int pageIndex = getNextPage();
|
|
CellLayout page = getPageAt(pageIndex);
|
|
page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray);
|
|
if (mFolder.isLayoutRtl()) {
|
|
sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1;
|
|
}
|
|
return Math.min(mAllocatedContentSize - 1,
|
|
pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
|
|
}
|
|
|
|
@Override
|
|
protected PageMarkerResources getPageIndicatorMarker(int pageIndex) {
|
|
return new PageMarkerResources(R.drawable.ic_pageindicator_current_folder,
|
|
R.drawable.ic_pageindicator_default_folder);
|
|
}
|
|
|
|
public boolean isFull() {
|
|
return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
|
|
}
|
|
|
|
public View getFirstItem() {
|
|
if (getChildCount() < 1) {
|
|
return null;
|
|
}
|
|
ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
|
|
if (mGridCountX > 0) {
|
|
return currContainer.getChildAt(0, 0);
|
|
} else {
|
|
return currContainer.getChildAt(0);
|
|
}
|
|
}
|
|
|
|
public View getLastItem() {
|
|
if (getChildCount() < 1) {
|
|
return null;
|
|
}
|
|
ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
|
|
int lastRank = currContainer.getChildCount() - 1;
|
|
if (mGridCountX > 0) {
|
|
return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
|
|
} else {
|
|
return currContainer.getChildAt(lastRank);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iterates over all its items in a reading order.
|
|
* @return the view for which the operator returned true.
|
|
*/
|
|
public View iterateOverItems(ItemOperator op) {
|
|
for (int k = 0 ; k < getChildCount(); k++) {
|
|
CellLayout page = getPageAt(k);
|
|
for (int j = 0; j < page.getCountY(); j++) {
|
|
for (int i = 0; i < page.getCountX(); i++) {
|
|
View v = page.getChildAt(i, j);
|
|
if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) {
|
|
return v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public String getAccessibilityDescription() {
|
|
return String.format(getContext().getString(R.string.folder_opened),
|
|
mGridCountX, mGridCountY);
|
|
}
|
|
|
|
/**
|
|
* Sets the focus on the first visible child.
|
|
*/
|
|
public void setFocusOnFirstChild() {
|
|
View firstChild = getCurrentCellLayout().getChildAt(0, 0);
|
|
if (firstChild != null) {
|
|
firstChild.requestFocus();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void notifyPageSwitchListener() {
|
|
super.notifyPageSwitchListener();
|
|
if (mFolder != null) {
|
|
mFolder.updateTextViewFocus();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scrolls the current view by a fraction
|
|
*/
|
|
public void showScrollHint(int direction) {
|
|
float fraction = (direction == DragController.SCROLL_LEFT) ^ mIsRtl
|
|
? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION;
|
|
int hint = (int) (fraction * getWidth());
|
|
int scroll = getScrollForPage(getNextPage()) + hint;
|
|
int delta = scroll - getScrollX();
|
|
if (delta != 0) {
|
|
mScroller.setInterpolator(new DecelerateInterpolator());
|
|
mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION);
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
public void clearScrollHint() {
|
|
if (getScrollX() != getScrollForPage(getNextPage())) {
|
|
snapToPage(getNextPage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finish animation all the views which are animating across pages
|
|
*/
|
|
public void completePendingPageChanges() {
|
|
if (!mPendingAnimations.isEmpty()) {
|
|
HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
|
|
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
|
|
protected void onPageBeginMoving() {
|
|
super.onPageBeginMoving();
|
|
getVisiblePages(sTempPosArray);
|
|
for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) {
|
|
verifyVisibleHighResIcons(i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures that all the icons on the given page are of high-res
|
|
*/
|
|
public void verifyVisibleHighResIcons(int pageNo) {
|
|
CellLayout page = getPageAt(pageNo);
|
|
if (page != null) {
|
|
ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
|
|
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
|
|
((BubbleTextView) parent.getChildAt(i)).verifyHighRes();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getAllocatedContentSize() {
|
|
return mAllocatedContentSize;
|
|
}
|
|
|
|
/**
|
|
* Reorders the items such that the {@param empty} spot moves to {@param target}
|
|
*/
|
|
public void realTimeReorder(int empty, int target) {
|
|
completePendingPageChanges();
|
|
int delay = 0;
|
|
float delayAmount = START_VIEW_REORDER_DELAY;
|
|
|
|
// Animation only happens on the current page.
|
|
int pageToAnimate = getNextPage();
|
|
|
|
int pageT = target / mMaxItemsPerPage;
|
|
int pagePosT = target % mMaxItemsPerPage;
|
|
|
|
if (pageT != pageToAnimate) {
|
|
Log.e(TAG, "Cannot animate when the target cell is invisible");
|
|
}
|
|
int pagePosE = empty % mMaxItemsPerPage;
|
|
int pageE = empty / mMaxItemsPerPage;
|
|
|
|
int startPos, endPos;
|
|
int moveStart, moveEnd;
|
|
int direction;
|
|
|
|
if (target == empty) {
|
|
// No animation
|
|
return;
|
|
} else if (target > empty) {
|
|
// Items will move backwards to make room for the empty cell.
|
|
direction = 1;
|
|
|
|
// If empty cell is in a different page, move them instantly.
|
|
if (pageE < pageToAnimate) {
|
|
moveStart = empty;
|
|
// Instantly move the first item in the current page.
|
|
moveEnd = pageToAnimate * mMaxItemsPerPage;
|
|
// Animate the 2nd item in the current page, as the first item was already moved to
|
|
// the last page.
|
|
startPos = 0;
|
|
} else {
|
|
moveStart = moveEnd = -1;
|
|
startPos = pagePosE;
|
|
}
|
|
|
|
endPos = pagePosT;
|
|
} else {
|
|
// The items will move forward.
|
|
direction = -1;
|
|
|
|
if (pageE > pageToAnimate) {
|
|
// Move the items immediately.
|
|
moveStart = empty;
|
|
// Instantly move the last item in the current page.
|
|
moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1;
|
|
|
|
// Animations start with the second last item in the page
|
|
startPos = mMaxItemsPerPage - 1;
|
|
} else {
|
|
moveStart = moveEnd = -1;
|
|
startPos = pagePosE;
|
|
}
|
|
|
|
endPos = pagePosT;
|
|
}
|
|
|
|
// Instant moving views.
|
|
while (moveStart != moveEnd) {
|
|
int rankToMove = moveStart + direction;
|
|
int p = rankToMove / mMaxItemsPerPage;
|
|
int pagePos = rankToMove % mMaxItemsPerPage;
|
|
int x = pagePos % mGridCountX;
|
|
int y = pagePos / mGridCountX;
|
|
|
|
final CellLayout page = getPageAt(p);
|
|
final View v = page.getChildAt(x, y);
|
|
if (v != null) {
|
|
if (pageToAnimate != p) {
|
|
page.removeView(v);
|
|
addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
|
|
} else {
|
|
// Do a fake animation before removing it.
|
|
final int newRank = moveStart;
|
|
final float oldTranslateX = v.getTranslationX();
|
|
|
|
Runnable endAction = new Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
mPendingAnimations.remove(v);
|
|
v.setTranslationX(oldTranslateX);
|
|
((CellLayout) v.getParent().getParent()).removeView(v);
|
|
addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
|
|
}
|
|
};
|
|
v.animate()
|
|
.translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
|
|
.setDuration(REORDER_ANIMATION_DURATION)
|
|
.setStartDelay(0)
|
|
.withEndAction(endAction);
|
|
mPendingAnimations.put(v, endAction);
|
|
}
|
|
}
|
|
moveStart = rankToMove;
|
|
}
|
|
|
|
if ((endPos - startPos) * direction <= 0) {
|
|
// No animation
|
|
return;
|
|
}
|
|
|
|
CellLayout page = getPageAt(pageToAnimate);
|
|
for (int i = startPos; i != endPos; i += direction) {
|
|
int nextPos = i + direction;
|
|
View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX);
|
|
if (v != null) {
|
|
((ItemInfo) v.getTag()).rank -= direction;
|
|
}
|
|
if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX,
|
|
REORDER_ANIMATION_DURATION, delay, true, true)) {
|
|
delay += delayAmount;
|
|
delayAmount *= VIEW_REORDER_DELAY_FACTOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setMarkerScale(float scale) {
|
|
int count = mPageIndicator.getChildCount();
|
|
for (int i = 0; i < count; i++) {
|
|
View marker = mPageIndicator.getChildAt(i);
|
|
marker.animate().cancel();
|
|
marker.setScaleX(scale);
|
|
marker.setScaleY(scale);
|
|
}
|
|
}
|
|
|
|
public void animateMarkers() {
|
|
int count = mPageIndicator.getChildCount();
|
|
Interpolator interpolator = new OvershootInterpolator(PAGE_INDICATOR_OVERSHOOT_TENSION);
|
|
for (int i = 0; i < count; i++) {
|
|
mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1)
|
|
.setInterpolator(interpolator)
|
|
.setDuration(PAGE_INDICATOR_ANIMATION_DURATION)
|
|
.setStartDelay(PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY * i
|
|
+ PAGE_INDICATOR_ANIMATION_START_DELAY);
|
|
}
|
|
}
|
|
|
|
public int itemsPerPage() {
|
|
return mMaxItemsPerPage;
|
|
}
|
|
|
|
@Override
|
|
protected void getEdgeVerticalPostion(int[] pos) {
|
|
pos[0] = 0;
|
|
pos[1] = getViewportHeight();
|
|
}
|
|
}
|