Merge "Initial implementation: Broadcasts on app launch." into jb-ub-gel-agar
diff --git a/src/com/android/launcher3/ApplicationInfo.java b/src/com/android/launcher3/ApplicationInfo.java
index 1bc147a..1b396f7 100644
--- a/src/com/android/launcher3/ApplicationInfo.java
+++ b/src/com/android/launcher3/ApplicationInfo.java
@@ -60,6 +60,10 @@
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
+ protected Intent getIntent() {
+ return intent;
+ }
+
/**
* Must not hold the Context.
*/
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 763ec3f..c98f761 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -55,6 +55,8 @@
private TransitionDrawable mRemoveDrawable;
private TransitionDrawable mCurrentDrawable;
+ private boolean mWaitingForUninstall = false;
+
public DeleteDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -251,7 +253,6 @@
return AppsCustomizePagedView.DISABLE_ALL_APPS && isWorkspaceOrFolderApplication(d);
}
- private boolean mWaitingForUninstall = false;
private void completeDrop(final DragObject d) {
ItemInfo item = (ItemInfo) d.dragInfo;
boolean wasWaitingForUninstall = mWaitingForUninstall;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index bb3993e..c70cbe0 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -775,7 +775,7 @@
if (target != this) {
if (mOnExitAlarm.alarmPending()) {
mOnExitAlarm.cancelAlarm();
- if (successfulDrop) {
+ if (!successfulDrop) {
mSuppressFolderDeletion = true;
}
completeDragExit();
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 92f126c..fa713a4 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -27,6 +27,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Looper;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -167,8 +168,6 @@
public float mOuterRingSize;
public float mInnerRingSize;
public FolderIcon mFolderIcon = null;
- public Drawable mOuterRingDrawable = null;
- public Drawable mInnerRingDrawable = null;
public static Drawable sSharedOuterRingDrawable = null;
public static Drawable sSharedInnerRingDrawable = null;
public static int sPreviewSize = -1;
@@ -180,12 +179,14 @@
public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
mFolderIcon = folderIcon;
Resources res = launcher.getResources();
- mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
- mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
// We need to reload the static values when configuration changes in case they are
// different in another configuration
if (sStaticValuesDirty) {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("FolderRingAnimator loading drawables on non-UI thread "
+ + Thread.currentThread());
+ }
sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index f97ed53..244b3db 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -38,8 +38,6 @@
public class InstallShortcutReceiver extends BroadcastReceiver {
public static final String ACTION_INSTALL_SHORTCUT =
"com.android.launcher3.action.INSTALL_SHORTCUT";
- public static final String NEW_APPS_PAGE_KEY = "apps.new.page";
- public static final String NEW_APPS_LIST_KEY = "apps.new.list";
public static final String DATA_INTENT_KEY = "intent.data";
public static final String LAUNCH_INTENT_KEY = "intent.launch";
@@ -51,11 +49,10 @@
public static final String APPS_PENDING_INSTALL = "apps_to_install";
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
- public static final int NEW_SHORTCUT_STAGGER_DELAY = 75;
+ public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0;
private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1;
- private static final int INSTALL_SHORTCUT_NO_SPACE = -2;
// A mime-type representing shortcut data
public static final String SHORTCUT_MIMETYPE =
@@ -201,12 +198,12 @@
PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, name, intent);
info.icon = icon;
info.iconResource = iconResource;
- if (mUseInstallQueue || launcherNotLoaded) {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- addToInstallQueue(sp, info);
- } else {
- processInstallShortcut(context, info);
+
+ String spKey = LauncherAppState.getSharedPreferencesKey();
+ SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ addToInstallQueue(sp, info);
+ if (!mUseInstallQueue && !launcherNotLoaded) {
+ flushInstallQueue(context);
}
}
@@ -221,142 +218,59 @@
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp);
- Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
- while (iter.hasNext()) {
- processInstallShortcut(context, iter.next());
+ if (!installQueue.isEmpty()) {
+ Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
+ ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
+ int result = INSTALL_SHORTCUT_SUCCESSFUL;
+ String duplicateName = "";
+ while (iter.hasNext()) {
+ final PendingInstallShortcutInfo pendingInfo = iter.next();
+ final Intent data = pendingInfo.data;
+ final Intent intent = pendingInfo.launchIntent;
+ final String name = pendingInfo.name;
+ final boolean exists = LauncherModel.shortcutExists(context, name, intent);
+ final boolean allowDuplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
+
+ // TODO-XXX: Disable duplicates for now
+ if (!exists /* && allowDuplicate */) {
+ // Generate a shortcut info to add into the model
+ ShortcutInfo info = getShortcutInfo(context, pendingInfo.data,
+ pendingInfo.launchIntent);
+ addShortcuts.add(info);
+ }
+ /*
+ else if (exists && !allowDuplicate) {
+ result = INSTALL_SHORTCUT_IS_DUPLICATE;
+ duplicateName = name;
+ }
+ */
+ }
+
+ // Notify the user once if we weren't able to place any duplicates
+ if (result == INSTALL_SHORTCUT_IS_DUPLICATE) {
+ Toast.makeText(context, context.getString(R.string.shortcut_duplicate,
+ duplicateName), Toast.LENGTH_SHORT).show();
+ }
+
+ // Add the new apps to the model and bind them
+ if (!addShortcuts.isEmpty()) {
+ LauncherAppState app = LauncherAppState.getInstance();
+ app.getModel().addAndBindAddedApps(context, addShortcuts);
+ }
}
}
- private static void processInstallShortcut(Context context,
- PendingInstallShortcutInfo pendingInfo) {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
-
- final Intent data = pendingInfo.data;
- final Intent intent = pendingInfo.launchIntent;
- final String name = pendingInfo.name;
-
- // Lock on the app so that we don't try and get the items while apps are being added
+ private static ShortcutInfo getShortcutInfo(Context context, Intent data,
+ Intent launchIntent) {
+ if (launchIntent.getAction() == null) {
+ launchIntent.setAction(Intent.ACTION_VIEW);
+ } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) &&
+ launchIntent.getCategories() != null &&
+ launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+ launchIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ }
LauncherAppState app = LauncherAppState.getInstance();
- final int[] result = {INSTALL_SHORTCUT_SUCCESSFUL};
- boolean found = false;
- synchronized (app) {
- // Flush the LauncherModel worker thread, so that if we just did another
- // processInstallShortcut, we give it time for its shortcut to get added to the
- // database (getItemsInLocalCoordinates reads the database)
- app.getModel().flushWorkerThread();
- final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
- final boolean exists = LauncherModel.shortcutExists(context, name, intent);
-
- // Try adding to the workspace screens incrementally, starting at the default or center
- // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
- final int screen = Launcher.DEFAULT_SCREEN;
- for (int i = 0; i < Launcher.SCREEN_COUNT && !found; i++) {
- int si = i;
- if (0 <= si && si < Launcher.SCREEN_COUNT) {
- found = installShortcut(context, data, items, name, intent, si, exists, sp,
- result);
- }
- }
- }
-
- // We only report error messages (duplicate shortcut or out of space) as the add-animation
- // will provide feedback otherwise
- if (!found) {
- if (result[0] == INSTALL_SHORTCUT_NO_SPACE) {
- Toast.makeText(context, context.getString(R.string.completely_out_of_space),
- Toast.LENGTH_SHORT).show();
- } else if (result[0] == INSTALL_SHORTCUT_IS_DUPLICATE) {
- Toast.makeText(context, context.getString(R.string.shortcut_duplicate, name),
- Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- private static boolean installShortcut(Context context, Intent data, ArrayList<ItemInfo> items,
- String name, final Intent intent, final int screen, boolean shortcutExists,
- final SharedPreferences sharedPrefs, int[] result) {
- int[] tmpCoordinates = new int[2];
- if (findEmptyCell(context, items, tmpCoordinates, screen)) {
- if (intent != null) {
- if (intent.getAction() == null) {
- intent.setAction(Intent.ACTION_VIEW);
- } else if (intent.getAction().equals(Intent.ACTION_MAIN) &&
- intent.getCategories() != null &&
- intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
-
- // By default, we allow for duplicate entries (located in
- // different places)
- boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
- if (duplicate || !shortcutExists) {
- new Thread("setNewAppsThread") {
- public void run() {
- synchronized (sLock) {
- // If the new app is going to fall into the same page as before,
- // then just continue adding to the current page
- final int newAppsScreen = sharedPrefs.getInt(
- NEW_APPS_PAGE_KEY, screen);
- SharedPreferences.Editor editor = sharedPrefs.edit();
- if (newAppsScreen == -1 || newAppsScreen == screen) {
- addToStringSet(sharedPrefs,
- editor, NEW_APPS_LIST_KEY, intent.toUri(0));
- }
- editor.putInt(NEW_APPS_PAGE_KEY, screen);
- editor.commit();
- }
- }
- }.start();
-
- // Update the Launcher db
- LauncherAppState app = LauncherAppState.getInstance();
- ShortcutInfo info = app.getModel().addShortcut(context, data,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
- tmpCoordinates[0], tmpCoordinates[1], true);
- if (info == null) {
- return false;
- }
- } else {
- result[0] = INSTALL_SHORTCUT_IS_DUPLICATE;
- }
-
- return true;
- }
- } else {
- result[0] = INSTALL_SHORTCUT_NO_SPACE;
- }
-
- return false;
- }
-
- // TODO: this needs to be updated to take a screenId instead of a screen index
- private static boolean findEmptyCell(Context context, ArrayList<ItemInfo> items, int[] xy,
- int screen) {
- final int xCount = LauncherModel.getCellCountX();
- final int yCount = LauncherModel.getCellCountY();
- boolean[][] occupied = new boolean[xCount][yCount];
-
- ItemInfo item = null;
- int cellX, cellY, spanX, spanY;
- for (int i = 0; i < items.size(); ++i) {
- item = items.get(i);
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (item.screenId == screen) {
- cellX = item.cellX;
- cellY = item.cellY;
- spanX = item.spanX;
- spanY = item.spanY;
- for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
- for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
- occupied[x][y] = true;
- }
- }
- }
- }
- }
-
- return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
+ return app.getModel().infoFromShortcutIntent(context, data, null);
}
}
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 7ab30a9..8c4cefd 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -118,6 +118,10 @@
LauncherModel.checkItemInfo(this);
}
+ protected Intent getIntent() {
+ throw new RuntimeException("Unexpected Intent");
+ }
+
/**
* Write the fields of this item to the DB
*
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c3ea1ef..bd343ea 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -311,8 +311,6 @@
// Holds the page that we need to animate to, and the icon views that we need to animate up
// when we scroll to that page on resume.
- private long mNewShortcutAnimateScreenId = -1;
- private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>();
private ImageView mFolderIconImageView;
private Bitmap mFolderIconBitmap;
private Canvas mFolderIconCanvas;
@@ -773,7 +771,7 @@
setWorkspaceBackground(mState == State.WORKSPACE);
// Process any items that were added while Launcher was away
- InstallShortcutReceiver.flushInstallQueue(this);
+ InstallShortcutReceiver.disableAndFlushInstallQueue(this);
mPaused = false;
sPausedFromUserAction = false;
@@ -806,6 +804,12 @@
(System.currentTimeMillis() - startTimeCallbacks));
}
}
+ if (mOnResumeCallbacks.size() > 0) {
+ for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
+ mOnResumeCallbacks.get(i).run();
+ }
+ mOnResumeCallbacks.clear();
+ }
// Reset the pressed state of icons that were locked in the press state while activities
// were launching
@@ -838,6 +842,9 @@
// when Launcher resumes and we are still in AllApps.
updateWallpaperVisibility(true);
+ // Ensure that items added to Launcher are queued until Launcher returns
+ InstallShortcutReceiver.enableInstallQueue();
+
super.onPause();
mPaused = true;
mDragController.cancelDrag();
@@ -3445,11 +3452,11 @@
}
public void addOnResumeCallback(Runnable run) {
- mBindOnResumeCallbacks.add(run);
+ mOnResumeCallbacks.add(run);
}
public void removeOnResumeCallback(Runnable run) {
- mBindOnResumeCallbacks.remove(run);
+ mOnResumeCallbacks.remove(run);
}
/**
@@ -3499,8 +3506,6 @@
mBindOnResumeCallbacks.clear();
final Workspace workspace = mWorkspace;
- mNewShortcutAnimateScreenId = -1;
- mNewShortcutAnimateViews.clear();
mWorkspace.clearDropTargets();
int count = workspace.getChildCount();
for (int i = 0; i < count; i++) {
@@ -3568,12 +3573,11 @@
}
// Get the list of added shortcuts and intersect them with the set of shortcuts here
- Set<String> newApps = new HashSet<String>();
- newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);
-
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<Animator>();
+ final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
+ long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
@@ -3587,7 +3591,6 @@
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
ShortcutInfo info = (ShortcutInfo) item;
- String uri = info.intent.toUri(0).toString();
View shortcut = createShortcut(info);
/*
@@ -3602,27 +3605,13 @@
workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);
- boolean animateIconUp = false;
- synchronized (newApps) {
- if (newApps.contains(uri)) {
- animateIconUp = newApps.remove(uri);
- }
- }
- if (forceAnimateIcons) {
+ if (animateIcons) {
// Animate all the applications up now
shortcut.setAlpha(0f);
shortcut.setScaleX(0f);
shortcut.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
- } else if (animateIconUp) {
- // Prepare the view to be animated up
- shortcut.setAlpha(0f);
- shortcut.setScaleX(0f);
- shortcut.setScaleY(0f);
- mNewShortcutAnimateScreenId = item.screenId;
- if (!mNewShortcutAnimateViews.contains(shortcut)) {
- mNewShortcutAnimateViews.add(shortcut);
- }
+ newShortcutsScreenId = item.screenId;
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -3637,7 +3626,16 @@
}
}
- if (forceAnimateIcons) {
+ if (animateIcons) {
+ // Animate to the correct page
+ if (newShortcutsScreenId > -1) {
+ long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
+ int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
+ if (newShortcutsScreenId != currentScreenId) {
+ mWorkspace.snapToPage(newScreenIndex);
+ }
+ }
+
// We post the animation slightly delayed to prevent slowdowns when we are loading
// right after we return to launcher.
mWorkspace.postDelayed(new Runnable() {
@@ -3750,32 +3748,6 @@
// package changes in bindSearchablesChanged()
updateAppMarketIcon();
- // Animate up any icons as necessary
- if (mVisible || mWorkspaceLoading) {
- Runnable newAppsRunnable = new Runnable() {
- @Override
- public void run() {
- runNewAppsAnimation(false);
- }
- };
-
- boolean willSnapPage = mNewShortcutAnimateScreenId > -1 &&
- mNewShortcutAnimateScreenId != mWorkspace.getCurrentPage();
- if (canRunNewAppsAnimation()) {
- // If the user has not interacted recently, then either snap to the new page to show
- // the new-apps animation or just run them if they are to appear on the current page
- if (willSnapPage) {
- mWorkspace.snapToScreenId(mNewShortcutAnimateScreenId, newAppsRunnable);
- } else {
- runNewAppsAnimation(false);
- }
- } else {
- // If the user has interacted recently, then just add the items in place if they
- // are on another page (or just normally if they are added to the current page)
- runNewAppsAnimation(willSnapPage);
- }
- }
-
mWorkspaceLoading = false;
if (upgradePath) {
mWorkspace.stripDuplicateApps();
@@ -3806,64 +3778,6 @@
return bounceAnim;
}
- /**
- * Runs a new animation that scales up icons that were added while Launcher was in the
- * background.
- *
- * @param immediate whether to run the animation or show the results immediately
- */
- private void runNewAppsAnimation(boolean immediate) {
- AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- Collection<Animator> bounceAnims = new ArrayList<Animator>();
-
- // Order these new views spatially so that they animate in order
- Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() {
- @Override
- public int compare(View a, View b) {
- CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams();
- CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams();
- int cellCountX = LauncherModel.getCellCountX();
- return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX);
- }
- });
-
- // Animate each of the views in place (or show them immediately if requested)
- if (immediate) {
- for (View v : mNewShortcutAnimateViews) {
- v.setAlpha(1f);
- v.setScaleX(1f);
- v.setScaleY(1f);
- }
- } else {
- for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) {
- View v = mNewShortcutAnimateViews.get(i);
- bounceAnims.add(createNewAppBounceAnimation(v, i));
- }
- anim.playTogether(bounceAnims);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mWorkspace != null) {
- mWorkspace.postDelayed(mBuildLayersRunnable, 500);
- }
- }
- });
- anim.start();
- }
-
- // Clean up
- mNewShortcutAnimateScreenId = -1;
- mNewShortcutAnimateViews.clear();
- new Thread("clearNewAppsThread") {
- public void run() {
- mSharedPrefs.edit()
- .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1)
- .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null)
- .commit();
- }
- }.start();
- }
-
@Override
public void bindSearchablesChanged() {
boolean searchVisible = updateGlobalSearchIcon();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b231e3a..cec446f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -68,7 +68,7 @@
static final String TAG = "Launcher.Model";
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
- private final boolean mAppsCanBeOnExternalStorage;
+ private final boolean mAppsCanBeOnRemoveableStorage;
private final LauncherAppState mApp;
private final Object mLock = new Object();
@@ -172,7 +172,7 @@
LauncherModel(LauncherAppState app, IconCache iconCache) {
final Context context = app.getContext();
- mAppsCanBeOnExternalStorage = !Environment.isExternalStorageEmulated();
+ mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache);
mIconCache = iconCache;
@@ -268,8 +268,15 @@
return null;
}
- public void addAndBindAddedApps(final Context context, final ArrayList<ApplicationInfo> added,
+ public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> added) {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ addAndBindAddedApps(context, added, cb);
+ }
+ public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> added,
final Callbacks callbacks) {
+ if (added.isEmpty()) {
+ throw new RuntimeException("EMPTY ADDED ARRAY?");
+ }
// Process the newly added applications and add them to the database first
Runnable r = new Runnable() {
public void run() {
@@ -277,11 +284,11 @@
final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
synchronized(sBgLock) {
- Iterator<ApplicationInfo> iter = added.iterator();
+ Iterator<ItemInfo> iter = added.iterator();
while (iter.hasNext()) {
- ApplicationInfo a = iter.next();
+ ItemInfo a = iter.next();
final String name = a.title.toString();
- final Intent launchIntent = a.intent;
+ final Intent launchIntent = a.getIntent();
// Short-circuit this logic if the icon exists somewhere on the workspace
if (LauncherModel.shortcutExists(context, name, launchIntent)) {
@@ -319,7 +326,14 @@
throw new RuntimeException("Coordinates should not be null");
}
- final ShortcutInfo shortcutInfo = a.makeShortcut();
+ ShortcutInfo shortcutInfo;
+ if (a instanceof ShortcutInfo) {
+ shortcutInfo = (ShortcutInfo) a;
+ } else if (a instanceof ApplicationInfo) {
+ shortcutInfo = ((ApplicationInfo) a).makeShortcut();
+ } else {
+ throw new RuntimeException("Unexpected info type");
+ }
// Add the shortcut to the db
addItemToDatabase(context, shortcutInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
@@ -329,16 +343,38 @@
}
}
- runOnMainThread(new Runnable() {
- public void run() {
- Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
- if (callbacks == cb && cb != null) {
- callbacks.bindAddScreens(addedWorkspaceScreensFinal);
- callbacks.bindItems(addedShortcutsFinal, 0,
- addedShortcutsFinal.size(), true);
+ if (!addedShortcutsFinal.isEmpty()) {
+ runOnMainThread(new Runnable() {
+ public void run() {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks == cb && cb != null) {
+ callbacks.bindAddScreens(addedWorkspaceScreensFinal);
+
+ ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
+ long lastScreenId = info.screenId;
+ final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
+ final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
+ for (ItemInfo i : addedShortcutsFinal) {
+ if (i.screenId == lastScreenId) {
+ addAnimated.add(i);
+ } else {
+ addNotAnimated.add(i);
+ }
+ }
+ // We add the items without animation on non-visible pages, and with
+ // animations on the new page (which we will try and snap to).
+ if (!addNotAnimated.isEmpty()) {
+ callbacks.bindItems(addNotAnimated, 0,
+ addNotAnimated.size(), false);
+ }
+ if (!addAnimated.isEmpty()) {
+ callbacks.bindItems(addAnimated, 0,
+ addAnimated.size(), true);
+ }
+ }
}
- }
- });
+ });
+ }
}
};
runOnWorkerThread(r);
@@ -1455,7 +1491,7 @@
// Cross reference all the applications in our apps list with items in the workspace
ArrayList<ItemInfo> tmpInfos;
- ArrayList<ApplicationInfo> added = new ArrayList<ApplicationInfo>();
+ ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
synchronized (sBgLock) {
for (ApplicationInfo app : mBgAllAppsList.data) {
tmpInfos = getItemInfoForComponentName(app.componentName);
@@ -1606,9 +1642,24 @@
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ id = c.getLong(idIndex);
intentDescription = c.getString(intentIndex);
try {
intent = Intent.parseUri(intentDescription, 0);
+ ComponentName cn = intent.getComponent();
+ if (!isValidPackage(manager, cn)) {
+ if (!mAppsCanBeOnRemoveableStorage) {
+ // Log the invalid package, and remove it from the database
+ Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
+ contentResolver.delete(uri, null, null);
+ Log.e(TAG, "Invalid package removed in loadWorkspace: " + cn);
+ } else {
+ // If apps can be on external storage, then we just leave
+ // them for the user to remove (maybe add treatment to it)
+ Log.e(TAG, "Invalid package found in loadWorkspace: " + cn);
+ }
+ continue;
+ }
} catch (URISyntaxException e) {
continue;
}
@@ -1635,8 +1686,8 @@
}
if (info != null) {
+ info.id = id;
info.intent = intent;
- info.id = c.getLong(idIndex);
container = c.getInt(containerIndex);
info.container = container;
info.screenId = c.getInt(screenIndex);
@@ -1664,15 +1715,6 @@
// now that we've loaded everthing re-save it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
- } else {
- // Failed to load the shortcut, probably because the
- // activity manager couldn't resolve it (maybe the app
- // was uninstalled), or the db row was somehow screwed up.
- // Delete it.
- id = c.getLong(idIndex);
- Log.e(TAG, "Error loading shortcut " + id + ", removing it");
- contentResolver.delete(LauncherSettings.Favorites.getContentUri(
- id, false), null, null);
}
break;
@@ -2402,8 +2444,9 @@
if (added != null) {
// Ensure that we add all the workspace applications to the db
+ final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
- addAndBindAddedApps(context, added, cb);
+ addAndBindAddedApps(context, addedInfos, cb);
}
if (modified != null) {
final ArrayList<ApplicationInfo> modifiedFinal = modified;
@@ -2492,6 +2535,18 @@
return widgetsAndShortcuts;
}
+ private boolean isValidPackage(PackageManager pm, ComponentName cn) {
+ if (cn == null) {
+ return false;
+ }
+
+ try {
+ return (pm.getActivityInfo(cn, 0) != null);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
/**
* This is called from the code that adds shortcuts from the intent receiver. This
* doesn't have a Cursor, but
@@ -2507,26 +2562,12 @@
*/
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
- Bitmap icon = null;
- final ShortcutInfo info = new ShortcutInfo();
-
ComponentName componentName = intent.getComponent();
- if (componentName == null) {
+ if (!isValidPackage(manager, componentName)) {
+ Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
return null;
}
- try {
- PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
- if (!pi.applicationInfo.enabled) {
- // If we return null here, the corresponding item will be removed from the launcher
- // db and will not appear in the workspace.
- return null;
- }
- info.initFlagsAndFirstInstallTime(pi);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "getPackInfo failed for package " + componentName.getPackageName());
- }
-
// TODO: See if the PackageManager knows about this case. If it doesn't
// then return null & delete this.
@@ -2538,6 +2579,8 @@
// Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
// if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
// via resolveActivity().
+ final ShortcutInfo info = new ShortcutInfo();
+ Bitmap icon = null;
ResolveInfo resolveInfo = null;
ComponentName oldComponent = intent.getComponent();
Intent newIntent = new Intent(intent.getAction(), null);
@@ -2864,7 +2907,7 @@
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) {
+ if (!mAppsCanBeOnRemoveableStorage) {
return false;
}
// If this icon doesn't have a custom icon, check to see
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index b6139b7..b4e5642 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -67,6 +67,10 @@
ShortcutInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
+
+ protected Intent getIntent() {
+ return intent;
+ }
public ShortcutInfo(Context context, ShortcutInfo info) {
super(info);
@@ -79,7 +83,8 @@
}
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
customIcon = info.customIcon;
- initFlagsAndFirstInstallTime(getPackageInfo(context, intent.getComponent().getPackageName()));
+ initFlagsAndFirstInstallTime(
+ getPackageInfo(context, intent.getComponent().getPackageName()));
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
@@ -137,7 +142,8 @@
intent.setComponent(className);
intent.setFlags(launchFlags);
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
- initFlagsAndFirstInstallTime(getPackageInfo(context, intent.getComponent().getPackageName()));
+ initFlagsAndFirstInstallTime(
+ getPackageInfo(context, intent.getComponent().getPackageName()));
}
@Override
diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java
index 8e968cd..2e1ed69 100644
--- a/src/com/android/launcher3/UninstallShortcutReceiver.java
+++ b/src/com/android/launcher3/UninstallShortcutReceiver.java
@@ -79,19 +79,15 @@
private static void processUninstallShortcut(Context context,
PendingUninstallShortcutInfo pendingInfo) {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sharedPrefs = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
-
final Intent data = pendingInfo.data;
LauncherAppState app = LauncherAppState.getInstance();
synchronized (app) { // TODO: make removeShortcut internally threadsafe
- removeShortcut(context, data, sharedPrefs);
+ removeShortcut(context, data);
}
}
- private static void removeShortcut(Context context, Intent data,
- final SharedPreferences sharedPrefs) {
+ private static void removeShortcut(Context context, Intent data) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
@@ -132,33 +128,6 @@
Toast.makeText(context, context.getString(R.string.shortcut_uninstalled, name),
Toast.LENGTH_SHORT).show();
}
-
- // Remove any items due to be animated
- boolean appRemoved;
- Set<String> newApps = new HashSet<String>();
- newApps = sharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);
- synchronized (newApps) {
- do {
- appRemoved = newApps.remove(intent.toUri(0).toString());
- } while (appRemoved);
- }
- if (appRemoved) {
- final Set<String> savedNewApps = newApps;
- new Thread("setNewAppsThread-remove") {
- public void run() {
- synchronized (savedNewApps) {
- SharedPreferences.Editor editor = sharedPrefs.edit();
- editor.putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY,
- savedNewApps);
- if (savedNewApps.isEmpty()) {
- // Reset the page index if there are no more items
- editor.putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1);
- }
- editor.commit();
- }
- }
- }.start();
- }
}
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9424eef..6989c9a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3969,34 +3969,6 @@
// Strip all the empty screens
stripEmptyScreens();
-
- // Clean up new-apps animation list
- final Context context = getContext();
- post(new Runnable() {
- @Override
- public void run() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey,
- Context.MODE_PRIVATE);
- Set<String> newApps = sp.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY,
- null);
-
- // Remove all queued items that match the same package
- if (newApps != null) {
- synchronized (newApps) {
- Iterator<String> iter = newApps.iterator();
- while (iter.hasNext()) {
- try {
- Intent intent = Intent.parseUri(iter.next(), 0);
- if (componentNames.contains(intent.getComponent())) {
- iter.remove();
- }
- } catch (URISyntaxException e) {}
- }
- }
- }
- }
- });
}
void updateShortcuts(ArrayList<ApplicationInfo> apps) {