summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout/apps_customize_pane.xml5
-rw-r--r--res/values/config.xml2
-rw-r--r--res/values/dimens.xml8
-rw-r--r--src/com/android/launcher2/AppsCustomizePagedView.java185
-rw-r--r--src/com/android/launcher2/AppsCustomizeTabHost.java56
-rw-r--r--src/com/android/launcher2/CellLayout.java30
-rw-r--r--src/com/android/launcher2/Folder.java10
-rw-r--r--src/com/android/launcher2/FolderIcon.java77
-rw-r--r--src/com/android/launcher2/IconCache.java5
-rw-r--r--src/com/android/launcher2/Launcher.java29
-rw-r--r--src/com/android/launcher2/LauncherModel.java63
-rw-r--r--src/com/android/launcher2/PagedView.java21
-rw-r--r--src/com/android/launcher2/PagedViewCellLayout.java4
-rw-r--r--src/com/android/launcher2/Workspace.java30
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