Merge "Refactoring folder content" into ub-launcher3-burnaby

This commit is contained in:
Sunny Goyal
2015-03-05 04:30:21 +00:00
committed by Android (Google) Code Review
5 changed files with 441 additions and 422 deletions
+35 -26
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
<!--
Copyright (C) 2008 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.
@@ -14,42 +15,50 @@
limitations under the License.
-->
<com.android.launcher3.Folder
xmlns:android="http://schemas.android.com/apk/res/android"
<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:orientation="vertical"
android:background="@drawable/quantum_panel">
android:background="@drawable/quantum_panel"
android:orientation="vertical" >
<ScrollView
android:id="@+id/scroll_view"
<FrameLayout
android:id="@+id/folder_content_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.launcher3.CellLayout
android:id="@+id/folder_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:cacheColorHint="#ff333333"
android:hapticFeedbackEnabled="false" />
</ScrollView>
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.FolderCellLayout
android:id="@+id/folder_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#ff333333"
android:hapticFeedbackEnabled="false" />
</FrameLayout>
<com.android.launcher3.FolderEditText
android:id="@+id/folder_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:paddingTop="@dimen/folder_name_padding"
android:paddingBottom="@dimen/folder_name_padding"
android:background="#00000000"
android:hint="@string/folder_hint_text"
android:textSize="14sp"
android:textColor="#ff777777"
android:textColorHint="#ff808080"
android:textColorHighlight="#ffCCCCCC"
android:textCursorDrawable="@null"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:singleLine="true"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
android:fontFamily="sans-serif-condensed"/>
</com.android.launcher3.Folder>
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" />
</com.android.launcher3.Folder>
+1 -3
View File
@@ -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();
@@ -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;
}
}
}
+120 -307
View File
@@ -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);
int contentWidth = getContentAreaWidth();
int contentHeight = getContentAreaHeight();
mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight());
mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec);
mFolderName.measure(contentAreaWidthSpec,
MeasureSpec.makeMeasureSpec(mFolderNameHeight, MeasureSpec.EXACTLY));
setMeasuredDimension(width, height);
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);
}
private void arrangeChildren(ArrayList<View> list) {
int[] vacant = new int[2];
if (list == null) {
list = getItemsInReadingOrder();
}
mContent.removeAllViews();
/**
* Rearranges the children based on their rank.
*/
public void rearrangeChildren() {
rearrangeChildren(-1);
}
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);
}
/**
* 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();
@@ -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} &amp; {@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;
}
}
}
}
}
}