Merge "Fix stop point for physics in landscape."
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index d977885..6d0655e 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -120,7 +120,6 @@
     private boolean mAnimateNextZoom;
     private float mNextZoom;
     private float mZoom;
-    private float mPosX;
     private float mVelocity;
     private AAMessage mMessageProc;
 
@@ -128,6 +127,8 @@
     private int mRowsPerPage;
     private boolean mSurrendered;
 
+    private int mRestoreFocusIndex = -1;
+    
     @SuppressWarnings({"UnusedDeclaration"})
     static class Defines {
         public static final int ALLOC_PARAMS = 0;
@@ -306,8 +307,7 @@
                 if (mRollo.mState.iconCount > 0) {
                     if (mLastSelection == SELECTION_ICONS) {
                         int selection = mLastSelectedIcon;
-                        final int firstIcon = Math.round(mPosX) *
-                                mColumnsPerPage;
+                        final int firstIcon = Math.round(mRollo.mScrollPos) * mColumnsPerPage;
                         if (selection < 0 || // No selection
                                 selection < firstIcon || // off the top of the screen
                                 selection >= mRollo.mState.iconCount || // past last icon
@@ -361,8 +361,7 @@
         if (!mArrowNavigation && mRollo.mState.iconCount > 0) {
             // Select the first icon when we gain keyboard focus
             mArrowNavigation = true;
-            mRollo.selectIcon(Math.round(mPosX) * mColumnsPerPage,
-                    SELECTED_FOCUSED);
+            mRollo.selectIcon(Math.round(mRollo.mScrollPos) * mColumnsPerPage, SELECTED_FOCUSED);
             mRollo.mState.save();
         }
     }
@@ -394,16 +393,18 @@
         }
 
         if (iconCount > 0) {
+            final boolean isPortrait = getWidth() < getHeight();
+            
             mArrowNavigation = true;
 
             int currentSelection = mRollo.mState.selectedIconIndex;
-            int currentTopRow = Math.round(mPosX);
+            int currentTopRow = Math.round(mRollo.mScrollPos);
 
             // The column of the current selection, in the range 0..COLUMNS_PER_PAGE_PORTRAIT-1
             final int currentPageCol = currentSelection % mColumnsPerPage;
 
             // The row of the current selection, in the range 0..ROWS_PER_PAGE_PORTRAIT-1
-            final int currentPageRow = (currentSelection - (currentTopRow* mColumnsPerPage))
+            final int currentPageRow = (currentSelection - (currentTopRow * mColumnsPerPage))
                     / mRowsPerPage;
 
             int newSelection = currentSelection;
@@ -411,26 +412,30 @@
             switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_UP:
                 if (mLastSelection == SELECTION_HOME) {
-                    mRollo.setHomeSelected(SELECTED_NONE);
-                    int lastRowCount = iconCount % mColumnsPerPage;
-                    if (lastRowCount == 0) {
-                        lastRowCount = mColumnsPerPage;
-                    }
-                    newSelection = iconCount - lastRowCount + (mColumnsPerPage / 2);
-                    if (newSelection >= iconCount) {
-                        newSelection = iconCount-1;
-                    }
-                    int target = (newSelection / mColumnsPerPage)
-                            - (mRowsPerPage - 1);
-                    if (target < 0) {
-                        target = 0;
-                    }
-                    if (currentTopRow != target) {
-                        mRollo.moveTo(target);
+                    if (isPortrait) {
+                        mRollo.setHomeSelected(SELECTED_NONE);
+                        int lastRowCount = iconCount % mColumnsPerPage;
+                        if (lastRowCount == 0) {
+                            lastRowCount = mColumnsPerPage;
+                        }
+                        newSelection = iconCount - lastRowCount + (mColumnsPerPage / 2);
+                        if (newSelection >= iconCount) {
+                            newSelection = iconCount-1;
+                        }
+                        int target = (newSelection / mColumnsPerPage) - (mRowsPerPage - 1);
+                        if (target < 0) {
+                            target = 0;
+                        }
+                        if (currentTopRow != target) {
+                            mRollo.moveTo(target);
+                        }
                     }
                 } else {
                     if (currentPageRow > 0) {
                         newSelection = currentSelection - mColumnsPerPage;
+                        if (currentTopRow > newSelection / mColumnsPerPage) {
+                            mRollo.moveTo(newSelection / mColumnsPerPage);
+                        }
                     } else if (currentTopRow > 0) {
                         newSelection = currentSelection - mColumnsPerPage;
                         mRollo.moveTo(newSelection / mColumnsPerPage);
@@ -460,10 +465,9 @@
                             newSelection = iconCount - 1;
                         }
                         if (currentPageRow >= mRowsPerPage - 1) {
-                            mRollo.moveTo((newSelection / mColumnsPerPage) -
-                                    mRowsPerPage + 1);
+                            mRollo.moveTo((newSelection / mColumnsPerPage) - mRowsPerPage + 1);
                         }
-                    } else {
+                    } else if (isPortrait) {
                         newSelection = -1;
                         mRollo.setHomeSelected(SELECTED_FOCUSED);
                     }
@@ -476,12 +480,20 @@
                     if (currentPageCol > 0) {
                         newSelection = currentSelection - 1;
                     }
+                } else if (!isPortrait) {
+                    newSelection = ((int) (mRollo.mScrollPos) * mColumnsPerPage) +
+                            (mRowsPerPage / 2 * mColumnsPerPage) + mColumnsPerPage - 1;
+                    mRollo.setHomeSelected(SELECTED_NONE);
                 }
                 handled = true;
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 if (mLastSelection != SELECTION_HOME) {
-                    if ((currentPageCol < mColumnsPerPage - 1) &&
+                    if (!isPortrait && (currentPageCol == mColumnsPerPage - 1 ||
+                            currentSelection == iconCount - 1)) {
+                        newSelection = -1;
+                        mRollo.setHomeSelected(SELECTED_FOCUSED);
+                    } else if ((currentPageCol < mColumnsPerPage - 1) &&
                             (currentSelection < iconCount - 1)) {
                         newSelection = currentSelection + 1;
                     }
@@ -538,7 +550,7 @@
                     mRollo.clearSelectedIcon();
                 } else {
                     mDownIconIndex = mCurrentIconIndex
-                            = mRollo.selectIcon(x, y, mPosX, SELECTED_PRESSED);
+                            = mRollo.selectIcon(x, y, SELECTED_PRESSED);
                     if (mDownIconIndex < 0) {
                         // if nothing was selected, no long press.
                         cancelLongPress();
@@ -567,7 +579,7 @@
                 if (!mStartedScrolling && slop < mSlop) {
                     // don't update anything so when we do start scrolling
                     // below, we get the right delta.
-                    mCurrentIconIndex = mRollo.chooseTappedIcon(x, y, mPosX);
+                    mCurrentIconIndex = mRollo.chooseTappedIcon(x, y);
                     if (mDownIconIndex != mCurrentIconIndex) {
                         // If a different icon is selected, don't allow it to be picked up.
                         // This handles off-axis dragging.
@@ -757,6 +769,13 @@
         if (mRollo != null && reload) {
             mRollo.setApps(list);
         }
+        
+        if (hasFocus() && mRestoreFocusIndex != -1) {
+            mRollo.selectIcon(mRestoreFocusIndex, SELECTED_FOCUSED);
+            mRollo.mState.save();
+            mRestoreFocusIndex = -1;
+        }
+        
         mLocks &= ~LOCK_ICONS_PENDING;
     }
 
@@ -856,7 +875,7 @@
 
     class AAMessage extends RenderScript.RSMessage {
         public void run() {
-            mPosX = ((float)mData[0]) / (1 << 16);
+            mRollo.mScrollPos = ((float)mData[0]) / (1 << 16);
             mVelocity = ((float)mData[1]) / (1 << 16);
             mZoom = ((float)mData[2]) / (1 << 16);
             mZoomDirty = false;
@@ -905,6 +924,8 @@
 
         private Bitmap mSelectionBitmap;
         private Canvas mSelectionCanvas;
+        
+        private float mScrollPos;        
 
         Params mParams;
         State mState;
@@ -923,7 +944,7 @@
 
         private boolean checkClickOK() {
             return (Math.abs(mAllApps.mVelocity) < 0.4f) &&
-                   (Math.abs(mAllApps.mPosX - Math.round(mAllApps.mPosX)) < 0.4f);
+                   (Math.abs(mScrollPos - Math.round(mScrollPos)) < 0.4f);
         }
 
         void pause() {
@@ -1239,8 +1260,10 @@
         }
 
         private void setZoom(float zoom, boolean animate) {
-            mRollo.clearSelectedIcon();
-            mRollo.setHomeSelected(SELECTED_NONE);
+            if (animate) {
+                mRollo.clearSelectedIcon();
+                mRollo.setHomeSelected(SELECTED_NONE);
+            }
             if (zoom > 0.001f) {
                 mRollo.mState.zoomTarget = zoom;
             } else {
@@ -1396,7 +1419,9 @@
             mInvokeMoveTo.execute();
         }
 
-        int chooseTappedIcon(int x, int y, float pos) {
+        int chooseTappedIcon(int x, int y) {
+            float pos = mScrollPos;
+
             // Adjust for scroll position if not zero.
             y += (pos - ((int)pos)) * (mTouchYBorders[1] - mTouchYBorders[0]);
 
@@ -1421,7 +1446,7 @@
                 return -1;
             }
 
-            int index = (((int)pos) * columnsCount) + (row * columnsCount) + col;
+            int index = (((int) pos) * columnsCount) + (row * columnsCount) + col;
 
             if (index >= mState.iconCount) {
                 return -1;
@@ -1435,8 +1460,8 @@
          *
          * @return the index of the icon that was selected.
          */
-        int selectIcon(int x, int y, float pos, int pressed) {
-            final int index = chooseTappedIcon(x, y, pos);
+        int selectIcon(int x, int y, int pressed) {
+            final int index = chooseTappedIcon(x, y);
             selectIcon(index, pressed);
             return index;
         }
@@ -1450,6 +1475,9 @@
         void selectIcon(int index, int pressed) {
             final ArrayList<ApplicationInfo> appsList = mAllApps.mAllAppsList;
             if (appsList == null || index < 0 || index >= appsList.size()) {
+                if (mAllApps != null) {
+                    mAllApps.mRestoreFocusIndex = index;
+                }
                 mState.selectedIconIndex = -1;
                 if (mAllApps.mLastSelection == SELECTION_ICONS) {
                     mAllApps.mLastSelection = SELECTION_NONE;
@@ -1560,7 +1588,7 @@
         Log.d(TAG, "mZoomDirty=" + mZoomDirty);
         Log.d(TAG, "mAnimateNextZoom=" + mAnimateNextZoom);
         Log.d(TAG, "mZoom=" + mZoom);
-        Log.d(TAG, "mPosX=" + mPosX);
+        Log.d(TAG, "mScrollPos=" + mRollo.mScrollPos);
         Log.d(TAG, "mVelocity=" + mVelocity);
         Log.d(TAG, "mMessageProc=" + mMessageProc);
         if (mRollo != null) {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index f7cb749..1f48f5f 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -551,9 +551,9 @@
         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
         mAllAppsGrid.setLauncher(this);
         mAllAppsGrid.setDragController(dragController);
-        ((View)mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
+        ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
         // Manage focusability manually since this thing is always visible
-        ((View)mAllAppsGrid).setFocusable(false); 
+        ((View) mAllAppsGrid).setFocusable(false); 
 
         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
         final Workspace workspace = mWorkspace;
@@ -1707,8 +1707,8 @@
     void showAllApps(boolean animated) {
         mAllAppsGrid.zoom(1.0f, animated);
 
-        ((View)mAllAppsGrid).setFocusable(true);
-        ((View)mAllAppsGrid).requestFocus();
+        ((View) mAllAppsGrid).setFocusable(true);
+        ((View) mAllAppsGrid).requestFocus();
         
         // TODO: fade these two too
         mDeleteZone.setVisibility(View.GONE);
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 6560c5d..c6813c3 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -624,15 +624,21 @@
 
             /**
              * Gets the callbacks object.  If we've been stopped, or if the launcher object
-             * has somehow been garbage collected, return null instead.
+             * has somehow been garbage collected, return null instead.  Pass in the Callbacks
+             * object that was around when the deferred message was scheduled, and if there's
+             * a new Callbacks object around then also return null.  This will save us from
+             * calling onto it with data that will be ignored.
              */
-            Callbacks tryGetCallbacks() {
+            Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
                 synchronized (mLock) {
                     if (mStopped) {
                         return null;
                     }
 
                     final Callbacks callbacks = mCallbacks.get();
+                    if (callbacks != oldCallbacks) {
+                        return null;
+                    }
                     if (callbacks == null) {
                         Log.w(TAG, "no mCallbacks");
                         return null;
@@ -897,8 +903,8 @@
 
                 // Don't use these two variables in any of the callback runnables.
                 // Otherwise we hold a reference to them.
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks == null) {
+                final Callbacks oldCallbacks = mCallbacks.get();
+                if (oldCallbacks == null) {
                     // This launcher has exited and nobody bothered to tell us.  Just bail.
                     Log.w(TAG, "LoaderThread running with no launcher");
                     return;
@@ -908,7 +914,7 @@
                 // Tell the workspace that we're about to start firing items at it
                 mHandler.post(new Runnable() {
                     public void run() {
-                        Callbacks callbacks = tryGetCallbacks();
+                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
                             callbacks.startBinding();
                         }
@@ -921,7 +927,7 @@
                     final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
                     mHandler.post(new Runnable() {
                         public void run() {
-                            Callbacks callbacks = tryGetCallbacks();
+                            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                             if (callbacks != null) {
                                 callbacks.bindItems(mItems, start, start+chunkSize);
                             }
@@ -930,7 +936,7 @@
                 }
                 mHandler.post(new Runnable() {
                     public void run() {
-                        Callbacks callbacks = tryGetCallbacks();
+                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
                             callbacks.bindFolders(mFolders);
                         }
@@ -949,7 +955,7 @@
                 // but since getCurrentScreen() just returns the int, we should be okay.  This
                 // is just a hint for the order, and if it's wrong, we'll be okay.
                 // TODO: instead, we should have that push the current screen into here.
-                final int currentScreen = callbacks.getCurrentWorkspaceScreen();
+                final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
                 N = mAppWidgets.size();
                 // once for the current screen
                 for (int i=0; i<N; i++) {
@@ -957,7 +963,7 @@
                     if (widget.screen == currentScreen) {
                         mHandler.post(new Runnable() {
                             public void run() {
-                                Callbacks callbacks = tryGetCallbacks();
+                                Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                                 if (callbacks != null) {
                                     callbacks.bindAppWidget(widget);
                                 }
@@ -971,7 +977,7 @@
                     if (widget.screen != currentScreen) {
                         mHandler.post(new Runnable() {
                             public void run() {
-                                Callbacks callbacks = tryGetCallbacks();
+                                Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                                 if (callbacks != null) {
                                     callbacks.bindAppWidget(widget);
                                 }
@@ -982,7 +988,7 @@
                 // Tell the workspace that we're done.
                 mHandler.post(new Runnable() {
                     public void run() {
-                        Callbacks callbacks = tryGetCallbacks();
+                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
                             callbacks.finishBindingItems();
                         }
@@ -1006,7 +1012,7 @@
                 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
                 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 
-                final Callbacks callbacks = tryGetCallbacks();
+                final Callbacks callbacks = mCallbacks.get();
                 if (callbacks == null) {
                     return;
                 }
@@ -1042,12 +1048,13 @@
                             = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
                     // We're adding this now, so clear out this so we don't re-send them.
                     mAllAppsList.added = new ArrayList<ApplicationInfo>();
+                    final Callbacks old = mCallbacks.get();
                     mHandler.post(new Runnable() {
                         public void run() {
                             final long t = SystemClock.uptimeMillis();
                             final int count = results.size();
 
-                            Callbacks callbacks = tryGetCallbacks();
+                            Callbacks callbacks = tryGetCallbacks(old);
                             if (callbacks != null) {
                                 callbacks.bindAllApplications(results);
                             }
@@ -1195,14 +1202,14 @@
             } else {
                 info.customIcon = true;
             }
-            info.setIcon(icon);
             break;
         default:
-            info.setIcon(getFallbackIcon());
+            icon = getFallbackIcon();
             info.usingFallbackIcon = true;
             info.customIcon = false;
             break;
         }
+        info.setIcon(icon);
         return info;
     }