diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2015-03-05 04:30:21 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-03-05 04:30:22 +0000 |
commit | 0b0d9d40ee055e166235e0e68141a3c06f8d0e94 (patch) | |
tree | 36416e06863b85e3511fd1db51bb957b50569176 /src | |
parent | bd6fde13dfa46b723d7a0560d40aa0d977e8cf6a (diff) | |
parent | c3a609f950a713d995f1968574d8ed7b4449f415 (diff) | |
download | android_packages_apps_Trebuchet-0b0d9d40ee055e166235e0e68141a3c06f8d0e94.tar.gz android_packages_apps_Trebuchet-0b0d9d40ee055e166235e0e68141a3c06f8d0e94.tar.bz2 android_packages_apps_Trebuchet-0b0d9d40ee055e166235e0e68141a3c06f8d0e94.zip |
Merge "Refactoring folder content" into ub-launcher3-burnaby
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/launcher3/FocusHelper.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher3/FocusOnlyTabWidget.java | 86 | ||||
-rw-r--r-- | src/com/android/launcher3/Folder.java | 433 | ||||
-rw-r--r-- | src/com/android/launcher3/FolderCellLayout.java | 285 |
4 files changed, 409 insertions, 399 deletions
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index c16e45b31..737f6cca7 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -22,7 +22,6 @@ import android.view.KeyEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; -import android.widget.ScrollView; import com.android.launcher3.util.FocusLogic; @@ -398,8 +397,7 @@ public class FocusHelper { // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); final CellLayout layout = (CellLayout) parent.getParent(); - final ScrollView scrollView = (ScrollView) layout.getParent(); - final Folder folder = (Folder) scrollView.getParent(); + final Folder folder = (Folder) layout.getParent().getParent(); View title = folder.mFolderName; Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); final int countX = layout.getCountX(); diff --git a/src/com/android/launcher3/FocusOnlyTabWidget.java b/src/com/android/launcher3/FocusOnlyTabWidget.java deleted file mode 100644 index 08fc311bc..000000000 --- a/src/com/android/launcher3/FocusOnlyTabWidget.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2011 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.view.View; -import android.widget.TabWidget; - -public class FocusOnlyTabWidget extends TabWidget { - public FocusOnlyTabWidget(Context context) { - super(context); - } - - public FocusOnlyTabWidget(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public FocusOnlyTabWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public View getSelectedTab() { - final int count = getTabCount(); - for (int i = 0; i < count; ++i) { - View v = getChildTabViewAt(i); - if (v.isSelected()) { - return v; - } - } - return null; - } - - public int getChildTabIndex(View v) { - final int tabCount = getTabCount(); - for (int i = 0; i < tabCount; ++i) { - if (getChildTabViewAt(i) == v) { - return i; - } - } - return -1; - } - - public void setCurrentTabToFocusedTab() { - View tab = null; - int index = -1; - final int count = getTabCount(); - for (int i = 0; i < count; ++i) { - View v = getChildTabViewAt(i); - if (v.hasFocus()) { - tab = v; - index = i; - break; - } - } - if (index > -1) { - super.setCurrentTab(index); - super.onFocusChange(tab, true); - } - } - public void superOnFocusChange(View v, boolean hasFocus) { - super.onFocusChange(v, hasFocus); - } - - @Override - public void onFocusChange(android.view.View v, boolean hasFocus) { - if (v == this && hasFocus && getTabCount() > 0) { - getSelectedTab().requestFocus(); - return; - } - } -} diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 11e9835e8..80fc32880 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -45,7 +45,6 @@ import android.view.animation.AccelerateInterpolator; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import com.android.launcher3.FolderInfo.FolderListener; @@ -61,64 +60,65 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View.OnFocusChangeListener { private static final String TAG = "Launcher.Folder"; - protected DragController mDragController; - protected Launcher mLauncher; - protected FolderInfo mInfo; + /** + * We avoid measuring {@link #mContentWrapper} with a 0 width or height, as this + * results in CellLayout being measured as UNSPECIFIED, which it does not support. + */ + private static final int MIN_CONTENT_DIMEN = 5; static final int STATE_NONE = -1; static final int STATE_SMALL = 0; static final int STATE_ANIMATING = 1; static final int STATE_OPEN = 2; - private int mExpandDuration; - private int mMaterialExpandDuration; - private int mMaterialExpandStagger; - protected CellLayout mContent; - private ScrollView mScrollView; - private final LayoutInflater mInflater; - private final IconCache mIconCache; - private int mState = STATE_NONE; - private static final int REORDER_ANIMATION_DURATION = 230; private static final int REORDER_DELAY = 250; private static final int ON_EXIT_CLOSE_DELAY = 400; - private boolean mRearrangeOnClose = false; + private static final Rect sTempRect = new Rect(); + + private static String sDefaultFolderName; + private static String sHintText; + + private final Alarm mReorderAlarm = new Alarm(); + private final Alarm mOnExitAlarm = new Alarm(); + + private final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>(); + + private final int mExpandDuration; + private final int mMaterialExpandDuration; + private final int mMaterialExpandStagger; + + private final InputMethodManager mInputMethodManager; + + protected final Launcher mLauncher; + protected DragController mDragController; + protected FolderInfo mInfo; + private FolderIcon mFolderIcon; - private int mMaxCountX; - private int mMaxCountY; - private int mMaxNumItems; - private ArrayList<View> mItemsInReadingOrder = new ArrayList<View>(); + + private FolderCellLayout mContent; + private View mContentWrapper; + FolderEditText mFolderName; + + private View mBottomContent; + private int mBottomContentHeight; + + // Cell ranks used for drag and drop + private int mTargetRank, mPrevTargetRank, mEmptyCellRank; + + private int mState = STATE_NONE; + private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; private ShortcutInfo mCurrentDragInfo; private View mCurrentDragView; private boolean mIsExternalDrag; boolean mSuppressOnAdd = false; - private int[] mTargetCell = new int[2]; - private int[] mPreviousTargetCell = new int[2]; - private int[] mEmptyCell = new int[2]; - private Alarm mReorderAlarm = new Alarm(); - private Alarm mOnExitAlarm = new Alarm(); - private int mFolderNameHeight; - private Rect mTempRect = new Rect(); private boolean mDragInProgress = false; private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - FolderEditText mFolderName; private float mFolderIconPivotX; private float mFolderIconPivotY; - private boolean mIsEditingName = false; - private InputMethodManager mInputMethodManager; - - private static String sDefaultFolderName; - private static String sHintText; - - private FocusIndicatorView mFocusIndicatorHandler; - - // We avoid measuring the scroll view with a 0 width or height, as this - // results in CellLayout being measured as UNSPECIFIED, which it does - // not support. - private static final int MIN_CONTENT_DIMEN = 5; private boolean mDestroyed; @@ -130,25 +130,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * Used to inflate the Workspace from XML. * * @param context The application's context. - * @param attrs The attribtues set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. */ public Folder(Context context, AttributeSet attrs) { super(context, attrs); - - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); setAlwaysDrawnWithCacheEnabled(false); - mInflater = LayoutInflater.from(context); - mIconCache = app.getIconCache(); - - Resources res = getResources(); - mMaxCountX = (int) grid.numColumns; - mMaxCountY = (int) grid.numRows; - mMaxNumItems = mMaxCountX * mMaxCountY; - mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + Resources res = getResources(); mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration); mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration); mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger); @@ -162,20 +152,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mLauncher = (Launcher) context; // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving - // reliable behvior when clicking the text field (since it will always gain focus on click). + // reliable behavior when clicking the text field (since it will always gain focus on click). setFocusableInTouchMode(true); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mScrollView = (ScrollView) findViewById(R.id.scroll_view); - mContent = (CellLayout) findViewById(R.id.folder_content); - - mFocusIndicatorHandler = new FocusIndicatorView(getContext()); - mContent.addView(mFocusIndicatorHandler, 0); - mFocusIndicatorHandler.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; - mFocusIndicatorHandler.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; + mContentWrapper = findViewById(R.id.folder_content_wrapper); + mContent = (FolderCellLayout) findViewById(R.id.folder_content); + mContent.setFolder(this); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -184,22 +170,25 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setGridSize(0, 0); mContent.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); mContent.setInvertIfRtl(true); + mFolderName = (FolderEditText) findViewById(R.id.folder_name); mFolderName.setFolder(this); mFolderName.setOnFocusChangeListener(this); - // We find out how tall the text view 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; - mFolderName.measure(measureSpec, measureSpec); - mFolderNameHeight = mFolderName.getMeasuredHeight(); - // We disable action mode for now since it messes up the view on phones mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback); mFolderName.setOnEditorActionListener(this); mFolderName.setSelectAllOnFocus(true); 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 + // we can allocate the appropriate amount of space for it. + int measureSpec = MeasureSpec.UNSPECIFIED; + mBottomContent.measure(measureSpec, measureSpec); + mBottomContentHeight = mBottomContent.getMeasuredHeight(); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -240,8 +229,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mLauncher.getWorkspace().beginDragShared(v, this); mCurrentDragInfo = item; - mEmptyCell[0] = item.cellX; - mEmptyCell[1] = item.cellY; + mEmptyCellRank = item.rank; mCurrentDragView = v; mContent.removeView(mCurrentDragView); @@ -298,10 +286,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mFolderName; } - public CellLayout getContent() { - return mContent; - } - /** * We need to handle touch events to prevent them from falling through to the workspace below. */ @@ -314,7 +298,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mDragController = dragController; } - void setFolderIcon(FolderIcon icon) { + public void setFolderIcon(FolderIcon icon) { mFolderIcon = icon; } @@ -334,30 +318,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList void bind(FolderInfo info) { mInfo = info; ArrayList<ShortcutInfo> children = info.contents; - ArrayList<ShortcutInfo> overflow = new ArrayList<ShortcutInfo>(); - - final int totalChildren = children.size(); - setupContentForNumItems(totalChildren); - - // Arrange children in the grid based on the rank. Collections.sort(children, Utilities.RANK_COMPARATOR); - final int countX = mContent.getCountX(); - - int visibleChildren = 0; - for (int i = 0; i < children.size(); i++) { - ShortcutInfo child = children.get(i); - child.cellX = i % countX; - child.cellY = i / countX; - - if (createAndAddShortcut(child) == null) { - overflow.add(child); - } else { - visibleChildren++; - } - } - // We rearrange the items in case there are any empty gaps - setupContentForNumItems(visibleChildren); + ArrayList<ShortcutInfo> overflow = mContent.bindItems(children); // If our folder has too many items we prune them from the list. This is an issue // when upgrading from the old Folders implementation which could contain an unlimited @@ -367,6 +330,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList LauncherModel.deleteItemFromDatabase(mLauncher, item); } + DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); + if (lp == null) { + lp = new DragLayer.LayoutParams(0, 0); + lp.customPosition = true; + setLayoutParams(lp); + } + centerAboutIcon(); + mItemsInvalidated = true; updateTextViewFocus(); mInfo.addListener(this); @@ -470,8 +441,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList iconsAlpha.setStartDelay(mMaterialExpandStagger); iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - mFolderName.setAlpha(0f); - Animator textAlpha = LauncherAnimUtils.ofFloat(mFolderName, "alpha", 0f, 1f); + mBottomContent.setAlpha(0f); + Animator textAlpha = LauncherAnimUtils.ofFloat(mBottomContent, "alpha", 0f, 1f); textAlpha.setDuration(mMaterialExpandDuration); textAlpha.setStartDelay(mMaterialExpandStagger); textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); @@ -524,14 +495,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void beginExternalDrag(ShortcutInfo item) { - setupContentForNumItems(getItemCount() + 1); - findAndSetEmptyCells(item); - mCurrentDragInfo = item; - mEmptyCell[0] = item.cellX; - mEmptyCell[1] = item.cellY; + mEmptyCellRank = mContent.allocateNewLastItemRank(); mIsExternalDrag = true; - mDragInProgress = true; } @@ -588,132 +554,34 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList !isFull()); } - protected boolean findAndSetEmptyCells(ShortcutInfo item) { - int[] emptyCell = new int[2]; - if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) { - item.cellX = emptyCell[0]; - item.cellY = emptyCell[1]; - return true; - } else { - return false; - } - } - - protected View createAndAddShortcut(ShortcutInfo item) { - final BubbleTextView textView = - (BubbleTextView) mInflater.inflate(R.layout.folder_application, this, false); - textView.applyFromShortcutInfo(item, mIconCache, false); - - textView.setOnClickListener(this); - textView.setOnLongClickListener(this); - textView.setOnFocusChangeListener(mFocusIndicatorHandler); - - // We need to check here to verify that the given item's location isn't already occupied - // by another item. - if (mContent.getChildAt(item.cellX, item.cellY) != null || item.cellX < 0 || item.cellY < 0 - || item.cellX >= mContent.getCountX() || item.cellY >= mContent.getCountY()) { - // This shouldn't happen, log it. - Log.e(TAG, "Folder order not properly persisted during bind"); - if (!findAndSetEmptyCells(item)) { - return null; - } - } - - CellLayout.LayoutParams lp = - new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY); - boolean insert = false; - textView.setOnKeyListener(new FolderKeyEventListener()); - mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true); - return textView; - } - public void onDragEnter(DragObject d) { - mPreviousTargetCell[0] = -1; - mPreviousTargetCell[1] = -1; + mPrevTargetRank = -1; mOnExitAlarm.cancelAlarm(); } OnAlarmListener mReorderAlarmListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { - realTimeReorder(mEmptyCell, mTargetCell); + mContent.realTimeReorder(mEmptyCellRank, mTargetRank); + mEmptyCellRank = mTargetRank; } }; - boolean readingOrderGreaterThan(int[] v1, int[] v2) { - if (v1[1] > v2[1] || (v1[1] == v2[1] && v1[0] > v2[0])) { - return true; - } else { - return false; - } - } - - private void realTimeReorder(int[] empty, int[] target) { - boolean wrap; - int startX; - int endX; - int startY; - int delay = 0; - float delayAmount = 30; - if (readingOrderGreaterThan(target, empty)) { - wrap = empty[0] >= mContent.getCountX() - 1; - startY = wrap ? empty[1] + 1 : empty[1]; - for (int y = startY; y <= target[1]; y++) { - startX = y == empty[1] ? empty[0] + 1 : 0; - endX = y < target[1] ? mContent.getCountX() - 1 : target[0]; - for (int x = startX; x <= endX; x++) { - View v = mContent.getChildAt(x,y); - if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION, delay, true, true)) { - empty[0] = x; - empty[1] = y; - delay += delayAmount; - delayAmount *= 0.9; - } - } - } - } else { - wrap = empty[0] == 0; - startY = wrap ? empty[1] - 1 : empty[1]; - for (int y = startY; y >= target[1]; y--) { - startX = y == empty[1] ? empty[0] - 1 : mContent.getCountX() - 1; - endX = y > target[1] ? 0 : target[0]; - for (int x = startX; x >= endX; x--) { - View v = mContent.getChildAt(x,y); - if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION, delay, true, true)) { - empty[0] = x; - empty[1] = y; - delay += delayAmount; - delayAmount *= 0.9; - } - } - } - } - } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } public void onDragOver(DragObject d) { - final int scrollOffset = mScrollView.getScrollY(); final float[] r = d.getVisualCenter(null); r[0] -= getPaddingLeft(); r[1] -= getPaddingTop(); - mTargetCell = mContent.findNearestArea( - (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); - if (isLayoutRtl()) { - mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; - } - if (mTargetCell[0] != mPreviousTargetCell[0] - || mTargetCell[1] != mPreviousTargetCell[1]) { + mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1], 1, 1); + if (mTargetRank != mPrevTargetRank) { mReorderAlarm.cancelAlarm(); mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); mReorderAlarm.setAlarm(REORDER_DELAY); - mPreviousTargetCell[0] = mTargetCell[0]; - mPreviousTargetCell[1] = mTargetCell[1]; + mPrevTargetRank = mTargetRank; } } @@ -764,7 +632,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList replaceFolderWithFinalItem(); } } else { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); // The drag failed, we need to return the item to the folder mFolderIcon.onDrop(d); } @@ -865,37 +733,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return true; } - private void setupContentDimensions(int count) { - ArrayList<View> list = getItemsInReadingOrder(); - - int countX = mContent.getCountX(); - int countY = mContent.getCountY(); - boolean done = false; - - while (!done) { - int oldCountX = countX; - int oldCountY = countY; - if (countX * countY < count) { - // Current grid is too small, expand it - if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) { - countX++; - } else if (countY < mMaxCountY) { - countY++; - } - if (countY == 0) countY++; - } else if ((countY - 1) * countX >= count && countY >= countX) { - countY = Math.max(0, countY - 1); - } else if ((countX - 1) * countY >= count) { - countX = Math.max(0, countX - 1); - } - done = countX == oldCountX && countY == oldCountY; - } - mContent.setGridSize(countX, countY); - arrangeChildren(list); - } - public boolean isFull() { - return getItemCount() >= mMaxNumItems; + return mContent.isFull(); } private void centerAboutIcon() { @@ -905,13 +744,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); int height = getFolderHeight(); - float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, mTempRect); + float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - int centerX = (int) (mTempRect.left + mTempRect.width() * scale / 2); - int centerY = (int) (mTempRect.top + mTempRect.height() * scale / 2); + int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2); + int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2); int centeredLeft = centerX - width / 2; int centeredTop = centerY - height / 2; int currentPage = mLauncher.getWorkspace().getNextPage(); @@ -964,18 +803,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mFolderIconPivotY; } - private void setupContentForNumItems(int count) { - setupContentDimensions(count); - - DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); - if (lp == null) { - lp = new DragLayer.LayoutParams(0, 0); - lp.customPosition = true; - setLayoutParams(lp); - } - centerAboutIcon(); - } - private int getContentAreaHeight() { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -983,7 +810,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int maxContentAreaHeight = grid.availableHeightPx - workspacePadding.top - workspacePadding.bottom - - mFolderNameHeight; + mBottomContentHeight; int height = Math.min(maxContentAreaHeight, mContent.getDesiredHeight()); return Math.max(height, MIN_CONTENT_DIMEN); @@ -994,54 +821,52 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } private int getFolderHeight() { - int height = getPaddingTop() + getPaddingBottom() - + getContentAreaHeight() + mFolderNameHeight; - return height; + return getFolderHeight(getContentAreaHeight()); + } + + private int getFolderHeight(int contentAreaHeight) { + return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mBottomContentHeight; } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); - int height = getFolderHeight(); - int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(getContentAreaWidth(), - MeasureSpec.EXACTLY); - int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(), - MeasureSpec.EXACTLY); - - mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight()); - mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec); - mFolderName.measure(contentAreaWidthSpec, - MeasureSpec.makeMeasureSpec(mFolderNameHeight, MeasureSpec.EXACTLY)); - setMeasuredDimension(width, height); - } - - private void arrangeChildren(ArrayList<View> list) { - int[] vacant = new int[2]; - if (list == null) { - list = getItemsInReadingOrder(); - } - mContent.removeAllViews(); + int contentWidth = getContentAreaWidth(); + int contentHeight = getContentAreaHeight(); - for (int i = 0; i < list.size(); i++) { - View v = list.get(i); - mContent.getVacantCell(vacant, 1, 1); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - lp.cellX = vacant[0]; - lp.cellY = vacant[1]; - ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != vacant[0] || info.cellY != vacant[1]) { - info.cellX = vacant[0]; - info.cellY = vacant[1]; - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); - } - boolean insert = false; - mContent.addViewToCellLayout(v, insert ? 0 : -1, (int)info.id, lp, true); - } + int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY); + int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY); + + mContent.setFixedSize(contentWidth, contentHeight); + mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec); + + // Move the bottom content below mContent + mBottomContent.measure(contentAreaWidthSpec, + MeasureSpec.makeMeasureSpec(mBottomContentHeight, MeasureSpec.EXACTLY)); + + int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth; + int folderHeight = getFolderHeight(contentHeight); + setMeasuredDimension(folderWidth, folderHeight); + } + + /** + * Rearranges the children based on their rank. + */ + public void rearrangeChildren() { + rearrangeChildren(-1); + } + + /** + * Rearranges the children based on their rank. + * @param itemCount if greater than the total children count, empty spaces are left at the end, + * otherwise it is ignored. + */ + public void rearrangeChildren(int itemCount) { + ArrayList<View> views = getItemsInReadingOrder(); + mContent.arrangeChildren(views, Math.max(itemCount, views.size())); mItemsInvalidated = true; } public int getItemCount() { - return mContent.getShortcutsAndWidgets().getChildCount(); + return mContent.getItemCount(); } public View getItemAt(int index) { @@ -1058,7 +883,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderIcon.requestFocus(); if (mRearrangeOnClose) { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); mRearrangeOnClose = false; } if (getItemCount() <= 1) { @@ -1153,9 +978,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View currentDragView; ShortcutInfo si = mCurrentDragInfo; if (mIsExternalDrag) { - si.cellX = mEmptyCell[0]; - si.cellY = mEmptyCell[1]; - + currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank); // Actually move the item in the database if it was an external drag. Call this // before creating the view, so that ShortcutInfo is updated appropriately. LauncherModel.addOrMoveItemInDatabase( @@ -1166,14 +989,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList updateItemLocationsInDatabaseBatch(); } mIsExternalDrag = false; - - currentDragView = createAndAddShortcut(si); } else { currentDragView = mCurrentDragView; - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) currentDragView.getLayoutParams(); - si.cellX = lp.cellX = mEmptyCell[0]; - si.cellX = lp.cellY = mEmptyCell[1]; - mContent.addViewToCellLayout(currentDragView, -1, (int) si.id, lp, true); + mContent.addViewForRank(currentDragView, si, mEmptyCellRank); } if (d.dragView.hasDrawn()) { @@ -1192,7 +1010,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList currentDragView.setVisibility(VISIBLE); } mItemsInvalidated = true; - setupContentDimensions(getItemCount()); + rearrangeChildren(); // Temporarily suppress the listener, as we did all the work already here. mSuppressOnAdd = true; @@ -1219,12 +1037,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set if (mSuppressOnAdd) return; - if (!findAndSetEmptyCells(item)) { - // The current layout is full, can we expand it? - setupContentForNumItems(getItemCount() + 1); - findAndSetEmptyCells(item); - } - createAndAddShortcut(item); + mContent.createAndAddShortcutToEnd(item); LauncherModel.addOrMoveItemInDatabase( mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); } @@ -1239,7 +1052,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (mState == STATE_ANIMATING) { mRearrangeOnClose = true; } else { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); } if (getItemCount() <= 1) { replaceFolderWithFinalItem(); diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java new file mode 100644 index 000000000..66f5224bb --- /dev/null +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -0,0 +1,285 @@ +package com.android.launcher3; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; + +import java.util.ArrayList; + +public class FolderCellLayout extends CellLayout { + + 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]; + + private final FolderKeyEventListener mKeyListener = new FolderKeyEventListener(); + private final LayoutInflater mInflater; + private final IconCache mIconCache; + + private final int mMaxCountX; + private final int mMaxCountY; + private final int mMaxNumItems; + + // Indicates the last number of items used to set up the grid size + private int mAllocatedContentSize; + + private Folder mFolder; + private FocusIndicatorView mFocusIndicatorView; + + public FolderCellLayout(Context context) { + this(context, null); + } + + public FolderCellLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FolderCellLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + mMaxCountX = (int) grid.numColumns; + mMaxCountY = (int) grid.numRows; + mMaxNumItems = mMaxCountX * mMaxCountY; + + mInflater = LayoutInflater.from(context); + mIconCache = app.getIconCache(); + } + + public void setFolder(Folder folder) { + mFolder = folder; + mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_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; + int countX = getCountX(); + int countY = getCountY(); + boolean done = false; + + while (!done) { + int oldCountX = countX; + int oldCountY = countY; + if (countX * countY < count) { + // Current grid is too small, expand it + if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) { + countX++; + } else if (countY < mMaxCountY) { + countY++; + } + if (countY == 0) countY++; + } else if ((countY - 1) * countX >= count && countY >= countX) { + countY = Math.max(0, countY - 1); + } else if ((countX - 1) * countY >= count) { + countX = Math.max(0, countX - 1); + } + done = countX == oldCountX && countY == oldCountY; + } + setGridSize(countX, countY); + } + + /** + * 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<ShortcutInfo> extra = new ArrayList<ShortcutInfo>(); + setupContentDimensions(Math.min(items.size(), mMaxNumItems)); + + int countX = getCountX(); + int rank = 0; + for (ShortcutInfo item : items) { + if (rank >= mMaxNumItems) { + extra.add(item); + continue; + } + + item.rank = rank; + item.cellX = rank % countX; + item.cellY = rank / countX; + addNewView(item); + rank++; + } + return extra; + } + + /** + * Create space for a new item at the end, and returns the rank for that item. + * Resizes the content if necessary. + */ + public int allocateNewLastItemRank() { + int rank = getItemCount(); + mFolder.rearrangeChildren(rank + 1); + return rank; + } + + /** + * Adds the new item to the end of the grid. Resizes the content if necessary. + */ + public View createAndAddShortcutToEnd(ShortcutInfo item) { + int rank = allocateNewLastItemRank(); + return createAndAddViewForRank(item, rank); + } + + public View createAndAddViewForRank(ShortcutInfo item, int rank) { + updateItemXY(item, rank); + return addNewView(item); + } + + /** + * 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) { + updateItemXY(item, rank); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + lp.cellX = item.cellX; + lp.cellY = item.cellY; + addViewToCellLayout(view, -1, (int) item.id, lp, true); + } + + /** + * Updates the item cellX and cellY position + */ + private void updateItemXY(ShortcutInfo item, int rank) { + item.rank = rank; + int countX = getCountX(); + item.cellX = rank % countX; + item.cellY = rank / countX; + } + + private View addNewView(ShortcutInfo item) { + final BubbleTextView textView = (BubbleTextView) mInflater.inflate( + R.layout.folder_application, 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); + addViewToCellLayout(textView, -1, (int)item.id, lp, true); + return textView; + } + + /** + * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])} + * + * @return The rank of a vacant area that can contain this object, + * nearest the requested location. + */ + public int findNearestArea(int pixelX, int pixelY, int spanX, int spanY) { + findNearestArea(pixelX, pixelY, spanX, spanY, null, false, sTempPosArray); + if (mFolder.isLayoutRtl()) { + sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1; + } + + // Convert this position to rank. + return Math.min(mAllocatedContentSize - 1, + sTempPosArray[1] * getCountX() + sTempPosArray[0]); + } + + public boolean isFull() { + return getItemCount() >= mMaxNumItems; + } + + public int getItemCount() { + return getShortcutsAndWidgets().getChildCount(); + } + + /** + * Updates position and rank of all the children in the view based. + * @param list the ordered list of children. + * @param itemCount if greater than the total children count, empty spaces are left at the end. + */ + public void arrangeChildren(ArrayList<View> list, int itemCount) { + setupContentDimensions(itemCount); + removeAllViews(); + + int newX, newY; + int rank = 0; + int countX = getCountX(); + for (View v : list) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); + newX = rank % countX; + newY = rank / countX; + 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 ++; + addViewToCellLayout(v, -1, (int)info.id, lp, true); + } + } + + /** + * Reorders the items such that the {@param empty} spot moves to {@param target} + */ + public void realTimeReorder(int empty, int target) { + boolean wrap; + int startX; + int endX; + int startY; + int delay = 0; + float delayAmount = START_VIEW_REORDER_DELAY; + + int countX = getCountX(); + int emptyX = empty % getCountX(); + int emptyY = empty / countX; + + int targetX = target % countX; + int targetY = target / countX; + + if (target > empty) { + wrap = emptyX == countX - 1; + startY = wrap ? emptyY + 1 : emptyY; + for (int y = startY; y <= targetY; y++) { + startX = y == emptyY ? emptyX + 1 : 0; + endX = y < targetY ? countX - 1 : targetX; + for (int x = startX; x <= endX; x++) { + View v = getChildAt(x,y); + if (animateChildToPosition(v, emptyX, emptyY, + REORDER_ANIMATION_DURATION, delay, true, true)) { + emptyX = x; + emptyY = y; + delay += delayAmount; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; + } + } + } + } else { + wrap = emptyX == 0; + startY = wrap ? emptyY - 1 : emptyY; + for (int y = startY; y >= targetY; y--) { + startX = y == emptyY ? emptyX - 1 : countX - 1; + endX = y > targetY ? 0 : targetX; + for (int x = startX; x >= endX; x--) { + View v = getChildAt(x,y); + if (animateChildToPosition(v, emptyX, emptyY, + REORDER_ANIMATION_DURATION, delay, true, true)) { + emptyX = x; + emptyY = y; + delay += delayAmount; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; + } + } + } + } + } +} |