diff options
-rw-r--r-- | res/layout/apps_customize_pane.xml | 5 | ||||
-rw-r--r-- | res/values/config.xml | 2 | ||||
-rw-r--r-- | res/values/dimens.xml | 8 | ||||
-rw-r--r-- | src/com/android/launcher2/AppsCustomizePagedView.java | 185 | ||||
-rw-r--r-- | src/com/android/launcher2/AppsCustomizeTabHost.java | 56 | ||||
-rw-r--r-- | src/com/android/launcher2/CellLayout.java | 30 | ||||
-rw-r--r-- | src/com/android/launcher2/Folder.java | 10 | ||||
-rw-r--r-- | src/com/android/launcher2/FolderIcon.java | 77 | ||||
-rw-r--r-- | src/com/android/launcher2/IconCache.java | 5 | ||||
-rw-r--r-- | src/com/android/launcher2/Launcher.java | 29 | ||||
-rw-r--r-- | src/com/android/launcher2/LauncherModel.java | 63 | ||||
-rw-r--r-- | src/com/android/launcher2/PagedView.java | 21 | ||||
-rw-r--r-- | src/com/android/launcher2/PagedViewCellLayout.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher2/Workspace.java | 30 |
14 files changed, 383 insertions, 142 deletions
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml index 05e7fc124..95b7dc2ef 100644 --- a/res/layout/apps_customize_pane.xml +++ b/res/layout/apps_customize_pane.xml @@ -63,6 +63,11 @@ launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x" launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y" launcher:maxGap="@dimen/workspace_max_gap" /> + <ImageView + android:id="@+id/animation_buffer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" /> <include android:id="@+id/paged_view_indicator" diff --git a/res/values/config.xml b/res/values/config.xml index 7eebc4a72..37710b696 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -31,7 +31,7 @@ <integer name="config_appsCustomizeWorkspaceShrinkTime">1000</integer> <!-- Tab transition animation duration --> - <integer name="config_tabTransitionDuration">100</integer> + <integer name="config_tabTransitionDuration">200</integer> <!-- The slope, in percent, of the drag movement needed to drag an item out of AppsCustomize (y / x * 100%) --> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 39342377d..a51642435 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -83,10 +83,10 @@ <dimen name="dragViewOffsetY">-8dp</dimen> <!-- 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> + <dimen name="app_widget_padding_left">3dp</dimen> + <dimen name="app_widget_padding_right">3dp</dimen> + <dimen name="app_widget_padding_top">1dp</dimen> + <dimen name="app_widget_padding_bottom">1dp</dimen> <!-- Folders --> <!-- The size of the image which sits behind the preview of the folder contents --> diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java index 9d0399544..949d87266 100644 --- a/src/com/android/launcher2/AppsCustomizePagedView.java +++ b/src/com/android/launcher2/AppsCustomizePagedView.java @@ -164,6 +164,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private final LayoutInflater mLayoutInflater; private final PackageManager mPackageManager; + // Save and Restore + private int mSaveInstanceStateItemIndex = -1; + private int mRestorePage = -1; + // Content private ContentType mContentType; private ArrayList<ApplicationInfo> mApps; @@ -188,6 +192,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Previews & outlines ArrayList<AppsCustomizeAsyncTask> mRunningTasks; private HolographicOutlineHelper mHolographicOutlineHelper; + private static final int sPageSleepDelay = 200; public AppsCustomizePagedView(Context context, AttributeSet attrs) { super(context, attrs); @@ -252,6 +257,58 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } + /** Returns the item index of the center item on this page so that we can restore to this + * item index when we rotate. */ + private int getMiddleComponentIndexOnCurrentPage() { + int i = -1; + if (getPageCount() > 0) { + int currentPage = getCurrentPage(); + switch (mContentType) { + case Applications: { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(currentPage); + PagedViewCellLayoutChildren childrenLayout = layout.getChildrenLayout(); + int numItemsPerPage = mCellCountX * mCellCountY; + int childCount = childrenLayout.getChildCount(); + if (childCount > 0) { + i = (currentPage * numItemsPerPage) + (childCount / 2); + }} + break; + case Widgets: { + PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(currentPage); + int numItemsPerPage = mWidgetCountX * mWidgetCountY; + int childCount = layout.getChildCount(); + if (childCount > 0) { + i = (currentPage * numItemsPerPage) + (childCount / 2); + }} + break; + } + } + return i; + } + + /** Get the index of the item to restore to if we need to restore the current page. */ + int getSaveInstanceStateIndex() { + if (mSaveInstanceStateItemIndex == -1) { + mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage(); + } + return mSaveInstanceStateItemIndex; + } + + /** Returns the page in the current orientation which is expected to contain the specified + * item index. */ + int getPageForComponent(int index) { + switch (mContentType) { + case Applications: { + int numItemsPerPage = mCellCountX * mCellCountY; + return (index / numItemsPerPage); + } + case Widgets: { + int numItemsPerPage = mWidgetCountX * mWidgetCountY; + return (index / numItemsPerPage); + }} + return -1; + } + /** * This differs from isDataReady as this is the test done if isDataReady is not set. */ @@ -261,6 +318,20 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen return !mApps.isEmpty(); } + /** Restores the page for an item at the specified index */ + void restorePageForIndex(int index) { + if (index < 0) return; + + int page = getPageForComponent(index); + if (page > -1) { + if (getChildCount() > 0) { + invalidatePageData(page); + } else { + mRestorePage = page; + } + } + } + protected void onDataReady(int width, int height) { // Note that we transpose the counts in portrait so that we get a similar layout boolean isLandscape = getResources().getConfiguration().orientation == @@ -288,7 +359,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); mWidgetSpacingLayout.measure(widthSpec, heightSpec); mContentWidth = mWidgetSpacingLayout.getContentWidth(); - invalidatePageData(); + invalidatePageData(Math.max(0, mRestorePage)); + mRestorePage = -1; } @Override @@ -323,6 +395,19 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } public void onPackagesUpdated() { + // TODO: this isn't ideal, but we actually need to delay here. This call is triggered + // by a broadcast receiver, and in order for it to work correctly, we need to know that + // the AppWidgetService has already received and processed the same broadcast. Since there + // is no guarantee about ordering of broadcast receipt, we just delay here. Ideally, + // we should have a more precise way of ensuring the AppWidgetService is up to date. + postDelayed(new Runnable() { + public void run() { + updatePackages(); + } + }, 500); + } + + public void updatePackages() { // Get the list of widgets and shortcuts boolean wasEmpty = mWidgets.isEmpty(); mWidgets.clear(); @@ -489,7 +574,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void setContentType(ContentType type) { mContentType = type; - invalidatePageData(0); + invalidatePageData(0, true); } public boolean isContentType(ContentType type) { @@ -536,7 +621,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen addView(layout); } } - public void syncAppsPageItems(int page) { + public void syncAppsPageItems(int page, boolean immediate) { // ensure that we have the right number of items on the pages int numPages = getPageCount(); int numCells = mCellCountX * mCellCountY; @@ -592,6 +677,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen return Process.THREAD_PRIORITY_DEFAULT; } } + private int getSleepForPage(int page) { + int pageDiff = Math.abs(page - mCurrentPage) - 1; + return Math.max(0, pageDiff * sPageSleepDelay); + } /** * Creates and executes a new AsyncTask to load a page of widget previews. */ @@ -612,37 +701,16 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } + // We introduce a slight delay to order the loading of side pages so that we don't thrash + final int sleepMs = getSleepForPage(page); AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, cellCountX, new AsyncTaskCallback() { @Override public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { - // Ensure that this task starts running at the correct priority - task.syncThreadPriority(); - - // Load each of the widget/shortcut previews - ArrayList<Object> items = data.items; - ArrayList<Bitmap> images = data.generatedImages; - int count = items.size(); - int cellWidth = data.cellWidth; - int cellHeight = data.cellHeight; - for (int i = 0; i < count && !task.isCancelled(); ++i) { - // Before work on each item, ensure that this task is running at the correct - // priority - task.syncThreadPriority(); - - Object rawInfo = items.get(i); - if (rawInfo instanceof AppWidgetProviderInfo) { - AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; - int[] cellSpans = CellLayout.rectToCell(getResources(), - info.minWidth, info.minHeight, null); - images.add(getWidgetPreview(info, cellSpans[0],cellSpans[1], - cellWidth, cellHeight)); - } else if (rawInfo instanceof ResolveInfo) { - // Fill in the shortcuts information - ResolveInfo info = (ResolveInfo) rawInfo; - images.add(getShortcutPreview(info, cellWidth, cellHeight)); - } - } + try { + Thread.sleep(sleepMs); + } catch (Exception e) {} + loadWidgetPreviewsInBackground(task, data); } }, new AsyncTaskCallback() { @@ -863,7 +931,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen addView(layout); } } - public void syncWidgetPageItems(int page) { + public void syncWidgetPageItems(int page, boolean immediate) { int numItemsPerPage = mWidgetCountX * mWidgetCountY; int contentWidth = mWidgetSpacingLayout.getContentWidth(); int contentHeight = mWidgetSpacingLayout.getContentHeight(); @@ -880,7 +948,50 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen items.add(mWidgets.get(i)); } - prepareLoadWidgetPreviewsTask(page, items, cellWidth, cellHeight, mWidgetCountX); + if (immediate) { + AsyncTaskPageData data = new AsyncTaskPageData(page, items, cellWidth, cellHeight, + mWidgetCountX, null, null); + loadWidgetPreviewsInBackground(null, data); + onSyncWidgetPageItems(data); + } else { + prepareLoadWidgetPreviewsTask(page, items, cellWidth, cellHeight, mWidgetCountX); + } + } + private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, + AsyncTaskPageData data) { + if (task != null) { + // Ensure that this task starts running at the correct priority + task.syncThreadPriority(); + } + + // Load each of the widget/shortcut previews + ArrayList<Object> items = data.items; + ArrayList<Bitmap> images = data.generatedImages; + int count = items.size(); + int cellWidth = data.cellWidth; + int cellHeight = data.cellHeight; + for (int i = 0; i < count; ++i) { + if (task != null) { + // Ensure we haven't been cancelled yet + if (task.isCancelled()) break; + // Before work on each item, ensure that this task is running at the correct + // priority + task.syncThreadPriority(); + } + + Object rawInfo = items.get(i); + if (rawInfo instanceof AppWidgetProviderInfo) { + AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; + int[] cellSpans = CellLayout.rectToCell(getResources(), + info.minWidth, info.minHeight, null); + images.add(getWidgetPreview(info, cellSpans[0],cellSpans[1], + cellWidth, cellHeight)); + } else if (rawInfo instanceof ResolveInfo) { + // Fill in the shortcuts information + ResolveInfo info = (ResolveInfo) rawInfo; + images.add(getShortcutPreview(info, cellWidth, cellHeight)); + } + } } private void onSyncWidgetPageItems(AsyncTaskPageData data) { int page = data.page; @@ -991,13 +1102,13 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } @Override - public void syncPageItems(int page) { + public void syncPageItems(int page, boolean immediate) { switch (mContentType) { case Applications: - syncAppsPageItems(page); + syncAppsPageItems(page, immediate); break; case Widgets: - syncWidgetPageItems(page); + syncWidgetPageItems(page, immediate); break; } } @@ -1036,6 +1147,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen setChildrenDrawnWithCacheEnabled(false); */ super.onPageEndMoving(); + + // We reset the save index when we change pages so that it will be recalculated on next + // rotation + mSaveInstanceStateItemIndex = -1; } /* diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java index 6bd4c0355..d6ae1459e 100644 --- a/src/com/android/launcher2/AppsCustomizeTabHost.java +++ b/src/com/android/launcher2/AppsCustomizeTabHost.java @@ -18,15 +18,19 @@ package com.android.launcher2; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; import android.widget.TabHost; import android.widget.TabWidget; import android.widget.TextView; @@ -44,6 +48,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona private ViewGroup mTabs; private ViewGroup mTabsContainer; private AppsCustomizePagedView mAppsCustomizePane; + private ImageView mAnimationBuffer; private boolean mInTransition; private boolean mResetAfterTransition; @@ -89,6 +94,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona mTabs = tabs; mTabsContainer = tabsContainer; mAppsCustomizePane = appsCustomizePane; + mAnimationBuffer = (ImageView) findViewById(R.id.animation_buffer); if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException(); // Configure the tabs content factory to return the same paged view (that we change the @@ -168,31 +174,53 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona final Resources res = getResources(); final int duration = res.getInteger(R.integer.config_tabTransitionDuration); - ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 0f); - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { + // We post a runnable here because there is a delay while the first page is loading and + // the feedback from having changed the tab almost feels better than having it stick + post(new Runnable() { @Override - public void onAnimationStart(android.animation.Animator animation) { + public void run() { + // Setup the animation buffer + Bitmap b = Bitmap.createBitmap(mAppsCustomizePane.getMeasuredWidth(), + mAppsCustomizePane.getMeasuredHeight(), Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + mAppsCustomizePane.draw(c); + mAppsCustomizePane.setAlpha(0f); + mAnimationBuffer.setImageBitmap(b); + mAnimationBuffer.setAlpha(1f); + mAnimationBuffer.setVisibility(View.VISIBLE); + c.setBitmap(null); + b = null; + + // Toggle the new content onTabChangedStart(); - } - @Override - public void onAnimationEnd(android.animation.Animator animation) { onTabChangedEnd(type); - ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 1f); - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { + // Animate the transition + ObjectAnimator outAnim = ObjectAnimator.ofFloat(mAnimationBuffer, "alpha", 0f); + outAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimationBuffer.setVisibility(View.GONE); + mAnimationBuffer.setImageBitmap(null); + } + }); + ObjectAnimator inAnim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 1f); + inAnim.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationEnd(android.animation.Animator animation) { + public void onAnimationEnd(Animator animation) { if (!LauncherApplication.isScreenLarge()) { mAppsCustomizePane.flashScrollingIndicator(); } + mAppsCustomizePane.loadAssociatedPages( + mAppsCustomizePane.getCurrentPage()); } }); - anim.start(); + AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(outAnim, inAnim); + animSet.setDuration(duration); + animSet.start(); } }); - anim.start(); } } diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 6f59d1f3c..1841713f5 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -792,27 +792,35 @@ public class CellLayout extends ViewGroup { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + // First we clear the tag to ensure that on every touch down we start with a fresh slate, + // even in the case where we return early. Not clearing here was causing bugs whereby on + // long-press we'd end up picking up an item from a previous drag operation. + final int action = ev.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + clearTagCellInfo(); + } + if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) { return true; } - final int action = ev.getAction(); - final CellInfo cellInfo = mCellInfo; if (action == MotionEvent.ACTION_DOWN) { setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY()); - } else if (action == MotionEvent.ACTION_UP) { - cellInfo.cell = null; - cellInfo.cellX = -1; - cellInfo.cellY = -1; - cellInfo.spanX = 0; - cellInfo.spanY = 0; - setTag(cellInfo); } - return false; } - @Override + private void clearTagCellInfo() { + final CellInfo cellInfo = mCellInfo; + cellInfo.cell = null; + cellInfo.cellX = -1; + cellInfo.cellY = -1; + cellInfo.spanX = 0; + cellInfo.spanY = 0; + setTag(cellInfo); + } + public CellInfo getTag() { return (CellInfo) super.getTag(); } diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java index c490d5e6d..1fcfebc5a 100644 --- a/src/com/android/launcher2/Folder.java +++ b/src/com/android/launcher2/Folder.java @@ -297,13 +297,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList ArrayList<ShortcutInfo> children = info.contents; ArrayList<ShortcutInfo> overflow = new ArrayList<ShortcutInfo>(); setupContentForNumItems(children.size()); + int count = 0; for (int i = 0; i < children.size(); i++) { ShortcutInfo child = (ShortcutInfo) children.get(i); if (!createAndAddShortcut(child)) { overflow.add(child); + } else { + count++; } } + // We rearrange the items in case there are any empty gaps + setupContentForNumItems(count); + // 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 // number of items. @@ -507,8 +513,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // by another item. If it is, we need to find the next available slot and assign // it that position. This is an issue when upgrading from the old Folders implementation // which could contain an unlimited number of items. - if (mContent.getChildAt(item.cellX, item.cellY) != null || - item.cellX < 0 || item.cellY < 0) { + if (mContent.getChildAt(item.cellX, item.cellY) != null || item.cellX < 0 || item.cellY < 0 + || item.cellX >= mContent.getCountX() || item.cellY >= mContent.getCountY()) { if (!findAndSetEmptyCells(item)) { return false; } diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index 10928c05e..f1a150856 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -321,44 +321,53 @@ public class FolderIcon extends LinearLayout implements FolderListener { float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) { item.cellX = -1; item.cellY = -1; - DragLayer dragLayer = mLauncher.getDragLayer(); - Rect from = new Rect(); - dragLayer.getViewRectRelativeToSelf(animateView, from); - Rect to = finalRect; - if (to == null) { - to = new Rect(); - Workspace workspace = mLauncher.getWorkspace(); - // Set cellLayout and this to it's final state to compute final animation locations - workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); - float scaleX = getScaleX(); - float scaleY = getScaleY(); - setScaleX(1.0f); - setScaleY(1.0f); - scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); - // Finished computing final animation locations, restore current state - setScaleX(scaleX); - setScaleY(scaleY); - workspace.resetTransitionTransform((CellLayout) getParent().getParent()); - } - int[] center = new int[2]; - float scale = getLocalCenterForIndex(index, center); - center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); - center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); + // Typically, the animateView corresponds to the DragView; however, if this is being done + // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we + // will not have a view to animate + if (animateView != null) { + DragLayer dragLayer = mLauncher.getDragLayer(); + Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(animateView, from); + Rect to = finalRect; + if (to == null) { + to = new Rect(); + Workspace workspace = mLauncher.getWorkspace(); + // Set cellLayout and this to it's final state to compute final animation locations + workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); + float scaleX = getScaleX(); + float scaleY = getScaleY(); + setScaleX(1.0f); + setScaleY(1.0f); + scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); + // Finished computing final animation locations, restore current state + setScaleX(scaleX); + setScaleY(scaleY); + workspace.resetTransitionTransform((CellLayout) getParent().getParent()); + } - to.offset(center[0] - animateView.getMeasuredWidth() / 2, - center[1] - animateView.getMeasuredHeight() / 2); + int[] center = new int[2]; + float scale = getLocalCenterForIndex(index, center); + center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); + center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); - float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; + to.offset(center[0] - animateView.getMeasuredWidth() / 2, + center[1] - animateView.getMeasuredHeight() / 2); - dragLayer.animateView(animateView, from, to, finalAlpha, scale * scaleRelativeToDragLayer, - DROP_IN_ANIMATION_DURATION, new DecelerateInterpolator(2), - new AccelerateInterpolator(2), postAnimationRunnable, false); - postDelayed(new Runnable() { - public void run() { - addItem(item); - } - }, DROP_IN_ANIMATION_DURATION); + float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; + + dragLayer.animateView(animateView, from, to, finalAlpha, + scale * scaleRelativeToDragLayer, DROP_IN_ANIMATION_DURATION, + new DecelerateInterpolator(2), new AccelerateInterpolator(2), + postAnimationRunnable, false); + postDelayed(new Runnable() { + public void run() { + addItem(item); + } + }, DROP_IN_ANIMATION_DURATION); + } else { + addItem(item); + } } public void onDrop(DragObject d) { diff --git a/src/com/android/launcher2/IconCache.java b/src/com/android/launcher2/IconCache.java index 247e164ec..7f3ae860c 100644 --- a/src/com/android/launcher2/IconCache.java +++ b/src/com/android/launcher2/IconCache.java @@ -160,13 +160,14 @@ public class IconCache { } } - public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo) { + public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo, + HashMap<Object, CharSequence> labelCache) { synchronized (mCache) { if (resolveInfo == null || component == null) { return null; } - CacheEntry entry = cacheLocked(component, resolveInfo, null); + CacheEntry entry = cacheLocked(component, resolveInfo, labelCache); return entry.icon; } } diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 7ab41bc4e..a0601e004 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -40,7 +40,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.content.Intent.ShortcutIconResource; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -51,7 +50,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -87,6 +85,7 @@ import android.widget.Toast; import com.android.common.Search; import com.android.launcher.R; +import com.android.launcher2.DropTarget.DragObject; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -670,10 +669,12 @@ public final class Launcher extends Activity mAppsCustomizeContent.setContentType( mAppsCustomizeTabHost.getContentTypeForTabTag(curTab)); mAppsCustomizeTabHost.setCurrentTabByTag(curTab); + mAppsCustomizeContent.loadAssociatedPages( + mAppsCustomizeContent.getCurrentPage()); } - // Note: currently we do not restore the page for the AppsCustomize pane because the - // change in layout can drastically affect the saved page index + int currentIndex = savedState.getInt("apps_customize_currentIndex"); + mAppsCustomizeContent.restorePageForIndex(currentIndex); } } @@ -799,11 +800,25 @@ public final class Launcher extends Activity boolean foundCellSpan = false; + ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null); + final View view = createShortcut(info); + // First we check if we already know the exact location where we want to add this item. if (cellX >= 0 && cellY >= 0) { cellXY[0] = cellX; cellXY[1] = cellY; foundCellSpan = true; + + // If appropriate, either create a folder or add to an existing folder + if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, + true, null,null)) { + return; + } + DragObject dragObject = new DragObject(); + dragObject.dragInfo = info; + if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, dragObject, true)) { + return; + } } else if (touchXY != null) { // when dragging and dropping, just find the closest free spot int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY); @@ -817,11 +832,9 @@ public final class Launcher extends Activity return; } - final ShortcutInfo info = mModel.addShortcut( - this, data, container, screen, cellXY[0], cellXY[1], false); + LauncherModel.addItemToDatabase(this, info, container, screen, cellXY[0], cellXY[1], false); if (!mRestoring) { - final View view = createShortcut(info); mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked()); } @@ -1132,6 +1145,8 @@ public final class Launcher extends Activity if (currentTabTag != null) { outState.putString("apps_customize_currentTab", currentTabTag); } + int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex(); + outState.putInt("apps_customize_currentIndex", currentIndex); } } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 547d51f2c..f14140c16 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -661,11 +661,13 @@ public class LauncherModel extends BroadcastReceiver { private boolean mStopped; private boolean mLoadAndBindStepFinished; private HashMap<Object, CharSequence> mLabelCache; + private HashMap<Object, byte[]> mDbIconCache; LoaderTask(Context context, boolean isLaunching) { mContext = context; mIsLaunching = isLaunching; mLabelCache = new HashMap<Object, CharSequence>(); + mDbIconCache = new HashMap<Object, byte[]>(); } boolean isLaunching() { @@ -731,10 +733,15 @@ public class LauncherModel extends BroadcastReceiver { final Callbacks cbk = mCallbacks.get(); final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true; + // We update the icons in the database afterwards in case they have changed + mDbIconCache.clear(); + keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { + if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } @@ -754,6 +761,7 @@ public class LauncherModel extends BroadcastReceiver { // settled down. synchronized (mLock) { if (mIsLaunching) { + if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } @@ -769,6 +777,14 @@ public class LauncherModel extends BroadcastReceiver { } } + + // Update the saved icons if necessary + if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); + for (Object key : mDbIconCache.keySet()) { + updateSavedIcon(mContext, (ShortcutInfo) key, mDbIconCache.get(key)); + } + mDbIconCache.clear(); + // Clear out this reference, otherwise we end up holding it until all of the // callback runnables are done. mContext = null; @@ -970,7 +986,7 @@ public class LauncherModel extends BroadcastReceiver { // now that we've loaded everthing re-save it with the // icon in case it disappears somehow. - updateSavedIcon(context, info, c, iconIndex); + queueIconToBeChecked(mDbIconCache, info, c, iconIndex); } else { // Failed to load the shortcut, probably because the // activity manager couldn't resolve it (maybe the app @@ -1522,7 +1538,7 @@ public class LauncherModel extends BroadcastReceiver { // have icons anyway. final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0); if (resolveInfo != null) { - icon = mIconCache.getIcon(componentName, resolveInfo); + icon = mIconCache.getIcon(componentName, resolveInfo, labelCache); } // the db if (icon == null) { @@ -1750,10 +1766,11 @@ public class LauncherModel extends BroadcastReceiver { return info; } - void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) { + boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c, + int iconIndex) { // If apps can't be on SD, don't even bother. if (!mAppsCanBeOnExternalStorage) { - return; + return false; } // If this icon doesn't have a custom icon, check to see // what's stored in the DB, and if it doesn't match what @@ -1762,25 +1779,29 @@ public class LauncherModel extends BroadcastReceiver { // package manager can't find an icon (for example because // the app is on SD) then we can use that instead. if (!info.customIcon && !info.usingFallbackIcon) { - boolean needSave; - byte[] data = c.getBlob(iconIndex); - try { - if (data != null) { - Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length); - Bitmap loaded = info.getIcon(mIconCache); - needSave = !saved.sameAs(loaded); - } else { - needSave = true; - } - } catch (Exception e) { + cache.put(info, c.getBlob(iconIndex)); + return true; + } + return false; + } + void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) { + boolean needSave = false; + try { + if (data != null) { + Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length); + Bitmap loaded = info.getIcon(mIconCache); + needSave = !saved.sameAs(loaded); + } else { needSave = true; } - if (needSave) { - Log.d(TAG, "going to save icon bitmap for info=" + info); - // This is slower than is ideal, but this only happens once - // or when the app is updated with a new icon. - updateItemInDatabase(context, info); - } + } catch (Exception e) { + needSave = true; + } + if (needSave) { + Log.d(TAG, "going to save icon bitmap for info=" + info); + // This is slower than is ideal, but this only happens once + // or when the app is updated with a new icon. + updateItemInDatabase(context, info); } } diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index d2d734cf3..40e23286e 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -1494,7 +1494,10 @@ public abstract class PagedView extends ViewGroup { }; } - public void loadAssociatedPages(int page) { + protected void loadAssociatedPages(int page) { + loadAssociatedPages(page, false); + } + protected void loadAssociatedPages(int page, boolean immediateAndOnly) { if (mContentIsRefreshable) { final int count = getChildCount(); if (page < count) { @@ -1503,11 +1506,14 @@ public abstract class PagedView extends ViewGroup { if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" + upperPageBound); for (int i = 0; i < count; ++i) { + if ((i != page) && immediateAndOnly) { + continue; + } Page layout = (Page) getChildAt(i); final int childCount = layout.getPageChildCount(); if (lowerPageBound <= i && i <= upperPageBound) { if (mDirtyPageContent.get(i)) { - syncPageItems(i); + syncPageItems(i, (i == page) && immediateAndOnly); mDirtyPageContent.set(i, false); } } else { @@ -1607,7 +1613,7 @@ public abstract class PagedView extends ViewGroup { * This method is called to synchronize the items that are on a particular page. If views on * the page can be reused, then they should be updated within this method. */ - public abstract void syncPageItems(int page); + public abstract void syncPageItems(int page, boolean immediate); protected void postInvalidatePageData(final boolean clearViews) { post(new Runnable() { @@ -1622,9 +1628,12 @@ public abstract class PagedView extends ViewGroup { } protected void invalidatePageData() { - invalidatePageData(-1); + invalidatePageData(-1, false); } protected void invalidatePageData(int currentPage) { + invalidatePageData(currentPage, false); + } + protected void invalidatePageData(int currentPage, boolean immediateAndOnly) { if (!mIsDataReady) { return; } @@ -1640,7 +1649,7 @@ public abstract class PagedView extends ViewGroup { // Set a new page as the current page if necessary if (currentPage > -1) { - setCurrentPage(currentPage); + setCurrentPage(Math.min(getPageCount() - 1, currentPage)); } // Mark each of the pages as dirty @@ -1651,7 +1660,7 @@ public abstract class PagedView extends ViewGroup { } // Load any pages that are necessary for the current window of views - loadAssociatedPages(mCurrentPage); + loadAssociatedPages(mCurrentPage, immediateAndOnly); mDirtyPageAlpha = true; updateAdjacentPagesAlpha(); requestLayout(); diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java index 63cf9e8bf..803e70060 100644 --- a/src/com/android/launcher2/PagedViewCellLayout.java +++ b/src/com/android/launcher2/PagedViewCellLayout.java @@ -192,6 +192,10 @@ public class PagedViewCellLayout extends ViewGroup implements Page { return mChildren.getChildCount(); } + public PagedViewCellLayoutChildren getChildrenLayout() { + return mChildren; + } + @Override public View getChildOnPageAt(int i) { return mChildren.getChildAt(i); diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 947c94624..b6a1666ab 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -2208,8 +2208,15 @@ public class Workspace extends SmoothPagedView sourceInfo.cellX = -1; sourceInfo.cellY = -1; - fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale, - postAnimationRunnable); + // If the dragView is null, we can't animate + boolean animate = dragView != null; + if (animate) { + fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale, + postAnimationRunnable); + } else { + fi.addItem(destInfo); + fi.addItem(sourceInfo); + } return true; } return false; @@ -2996,8 +3003,21 @@ public class Workspace extends SmoothPagedView if (info instanceof PendingAddItemInfo) { final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo; - mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null, - cellLayout, mTargetCell); + boolean findNearestVacantCell = true; + if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY, + cellLayout, mTargetCell); + if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, + true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo, + mDragTargetLayout, mTargetCell)) { + findNearestVacantCell = false; + } + } + if (findNearestVacantCell) { + mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null, + cellLayout, mTargetCell); + } + Runnable onAnimationCompleteRunnable = new Runnable() { @Override public void run() { @@ -3544,7 +3564,7 @@ public class Workspace extends SmoothPagedView } @Override - public void syncPageItems(int page) { + public void syncPageItems(int page, boolean immediate) { } @Override |