diff options
24 files changed, 359 insertions, 142 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 63e3c66ad..d9404a7bc 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -61,7 +61,7 @@ <uses-permission android:name="android.permission.BIND_APPWIDGET" /> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" /> - + <application android:name="com.android.launcher2.LauncherApplication" android:process="@string/process" @@ -69,14 +69,12 @@ android:icon="@drawable/ic_launcher_home" android:hardwareAccelerated="@bool/config_hardwareAccelerated" android:largeHeap="true"> - <activity android:name="com.android.launcher2.Launcher" android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" - android:theme="@style/Theme" - android:windowSoftInputMode="stateUnspecified|adjustNothing"> + android:theme="@style/Theme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> diff --git a/res/drawable-hdpi/portal_container_holo.9.png b/res/drawable-hdpi/portal_container_holo.9.png Binary files differindex af2fa98d7..a2fbcb6df 100644 --- a/res/drawable-hdpi/portal_container_holo.9.png +++ b/res/drawable-hdpi/portal_container_holo.9.png diff --git a/res/drawable-hdpi/portal_ring_inner_holo.png b/res/drawable-hdpi/portal_ring_inner_holo.png Binary files differindex 8a9e85b7f..e671d1b62 100644 --- a/res/drawable-hdpi/portal_ring_inner_holo.png +++ b/res/drawable-hdpi/portal_ring_inner_holo.png diff --git a/res/drawable-hdpi/portal_ring_outer_holo.png b/res/drawable-hdpi/portal_ring_outer_holo.png Binary files differindex 5b4641946..7aad6076e 100644 --- a/res/drawable-hdpi/portal_ring_outer_holo.png +++ b/res/drawable-hdpi/portal_ring_outer_holo.png diff --git a/res/drawable-mdpi/portal_container_holo.9.png b/res/drawable-mdpi/portal_container_holo.9.png Binary files differindex 42aca5f51..d2f9b58b1 100644 --- a/res/drawable-mdpi/portal_container_holo.9.png +++ b/res/drawable-mdpi/portal_container_holo.9.png diff --git a/res/drawable-mdpi/portal_ring_inner_holo.png b/res/drawable-mdpi/portal_ring_inner_holo.png Binary files differindex 4a6469488..dc0c04170 100644 --- a/res/drawable-mdpi/portal_ring_inner_holo.png +++ b/res/drawable-mdpi/portal_ring_inner_holo.png diff --git a/res/drawable-mdpi/portal_ring_outer_holo.png b/res/drawable-mdpi/portal_ring_outer_holo.png Binary files differindex 90cf77fa8..5a7e7402b 100644 --- a/res/drawable-mdpi/portal_ring_outer_holo.png +++ b/res/drawable-mdpi/portal_ring_outer_holo.png diff --git a/res/layout-land/folder_icon.xml b/res/layout-land/folder_icon.xml index c76a75666..60569c553 100644 --- a/res/layout-land/folder_icon.xml +++ b/res/layout-land/folder_icon.xml @@ -4,9 +4,9 @@ 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. @@ -14,5 +14,19 @@ limitations under the License. --> -<com.android.launcher2.FolderIcon xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/WorkspaceIcon.Landscape" /> +<com.android.launcher2.FolderIcon + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <ImageView + android:id="@+id/preview_background" + android:layout_gravity="center_horizontal" + android:layout_width="@dimen/folder_preview_size" + android:layout_height="@dimen/folder_preview_size" + android:src="@drawable/portal_ring_inner_holo"/> + <com.android.launcher2.BubbleTextView + android:id="@+id/folder_name" + style="@style/WorkspaceIcon.Landscape"/> +</com.android.launcher2.FolderIcon> diff --git a/res/layout-port/folder_icon.xml b/res/layout-port/folder_icon.xml index 49049cfaf..3f776dec0 100644 --- a/res/layout-port/folder_icon.xml +++ b/res/layout-port/folder_icon.xml @@ -4,9 +4,9 @@ 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. @@ -14,5 +14,19 @@ limitations under the License. --> -<com.android.launcher2.FolderIcon xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/WorkspaceIcon.Portrait" /> +<com.android.launcher2.FolderIcon + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <ImageView + android:id="@+id/preview_background" + android:layout_gravity="center_horizontal" + android:layout_width="@dimen/folder_preview_size" + android:layout_height="@dimen/folder_preview_size" + android:src="@drawable/portal_ring_inner_holo"/> + <com.android.launcher2.BubbleTextView + android:id="@+id/folder_name" + style="@style/WorkspaceIcon.Portrait"/> +</com.android.launcher2.FolderIcon> diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index 5ef959d71..7e25f356e 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -17,16 +17,20 @@ <com.android.launcher2.Folder xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/folder_padding" + android:paddingRight="@dimen/folder_padding" + android:paddingTop="@dimen/folder_padding" + android:paddingBottom="0dp" android:orientation="vertical" android:background="@drawable/portal_container_holo"> <com.android.launcher2.CellLayout - android:id="@id/folder_content" - android:layout_width="match_parent" - android:layout_height="match_parent" - + android:id="@+id/folder_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:cacheColorHint="#ff333333" - android:hapticFeedbackEnabled="false" launcher:widthGap="@dimen/workspace_width_gap" launcher:heightGap="@dimen/workspace_height_gap" @@ -36,4 +40,18 @@ launcher:xAxisEndPadding="0dip" launcher:yAxisStartPadding="8dip" launcher:yAxisEndPadding="8dip"/> -</com.android.launcher2.Folder> + + <EditText + android:id="@+id/folder_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:paddingTop="@dimen/folder_content_name_gap" + android:paddingBottom="@dimen/folder_padding" + android:background="#00000000" + android:hint="@string/default_folder_name" + android:textSize="16sp" + android:textColor="#FFF" + android:gravity="center_horizontal" + android:singleLine="true"/> +</com.android.launcher2.Folder>
\ No newline at end of file diff --git a/res/values-large/dimens.xml b/res/values-large/dimens.xml index 924832a55..eb48859a6 100644 --- a/res/values-large/dimens.xml +++ b/res/values-large/dimens.xml @@ -106,9 +106,16 @@ <!-- Max number of rows in all apps, because too many looks weird. --> <integer name="all_apps_view_maxCellCountY">6</integer> +<!-- Workspace grid --> <!-- Padding applied to AppWidgets --> <dimen name="app_widget_padding_left">12dp</dimen> <dimen name="app_widget_padding_right">12dp</dimen> <dimen name="app_widget_padding_top">4dp</dimen> <dimen name="app_widget_padding_bottom">20dp</dimen> + +<!-- Folders --> + <!-- The size of the image which sits behind the preview of the folder contents --> + <dimen name="folder_preview_size">80dp</dimen> + <!-- The amount that the preview contents are inset from the preview background --> + <dimen name="folder_preview_padding">4dp</dimen> </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 15dbc3ab6..141df0615 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -83,9 +83,18 @@ <dimen name="workspace_width_gap">-1dp</dimen> <dimen name="workspace_height_gap">-1dp</dimen> +<!-- Workspace grid --> <!-- Padding applied to AppWidgets --> <dimen name="app_widget_padding_left">0dp</dimen> <dimen name="app_widget_padding_right">0dp</dimen> <dimen name="app_widget_padding_top">0dp</dimen> <dimen name="app_widget_padding_bottom">0dp</dimen> + +<!-- Folders --> + <!-- The size of the image which sits behind the preview of the folder contents --> + <dimen name="folder_preview_size">56dp</dimen> + <!-- The amount that the preview contents are inset from the preview background --> + <dimen name="folder_preview_padding">4dp</dimen> + <dimen name="folder_padding">18dp</dimen> + <dimen name="folder_content_name_gap">10dp</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 0ad1a46f2..70f212c2e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -254,9 +254,12 @@ <string name="default_browser_url" translatable="false"> http://www.google.com/m?client=ms-{CID}&source=android-home-hotseat</string> - <!-- Text to inform the user that they can't uninstall a system application --> + <!-- Text to inform the user that they can't uninstall a system application --> <string name="uninstall_system_app_text">This is a system application and cannot be uninstalled.</string> <!-- Title of the Android Dreams (screensaver) module --> <string name="dream_name">Rocket Launcher</string> + + <!-- Default folder title --> + <string name="default_folder_name">Unnamed Folder</string> </resources> diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java index 703b3a8cb..8c0c27ca2 100644 --- a/src/com/android/launcher2/BubbleTextView.java +++ b/src/com/android/launcher2/BubbleTextView.java @@ -262,10 +262,12 @@ public class BubbleTextView extends TextView implements VisibilityChangedBroadca } void setCellLayoutPressedOrFocusedIcon() { - CellLayoutChildren parent = (CellLayoutChildren) getParent(); - if (parent != null) { - CellLayout layout = (CellLayout) parent.getParent(); - layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null); + if (getParent() instanceof CellLayoutChildren) { + CellLayoutChildren parent = (CellLayoutChildren) getParent(); + if (parent != null) { + CellLayout layout = (CellLayout) parent.getParent(); + layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null); + } } } diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index a9ba88d94..c1aa2d589 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -298,6 +298,7 @@ public class CellLayout extends ViewGroup { mCountX = x; mCountY = y; mOccupied = new boolean[mCountX][mCountY]; + requestLayout(); } private void invalidateBubbleTextView(BubbleTextView icon) { @@ -971,7 +972,8 @@ public class CellLayout extends ViewGroup { return mChildren.getChildAt(x, y); } - public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration) { + public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration, + int delay) { CellLayoutChildren clc = getChildrenLayout(); if (clc.indexOfChild(child) != -1 && !mOccupied[cellX][cellY]) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -996,6 +998,10 @@ public class CellLayout extends ViewGroup { int newX = lp.x; int newY = lp.y; + lp.x = oldX; + lp.y = oldY; + child.requestLayout(); + PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", oldX, newX); PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", oldY, newY); ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, x, y); @@ -1023,6 +1029,7 @@ public class CellLayout extends ViewGroup { cancelled = true; } }); + oa.setStartDelay(delay); oa.start(); return true; } diff --git a/src/com/android/launcher2/CellLayoutChildren.java b/src/com/android/launcher2/CellLayoutChildren.java index 6e78885de..1caecc001 100644 --- a/src/com/android/launcher2/CellLayoutChildren.java +++ b/src/com/android/launcher2/CellLayoutChildren.java @@ -83,6 +83,7 @@ public class CellLayoutChildren extends ViewGroup { lp.setup(mCellWidth, mCellHeight, mWidthGap, mHeightGap); } + public void measureChild(View child) { final int cellWidth = mCellWidth; final int cellHeight = mCellHeight; @@ -92,7 +93,6 @@ public class CellLayoutChildren extends ViewGroup { int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - child.measure(childWidthMeasureSpec, childheightMeasureSpec); } diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java index af47bea78..c4d75d6eb 100644 --- a/src/com/android/launcher2/DragLayer.java +++ b/src/com/android/launcher2/DragLayer.java @@ -25,6 +25,7 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.ImageView; @@ -62,13 +63,13 @@ public class DragLayer extends FrameLayout { mLauncher = launcher; mDragController = controller; } - + @Override public boolean dispatchKeyEvent(KeyEvent event) { return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); } - private boolean handleTouchDown(MotionEvent ev) { + private boolean handleTouchDown(MotionEvent ev, boolean intercept) { Rect hitRect = new Rect(); int x = (int) ev.getX(); int y = (int) ev.getY(); @@ -85,17 +86,21 @@ public class DragLayer extends FrameLayout { } } } - if (mCurrentFolder != null) { - mCurrentFolder.getHitRect(hitRect); - int[] screenPos = new int[2]; - View parent = (View) mCurrentFolder.getParent(); - if (parent != null) { - parent.getLocationOnScreen(screenPos); - hitRect.offset(screenPos[0], screenPos[1]); + + if (mCurrentFolder != null && intercept) { + if (mCurrentFolder.isEditingName()) { + getDescendantRectRelativeToSelf(mCurrentFolder.getEditTextRegion(), hitRect); if (!hitRect.contains(x, y)) { - mLauncher.closeFolder(); + mCurrentFolder.dismissEditingName(); + return true; } } + + getDescendantRectRelativeToSelf(mCurrentFolder, hitRect); + if (!hitRect.contains(x, y)) { + mLauncher.closeFolder(); + return true; + } } return false; } @@ -103,7 +108,7 @@ public class DragLayer extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (handleTouchDown(ev)) { + if (handleTouchDown(ev, true)) { return true; } } @@ -121,7 +126,7 @@ public class DragLayer extends FrameLayout { if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (handleTouchDown(ev)) { + if (handleTouchDown(ev, false)) { return true; } } @@ -143,6 +148,18 @@ public class DragLayer extends FrameLayout { return mDragController.onTouchEvent(ev); } + private void getDescendantRectRelativeToSelf(View descendant, Rect r) { + descendant.getHitRect(r); + + ViewParent viewParent = descendant.getParent(); + while (viewParent instanceof View && viewParent != this) { + final View view = (View)viewParent; + r.offset(view.getLeft() + (int) (view.getTranslationX() + 0.5f) - view.getScrollX(), + view.getTop() + (int) (view.getTranslationY() + 0.5f) - view.getScrollY()); + viewParent = view.getParent(); + } + } + @Override public boolean dispatchUnhandledMove(View focused, int direction) { return mDragController.dispatchUnhandledMove(focused, direction); diff --git a/src/com/android/launcher2/FastBitmapDrawable.java b/src/com/android/launcher2/FastBitmapDrawable.java index 3e75fb6d0..9fa62da4a 100644 --- a/src/com/android/launcher2/FastBitmapDrawable.java +++ b/src/com/android/launcher2/FastBitmapDrawable.java @@ -64,6 +64,10 @@ class FastBitmapDrawable extends Drawable { mPaint.setAlpha(alpha); } + public void setFilterBitmap(boolean filterBitmap) { + mPaint.setFilterBitmap(filterBitmap); + } + public int getAlpha() { return mAlpha; } diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java index d81183c6b..960fa551c 100644 --- a/src/com/android/launcher2/Folder.java +++ b/src/com/android/launcher2/Folder.java @@ -23,16 +23,22 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; -import android.graphics.Color; +import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.ActionMode; +import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.LinearLayout; import android.widget.TextView; @@ -48,7 +54,8 @@ import java.util.ArrayList; * Represents a set of icons chosen by the user or generated by the system. */ public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener, - OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener { + OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, + TextView.OnEditorActionListener { protected DragController mDragController; @@ -90,6 +97,11 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL private int[] mEmptyCell = new int[2]; private Alarm mReorderAlarm = new Alarm(); private Alarm mOnExitAlarm = new Alarm(); + private TextView mFolderName; + private int mFolderNameHeight; + + private boolean mIsEditingName = false; + private InputMethodManager mInputMethodManager; /** * Used to inflate the Workspace from XML. @@ -102,10 +114,14 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL setAlwaysDrawnWithCacheEnabled(false); mInflater = LayoutInflater.from(context); mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache(); - mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration); - mMaxCountX = LauncherModel.getCellCountX() - 1; mMaxCountY = LauncherModel.getCellCountY() - 1; + + mInputMethodManager = (InputMethodManager) + mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + + Resources res = getResources(); + mExpandDuration = res.getInteger(R.integer.config_folderAnimDuration); } @Override @@ -114,8 +130,37 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL mContent = (CellLayout) findViewById(R.id.folder_content); mContent.setGridSize(0, 0); mContent.enableHardwareLayers(); + mFolderName = (TextView) findViewById(R.id.folder_name); + + // 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.setCursorVisible(false); + mFolderName.setOnEditorActionListener(this); } + private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + return false; + } + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + return false; + } + + public void onDestroyActionMode(ActionMode mode) { + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + }; + public void onItemClick(AdapterView parent, View v, int position, long id) { ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); int[] pos = new int[2]; @@ -138,6 +183,17 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL } } + private Rect mHitRect = new Rect(); + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mFolderName.getHitRect(mHitRect); + if (mHitRect.contains((int) ev.getX(), (int) ev.getY()) && !mIsEditingName) { + startEditingFolderName(); + } + } + return false; + } + public boolean onLongClick(View v) { Object tag = v.getTag(); if (tag instanceof ShortcutInfo) { @@ -158,13 +214,44 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL mCurrentDragView = v; mContent.removeView(mCurrentDragView); mInfo.remove(item); - } else { - mLauncher.closeFolder(this); - mLauncher.showRenameDialog(mInfo); } return true; } + public boolean isEditingName() { + return mIsEditingName; + } + + public void startEditingFolderName() { + mFolderName.setCursorVisible(true); + mIsEditingName = true; + } + + public void dismissEditingName() { + mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + doneEditingFolderName(true); + } + + public void doneEditingFolderName(boolean commit) { + mInfo.setTitle(mFolderName.getText()); + LauncherModel.updateItemInDatabase(mLauncher, mInfo); + mFolderName.setCursorVisible(false); + mFolderName.clearFocus(); + mIsEditingName = false; + } + + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + dismissEditingName(); + return true; + } + return false; + } + + public View getEditTextRegion() { + return mFolderName; + } + public Drawable getDragDrawable() { return mIconDrawable; } @@ -216,12 +303,13 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL // forcing a layout // TODO: find out if this is still necessary mContent.requestLayout(); - requestFocus(); } void onClose() { - final Workspace workspace = mLauncher.getWorkspace(); - workspace.getChildAt(workspace.getCurrentPage()).requestFocus(); + CellLayoutChildren clc = (CellLayoutChildren) getParent(); + final CellLayout cellLayout = (CellLayout) clc.getParent(); + cellLayout.removeViewWithoutMarkingCells(Folder.this); + clearFocus(); } void bind(FolderInfo info) { @@ -234,6 +322,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL } mItemsInvalidated = true; mInfo.addListener(this); + mFolderName.setText(mInfo.title); } /** @@ -322,10 +411,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL public void animateClosed() { if (!(getParent() instanceof CellLayoutChildren)) return; - CellLayoutChildren clc = (CellLayoutChildren) getParent(); - final CellLayout cellLayout = (CellLayout) clc.getParent(); ObjectAnimator oa; - if (mMode == PARTIAL_GROW) { PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f); @@ -356,8 +442,8 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL oa.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + onClose(); onCloseComplete(); - cellLayout.removeViewWithoutMarkingCells(Folder.this); mState = STATE_SMALL; } @Override @@ -438,6 +524,8 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL 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]; @@ -447,9 +535,11 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL for (int x = startX; x <= endX; x++) { View v = mContent.getChildAt(x,y); if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION)) { + REORDER_ANIMATION_DURATION, delay)) { empty[0] = x; empty[1] = y; + delay += delayAmount; + delayAmount *= 0.9; } } } @@ -462,9 +552,11 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL for (int x = startX; x >= endX; x--) { View v = mContent.getChildAt(x,y); if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION)) { + REORDER_ANIMATION_DURATION, delay)) { empty[0] = x; empty[1] = y; + delay += delayAmount; + delayAmount *= 0.9; } } } @@ -591,7 +683,9 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); - int height = getPaddingTop() + getPaddingBottom() + mContent.getDesiredHeight(); + // Technically there is no padding at the bottom, but we add space equal to the padding + // and have to account for that here. + int height = getPaddingTop() + mContent.getDesiredHeight() + mFolderNameHeight; int centerX = iconLp.x + iconLp.width / 2; int centerY = iconLp.y + iconLp.height / 2; @@ -732,6 +826,8 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL public void onItemsChanged() { } + public void onTitleChanged(CharSequence title) { + } public ArrayList<View> getItemsInReadingOrder() { return getItemsInReadingOrder(true); diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index 18b242bfa..db3dfe8f2 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -30,7 +30,8 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher.R; @@ -42,39 +43,32 @@ import java.util.ArrayList; /** * An icon that can appear on in the workspace representing an {@link UserFolder}. */ -public class FolderIcon extends FrameLayout implements FolderListener { +public class FolderIcon extends LinearLayout implements FolderListener { private Launcher mLauncher; Folder mFolder; FolderInfo mInfo; // The number of icons to display in the - private static final int NUM_ITEMS_IN_PREVIEW = 4; + private static final int NUM_ITEMS_IN_PREVIEW = 3; private static final int CONSUMPTION_ANIMATION_DURATION = 100; // The degree to which the inner ring grows when accepting drop private static final float INNER_RING_GROWTH_FACTOR = 0.1f; - // The degree to which the inner ring is scaled in its natural state - private static final float INNER_RING_BASELINE_SCALE = 1.0f; - - // The degree to which the outer ring grows when accepting drop - private static final float OUTER_RING_BASELINE_SCALE = 0.7f; - // The degree to which the outer ring is scaled in its natural state - private static final float OUTER_RING_GROWTH_FACTOR = 0.3f; + private static final float OUTER_RING_GROWTH_FACTOR = 0.4f; // The amount of vertical spread between items in the stack [0...1] - private static final float PERSPECTIVE_SHIFT_FACTOR = 0.3f; + private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f; // The degree to which the item in the back of the stack is scaled [0...1] // (0 means it's not scaled at all, 1 means it's scaled to nothing) - private static final float PERSPECTIVE_SCALE_FACTOR = 0.3f; - - // The percentage of the FolderIcons view that will be dedicated to the items preview - private static final float SPACE_PERCENTAGE_FOR_ICONS = 0.8f; + private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; private int mOriginalWidth = -1; private int mOriginalHeight = -1; + private ImageView mPreviewBackground; + private BubbleTextView mFolderName; FolderRingAnimator mFolderRingAnimator = null; @@ -98,9 +92,10 @@ public class FolderIcon extends FrameLayout implements FolderListener { FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); - final Resources resources = launcher.getResources(); - Drawable d = iconCache.getFullResIcon(resources, R.drawable.portal_ring_inner_holo); - icon.setBackgroundDrawable(d); + icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_name); + icon.mFolderName.setText(folderInfo.title); + icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); + icon.setTag(folderInfo); icon.setOnClickListener(launcher); icon.mInfo = folderInfo; @@ -121,31 +116,36 @@ public class FolderIcon extends FrameLayout implements FolderListener { public static class FolderRingAnimator { public int mFolderLocX; public int mFolderLocY; - public float mOuterRingScale; - public float mInnerRingScale; + public float mOuterRingSize; + public float mInnerRingSize; public FolderIcon mFolderIcon = null; private Launcher mLauncher; public Drawable mOuterRingDrawable = null; public Drawable mInnerRingDrawable = null; public static Drawable sSharedOuterRingDrawable = null; public static Drawable sSharedInnerRingDrawable = null; + public static int sPreviewSize = -1; + public static int sPreviewPadding = -1; + private ValueAnimator mAcceptAnimator; private ValueAnimator mNeutralAnimator; public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) { mLauncher = launcher; mFolderIcon = folderIcon; - mOuterRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo); - mInnerRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo); + Resources res = launcher.getResources(); + mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); + mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); + + if (sPreviewSize < 0 || sPreviewPadding < 0) { + sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size); + sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); + } if (sSharedOuterRingDrawable == null) { - sSharedOuterRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo); + sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); } if (sSharedInnerRingDrawable == null) { - sSharedInnerRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo); + sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); } } @@ -164,8 +164,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { final float percent = (Float) animation.getAnimatedValue(); - mOuterRingScale = OUTER_RING_BASELINE_SCALE + percent * OUTER_RING_GROWTH_FACTOR; - mInnerRingScale = INNER_RING_BASELINE_SCALE + percent * INNER_RING_GROWTH_FACTOR; + mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize; + mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize; mLauncher.getWorkspace().invalidate(); if (mFolderIcon != null) { mFolderIcon.invalidate(); @@ -176,7 +176,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onAnimationStart(Animator animation) { if (mFolderIcon != null) { - mFolderIcon.setBackgroundDrawable(null); + mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE); } } }); @@ -192,10 +192,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { final float percent = (Float) animation.getAnimatedValue(); - mOuterRingScale = OUTER_RING_BASELINE_SCALE + OUTER_RING_GROWTH_FACTOR - - percent * OUTER_RING_GROWTH_FACTOR; - mInnerRingScale = INNER_RING_BASELINE_SCALE + INNER_RING_GROWTH_FACTOR - - percent * INNER_RING_GROWTH_FACTOR; + mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize; + mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize; mLauncher.getWorkspace().invalidate(); if (mFolderIcon != null) { mFolderIcon.invalidate(); @@ -206,7 +204,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onAnimationEnd(Animator animation) { if (mFolderIcon != null) { - mFolderIcon.setBackgroundDrawable(mInnerRingDrawable); + mFolderIcon.mPreviewBackground.setVisibility(VISIBLE); } mLauncher.getWorkspace().hideFolderAccept(FolderRingAnimator.this); } @@ -220,12 +218,12 @@ public class FolderIcon extends FrameLayout implements FolderListener { loc[1] = mFolderLocY; } - public float getOuterRingScale() { - return mOuterRingScale; + public float getOuterRingSize() { + return mOuterRingSize; } - public float getInnerRingScale() { - return mInnerRingScale; + public float getInnerRingSize() { + return mInnerRingSize; } } @@ -258,7 +256,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { mLauncher.getWorkspace().getLocationInWindow(wsLocation); int x = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2; - int y = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2; + int y = tvLocation[1] - wsLocation[1] + FolderRingAnimator.sPreviewSize / 2; mFolderRingAnimator.setLocation(x, y); } @@ -296,31 +294,38 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (mFolder == null) return; if (mFolder.getItemCount() == 0) return; canvas.save(); TextView v = (TextView) mFolder.getItemAt(0); Drawable d = v.getCompoundDrawables()[1]; + int intrinsicIconSize = d.getIntrinsicHeight(); if (mOriginalWidth < 0 || mOriginalHeight < 0) { mOriginalWidth = getMeasuredWidth(); mOriginalHeight = getMeasuredHeight(); } + final int previewSize = FolderRingAnimator.sPreviewSize; + final int previewPadding = FolderRingAnimator.sPreviewPadding; + + int halfAvailableSpace = (previewSize - 2 * previewPadding) / 2; + // cos(45) = 0.707 + ~= 0.1) + int availableSpace = (int) (halfAvailableSpace * (1 + 0.8f)); - int unscaledHeight = (int) (d.getIntrinsicHeight() * (1 + PERSPECTIVE_SHIFT_FACTOR)); - float baselineIconScale = SPACE_PERCENTAGE_FOR_ICONS / (unscaledHeight / (mOriginalHeight * 1.0f)); + int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); + float baselineIconScale = (1.0f * availableSpace / unscaledHeight); - int baselineHeight = (int) (d.getIntrinsicHeight() * baselineIconScale); - int totalStackHeight = (int) (baselineHeight * (1 + PERSPECTIVE_SHIFT_FACTOR)); - int baselineWidth = (int) (d.getIntrinsicWidth() * baselineIconScale); - float maxPerpectiveShift = baselineHeight * PERSPECTIVE_SHIFT_FACTOR; + int baselineSize = (int) (intrinsicIconSize * baselineIconScale); + float maxPerspectiveShift = baselineSize * PERSPECTIVE_SHIFT_FACTOR; ArrayList<View> items = mFolder.getItemsInReadingOrder(false); int firstItemIndex = Math.max(0, items.size() - NUM_ITEMS_IN_PREVIEW); - int xShift = (int) (mOriginalWidth - baselineWidth) / 2; - int yShift = (int) (mOriginalHeight - totalStackHeight) / 2; + int xShift = (mOriginalWidth - 2 * halfAvailableSpace) / 2; + int yShift = previewPadding; canvas.translate(xShift, yShift); for (int i = firstItemIndex; i < items.size(); i++) { int index = i - firstItemIndex; @@ -328,10 +333,17 @@ public class FolderIcon extends FrameLayout implements FolderListener { float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1); float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); - r = (float) Math.pow(r, 2); - float transY = r * maxPerpectiveShift; - float transX = (1 - scale) * baselineWidth / 2.0f; + //r = (float) Math.pow(r, 2); + + float offset = (1 - r) * maxPerspectiveShift; + float scaledSize = scale * baselineSize; + float scaleOffsetCorrection = (1 - scale) * baselineSize; + + // We want to imagine our coordinates from the bottom left, growing up and to the + // right. This is natural for the x-axis, but for the y-axis, we have to invert things. + float transY = 2 * halfAvailableSpace - (offset + scaledSize + scaleOffsetCorrection); + float transX = offset + scaleOffsetCorrection; v = (TextView) items.get(i); d = v.getCompoundDrawables()[1]; @@ -342,10 +354,12 @@ public class FolderIcon extends FrameLayout implements FolderListener { int overlayAlpha = (int) (80 * (1 - r)); if (d != null) { - d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + d.setBounds(0, 0, intrinsicIconSize, intrinsicIconSize); + d.setFilterBitmap(true); d.setColorFilter(Color.argb(overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP); d.draw(canvas); d.clearColorFilter(); + d.setFilterBitmap(false); } canvas.restore(); } @@ -366,4 +380,10 @@ public class FolderIcon extends FrameLayout implements FolderListener { invalidate(); requestLayout(); } + + public void onTitleChanged(CharSequence title) { + mFolderName.setText(title); + mFolderName.invalidate(); + mFolderName.requestLayout(); + } } diff --git a/src/com/android/launcher2/FolderInfo.java b/src/com/android/launcher2/FolderInfo.java index 805a51f39..b5b5b29c1 100644 --- a/src/com/android/launcher2/FolderInfo.java +++ b/src/com/android/launcher2/FolderInfo.java @@ -70,6 +70,13 @@ class FolderInfo extends ItemInfo { } } + public void setTitle(CharSequence title) { + this.title = title; + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).onTitleChanged(title); + } + } + @Override void onAddToDatabase(ContentValues values) { super.onAddToDatabase(values); @@ -95,6 +102,7 @@ class FolderInfo extends ItemInfo { interface FolderListener { public void onAdd(ShortcutInfo item); public void onRemove(ShortcutInfo item); + public void onTitleChanged(CharSequence title); public void onItemsChanged(); } } diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index bda55919d..323c52700 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -1728,7 +1728,12 @@ public final class Launcher extends Activity if (mState == State.APPS_CUSTOMIZE) { showWorkspace(true); } else if (mWorkspace.getOpenFolder() != null) { - closeFolder(); + Folder openFolder = mWorkspace.getOpenFolder(); + if (openFolder.isEditingName()) { + openFolder.dismissEditingName(); + } else { + closeFolder(); + } } else if (isPreviewVisible()) { dismissPreview(mPreviousView); dismissPreview(mNextView); @@ -1760,7 +1765,6 @@ public final class Launcher extends Activity } folder.animateClosed(); - folder.onClose(); } /** diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index 1fa23cf4c..ca0847ef9 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -692,7 +692,7 @@ public abstract class PagedView extends ViewGroup { } View v = getPageAt(focusablePage); if (v != null) { - v.requestFocus(direction, previouslyFocusedRect); + return v.requestFocus(direction, previouslyFocusedRect); } return false; } diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 1048fd570..b07e22b39 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -1199,11 +1199,12 @@ public class Workspace extends SmoothPagedView View currentPage = getChildAt(getCurrentPage()); Matrix m = currentPage.getMatrix(); - // Draw outer ring FolderRingAnimator fra = mFolderOuterRings.get(i); + + // Draw outer ring Drawable d = FolderRingAnimator.sSharedOuterRingDrawable; - int width = (int) (d.getIntrinsicWidth() * fra.getOuterRingScale()); - int height = (int) (d.getIntrinsicHeight() * fra.getOuterRingScale()); + int width = (int) fra.getOuterRingSize(); + int height = width; fra.getLocation(mTempLocation); // First we map the folder's location from window coordinates to its containing @@ -1226,34 +1227,28 @@ public class Workspace extends SmoothPagedView canvas.restore(); // Draw inner ring - if (fra.mFolderIcon != null) { - int folderWidth = fra.mFolderIcon != null ? - fra.mFolderIcon.getMeasuredWidth() : mCellWidth; - int folderHeight = fra.mFolderIcon != null ? - fra.mFolderIcon.getMeasuredWidth() : mCellHeight; - d = FolderRingAnimator.sSharedInnerRingDrawable; - width = (int) (folderWidth * fra.getInnerRingScale()); - height = (int) (folderHeight * fra.getInnerRingScale()); - - // First we map the folder's location from window coordinates to its containing - // CellLayout's coordinates. Then we transform the coordinates according to the - // CellLayout's transform. Finally, we map this back into the coordinates of the - // the window (ie. Workspace). - x = mTempLocation[0] + mScrollX - width / 2 - currentPage.getLeft(); - y = mTempLocation[1] + mScrollY - height / 2 - currentPage.getTop(); - mTempFloatTuple[0] = x; - mTempFloatTuple[1] = y; - m.mapPoints(mTempFloatTuple); - x = (int) (mTempFloatTuple[0]) + currentPage.getLeft(); - y = (int) (mTempFloatTuple[1]) + currentPage.getTop(); - - canvas.save(); - canvas.translate(x, y); - d.setBounds(0, 0, (int) (width * currentPage.getScaleX()), - (int) (height * currentPage.getScaleY())); - d.draw(canvas); - canvas.restore(); - } + d = FolderRingAnimator.sSharedInnerRingDrawable; + width = (int) fra.getInnerRingSize(); + height = width; + + // First we map the folder's location from window coordinates to its containing + // CellLayout's coordinates. Then we transform the coordinates according to the + // CellLayout's transform. Finally, we map this back into the coordinates of the + // the window (ie. Workspace). + x = mTempLocation[0] + mScrollX - width / 2 - currentPage.getLeft(); + y = mTempLocation[1] + mScrollY - height / 2 - currentPage.getTop(); + mTempFloatTuple[0] = x; + mTempFloatTuple[1] = y; + m.mapPoints(mTempFloatTuple); + x = (int) (mTempFloatTuple[0]) + currentPage.getLeft(); + y = (int) (mTempFloatTuple[1]) + currentPage.getTop(); + + canvas.save(); + canvas.translate(x, y); + d.setBounds(0, 0, (int) (width * currentPage.getScaleX()), + (int) (height * currentPage.getScaleY())); + d.draw(canvas); + canvas.restore(); } super.onDraw(canvas); } @@ -3025,8 +3020,9 @@ public class Workspace extends SmoothPagedView mCellWidth = mDragTargetLayout.getCellWidth(); mCellHeight = mDragTargetLayout.getCellHeight(); } + int x = tvLocation[0] - wsLocation[0] + v.getMeasuredWidth() / 2; - int y = tvLocation[1] - wsLocation[1] + v.getMeasuredHeight() / 2; + int y = tvLocation[1] - wsLocation[1] + FolderRingAnimator.sPreviewSize / 2; if (mDragFolderRingAnimator == null) { mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null); |