Merge "Adding a scrollable folder content implementation" into ub-launcher3-burnaby

This commit is contained in:
Sunny Goyal
2015-03-09 21:50:03 +00:00
committed by Android (Google) Code Review
9 changed files with 742 additions and 37 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

-1
View File
@@ -46,7 +46,6 @@
android:id="@+id/folder_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="#00000000"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/quantum_panel"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/folder_content_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
<com.android.launcher3.FocusIndicatorView
android:id="@+id/focus_indicator"
android:layout_width="20dp"
android:layout_height="20dp" />
<com.android.launcher3.FolderPagedView
android:id="@+id/folder_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:pageIndicator="@+id/folder_page_indicator" />
</FrameLayout>
<LinearLayout
android:id="@+id/folder_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<com.android.launcher3.FolderEditText
android:id="@+id/folder_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="#00000000"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
android:paddingBottom="@dimen/folder_name_padding"
android:paddingTop="@dimen/folder_name_padding"
android:singleLine="true"
android:textColor="#ff777777"
android:textColorHighlight="#ffCCCCCC"
android:textColorHint="#ff808080"
android:textCursorDrawable="@null"
android:textSize="14sp" />
<include
android:id="@+id/folder_page_indicator"
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
layout="@layout/page_indicator" />
</LinearLayout>
</com.android.launcher3.Folder>
@@ -36,7 +36,6 @@ import android.os.Process;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -45,6 +44,7 @@ import android.widget.ImageView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FocusHelper.PagedViewKeyListener;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.util.ArrayList;
@@ -139,7 +139,7 @@ class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTas
* The Apps/Customize page that displays all the applications, widgets, and shortcuts.
*/
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
View.OnClickListener, View.OnKeyListener, DragSource,
View.OnClickListener, DragSource,
PagedViewWidget.ShortPressListener, LauncherTransitionable {
static final String TAG = "AppsCustomizePagedView";
@@ -182,6 +182,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
private static final int sPageSleepDelay = 200;
private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener();
private Runnable mInflateWidgetRunnable = null;
private Runnable mBindWidgetRunnable = null;
static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
@@ -449,10 +451,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
mWidgetInstructionToast.show();
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
return FocusHelper.handleAppsCustomizeKeyEvent(v, keyCode, event);
}
/*
* PagedViewWithDraggableItems implementation
*/
@@ -959,7 +957,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
icon.setOnClickListener(mLauncher);
icon.setOnLongClickListener(this);
icon.setOnTouchListener(this);
icon.setOnKeyListener(this);
icon.setOnKeyListener(mKeyListener);
icon.setOnFocusChangeListener(layout.mFocusHandlerView);
int index = i - startIndex;
@@ -1141,7 +1139,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
widget.setOnClickListener(this);
widget.setOnLongClickListener(this);
widget.setOnTouchListener(this);
widget.setOnKeyListener(this);
widget.setOnKeyListener(mKeyListener);
// Layout each widget
int ix = i % mWidgetCountX;
+37 -8
View File
@@ -23,6 +23,7 @@ import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.FocusHelper.PagedViewKeyListener;
import com.android.launcher3.util.FocusLogic;
/**
@@ -64,10 +65,33 @@ public class FocusHelper {
// Key code handling methods.
//
/**
* A keyboard listener for scrollable folders
*/
public static class PagedFolderKeyEventListener extends PagedViewKeyListener {
private final Folder mFolder;
public PagedFolderKeyEventListener(Folder folder) {
mFolder = folder;
}
@Override
public void handleNoopKey(int keyCode, View v) {
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
mFolder.mFolderName.requestFocus();
playSoundEffect(keyCode, v);
}
}
}
/**
* Handles key events in the all apps screen.
*/
static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) {
public static class PagedViewKeyListener implements View.OnKeyListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent e) {
boolean consume = FocusLogic.shouldConsume(keyCode);
if (e.getAction() == KeyEvent.ACTION_UP) {
return consume;
@@ -87,15 +111,14 @@ public class FocusHelper {
parentLayout = (ViewGroup) itemContainer.getParent();
countX = ((CellLayout) parentLayout).getCountX();
countY = ((CellLayout) parentLayout).getCountY();
} else if (v.getParent() instanceof ViewGroup) {
//TODO(hyunyoungs): figure out when this needs to be called.
itemContainer = parentLayout = (ViewGroup) v.getParent();
countX = ((PagedViewGridLayout) parentLayout).getCellCountX();
countY = ((PagedViewGridLayout) parentLayout).getCellCountY();
} else {
throw new IllegalStateException(
"Parent of the focused item inside all apps screen is not a supported type.");
if (LauncherAppState.isDogfoodBuild()) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
}
}
final int iconIndex = itemContainer.indexOfChild(v);
final PagedView container = (PagedView) parentLayout.getParent();
final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
@@ -109,6 +132,7 @@ public class FocusHelper {
int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
iconIndex, pageIndex, pageCount);
if (newIconIndex == FocusLogic.NOOP) {
handleNoopKey(keyCode, v);
return consume;
}
switch (newIconIndex) {
@@ -163,10 +187,15 @@ public class FocusHelper {
if (child != null) {
child.requestFocus();
playSoundEffect(keyCode, v);
} else {
handleNoopKey(keyCode, v);
}
return consume;
}
public void handleNoopKey(int keyCode, View v) { }
}
/**
* Handles key events in the workspace hot seat (bottom of the screen).
* <p>Currently we don't special case for the phone UI in different orientations, even though
+19 -20
View File
@@ -66,6 +66,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
*/
private static final int MIN_CONTENT_DIMEN = 5;
private static final boolean ALLOW_FOLDER_SCROLL = true;
static final int STATE_NONE = -1;
static final int STATE_SMALL = 0;
@@ -100,8 +101,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private View mContentWrapper;
FolderEditText mFolderName;
private View mBottomContent;
private int mBottomContentHeight;
private View mFooter;
private int mFooterHeight;
// Cell ranks used for drag and drop
private int mTargetRank, mPrevTargetRank, mEmptyCellRank;
@@ -175,13 +176,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mFolderName.setInputType(mFolderName.getInputType() |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
// We only have the folder name at the bottom for now
mBottomContent = mFolderName;
// We find out how tall the bottom content wants to be (it is set to wrap_content), so that
mFooter = ALLOW_FOLDER_SCROLL ? findViewById(R.id.folder_footer) : mFolderName;
// We find out how tall footer wants to be (it is set to wrap_content), so that
// we can allocate the appropriate amount of space for it.
int measureSpec = MeasureSpec.UNSPECIFIED;
mBottomContent.measure(measureSpec, measureSpec);
mBottomContentHeight = mBottomContent.getMeasuredHeight();
mFooter.measure(measureSpec, measureSpec);
mFooterHeight = mFooter.getMeasuredHeight();
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -225,7 +225,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mEmptyCellRank = item.rank;
mCurrentDragView = v;
mContent.removeView(mCurrentDragView);
mContent.removeItem(mCurrentDragView);
mInfo.remove(mCurrentDragInfo);
mDragInProgress = true;
mItemAddedBackToSelfViaIcon = false;
@@ -359,7 +359,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
* @return A new UserFolder.
*/
static Folder fromXml(Context context) {
return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
return (Folder) LayoutInflater.from(context).inflate(
ALLOW_FOLDER_SCROLL ? R.layout.user_folder_scroll : R.layout.user_folder, null);
}
/**
@@ -434,8 +435,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
iconsAlpha.setStartDelay(mMaterialExpandStagger);
iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
mBottomContent.setAlpha(0f);
Animator textAlpha = LauncherAnimUtils.ofFloat(mBottomContent, "alpha", 0f, 1f);
mFooter.setAlpha(0f);
Animator textAlpha = LauncherAnimUtils.ofFloat(mFooter, "alpha", 0f, 1f);
textAlpha.setDuration(mMaterialExpandDuration);
textAlpha.setStartDelay(mMaterialExpandStagger);
textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
@@ -795,7 +796,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
int maxContentAreaHeight = grid.availableHeightPx -
workspacePadding.top - workspacePadding.bottom -
mBottomContentHeight;
mFooterHeight;
int height = Math.min(maxContentAreaHeight,
mContent.getDesiredHeight());
return Math.max(height, MIN_CONTENT_DIMEN);
@@ -810,7 +811,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
private int getFolderHeight(int contentAreaHeight) {
return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mBottomContentHeight;
return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mFooterHeight;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -822,10 +823,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent.setFixedSize(contentWidth, contentHeight);
mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
// Move the bottom content below mContent
mBottomContent.measure(contentAreaWidthSpec,
MeasureSpec.makeMeasureSpec(mBottomContentHeight, MeasureSpec.EXACTLY));
mFooter.measure(contentAreaWidthSpec,
MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY));
int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth;
int folderHeight = getFolderHeight(contentHeight);
@@ -929,7 +928,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// This method keeps track of the last item in the folder for the purposes
// of keyboard focus
private void updateTextViewFocus() {
public void updateTextViewFocus() {
View lastChild = mContent.getLastItem();
if (lastChild != null) {
mFolderName.setNextFocusDownId(lastChild.getId());
@@ -1028,7 +1027,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// the work associated with removing the item, so we don't have to do anything here.
if (item == mCurrentDragInfo) return;
View v = getViewForInfo(item);
mContent.removeView(v);
mContent.removeItem(v);
if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
} else {
@@ -1090,7 +1089,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public static interface FolderContent {
void setFolder(Folder f);
void removeView(View v);
void removeItem(View v);
boolean isFull();
int getItemCount();
@@ -1,3 +1,19 @@
/**
* 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.content.Context;
@@ -138,6 +154,11 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent
addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
}
@Override
public void removeItem(View v) {
removeView(v);
}
/**
* Updates the item cellX and cellY position
*/
@@ -0,0 +1,581 @@
/**
* 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.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
import com.android.launcher3.Workspace.ItemOperator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
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[] sTempPosArray = new int[2];
// TODO: Remove this restriction
private static final int MAX_ITEMS_PER_PAGE = 3;
private final LayoutInflater mInflater;
private final IconCache mIconCache;
private final HashMap<View, Runnable> mPageChangingViews = new HashMap<>();
private final CellLayout mFirstPage;
final int mMaxCountX;
final int mMaxCountY;
final int mMaxItemsPerPage;
private int mAllocatedContentSize;
private int mGridCountX;
private int mGridCountY;
private Folder mFolder;
private FocusIndicatorView mFocusIndicatorView;
private PagedFolderKeyEventListener mKeyListener;
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
LauncherAppState app = LauncherAppState.getInstance();
mFirstPage = newCellLayout();
addFullScreenPage(mFirstPage);
setCurrentPage(0);
setDataIsReady();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE);
mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE);
mMaxItemsPerPage = mMaxCountX * mMaxCountY;
mInflater = LayoutInflater.from(context);
mIconCache = app.getIconCache();
}
@Override
public void setFolder(Folder folder) {
mFolder = folder;
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
mKeyListener = new PagedFolderKeyEventListener(folder);
}
/**
* 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} &amp; {@link #mMaxCountY}.
*/
private void setupContentDimensions(int count) {
mAllocatedContentSize = count;
boolean done;
if (count >= mMaxItemsPerPage) {
mGridCountX = mMaxCountX;
mGridCountY = mMaxCountY;
done = true;
} else {
mGridCountX = mFirstPage.getCountX();
mGridCountY = mFirstPage.getCountY();
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;
}
setGridSize(mGridCountX, mGridCountY);
}
public void setGridSize(int countX, int countY) {
mGridCountX = countX;
mGridCountY = countY;
mFirstPage.setGridSize(mGridCountX, mGridCountY);
for (int i = getPageCount() - 1; i > 0; i--) {
getPageAt(i).setGridSize(mGridCountX, mGridCountY);
}
}
@Override
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
final int count = items.size();
if (getPageCount() > 1) {
Log.d(TAG, "Binding items to an non-empty view");
removeAllViews();
addView(mFirstPage);
mFirstPage.removeAllViews();
}
setupContentDimensions(count);
CellLayout page = mFirstPage;
int pagePosition = 0;
int rank = 0;
for (ShortcutInfo item : items) {
if (pagePosition >= mMaxItemsPerPage) {
// This page is full, add a new page.
pagePosition = 0;
page = newCellLayout();
addFullScreenPage(page);
}
item.cellX = pagePosition % mGridCountX;
item.cellY = pagePosition / mGridCountX;
item.rank = rank;
addNewView(item, page);
rank++;
pagePosition++;
}
return new ArrayList<ShortcutInfo>();
}
/**
* 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.
*/
@Override
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);
}
// Add a new page if last page is full
if (getPageAt(getChildCount() - 1).getShortcutsAndWidgets().getChildCount()
>= mMaxItemsPerPage) {
addFullScreenPage(newCellLayout());
}
setCurrentPage(getChildCount() - 1);
return rank;
}
@Override
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
int pageNo = updateItemXY(item, rank);
CellLayout page = getPageAt(pageNo);
return addNewView(item, page);
}
@Override
public void addViewForRank(View view, ShortcutInfo item, int rank) {
int pageNo = updateItemXY(item, rank);
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);
}
/**
* Updates the item cellX and cellY position and return the page number for that item.
*/
private int updateItemXY(ShortcutInfo item, int rank) {
item.rank = rank;
int pagePos = item.rank % mMaxItemsPerPage;
item.cellX = pagePos % mGridCountX;
item.cellY = pagePos / mGridCountX;
return item.rank / mMaxItemsPerPage;
}
private View addNewView(ShortcutInfo item, CellLayout target) {
final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
R.layout.folder_application, target.getShortcutsAndWidgets(), false);
textView.applyFromShortcutInfo(item, mIconCache, false);
textView.setOnClickListener(mFolder);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorView);
textView.setOnKeyListener(mKeyListener);
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(
item.cellX, item.cellY, item.spanX, item.spanY);
target.addViewToCellLayout(
textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
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());
}
@Override
public void addFullScreenPage(View page) {
LayoutParams lp = generateDefaultLayoutParams();
lp.isFullScreenPage = true;
super.addView(page, -1, lp);
}
private CellLayout newCellLayout() {
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
CellLayout layout = new CellLayout(getContext());
layout.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
layout.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
layout.setInvertIfRtl(true);
if (mFirstPage != null) {
layout.setGridSize(mFirstPage.getCountX(), mFirstPage.getCountY());
}
return layout;
}
@Override
public void setFixedSize(int width, int height) {
for (int i = getChildCount() - 1; i >= 0; i --) {
((CellLayout) getChildAt(i)).setFixedSize(width, height);
}
}
@Override
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.
*
*/
@Override
public void arrangeChildren(ArrayList<View> list, int itemCount) {
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 (View v : list) {
if (currentPage == null || position >= mMaxItemsPerPage) {
// Next page
if (pageItr.hasNext()) {
currentPage = pageItr.next();
} else {
currentPage = newCellLayout();
addFullScreenPage(currentPage);
}
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;
LauncherModel.addOrMoveItemInDatabase(getContext(), info,
mFolder.mInfo.id, 0, info.cellX, info.cellY);
}
lp.cellX = info.cellX;
lp.cellY = info.cellY;
rank ++;
position++;
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
}
boolean removed = false;
while (pageItr.hasNext()) {
CellLayout layout = pageItr.next();
if (layout != mFirstPage) {
removeView(layout);
removed = true;
}
}
if (removed) {
setCurrentPage(0);
}
}
@Override
protected void loadAssociatedPages(int page, boolean immediateAndOnly) { }
@Override
public void syncPages() { }
@Override
public void syncPageItems(int page, boolean immediate) { }
public int getDesiredWidth() {
return mFirstPage.getDesiredWidth();
}
public int getDesiredHeight() {
return mFirstPage.getDesiredHeight();
}
@Override
public int getItemCount() {
int lastPage = getChildCount() - 1;
return getPageAt(lastPage).getShortcutsAndWidgets().getChildCount()
+ lastPage * mMaxItemsPerPage;
}
@Override
public int findNearestArea(int pixelX, int pixelY) {
int pageIndex = getNextPage();
CellLayout page = getPageAt(pageIndex);
page.findNearestArea(pixelX, pixelY, 1, 1, null, false, 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_dark, R.drawable.ic_pageindicator_default_dark);
}
@Override
public boolean isFull() {
return false;
}
@Override
public View getLastItem() {
if (getChildCount() < 1) {
return null;
}
ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets();
int lastRank = lastContainer.getChildCount() - 1;
if (mGridCountX > 0) {
return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
} else {
return lastContainer.getChildAt(lastRank);
}
}
@Override
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;
}
@Override
public String getAccessibilityDescription() {
return String.format(getContext().getString(R.string.folder_opened),
mGridCountX, mGridCountY);
}
@Override
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();
}
}
@Override
public void realTimeReorder(int empty, int target) {
int delay = 0;
float delayAmount = 30;
// 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() {
mPageChangingViews.remove(v);
v.setTranslationX(oldTranslateX);
((CellLayout) v.getParent().getParent()).removeView(v);
addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
}
};
v.animate()
.translationXBy(direction > 0 ? -v.getWidth() : v.getWidth())
.setDuration(REORDER_ANIMATION_DURATION)
.setStartDelay(0)
.withEndAction(endAction);
mPageChangingViews.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 *= 0.9;
}
}
}
}