Merge "Cross referencing all shortcuts on workspace with the PackageManager to ensure we don't have zombie icons." into jb-ub-gel-agar
diff --git a/res/drawable-hdpi/now_page.png b/res/drawable-hdpi/now_page.png
new file mode 100644
index 0000000..9eef50c
--- /dev/null
+++ b/res/drawable-hdpi/now_page.png
Binary files differ
diff --git a/res/drawable-mdpi/now_page.png b/res/drawable-mdpi/now_page.png
new file mode 100644
index 0000000..cc4005d
--- /dev/null
+++ b/res/drawable-mdpi/now_page.png
Binary files differ
diff --git a/res/drawable-xhdpi/now_page.png b/res/drawable-xhdpi/now_page.png
new file mode 100644
index 0000000..e1da91c
--- /dev/null
+++ b/res/drawable-xhdpi/now_page.png
Binary files differ
diff --git a/res/layout/now_page_indicator_marker.xml b/res/layout/now_page_indicator_marker.xml
new file mode 100644
index 0000000..7d05627
--- /dev/null
+++ b/res/layout/now_page_indicator_marker.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.PageIndicatorMarker
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/inactive"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_page"
+ />
+ <ImageView
+ android:id="@+id/active"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_page"
+ android:alpha="0"
+ />
+</com.android.launcher3.PageIndicatorMarker>
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/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 3c064ba..514dca2 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -327,84 +327,15 @@
*/
public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
boolean includeRootScroll) {
- return DragLayer.getDescendantCoordRelativeToParent(descendant, this,
+ return Utilities.getDescendantCoordRelativeToParent(descendant, this,
coord, includeRootScroll);
}
- public static float getDescendantCoordRelativeToParent(View descendant, View root,
- int[] coord, boolean includeRootScroll) {
- ArrayList<View> ancestorChain = new ArrayList<View>();
-
- float[] pt = {coord[0], coord[1]};
-
- View v = descendant;
- while(v != root && v != null) {
- ancestorChain.add(v);
- v = (View) v.getParent();
- }
- ancestorChain.add(root);
-
- float scale = 1.0f;
- int count = ancestorChain.size();
- for (int i = 0; i < count; i++) {
- View v0 = ancestorChain.get(i);
- View v1 = i < count -1 ? ancestorChain.get(i + 1) : null;
-
- // For TextViews, scroll has a meaning which relates to the text position
- // which is very strange... ignore the scroll.
- if (v0 != descendant || includeRootScroll) {
- pt[0] -= v0.getScrollX();
- pt[1] -= v0.getScrollY();
- }
-
- v0.getMatrix().mapPoints(pt);
- pt[0] += v0.getLeft();
- pt[1] += v0.getTop();
- scale *= v0.getScaleX();
- }
-
- coord[0] = (int) Math.round(pt[0]);
- coord[1] = (int) Math.round(pt[1]);
- return scale;
- }
-
/**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
*/
public float mapCoordInSelfToDescendent(View descendant, int[] coord) {
- ArrayList<View> ancestorChain = new ArrayList<View>();
-
- float[] pt = {coord[0], coord[1]};
-
- View v = descendant;
- while(v != this) {
- ancestorChain.add(v);
- v = (View) v.getParent();
- }
- ancestorChain.add(this);
-
- float scale = 1.0f;
- Matrix inverse = new Matrix();
- int count = ancestorChain.size();
- for (int i = count - 1; i >= 0; i--) {
- View ancestor = ancestorChain.get(i);
- View next = i > 0 ? ancestorChain.get(i-1) : null;
-
- pt[0] += ancestor.getScrollX();
- pt[1] += ancestor.getScrollY();
-
- if (next != null) {
- pt[0] -= next.getLeft();
- pt[1] -= next.getTop();
- next.getMatrix().invert(inverse);
- inverse.mapPoints(pt);
- scale *= next.getScaleX();
- }
- }
-
- coord[0] = (int) Math.round(pt[0]);
- coord[1] = (int) Math.round(pt[1]);
- return scale;
+ return Utilities.mapCoordInSelfToDescendent(descendant, this, coord);
}
public void getViewRectRelativeToSelf(View v, Rect r) {
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/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 321c4e7..f990d25 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;
@@ -444,6 +442,11 @@
sPausedFromUserAction = true;
}
+ /** To be overriden by subclasses to hint to Launcher that we have custom content */
+ protected boolean hasCustomContentToLeft() {
+ return false;
+ }
+
private void updateGlobalIcons() {
boolean searchVisible = false;
boolean voiceVisible = false;
@@ -764,7 +767,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;
@@ -797,6 +800,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
@@ -829,6 +838,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();
@@ -868,13 +880,13 @@
}
// Add a fullscreen unpadded view to the workspace to the left all other screens.
- public QSBScroller addCustomContentToLeft(View customContent) {
- return addCustomContentToLeft(customContent, null);
+ public QSBScroller addToCustomContentPage(View customContent) {
+ return addToCustomContentPage(customContent, null);
}
- public QSBScroller addCustomContentToLeft(View customContent,
+ public QSBScroller addToCustomContentPage(View customContent,
CustomContentCallbacks callbacks) {
- mWorkspace.addCustomContentToLeft(customContent, callbacks);
+ mWorkspace.addToCustomContentPage(customContent, callbacks);
return mQsbScroller;
}
@@ -3433,11 +3445,11 @@
}
public void addOnResumeCallback(Runnable run) {
- mBindOnResumeCallbacks.add(run);
+ mOnResumeCallbacks.add(run);
}
public void removeOnResumeCallback(Runnable run) {
- mBindOnResumeCallbacks.remove(run);
+ mOnResumeCallbacks.remove(run);
}
/**
@@ -3487,8 +3499,6 @@
mBindOnResumeCallbacks.clear();
final Workspace workspace = mWorkspace;
- mNewShortcutAnimateScreenId = -1;
- mNewShortcutAnimateViews.clear();
mWorkspace.clearDropTargets();
int count = workspace.getChildCount();
for (int i = 0; i < count; i++) {
@@ -3556,12 +3566,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);
@@ -3575,7 +3584,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);
/*
@@ -3590,27 +3598,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:
@@ -3620,10 +3614,21 @@
workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);
break;
+ default:
+ throw new RuntimeException("Invalid Item Type");
}
}
- 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() {
@@ -3718,6 +3723,11 @@
mSavedState = null;
}
+ // Create the custom content page here before onLayout to prevent flashing
+ if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) {
+ mWorkspace.createCustomContentPage();
+ }
+
mWorkspace.restoreInstanceStateForRemainingPages();
// If we received the result of any pending adds while the loader was running (e.g. the
@@ -3731,35 +3741,8 @@
// 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.saveWorkspaceToDb();
mWorkspace.stripDuplicateApps();
mIntentsOnWorkspaceFromUpgradePath = mWorkspace.stripDuplicateApps();
}
@@ -3788,64 +3771,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 cb0cc2a..fc1c1d0 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -265,12 +265,18 @@
}
}
}
- // XXX: Create a new page and add it to the first spot
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() {
@@ -278,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)) {
@@ -294,26 +300,40 @@
Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
name, launchIntent, startSearchPageIndex);
if (coords == null) {
- // If we can't find a valid position, then just add a new screen.
- // This takes time so we need to re-queue the add until the new
- // page is added.
LauncherAppState appState = LauncherAppState.getInstance();
LauncherProvider lp = appState.getLauncherProvider();
- long screenId = lp.generateNewScreenId();
- // Update the model
- sBgWorkspaceScreens.add(screenId);
- updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
- // Save the screen id for binding in the workspace
- addedWorkspaceScreensFinal.add(screenId);
+
+ // If we can't find a valid position, then just add a new screen.
+ // This takes time so we need to re-queue the add until the new
+ // page is added. Create as many screens as necessary to satisfy
+ // the startSearchPageIndex.
+ int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
+ sBgWorkspaceScreens.size());
+ while (numPagesToAdd > 0) {
+ long screenId = lp.generateNewScreenId();
+ // Update the model
+ sBgWorkspaceScreens.add(screenId);
+ updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
+ // Save the screen id for binding in the workspace
+ addedWorkspaceScreensFinal.add(screenId);
+ numPagesToAdd--;
+ }
// Find the coordinate again
coords = LauncherModel.findNextAvailableIconSpace(context,
- a.title.toString(), a.intent, startSearchPageIndex);
+ name, launchIntent, startSearchPageIndex);
}
if (coords == null) {
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,
@@ -323,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);
@@ -1219,7 +1261,6 @@
private boolean mIsLoadingAndBindingWorkspace;
private boolean mStopped;
private boolean mLoadAndBindStepFinished;
- private boolean mIsUpgradePath;
private HashMap<Object, CharSequence> mLabelCache;
@@ -1237,7 +1278,8 @@
return mIsLoadingAndBindingWorkspace;
}
- private void loadAndBindWorkspace() {
+ /** Returns whether this is an upgrade path */
+ private boolean loadAndBindWorkspace() {
mIsLoadingAndBindingWorkspace = true;
// Load the workspace
@@ -1245,18 +1287,20 @@
Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
}
+ boolean isUpgradePath = false;
if (!mWorkspaceLoaded) {
- loadWorkspace();
+ isUpgradePath = loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
- return;
+ return isUpgradePath;
}
mWorkspaceLoaded = true;
}
}
// Bind the workspace
- bindWorkspace(-1);
+ bindWorkspace(-1, isUpgradePath);
+ return isUpgradePath;
}
private void waitForIdle() {
@@ -1325,13 +1369,15 @@
// Divide the set of loaded items into those that we are binding synchronously, and
// everything else that is to be bound normally (asynchronously).
- bindWorkspace(synchronousBindPage);
+ bindWorkspace(synchronousBindPage, false);
// XXX: For now, continue posting the binding of AllApps as there are other issues that
// arise from that.
onlyBindAllApps();
}
public void run() {
+ boolean isUpgrade = false;
+
synchronized (mLock) {
mIsLoaderTaskRunning = true;
}
@@ -1349,7 +1395,7 @@
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
- loadAndBindWorkspace();
+ isUpgrade = loadAndBindWorkspace();
if (mStopped) {
break keep_running;
@@ -1384,6 +1430,12 @@
sBgDbIconCache.clear();
}
+ // Ensure that all the applications that are in the system are represented on the home
+ // screen.
+ if (!isUpgrade) {
+ verifyApplications();
+ }
+
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
@@ -1434,6 +1486,29 @@
}
}
+ private void verifyApplications() {
+ final Context context = mApp.getContext();
+
+ // Cross reference all the applications in our apps list with items in the workspace
+ ArrayList<ItemInfo> tmpInfos;
+ ArrayList<ApplicationInfo> added = new ArrayList<ApplicationInfo>();
+ synchronized (sBgLock) {
+ for (ApplicationInfo app : mBgAllAppsList.data) {
+ tmpInfos = getItemInfoForComponentName(app.componentName);
+ if (tmpInfos.isEmpty()) {
+ // We are missing an application icon, so add this to the workspace
+ added.add(app);
+ // This is a rare event, so lets log it
+ Log.e(TAG, "Missing Application on load: " + app);
+ }
+ }
+ }
+ if (!added.isEmpty()) {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ addAndBindAddedApps(context, added, cb);
+ }
+ }
+
// check & update map of what's occupied; used to discard overlapping/invalid items
private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item) {
long containerIndex = item.screenId;
@@ -1487,7 +1562,8 @@
return true;
}
- private void loadWorkspace() {
+ /** Returns whether this is an upgradge path */
+ private boolean loadWorkspace() {
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
@@ -1497,11 +1573,10 @@
final boolean isSafeMode = manager.isSafeMode();
// Make sure the default workspace is loaded, if needed
- boolean loadOldDb = mApp.getLauncherProvider().shouldLoadOldDb();
- Uri contentUri = loadOldDb ? LauncherSettings.Favorites.OLD_CONTENT_URI :
- LauncherSettings.Favorites.CONTENT_URI;
+ mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
- mIsUpgradePath = loadOldDb;
+ // Check if we need to do any upgrade-path logic
+ boolean loadedOldDb = mApp.getLauncherProvider().justLoadedOldDb();
synchronized (sBgLock) {
sBgWorkspaceItems.clear();
@@ -1512,7 +1587,7 @@
sBgWorkspaceScreens.clear();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
-
+ final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
// +1 for the hotseat (it can be larger than the workspace)
@@ -1635,10 +1710,6 @@
folderInfo.add(info);
break;
}
- if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
- loadOldDb) {
- info.screenId = permuteScreens(info.screenId);
- }
sBgItemsIdMap.put(info.id, info);
// now that we've loaded everthing re-save it with the
@@ -1669,10 +1740,6 @@
sBgWorkspaceItems.add(folderInfo);
break;
}
- if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
- loadOldDb) {
- folderInfo.screenId = permuteScreens(folderInfo.screenId);
- }
sBgItemsIdMap.put(folderInfo.id, folderInfo);
sBgFolders.put(folderInfo.id, folderInfo);
@@ -1713,11 +1780,6 @@
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
continue;
}
- if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
- loadOldDb) {
- appWidgetInfo.screenId =
- permuteScreens(appWidgetInfo.screenId);
- }
appWidgetInfo.container = c.getInt(containerIndex);
// check & update map of what's occupied
@@ -1757,7 +1819,7 @@
}
}
- if (loadOldDb) {
+ if (loadedOldDb) {
long maxScreenId = 0;
// If we're importing we use the old screen order.
for (ItemInfo item: sBgItemsIdMap.values()) {
@@ -1773,6 +1835,15 @@
Collections.sort(sBgWorkspaceScreens);
mApp.getLauncherProvider().updateMaxScreenId(maxScreenId);
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
+
+ // Update the max item id after we load an old db
+ long maxItemId = 0;
+ // If we're importing we use the old screen order.
+ for (ItemInfo item: sBgItemsIdMap.values()) {
+ maxItemId = Math.max(maxItemId, item.id);
+ }
+ LauncherAppState app = LauncherAppState.getInstance();
+ app.getLauncherProvider().updateMaxItemId(maxItemId);
} else {
Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
@@ -1843,16 +1914,7 @@
}
}
}
- }
-
- // We rearrange the screens from the old launcher
- // 12345 -> 34512
- private long permuteScreens(long screen) {
- if (screen >= 2) {
- return screen - 2;
- } else {
- return screen + 3;
- }
+ return loadedOldDb;
}
/** Filters the set of items who are directly or indirectly (via another container) on the
@@ -2060,7 +2122,7 @@
/**
* Binds all loaded data to actual views on the main thread.
*/
- private void bindWorkspace(int synchronizeBindPage) {
+ private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
final long t = SystemClock.uptimeMillis();
Runnable r;
@@ -2152,7 +2214,7 @@
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
- callbacks.finishBindingItems(mIsUpgradePath);
+ callbacks.finishBindingItems(isUpgradePath);
}
// If we're profiling, ensure this is the last thing in the queue.
@@ -2382,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;
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index ae227a9..7d090e1 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -73,8 +73,10 @@
static final String TABLE_FAVORITES = "favorites";
static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
static final String PARAMETER_NOTIFY = "notify";
- static final String DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED =
- "DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED";
+ static final String UPGRADED_FROM_OLD_DATABASE =
+ "UPGRADED_FROM_OLD_DATABASE";
+ static final String EMPTY_DATABASE_CREATED =
+ "EMPTY_DATABASE_CREATED";
static final String DEFAULT_WORKSPACE_RESOURCE_ID =
"DEFAULT_WORKSPACE_RESOURCE_ID";
@@ -90,7 +92,7 @@
Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
private DatabaseHelper mOpenHelper;
- private static boolean sLoadOldDb;
+ private static boolean sJustLoadedFromOldDb;
@Override
public boolean onCreate() {
@@ -208,6 +210,10 @@
return mOpenHelper.generateNewItemId();
}
+ public void updateMaxItemId(long id) {
+ mOpenHelper.updateMaxItemId(id);
+ }
+
public long generateNewScreenId() {
return mOpenHelper.generateNewScreenId();
}
@@ -221,21 +227,21 @@
/**
* @param Should we load the old db for upgrade? first run only.
*/
- synchronized public boolean shouldLoadOldDb() {
+ synchronized public boolean justLoadedOldDb() {
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
- boolean loadOldDb = false || sLoadOldDb;
+ boolean loadedOldDb = false || sJustLoadedFromOldDb;
- sLoadOldDb = false;
- if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
+ sJustLoadedFromOldDb = false;
+ if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
SharedPreferences.Editor editor = sp.edit();
- editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
+ editor.remove(UPGRADED_FROM_OLD_DATABASE);
editor.commit();
- loadOldDb = true;
+ loadedOldDb = true;
}
- return loadOldDb;
+ return loadedOldDb;
}
/**
@@ -245,7 +251,7 @@
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
- if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
+ if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
int workspaceResId = origWorkspaceResId;
// Use default workspace resource if none provided
@@ -255,16 +261,21 @@
// Populate favorites table with initial favorites
SharedPreferences.Editor editor = sp.edit();
- editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
+ editor.remove(EMPTY_DATABASE_CREATED);
if (origWorkspaceResId != 0) {
editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
}
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
+ mOpenHelper.setFlagJustLoadedOldDb();
editor.commit();
}
}
+ private static interface ContentValuesCallback {
+ public void onRow(ContentValues values);
+ }
+
private static class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
@@ -341,10 +352,32 @@
sendAppWidgetResetNotify();
}
- if (!convertDatabase(db)) {
- // Set a shared pref so that we know we need to load the default workspace later
- setFlagToLoadDefaultWorkspaceLater();
+ // Try converting the old database
+ ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
+ public void onRow(ContentValues values) {
+ int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
+ if (container == Favorites.CONTAINER_DESKTOP) {
+ int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
+ screen = (int) upgradeLauncherDb_permuteScreens(screen);
+ values.put(LauncherSettings.Favorites.SCREEN, screen);
+ }
+ }
+ };
+ Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
+ "/old_favorites?notify=true");
+ if (!convertDatabase(db, uri, permuteScreensCb, true)) {
+ // Try and upgrade from the Launcher2 db
+ uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
+ if (!convertDatabase(db, uri, permuteScreensCb, false)) {
+ // If we fail, then set a flag to load the default workspace
+ setFlagEmptyDbCreated();
+ return;
+ }
}
+ // Right now, in non-default workspace cases, we want to run the final
+ // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
+ // set that flag too.
+ setFlagJustLoadedOldDb();
}
private void addWorkspacesTable(SQLiteDatabase db) {
@@ -354,20 +387,39 @@
");");
}
- private void setFlagToLoadDefaultWorkspaceLater() {
+ private void setFlagJustLoadedOldDb() {
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
- editor.putBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, true);
+ editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
+ editor.putBoolean(EMPTY_DATABASE_CREATED, false);
editor.commit();
}
- private boolean convertDatabase(SQLiteDatabase db) {
+ private void setFlagEmptyDbCreated() {
+ String spKey = LauncherAppState.getSharedPreferencesKey();
+ SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putBoolean(EMPTY_DATABASE_CREATED, true);
+ editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
+ editor.commit();
+ }
+
+ // We rearrange the screens from the old launcher
+ // 12345 -> 34512
+ private long upgradeLauncherDb_permuteScreens(long screen) {
+ if (screen >= 2) {
+ return screen - 2;
+ } else {
+ return screen + 3;
+ }
+ }
+
+ private boolean convertDatabase(SQLiteDatabase db, Uri uri,
+ ContentValuesCallback cb, boolean deleteRows) {
if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
boolean converted = false;
- final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
- "/old_favorites?notify=true");
final ContentResolver resolver = mContext.getContentResolver();
Cursor cursor = null;
@@ -378,28 +430,33 @@
}
// We already have a favorites database in the old provider
- if (cursor != null && cursor.getCount() > 0) {
+ if (cursor != null) {
try {
- converted = copyFromCursor(db, cursor) > 0;
+ if (cursor.getCount() > 0) {
+ converted = copyFromCursor(db, cursor, cb) > 0;
+ if (converted && deleteRows) {
+ resolver.delete(uri, null, null);
+ }
+ }
} finally {
cursor.close();
}
-
- if (converted) {
- resolver.delete(uri, null, null);
- }
}
if (converted) {
// Convert widgets from this import into widgets
if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
convertWidgets(db);
+
+ // Update max item id
+ mMaxItemId = initializeMaxItemId(db);
+ if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
}
return converted;
}
- private int copyFromCursor(SQLiteDatabase db, Cursor c) {
+ private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
@@ -434,23 +491,28 @@
values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
+ if (cb != null) {
+ cb.onRow(values);
+ }
rows[i++] = values;
}
- db.beginTransaction();
int total = 0;
- try {
- int numValues = rows.length;
- for (i = 0; i < numValues; i++) {
- if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
- return 0;
- } else {
- total++;
+ if (i > 0) {
+ db.beginTransaction();
+ try {
+ int numValues = rows.length;
+ for (i = 0; i < numValues; i++) {
+ if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
+ return 0;
+ } else {
+ total++;
+ }
}
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
}
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
}
return total;
@@ -458,7 +520,7 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (LOGD) Log.d(TAG, "onUpgrade triggered");
+ if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
int version = oldVersion;
if (version < 3) {
@@ -558,7 +620,7 @@
// This will never happen in the wild, but when we switch to using workspace
// screen ids, redo the import from old launcher.
- sLoadOldDb = true;
+ sJustLoadedFromOldDb = true;
addWorkspacesTable(db);
version = 13;
@@ -722,6 +784,10 @@
return mMaxItemId;
}
+ public void updateMaxItemId(long id) {
+ mMaxItemId = id + 1;
+ }
+
private long initializeMaxItemId(SQLiteDatabase db) {
Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
@@ -861,6 +927,10 @@
c.close();
}
}
+
+ // Update max item id
+ mMaxItemId = initializeMaxItemId(db);
+ if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
}
private static final void beginDocument(XmlPullParser parser, String firstElementName)
@@ -1018,6 +1088,11 @@
Log.w(TAG, "Got exception parsing favorites.", e);
}
+ // Update the max item id after we have loaded the database
+ if (mMaxItemId == -1) {
+ mMaxItemId = initializeMaxItemId(db);
+ }
+
return i;
}
diff --git a/src/com/android/launcher3/PageIndicator.java b/src/com/android/launcher3/PageIndicator.java
index d7778fb..ce98145 100644
--- a/src/com/android/launcher3/PageIndicator.java
+++ b/src/com/android/launcher3/PageIndicator.java
@@ -161,18 +161,17 @@
mWindowRange[1] = windowEnd;
}
- void addMarker(int index) {
+ void addMarker(int index, int layoutId) {
index = Math.max(0, Math.min(index, mMarkers.size()));
- int mLayoutId = R.layout.page_indicator_marker;
PageIndicatorMarker marker =
- (PageIndicatorMarker) mLayoutInflater.inflate(mLayoutId, this, false);
+ (PageIndicatorMarker) mLayoutInflater.inflate(layoutId, this, false);
mMarkers.add(index, marker);
offsetWindowCenterTo(mActiveMarkerIndex, true);
}
- void addMarkers(int count) {
- for (int i = 0; i < count; ++i) {
- addMarker(Integer.MAX_VALUE);
+ void addMarkers(ArrayList<Integer> layoutIds) {
+ for (int i = 0; i < layoutIds.size(); ++i) {
+ addMarker(Integer.MAX_VALUE, layoutIds.get(i));
}
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9a851ac..bb596a7 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -327,7 +327,12 @@
if (mPageIndicator == null && mPageIndicatorViewId > -1) {
mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
mPageIndicator.removeAllMarkers();
- mPageIndicator.addMarkers(getChildCount());
+
+ ArrayList<Integer> markers = new ArrayList<Integer>();
+ for (int i = 0; i < getChildCount(); ++i) {
+ markers.add(getPageIndicatorMarker(i));
+ }
+ mPageIndicator.addMarkers(markers);
}
}
@@ -404,6 +409,9 @@
PageIndicator getPageIndicator() {
return mPageIndicator;
}
+ protected int getPageIndicatorMarker(int pageIndex) {
+ return R.layout.page_indicator_marker;
+ }
public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
mPageSwitchListener = pageSwitchListener;
@@ -914,7 +922,8 @@
// Update the page indicator, we don't update the page indicator as we
// add/remove pages
if (mPageIndicator != null && !isReordering(false)) {
- mPageIndicator.addMarker(indexOfChild(child));
+ int pageIndex = indexOfChild(child);
+ mPageIndicator.addMarker(pageIndex, getPageIndicatorMarker(pageIndex));
}
// This ensures that when children are added, they get the correct transforms / alphas
@@ -1001,7 +1010,7 @@
// Check if the right edge of the page is in the viewport
mTmpIntPoint[0] = currPage.getMeasuredWidth();
- DragLayer.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
+ Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
if (mTmpIntPoint[0] < 0) {
break;
}
@@ -1011,7 +1020,7 @@
// Check if the left edge of the page is in the viewport
mTmpIntPoint[0] = 0;
- DragLayer.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
+ Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
if (mTmpIntPoint[0] >= viewportWidth) {
break;
}
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/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0cc29fa..cc22bb5 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -25,6 +25,7 @@
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
@@ -33,6 +34,10 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
import com.android.launcher3.R;
@@ -232,6 +237,96 @@
}
}
+ /**
+ * Given a coordinate relative to the descendant, find the coordinate in a parent view's
+ * coordinates.
+ *
+ * @param descendant The descendant to which the passed coordinate is relative.
+ * @param root The root view to make the coordinates relative to.
+ * @param coord The coordinate that we want mapped.
+ * @param includeRootScroll Whether or not to account for the scroll of the descendant:
+ * sometimes this is relevant as in a child's coordinates within the descendant.
+ * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
+ * this scale factor is assumed to be equal in X and Y, and so if at any point this
+ * assumption fails, we will need to return a pair of scale factors.
+ */
+ public static float getDescendantCoordRelativeToParent(View descendant, View root,
+ int[] coord, boolean includeRootScroll) {
+ ArrayList<View> ancestorChain = new ArrayList<View>();
+
+ float[] pt = {coord[0], coord[1]};
+
+ View v = descendant;
+ while(v != root && v != null) {
+ ancestorChain.add(v);
+ v = (View) v.getParent();
+ }
+ ancestorChain.add(root);
+
+ float scale = 1.0f;
+ int count = ancestorChain.size();
+ for (int i = 0; i < count; i++) {
+ View v0 = ancestorChain.get(i);
+ View v1 = i < count -1 ? ancestorChain.get(i + 1) : null;
+
+ // For TextViews, scroll has a meaning which relates to the text position
+ // which is very strange... ignore the scroll.
+ if (v0 != descendant || includeRootScroll) {
+ pt[0] -= v0.getScrollX();
+ pt[1] -= v0.getScrollY();
+ }
+
+ v0.getMatrix().mapPoints(pt);
+ pt[0] += v0.getLeft();
+ pt[1] += v0.getTop();
+ scale *= v0.getScaleX();
+ }
+
+ coord[0] = (int) Math.round(pt[0]);
+ coord[1] = (int) Math.round(pt[1]);
+ return scale;
+ }
+
+ /**
+ * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
+ */
+ public static float mapCoordInSelfToDescendent(View descendant, View root,
+ int[] coord) {
+ ArrayList<View> ancestorChain = new ArrayList<View>();
+
+ float[] pt = {coord[0], coord[1]};
+
+ View v = descendant;
+ while(v != root) {
+ ancestorChain.add(v);
+ v = (View) v.getParent();
+ }
+ ancestorChain.add(root);
+
+ float scale = 1.0f;
+ Matrix inverse = new Matrix();
+ int count = ancestorChain.size();
+ for (int i = count - 1; i >= 0; i--) {
+ View ancestor = ancestorChain.get(i);
+ View next = i > 0 ? ancestorChain.get(i-1) : null;
+
+ pt[0] += ancestor.getScrollX();
+ pt[1] += ancestor.getScrollY();
+
+ if (next != null) {
+ pt[0] -= next.getLeft();
+ pt[1] -= next.getTop();
+ next.getMatrix().invert(inverse);
+ inverse.mapPoints(pt);
+ scale *= next.getScaleX();
+ }
+ }
+
+ coord[0] = (int) Math.round(pt[0]);
+ coord[1] = (int) Math.round(pt[1]);
+ return scale;
+ }
+
private static void initStatics(Context context) {
final Resources resources = context.getResources();
final DisplayMetrics metrics = resources.getDisplayMetrics();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 91f5396..6989c9a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -528,19 +528,10 @@
return screenId;
}
- public void addCustomContentToLeft(View customContent, CustomContentCallbacks callbacks) {
+ public void createCustomContentPage() {
CellLayout customScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
- int spanX = customScreen.getCountX();
- int spanY = customScreen.getCountY();
-
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
- lp.canReorder = false;
- lp.isFullscreen = true;
-
- customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
-
Rect p = new Rect();
AppWidgetHostView.getDefaultPaddingForWidget(mLauncher, mLauncher.getComponentName(), p);
@@ -549,13 +540,28 @@
addFullScreenPage(customScreen);
- mCustomContentCallbacks = callbacks;
-
// Ensure that the current page and default page are maintained.
mDefaultPage++;
setCurrentPage(getCurrentPage() + 1);
}
+ public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks) {
+ if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
+ throw new RuntimeException("Expected custom content screen to exist");
+ }
+
+ // Add the custom content to the full screen custom page
+ CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
+ int spanX = customScreen.getCountX();
+ int spanY = customScreen.getCountY();
+ CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
+ lp.canReorder = false;
+ lp.isFullscreen = true;
+ customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
+
+ mCustomContentCallbacks = callbacks;
+ }
+
public long commitExtraEmptyScreen() {
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -770,6 +776,13 @@
protected void onWindowVisibilityChanged (int visibility) {
mLauncher.onWindowVisibilityChanged(visibility);
+ if (mCustomContentShowing && mCustomContentCallbacks != null) {
+ if (visibility == View.VISIBLE) {
+ mCustomContentCallbacks.onShow();
+ } else if (visibility == View.GONE) {
+ mCustomContentCallbacks.onHide();
+ }
+ }
}
@Override
@@ -1293,7 +1306,7 @@
}
}
- private boolean hasCustomContent() {
+ public boolean hasCustomContent() {
return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
}
@@ -1312,7 +1325,13 @@
progress = Math.max(0, progress);
setBackgroundAlpha(progress * 0.8f);
- float transY = progress * (getViewportHeight() - getPageIndicator().getTop());
+ float height = getViewportHeight();
+ if (getPageIndicator() != null) {
+ height -= getPageIndicator().getTop();
+ } else if (mLauncher.getHotseat() != null) {
+ height -= mLauncher.getHotseat().getTop();
+ }
+ float transY = progress * height;
if (mLauncher.getHotseat() != null) {
mLauncher.getHotseat().setTranslationY(transY);
@@ -3702,6 +3721,11 @@
setCurrentDropLayout(null);
if (0 <= page && page < getChildCount()) {
+ // Ensure that we are not dragging over to the custom content screen
+ if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
+ return false;
+ }
+
CellLayout layout = (CellLayout) getChildAt(page);
setCurrentDragOverlappingLayout(layout);
@@ -3945,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) {
@@ -4018,6 +4014,14 @@
}
@Override
+ protected int getPageIndicatorMarker(int pageIndex) {
+ if (getScreenIdForPageIndex(pageIndex) == CUSTOM_CONTENT_SCREEN_ID) {
+ return R.layout.now_page_indicator_marker;
+ }
+ return super.getPageIndicatorMarker(pageIndex);
+ }
+
+ @Override
public void syncPages() {
}