Merge "Fixing issue where play icon disappears after hitting home (Bug 6636269)" into jb-dev
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index a463d73..02789de 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -13,4 +13,8 @@
     <integer name="folder_max_count_x">-1</integer>
     <integer name="folder_max_count_y">-1</integer>
     <integer name="folder_max_num_items">-1</integer>
+
+    <!-- Camera distance for the overscroll effect. We use a higher value here because the 
+         workspace screens run nearly flush to the edge of the screen-->
+    <integer name="config_cameraDistance">14000</integer>
 </resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index 7a6f6d8..97a6e9f 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -12,4 +12,7 @@
     <bool name="config_useDropTargetDownTransition">true</bool>
     <!-- Whether or not to fade the side pages -->
     <bool name="config_workspaceFadeAdjacentScreens">true</bool>
+
+    <!-- Camera distance for the overscroll effect -->
+    <integer name="config_cameraDistance">6500</integer>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index a8d80fe..423a3a9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -67,6 +67,9 @@
     <integer name="config_dragFadeOutAlpha">80</integer>
     <integer name="config_dragFadeOutDuration">250</integer>
 
+    <!-- Camera distance for the overscroll effect -->
+    <integer name="config_cameraDistance">6500</integer>
+
     <!-- Folder max bounds and max number of items. Note: folder_max_count_x * folder_max_count_y
          >= folder_max_num_items. When these are set to -1, they are automatically determined. -->
     <integer name="folder_max_count_x">4</integer>
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 61329ad..a24bfda 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -434,16 +434,6 @@
         }
     }
 
-    /**
-     * This differs from isDataReady as this is the test done if isDataReady is not set.
-     */
-    private boolean testDataReady() {
-        // We only do this test once, and we default to the Applications page, so we only really
-        // have to wait for there to be apps.
-        // TODO: What if one of them is validly empty
-        return !mApps.isEmpty() && !mWidgets.isEmpty();
-    }
-
     /** Restores the page for an item at the specified index */
     void restorePageForIndex(int index) {
         if (index < 0) return;
@@ -515,7 +505,7 @@
     }
 
     void showAllAppsCling() {
-        if (!mHasShownAllAppsCling && isDataReady() && testDataReady()) {
+        if (!mHasShownAllAppsCling && isDataReady()) {
             mHasShownAllAppsCling = true;
             // Calculate the position for the cling punch through
             int[] offset = new int[2];
@@ -534,7 +524,7 @@
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
         if (!isDataReady()) {
-            if (testDataReady()) {
+            if (!mApps.isEmpty() && !mWidgets.isEmpty()) {
                 setDataIsReady();
                 setMeasuredDimension(width, height);
                 onDataReady(width, height);
@@ -561,7 +551,6 @@
 
     public void updatePackages() {
         // Get the list of widgets and shortcuts
-        boolean wasEmpty = mWidgets.isEmpty();
         mWidgets.clear();
         List<AppWidgetProviderInfo> widgets =
             AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
@@ -589,17 +578,9 @@
         mWidgets.addAll(shortcuts);
         Collections.sort(mWidgets,
                 new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager));
-        Log.d(TAG, "6549598 updatePackages mWidgets.size(): " + mWidgets.size() + " wasEmpty: " + wasEmpty);
+        Log.d(TAG, "6549598 updatePackages mWidgets.size(): " + mWidgets.size());
         updatePageCounts();
-
-        if (wasEmpty) {
-            // The next layout pass will trigger data-ready if both widgets and apps are set, so request
-            // a layout to do this test and invalidate the page data when ready.
-            if (testDataReady()) requestLayout();
-        } else {
-            cancelAllTasks();
-            invalidatePageData();
-        }
+        invalidateOnDataChange();
     }
 
     @Override
@@ -660,7 +641,6 @@
     }
 
     private void preloadWidget(final PendingAddWidgetInfo info) {
-        Log.d(TAG, "6557954 Preload widget: " + info.info);
         final AppWidgetProviderInfo pInfo = info.info;
         if (pInfo.configure != null) {
             return;
@@ -670,7 +650,6 @@
         mBindWidgetRunnable = new Runnable() {
             @Override
             public void run() {
-                Log.d(TAG, "    6557954 Preload, bind widget: " + info.info);
                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
                 if (AppWidgetManager.getInstance(mLauncher)
                             .bindAppWidgetIdIfAllowed(mWidgetLoadingId, info.componentName)) {
@@ -686,7 +665,6 @@
                 AppWidgetHostView hostView = mLauncher.
                         getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo);
                 info.boundWidget = hostView;
-                Log.d(TAG, "    6557954 Preload, inflate widget: " + info.info);
                 mWidgetCleanupState = WIDGET_INFLATED;
                 hostView.setVisibility(INVISIBLE);
                 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX,
@@ -711,49 +689,37 @@
         // the widget. This will need to be cleaned up if it turns out no long press occurs.
         if (mCreateWidgetInfo != null) {
             // Just in case the cleanup process wasn't properly executed. This shouldn't happen.
-            Log.d(TAG, "**** 6557954 Previous shortpress not cleaned up, cleaning up now: " + mCreateWidgetInfo.info);
             cleanupWidgetPreloading(false);
         }
         mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
-        Log.d(TAG, "6557954 Short press triggered for view: " + v + ", widget info: " + mCreateWidgetInfo.info);
         preloadWidget(mCreateWidgetInfo);
     }
 
     private void cleanupWidgetPreloading(boolean widgetWasAdded) {
-        Log.d(TAG, "6557954 Cleaning up widget, was added: " + widgetWasAdded);
-        if (mCreateWidgetInfo != null) {
-            Log.d(TAG, "    6557954 Cleaning up widget, widget info: " + mCreateWidgetInfo.info);
-        }
-
         if (!widgetWasAdded) {
             // If the widget was not added, we may need to do further cleanup.
             PendingAddWidgetInfo info = mCreateWidgetInfo;
             mCreateWidgetInfo = null;
 
             if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) {
-                Log.d(TAG, "    6557954 Cleaning up widget, remove preload callbacks");
                 // We never did any preloading, so just remove pending callbacks to do so
                 removeCallbacks(mBindWidgetRunnable);
                 removeCallbacks(mInflateWidgetRunnable);
             } else if (mWidgetCleanupState == WIDGET_BOUND) {
                  // Delete the widget id which was allocated
                 if (mWidgetLoadingId != -1) {
-                    Log.d(TAG, "    6557954 Cleaning up widget, delete widget id");
                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
                 }
 
                 // We never got around to inflating the widget, so remove the callback to do so.
-                Log.d(TAG, "    6557954 Cleaning up widget, remove callbacks");
                 removeCallbacks(mInflateWidgetRunnable);
             } else if (mWidgetCleanupState == WIDGET_INFLATED) {
                 // Delete the widget id which was allocated
                 if (mWidgetLoadingId != -1) {
-                    Log.d(TAG, "    6557954 Cleaning up widget, delete widget id");
                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
                 }
 
                 // The widget was inflated and added to the DragLayer -- remove it.
-                Log.d(TAG, "    6557954 Cleaning up widget, remove inflated widget from draglayer");
                 AppWidgetHostView widget = info.boundWidget;
                 mLauncher.getDragLayer().removeView(widget);
             }
@@ -766,9 +732,7 @@
 
     @Override
     public void cleanUpShortPress(View v) {
-        Log.d(TAG, "6557954 Cleanup shortpress");
         if (!mDraggingWidget) {
-            Log.d(TAG, "    6557954 Cleanup shortpress, cleanup cleanup preloading");
             cleanupWidgetPreloading(false);
         }
     }
@@ -779,15 +743,9 @@
         ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
         PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
 
-        if (createItemInfo instanceof PendingAddWidgetInfo) {
-            PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
-            Log.d(TAG, "6557954 Begin dragging widget, view: " + v + ", widget info: " + createWidgetInfo.info);
-        }
-
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
         if (image.getDrawable() == null) {
-            Log.d(TAG, "    6557954 Begin dragging widget, abort, no drawable set");
             mDraggingWidget = false;
             return false;
         }
@@ -1763,16 +1721,31 @@
     public boolean isAnimating() {
         return false;
     }
+
+    /**
+     * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can
+     * appropriately determine when to invalidate the PagedView page data.  In cases where the data
+     * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
+     * next onMeasure() pass, which will trigger an invalidatePageData() itself.
+     */
+    private void invalidateOnDataChange() {
+        if (!isDataReady()) {
+            // The next layout pass will trigger data-ready if both widgets and apps are set, so
+            // request a layout to trigger the page data when ready.
+            requestLayout();
+        } else {
+            cancelAllTasks();
+            invalidatePageData();
+        }
+    }
+
     @Override
     public void setApps(ArrayList<ApplicationInfo> list) {
         mApps = list;
         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
         Log.d(TAG, "6549598 setApps mApps.size(): " + mApps.size());
         updatePageCounts();
-
-        // The next layout pass will trigger data-ready if both widgets and apps are set, so 
-        // request a layout to do this test and invalidate the page data when ready.
-        if (testDataReady()) requestLayout();
+        invalidateOnDataChange();
     }
     private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
         // We add it in place, in alphabetical order
@@ -1790,7 +1763,7 @@
         addAppsWithoutInvalidate(list);
         Log.d(TAG, "6549598 addApps mApps.size(): " + mApps.size() + " list.size(): " + list.size());
         updatePageCounts();
-        invalidatePageData();
+        invalidateOnDataChange();
         Log.d(TAG, "6549598 addApps mNumAppsPages: " + mNumAppsPages);
     }
     private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
@@ -1820,7 +1793,7 @@
         removeAppsWithoutInvalidate(list);
         Log.d(TAG, "6549598 removeApps mApps.size(): " + mApps.size() + " list.size(): " + list.size());
         updatePageCounts();
-        invalidatePageData();
+        invalidateOnDataChange();
         Log.d(TAG, "6549598 removeApps mNumAppsPages: " + mNumAppsPages);
     }
     @Override
@@ -1832,7 +1805,7 @@
         addAppsWithoutInvalidate(list);
         Log.d(TAG, "6549598 updateApps mApps.size(): " + mApps.size() + " list.size(): " + list.size());
         updatePageCounts();
-        invalidatePageData();
+        invalidateOnDataChange();
         Log.d(TAG, "6549598 updateApps mNumAppsPages: " + mNumAppsPages);
     }
 
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index db65a31..1163f9e 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -100,6 +100,8 @@
     private boolean mSuppressFolderDeletion = false;
     private boolean mItemAddedBackToSelfViaIcon = false;
     FolderEditText mFolderName;
+    private float mFolderIconPivotX;
+    private float mFolderIconPivotY;
 
     private boolean mIsEditingName = false;
     private InputMethodManager mInputMethodManager;
@@ -839,20 +841,24 @@
         int folderPivotY = height / 2 + (centeredTop - top);
         setPivotX(folderPivotX);
         setPivotY(folderPivotY);
-        int folderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() *
+        mFolderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() *
                 (1.0f * folderPivotX / width));
-        int folderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
+        mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
                 (1.0f * folderPivotY / height));
 
-        mFolderIcon.setPivotX(folderIconPivotX);
-        mFolderIcon.setPivotY(folderIconPivotY);
-
         lp.width = width;
         lp.height = height;
         lp.x = left;
         lp.y = top;
     }
 
+    float getPivotXForIconAnimation() {
+        return mFolderIconPivotX;
+    }
+    float getPivotYForIconAnimation() {
+        return mFolderIconPivotY;
+    }
+
     private void setupContentForNumItems(int count) {
         setupContentDimensions(count);
 
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index d643084..4919b57 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -321,12 +321,7 @@
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
         animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
-
-        postDelayed(new Runnable() {
-            public void run() {
-                addItem(destInfo);
-            }
-        }, INITIAL_ITEM_ANIMATION_DURATION);
+        addItem(destInfo);
     }
 
     public void onDragExit(Object dragInfo) {
diff --git a/src/com/android/launcher2/HolographicLinearLayout.java b/src/com/android/launcher2/HolographicLinearLayout.java
index a40c727..0f997d5 100644
--- a/src/com/android/launcher2/HolographicLinearLayout.java
+++ b/src/com/android/launcher2/HolographicLinearLayout.java
@@ -68,6 +68,7 @@
 
     void invalidatePressedFocusedStates() {
         mHolographicHelper.invalidatePressedFocusedStates(mImageView);
+        invalidate();
     }
 
     @Override
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 9d118e7..0c0fdad 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -48,6 +48,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -278,6 +281,10 @@
     // when we scroll to that page on resume.
     private int mNewShortcutAnimatePage = -1;
     private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>();
+    private ImageView mFolderIconImageView;
+    private Bitmap mFolderIconBitmap;
+    private Canvas mFolderIconCanvas;
+    private Rect mRectForFolderAnimation = new Rect();
 
     private BubbleTextView mWaitingForResume;
 
@@ -1226,17 +1233,6 @@
                         // some communication back with the app.
                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
 
-                        // We had to enable the wallpaper visibility when launching apps from all
-                        // apps (so that the transitions would be the same as when launching from
-                        // workspace) so take this time to see if we need to re-disable the
-                        // wallpaper visibility to ensure performance.
-                        mWorkspace.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                disableWallpaperIfInAllApps();
-                            }
-                        });
-
                         observer.removeOnPreDrawListener(this);
                         return true;
                     }
@@ -1880,8 +1876,7 @@
                     (SearchManager) getSystemService(Context.SEARCH_SERVICE);
             ComponentName activityName = searchManager.getGlobalSearchActivity();
             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             if (activityName != null) {
                 intent.setPackage(activityName.getPackageName());
             }
@@ -1889,8 +1884,7 @@
             overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
         } catch (ActivityNotFoundException e) {
             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivitySafely(null, intent, "onClickVoiceButton");
         }
     }
@@ -2028,6 +2022,56 @@
         }
     }
 
+    /**
+     * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
+     * in the DragLayer in the exact absolute location of the original FolderIcon.
+     */
+    private void copyFolderIconToImage(FolderIcon fi) {
+        final int width = fi.getMeasuredWidth();
+        final int height = fi.getMeasuredHeight();
+
+        // Lazy load ImageView, Bitmap and Canvas
+        if (mFolderIconImageView == null) {
+            mFolderIconImageView = new ImageView(this);
+        }
+        if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
+                mFolderIconBitmap.getHeight() != height) {
+            mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            mFolderIconCanvas = new Canvas(mFolderIconBitmap);
+        }
+
+        DragLayer.LayoutParams lp;
+        if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
+            lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
+        } else {
+            lp = new DragLayer.LayoutParams(width, height);
+        }
+
+        mDragLayer.getViewRectRelativeToSelf(fi, mRectForFolderAnimation);
+        lp.customPosition = true;
+        lp.x = mRectForFolderAnimation.left;
+        lp.y = mRectForFolderAnimation.top;
+        lp.width = width;
+        lp.height = height;
+
+        mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        fi.draw(mFolderIconCanvas);
+        mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
+        if (fi.mFolder != null) {
+            mFolderIconImageView.setPivotX(fi.mFolder.getPivotXForIconAnimation());
+            mFolderIconImageView.setPivotY(fi.mFolder.getPivotYForIconAnimation());
+        }
+        // Just in case this image view is still in the drag layer from a previous animation,
+        // we remove it and re-add it.
+        if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
+            mDragLayer.removeView(mFolderIconImageView);
+        }
+        mDragLayer.addView(mFolderIconImageView, lp);
+        if (fi.mFolder != null) {
+            fi.mFolder.bringToFront();
+        }
+    }
+
     private void growAndFadeOutFolderIcon(FolderIcon fi) {
         if (fi == null) return;
         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
@@ -2041,31 +2085,38 @@
             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
         }
 
-        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(fi, alpha, scaleX, scaleY);
+        // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
+        copyFolderIconToImage(fi);
+        fi.setVisibility(View.INVISIBLE);
+
+        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mFolderIconImageView, alpha,
+                scaleX, scaleY);
         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
         oa.start();
     }
 
-    private void shrinkAndFadeInFolderIcon(FolderIcon fi) {
+    private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
         if (fi == null) return;
         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
 
-        FolderInfo info = (FolderInfo) fi.getTag();
-        CellLayout cl = null;
-        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            cl = (CellLayout) fi.getParent().getParent();
-        }
+        final CellLayout cl = (CellLayout) fi.getParent().getParent();
 
-        final CellLayout layout = cl;
-        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(fi, alpha, scaleX, scaleY);
+        // We remove and re-draw the FolderIcon in-case it has changed
+        mDragLayer.removeView(mFolderIconImageView);
+        copyFolderIconToImage(fi);
+        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mFolderIconImageView, alpha,
+                scaleX, scaleY);
         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (layout != null) {
-                    layout.clearFolderLeaveBehind();
+                if (cl != null) {
+                    cl.clearFolderLeaveBehind();
+                    // Remove the ImageView copy of the FolderIcon and make the original visible.
+                    mDragLayer.removeView(mFolderIconImageView);
+                    fi.setVisibility(View.VISIBLE);
                 }
             }
         });
@@ -2083,7 +2134,6 @@
         Folder folder = folderIcon.mFolder;
         FolderInfo info = folder.mInfo;
 
-        growAndFadeOutFolderIcon(folderIcon);
         info.opened = true;
 
         // Just verify that the folder hasn't already been added to the DragLayer.
@@ -2096,6 +2146,7 @@
                     folder.getParent() + ").");
         }
         folder.animateOpen();
+        growAndFadeOutFolderIcon(folderIcon);
     }
 
     public void closeFolder() {
@@ -2604,7 +2655,12 @@
             updateWallpaperVisibility(true);
         } else {
             // When launcher has focus again, disable the wallpaper if we are in AllApps
-            disableWallpaperIfInAllApps();
+            mWorkspace.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    disableWallpaperIfInAllApps();
+                }
+            }, 500);
         }
     }
 
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 11eb3c1..0192630 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -77,7 +77,6 @@
 
     // Y rotation to apply to the workspace screens
     private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
-    private static float CAMERA_DISTANCE = 6500;
 
     private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
     private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
@@ -196,6 +195,7 @@
     private boolean mIsStaticWallpaper;
     private int mWallpaperTravelWidth;
     private int mSpringLoadedPageSpacing;
+    private int mCameraDistance;
 
     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
     private static final int FOLDER_CREATION_TIMEOUT = 0;
@@ -321,6 +321,7 @@
             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
         mSpringLoadedPageSpacing =
                 res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
+        mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
 
         // if the value is manually specified, use that instead
         cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
@@ -1273,7 +1274,7 @@
             setFadeForOverScroll(Math.abs(scrollProgress));
             if (!mOverscrollTransformsSet) {
                 mOverscrollTransformsSet = true;
-                cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
+                cl.setCameraDistance(mDensity * mCameraDistance);
                 cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
                 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
                 cl.setOverscrollTransformsDirty(true);