Merge "Enabling setWallpaper button only if the image was loaded successfully" into ub-launcher3-burnaby
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 9d9fa10..196dc45 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -39,6 +39,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:background="@drawable/quantum_panel_dark"
+            android:scrollbars="vertical"
             android:elevation="15dp"
             android:visibility="gone" />
 
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index b75b3ef..808bddb 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -173,7 +173,7 @@
     private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
     private static final int MAX_NUM_MERGES_PHONE = 2;
 
-    private Context mContext;
+    private Launcher mLauncher;
 
     // The set of apps from the system not including predictions
     private List<AppInfo> mApps = new ArrayList<>();
@@ -200,7 +200,7 @@
     private int mNumPredictedAppsPerRow;
 
     public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) {
-        mContext = context;
+        mLauncher = (Launcher) context;
         mIndexer = new AlphabeticIndexCompat(context);
         mAppNameComparator = new AppNameComparator(context);
         setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow);
@@ -218,7 +218,7 @@
      */
     public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
         // Update the merge algorithm
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (grid.isPhone()) {
             mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f),
                     MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
@@ -381,7 +381,7 @@
 
         // As a special case for some languages (currently only Simplified Chinese), we may need to
         // coalesce sections
-        Locale curLocale = mContext.getResources().getConfiguration().locale;
+        Locale curLocale = mLauncher.getResources().getConfiguration().locale;
         TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
         boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
         if (localeRequiresSectionSorting) {
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index e6bf525..ea7c221 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -75,8 +75,8 @@
         mResizeMode = info.resizeMode;
         mDragLayer = dragLayer;
 
-        mMinHSpan = info.minSpanX;
-        mMinVSpan = info.minSpanY;
+        mMinHSpan = info.getMinSpanX(mLauncher);
+        mMinVSpan = info.getMinSpanY(mLauncher);
 
         setBackgroundResource(R.drawable.widget_resize_shadow);
         setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 31942b3..109be7e 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -80,6 +80,8 @@
     private Rect mBackgroundPadding = new Rect();
     private ScrollPositionState mScrollPosState = new ScrollPositionState();
 
+    private Launcher mLauncher;
+
     public AppsContainerRecyclerView(Context context) {
         this(context, null);
     }
@@ -96,6 +98,7 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr);
 
+        mLauncher = (Launcher) context;
         Resources res = context.getResources();
         int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size);
         mScrollbar = res.getDrawable(R.drawable.apps_list_scrollbar_thumb);
@@ -129,7 +132,7 @@
         mNumAppsPerRow = numAppsPerRow;
         mNumPredictedAppsPerRow = numPredictedAppsPerRow;
 
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
         pool.setMaxRecycledViews(AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 26a7cd7..9ff3bfa 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -199,15 +199,18 @@
     public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         Resources res = context.getResources();
 
+        mLauncher = (Launcher) context;
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+
         mContainerInset = context.getResources().getDimensionPixelSize(
                 R.dimen.apps_container_inset);
         mPredictionBarHeight = grid.allAppsCellHeightPx +
                 2 * res.getDimensionPixelSize(R.dimen.apps_prediction_icon_top_bottom_padding);
-        mLauncher = (Launcher) context;
+
         mLayoutInflater = LayoutInflater.from(context);
+
         mNumAppsPerRow = grid.appsViewNumCols;
         mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
         mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow);
@@ -219,6 +222,7 @@
         mLayoutManager = mAdapter.getLayoutManager();
         mItemDecoration = mAdapter.getItemDecoration();
         mContentMarginStart = mAdapter.getContentMarginStart();
+
         mApps.setAdapter(mAdapter);
     }
 
@@ -410,7 +414,7 @@
     protected void onFixedBoundsUpdated() {
         // Update the number of items in the grid
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
             mNumAppsPerRow = grid.appsViewNumCols;
             mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
@@ -448,7 +452,7 @@
         }
 
         // Update the apps recycler view, inset it by the container inset as well
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int startMargin = grid.isPhone() ? mContentMarginStart : 0;
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
         if (isRtl) {
@@ -565,8 +569,7 @@
 
     @Override
     public float getIntrinsicIconScaleFactor() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         return (float) grid.allAppsIconSizePx / grid.iconSizePx;
     }
 
@@ -760,7 +763,7 @@
      */
     private boolean handleTouchEvent(MotionEvent ev) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int x = (int) ev.getX();
         int y = (int) ev.getY();
 
@@ -883,7 +886,7 @@
      */
     private void showSearchField() {
         // Show the search bar and focus the search
-        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
                 getContext().getResources().getDisplayMetrics());
         mSearchBarContainerView.setVisibility(View.VISIBLE);
         mSearchBarContainerView.setAlpha(0f);
@@ -913,7 +916,7 @@
      */
     private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
         final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
-        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
                 getContext().getResources().getDisplayMetrics());
         if (animated) {
             // Hide the search bar and focus the recycler view
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 580930c..9c1c46b 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -106,6 +106,11 @@
 
         private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
         private Rect mTmpBounds = new Rect();
+        private Launcher mLauncher;
+
+        public GridItemDecoration(Context context) {
+            mLauncher = (Launcher) context;
+        }
 
         @Override
         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
@@ -113,7 +118,7 @@
                 return;
             }
 
-            DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+            DeviceProfile grid = mLauncher.getDeviceProfile();
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
             boolean hasDrawnPredictedAppsDivider = false;
             int childCount = parent.getChildCount();
@@ -294,7 +299,6 @@
     @Thunk Paint mSectionTextPaint;
     @Thunk Paint mPredictedAppsDividerPaint;
 
-
     public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
             PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener,
             View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
@@ -307,7 +311,7 @@
         mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL,
                 false);
         mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
-        mItemDecoration = new GridItemDecoration();
+        mItemDecoration = new GridItemDecoration(context);
         mLayoutInflater = LayoutInflater.from(context);
         mTouchListener = touchListener;
         mIconClickListener = iconClickListener;
@@ -323,7 +327,7 @@
         mSectionTextPaint.setAntiAlias(true);
 
         mPredictedAppsDividerPaint = new Paint();
-        mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1f, res.getDisplayMetrics()));
+        mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
         mPredictedAppsDividerPaint.setColor(0x1E000000);
         mPredictedAppsDividerPaint.setAntiAlias(true);
     }
diff --git a/src/com/android/launcher3/AppsRecyclerViewContainer.java b/src/com/android/launcher3/AppsRecyclerViewContainer.java
index cf4beca..6411bac 100644
--- a/src/com/android/launcher3/AppsRecyclerViewContainer.java
+++ b/src/com/android/launcher3/AppsRecyclerViewContainer.java
@@ -38,8 +38,8 @@
     public AppsRecyclerViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        Launcher launcher = (Launcher) context;
+        DeviceProfile grid = launcher.getDeviceProfile();
 
         mTouchFeedbackView = new ClickShadowView(context);
 
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index dac79a8..20c9314 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -78,7 +78,7 @@
 
     static AutoInstallsLayout get(Context context, String pkg, Resources targetRes,
             AppWidgetHost appWidgetHost, LayoutParserCallback callback) {
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        InvariantDeviceProfile grid = LauncherAppState.getInstance().getInvariantDeviceProfile();
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
@@ -165,7 +165,7 @@
             LayoutParserCallback callback, Resources res,
             int layoutId, String rootTag) {
         this(context, appWidgetHost, callback, res, layoutId, rootTag,
-                LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().hotseatAllAppsRank);
+                LauncherAppState.getInstance().getInvariantDeviceProfile().hotseatAllAppsRank);
     }
 
     public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d530009..edf5021 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -93,7 +93,7 @@
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = ((Launcher) context).getDeviceProfile();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1d211bf..27f1ac6 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -199,8 +199,7 @@
         setClipToPadding(false);
         mLauncher = (Launcher) context;
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
 
         mCellWidth = mCellHeight = -1;
@@ -208,8 +207,8 @@
         mWidthGap = mOriginalWidthGap = 0;
         mHeightGap = mOriginalHeightGap = 0;
         mMaxGap = Integer.MAX_VALUE;
-        mCountX = (int) grid.numColumns;
-        mCountY = (int) grid.numRows;
+        mCountX = (int) grid.inv.numColumns;
+        mCountY = (int) grid.inv.numRows;
         mOccupied = new boolean[mCountX][mCountY];
         mTmpOccupied = new boolean[mCountX][mCountY];
         mPreviousReorderDirection[0] = INVALID_DIRECTION;
@@ -499,8 +498,7 @@
         int previewOffset = FolderRingAnimator.sPreviewSize;
 
         // The folder outer / inner ring image(s)
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         for (int i = 0; i < mFolderOuterRings.size(); i++) {
             FolderRingAnimator fra = mFolderOuterRings.get(i);
 
@@ -841,8 +839,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -2729,18 +2726,18 @@
      * @param height Height in pixels
      * @param result An array of length 2 in which to store the result (may be null).
      */
-    public static int[] rectToCell(int width, int height, int[] result) {
+    public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = launcher.getDeviceProfile();
         Rect padding = grid.getWorkspacePadding(grid.isLandscape ?
                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
 
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
         int parentWidth = grid.calculateCellWidth(grid.widthPx
-                - padding.left - padding.right, (int) grid.numColumns);
+                - padding.left - padding.right, (int) grid.inv.numColumns);
         int parentHeight = grid.calculateCellHeight(grid.heightPx
-                - padding.top - padding.bottom, (int) grid.numRows);
+                - padding.top - padding.bottom, (int) grid.inv.numRows);
         int smallerSize = Math.min(parentWidth, parentHeight);
 
         // Always round up to next largest cell
@@ -2773,7 +2770,7 @@
             info.spanX = info.spanY = 1;
             return;
         }
-        int[] spans = rectToCell(minWidth, minHeight, null);
+        int[] spans = rectToCell(mLauncher, minWidth, minHeight, null);
         info.spanX = spans[0];
         info.spanY = spans[1];
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ad0afd9..8ab58b9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -27,6 +27,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.Surface;
@@ -44,42 +45,14 @@
 import java.util.Collections;
 import java.util.Comparator;
 
-
-class DeviceProfileQuery {
-    DeviceProfile profile;
-    float widthDps;
-    float heightDps;
-    float value;
-    PointF dimens;
-
-    DeviceProfileQuery(DeviceProfile p, float v) {
-        widthDps = p.minWidthDps;
-        heightDps = p.minHeightDps;
-        value = v;
-        dimens = new PointF(widthDps, heightDps);
-        profile = p;
-    }
-}
-
 public class DeviceProfile {
+
     public static interface DeviceProfileCallbacks {
         public void onAvailableSizeChanged(DeviceProfile grid);
     }
 
-    String name;
-    float minWidthDps;
-    float minHeightDps;
-    public int numRows;
-    public int numColumns;
-    public int numFolderRows;
-    public int numFolderColumns;
-    float numHotseatIcons;
-    float iconSize;
-    private float iconTextSize;
+    public final InvariantDeviceProfile inv;
     private int iconDrawablePaddingOriginalPx;
-    private float hotseatIconSize;
-
-    int defaultLayoutId;
 
     boolean isLandscape;
     public boolean isTablet;
@@ -107,11 +80,7 @@
     public int cellWidthPx;
     public int cellHeightPx;
 
-    public int iconSizePx;
-    public int iconTextSizePx;
     int iconDrawablePaddingPx;
-    int allAppsIconSizePx;
-    int allAppsIconTextSizePx;
     int allAppsCellWidthPx;
     int allAppsCellHeightPx;
     int allAppsCellPaddingPx;
@@ -123,7 +92,6 @@
     int hotseatCellHeightPx;
     int hotseatIconSizePx;
     int hotseatBarHeightPx;
-    int hotseatAllAppsRank;
     int allAppsNumRows;
     int allAppsNumCols;
     int appsViewNumCols;
@@ -133,6 +101,11 @@
     int pageIndicatorHeightPx;
     int allAppsButtonVisualSize;
 
+    int iconSizePx;
+    public int iconTextSizePx;
+    int allAppsIconSizePx;
+    int allAppsIconTextSizePx;
+
     float dragViewScale;
 
     int allAppsShortEdgeCount = -1;
@@ -140,46 +113,26 @@
 
     private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
 
-    DeviceProfile(String n, float w, float h,
-            int r, int c, int fr, int fc,
-            float is, float its, float hs, float his, int dlId) {
-        // Ensure that we have an odd number of hotseat items (since we need to place all apps)
-        if (hs % 2 == 0) {
-            throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
-        }
+    public DeviceProfile(Context context, InvariantDeviceProfile inv) {
+        // Determine the dynamic grid properties
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
 
-        name = n;
-        minWidthDps = w;
-        minHeightDps = h;
+        Point realSize = new Point();
+        display.getRealSize(realSize);
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
 
-        numRows = r;
-        numColumns = c;
-        numFolderRows = fr;
-        numFolderColumns = fc;
-
-        iconSize = is;
-        iconTextSize = its;
-        numHotseatIcons = hs;
-        hotseatIconSize = his;
-        defaultLayoutId = dlId;
+        this.inv = inv;
+        init(context, realSize.x, realSize.y, dm.widthPixels, dm.heightPixels);
     }
 
-    DeviceProfile() {
-    }
-
-    DeviceProfile(Context context,
-                  ArrayList<DeviceProfile> profiles,
-                  float minWidth, float minHeight,
-                  int wPx, int hPx,
-                  int awPx, int ahPx,
-                  Resources res) {
+    private void init(Context context, int wPx, int hPx, int awPx, int ahPx) {
+        Resources res = context.getResources();
         DisplayMetrics dm = res.getDisplayMetrics();
-        ArrayList<DeviceProfileQuery> points =
-                new ArrayList<DeviceProfileQuery>();
+
         transposeLayoutWithOrientation =
                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
-        minWidthDps = minWidth;
-        minHeightDps = minHeight;
 
         ComponentName cn = new ComponentName(context.getPackageName(),
                 this.getClass().getName());
@@ -205,56 +158,14 @@
         overviewModeScaleFactor =
                 res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
 
-        // Find the closes profile given the width/height
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, 0f));
-        }
-        DeviceProfile closestProfile = findClosestDeviceProfile(minWidth, minHeight, points);
-
-        // Snap to the closest row count
-        numRows = closestProfile.numRows;
-
-        // Snap to the closest column count
-        numColumns = closestProfile.numColumns;
-
-        numFolderRows = closestProfile.numFolderRows;
-        numFolderColumns = closestProfile.numFolderColumns;
-
-        // Snap to the closest hotseat size
-        numHotseatIcons = closestProfile.numHotseatIcons;
-        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
-
-        // Snap to the closest default layout id
-        defaultLayoutId = closestProfile.defaultLayoutId;
-
-        // Interpolate the icon size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.iconSize));
-        }
-        iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-
-        // AllApps uses the original non-scaled icon size
-        allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
-
-        // Interpolate the icon text size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.iconTextSize));
-        }
-        iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
-        // AllApps uses the original non-scaled icon text size
-        allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm);
 
-        // Interpolate the hotseat icon size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
-        }
-        // Hotseat
-        hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+        // AllApps uses the original non-scaled icon text size
+        allAppsIconTextSizePx = Utilities.pxFromDp(inv.iconTextSize, dm);
+
+        // AllApps uses the original non-scaled icon size
+        allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm);
 
         // If the partner customization apk contains any grid overrides, apply them
         applyPartnerDeviceProfileOverrides(context, dm);
@@ -273,22 +184,7 @@
     private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
         Partner p = Partner.get(ctx.getPackageManager());
         if (p != null) {
-            DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
-            if (partnerDp != null) {
-                if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
-                    numRows = numFolderRows = partnerDp.numRows;
-                    numColumns = numFolderColumns = partnerDp.numColumns;
-                }
-                if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
-                    allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
-                    allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount;
-                }
-                if (partnerDp.iconSize > 0) {
-                    iconSize = partnerDp.iconSize;
-                    // AllApps uses the original non-scaled icon size
-                    allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
-                }
-            }
+            p.applyDeviceProfileOverrides(this);
         }
     }
 
@@ -361,7 +257,7 @@
         float scale = 1f;
         int drawablePadding = iconDrawablePaddingOriginalPx;
         updateIconSize(1f, drawablePadding, resources, dm);
-        float usedHeight = (cellHeightPx * numRows);
+        float usedHeight = (cellHeightPx * inv.numRows);
 
         Rect workspacePadding = getWorkspacePadding();
         int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
@@ -379,10 +275,10 @@
 
     private void updateIconSize(float scale, int drawablePadding, Resources res,
                                 DisplayMetrics dm) {
-        iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
-        iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
+        iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+        iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = drawablePadding;
-        hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
+        hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
 
         // Search Bar
         searchBarSpaceWidthPx = Math.min(widthPx,
@@ -468,74 +364,6 @@
         updateAvailableDimensions(context);
     }
 
-    @Thunk float dist(PointF p0, PointF p1) {
-        return (float) Math.hypot(p1.x - p0.x, p1.y - p0.y);
-    }
-
-    private float weight(PointF a, PointF b,
-                        float pow) {
-        float d = dist(a, b);
-        if (d == 0f) {
-            return Float.POSITIVE_INFINITY;
-        }
-        return (float) (1f / Math.pow(d, pow));
-    }
-
-    /** Returns the closest device profile given the width and height and a list of profiles */
-    private DeviceProfile findClosestDeviceProfile(float width, float height,
-                                                   ArrayList<DeviceProfileQuery> points) {
-        return findClosestDeviceProfiles(width, height, points).get(0).profile;
-    }
-
-    /** Returns the closest device profiles ordered by closeness to the specified width and height */
-    private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
-                                                   ArrayList<DeviceProfileQuery> points) {
-        final PointF xy = new PointF(width, height);
-
-        // Sort the profiles by their closeness to the dimensions
-        ArrayList<DeviceProfileQuery> pointsByNearness = points;
-        Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
-            public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
-                return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
-            }
-        });
-
-        return pointsByNearness;
-    }
-
-    private float invDistWeightedInterpolate(float width, float height,
-                ArrayList<DeviceProfileQuery> points) {
-        float sum = 0;
-        float weights = 0;
-        float pow = 5;
-        float kNearestNeighbors = 3;
-        final PointF xy = new PointF(width, height);
-
-        ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
-                points);
-
-        for (int i = 0; i < pointsByNearness.size(); ++i) {
-            DeviceProfileQuery p = pointsByNearness.get(i);
-            if (i < kNearestNeighbors) {
-                float w = weight(xy, p.dimens, pow);
-                if (w == Float.POSITIVE_INFINITY) {
-                    return p.value;
-                }
-                weights += w;
-            }
-        }
-
-        for (int i = 0; i < pointsByNearness.size(); ++i) {
-            DeviceProfileQuery p = pointsByNearness.get(i);
-            if (i < kNearestNeighbors) {
-                float w = weight(xy, p.dimens, pow);
-                sum += w * p.value / weights;
-            }
-        }
-
-        return sum;
-    }
-
     /** Returns the search bar top offset */
     int getSearchBarTopOffset() {
         if (isTablet() && !isVerticalBarLayout()) {
@@ -571,7 +399,7 @@
                 // XXX: If the icon size changes across orientations, we will have to take
                 //      that into account here too.
                 int gap = (int) ((width - 2 * edgeMarginPx -
-                        (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
+                        (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
                 bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
                         availableWidthPx - (edgeMarginPx + gap),
                         searchBarSpaceHeightPx);
@@ -620,6 +448,7 @@
     Rect getWorkspacePadding() {
         return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
     }
+
     Rect getWorkspacePadding(int orientation) {
         Rect searchBarBounds = getSearchBarBounds(orientation);
         Rect padding = new Rect();
@@ -646,10 +475,10 @@
                         : Math.min(widthPx, heightPx);
                 int paddingTop = searchBarBounds.bottom;
                 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
-                int availableWidth = Math.max(0, width - (int) ((numColumns * cellWidthPx) +
-                        (numColumns * gapScale * cellWidthPx)));
+                int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
+                        (inv.numColumns * gapScale * cellWidthPx)));
                 int availableHeight = Math.max(0, height - paddingTop - paddingBottom
-                        - (int) (2 * numRows * cellHeightPx));
+                        - (int) (2 * inv.numRows * cellHeightPx));
                 padding.set(availableWidth / 2, paddingTop + availableHeight / 2,
                         availableWidth / 2, paddingBottom + availableHeight / 2);
             } else {
@@ -701,10 +530,10 @@
         }
     }
 
-    int calculateCellWidth(int width, int countX) {
+    public static int calculateCellWidth(int width, int countX) {
         return width / countX;
     }
-    int calculateCellHeight(int height, int countY) {
+    public static int calculateCellHeight(int height, int countY) {
         return height / countY;
     }
 
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
deleted file mode 100644
index d22427f..0000000
--- a/src/com/android/launcher3/DynamicGrid.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-
-import java.util.ArrayList;
-
-
-public class DynamicGrid {
-    @SuppressWarnings("unused")
-    private static final String TAG = "DynamicGrid";
-
-    private DeviceProfile mProfile;
-    private float mMinWidth;
-    private float mMinHeight;
-
-    // This is a static that we use for the default icon size on a 4/5-inch phone
-    static float DEFAULT_ICON_SIZE_DP = 60;
-    static float DEFAULT_ICON_SIZE_PX = 0;
-
-    public static float dpiFromPx(int size, DisplayMetrics metrics){
-        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-        return (size / densityRatio);
-    }
-    public static int pxFromDp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                size, metrics));
-    }
-    public static int pxFromSp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                size, metrics));
-    }
-
-    public DynamicGrid(Context context, Resources resources,
-                       int minWidthPx, int minHeightPx,
-                       int widthPx, int heightPx,
-                       int awPx, int ahPx) {
-        DisplayMetrics dm = resources.getDisplayMetrics();
-        ArrayList<DeviceProfile> deviceProfiles =
-                new ArrayList<DeviceProfile>();
-        DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
-        // Our phone profiles include the bar sizes in each orientation
-        deviceProfiles.add(new DeviceProfile("Super Short Stubby",
-                255, 300,  2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Shorter Stubby",
-                255, 400,  3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Short Stubby",
-                275, 420,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Stubby",
-                255, 450,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus S",
-                296, 491.33f,  4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus 4",
-                335, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus 5",
-                359, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Large Phone",
-                406, 694,  5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
-        // The tablet profile is odd in that the landscape orientation
-        // also includes the nav bar on the side
-        deviceProfiles.add(new DeviceProfile("Nexus 7",
-                575, 904,  5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
-        // Larger tablet profiles always have system bars on the top & bottom
-        deviceProfiles.add(new DeviceProfile("Nexus 10",
-                727, 1207,  5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
-        deviceProfiles.add(new DeviceProfile("20-inch Tablet",
-                1527, 2527,  7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
-        mMinWidth = dpiFromPx(minWidthPx, dm);
-        mMinHeight = dpiFromPx(minHeightPx, dm);
-        mProfile = new DeviceProfile(context, deviceProfiles,
-                mMinWidth, mMinHeight,
-                widthPx, heightPx,
-                awPx, ahPx,
-                resources);
-    }
-
-    public DeviceProfile getDeviceProfile() {
-        return mProfile;
-    }
-
-    public String toString() {
-        return "-------- DYNAMIC GRID ------- \n" +
-                "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
-                ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
-                " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
-                ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx +
-                ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
-                ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
-    }
-}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 678ed0f..fe50e3a 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -92,11 +92,13 @@
 
             final int pageIndex = pagedView.indexOfChild(cellLayout);
             final int pageCount = pagedView.getPageCount();
+            Launcher launcher  = (Launcher) v.getContext();
 
             int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
             // Process focus.
-            int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                    iconIndex, pageIndex, pageCount);
+            int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                    countY, matrix, iconIndex, pageIndex, pageCount,
+                    launcher.getDeviceProfile().isLayoutRtl);
             if (newIconIndex == FocusLogic.NOOP) {
                 handleNoopKey(keyCode, v);
                 return consume;
@@ -184,7 +186,8 @@
             return consume;
         }
 
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile profile = ((Launcher) v.getContext()).getDeviceProfile();
+
         if (DEBUG) {
             Log.v(TAG, String.format(
                     "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
@@ -248,8 +251,8 @@
         }
 
         // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                iconIndex, pageIndex, pageCount);
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl);
 
         View newIcon = null;
         if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
@@ -283,8 +286,8 @@
             return consume;
         }
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
+        Launcher launcher = (Launcher) v.getContext();
+        DeviceProfile profile = launcher.getDeviceProfile();
 
         if (DEBUG) {
             Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
@@ -295,9 +298,9 @@
         ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
         CellLayout iconLayout = (CellLayout) parent.getParent();
         final Workspace workspace = (Workspace) iconLayout.getParent();
-        final ViewGroup launcher = (ViewGroup) workspace.getParent();
-        final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
-        final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
+        final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
+        final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar);
+        final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
 
         final int iconIndex = parent.indexOfChild(v);
         final int pageIndex = workspace.indexOfChild(iconLayout);
@@ -331,8 +334,8 @@
         }
 
         // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                iconIndex, pageIndex, pageCount);
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl);
         View newIcon = null;
         switch (newIconIndex) {
             case FocusLogic.NOOP:
@@ -354,8 +357,8 @@
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout,
                         iconLayout.getCountX(), row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
-                        FocusLogic.PIVOT, newPageIndex, pageCount);
+                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
+                            matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl);
                     newIcon = parent.getChildAt(newIconIndex);
                 }
                 break;
@@ -387,8 +390,8 @@
                     workspace.snapToPage(newPageIndex);
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
-                        FocusLogic.PIVOT, newPageIndex, pageCount);
+                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
+                            matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl);
                     newIcon = parent.getChildAt(newIconIndex);
                 }
                 break;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index d0a7ba3..72dc1e9 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -945,8 +945,7 @@
 
         float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
         int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2);
         int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
@@ -1003,8 +1002,7 @@
     }
 
     private int getContentAreaHeight() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ?
                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
         int maxContentAreaHeight = grid.availableHeightPx -
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index b161b1c..8652eef 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -147,8 +147,8 @@
                     "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
                     "is dependent on this");
         }
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+        DeviceProfile grid = launcher.getDeviceProfile();
 
         FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
         icon.setClipToPadding(false);
@@ -217,8 +217,7 @@
                             + Thread.currentThread());
                 }
 
-                LauncherAppState app = LauncherAppState.getInstance();
-                DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+                DeviceProfile grid = launcher.getDeviceProfile();
                 sPreviewSize = grid.folderIconSizePx;
                 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
                 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
@@ -490,8 +489,7 @@
 
     private void computePreviewDrawingParams(int drawableSize, int totalSize) {
         if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
-            LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            DeviceProfile grid = mLauncher.getDeviceProfile();
 
             mIntrinsicIconSize = drawableSize;
             mTotalWidth = totalSize;
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 06ed588..0bd6501 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -80,9 +80,9 @@
         super(context, attrs);
         LauncherAppState app = LauncherAppState.getInstance();
 
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        mMaxCountX = (int) grid.numFolderColumns;
-        mMaxCountY = (int) grid.numFolderRows;
+        InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+        mMaxCountX = (int) profile.numFolderColumns;
+        mMaxCountY = (int) profile.numFolderRows;
 
         mMaxItemsPerPage = mMaxCountX * mMaxCountY;
 
@@ -229,7 +229,7 @@
     }
 
     private CellLayout createAndAddNewPage() {
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
         CellLayout page = new CellLayout(getContext());
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b8337b6..1c1342c 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -54,10 +54,7 @@
                 r.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
         mIsLandscape = context.getResources().getConfiguration().orientation ==
             Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    public void setup(Launcher launcher) {
-        mLauncher = launcher;
+        mLauncher = (Launcher) context;
     }
 
     CellLayout getLayout() {
@@ -108,15 +105,14 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
-        mAllAppsButtonRank = grid.hotseatAllAppsRank;
+        mAllAppsButtonRank = grid.inv.hotseatAllAppsRank;
         mContent = (CellLayout) findViewById(R.id.layout);
         if (grid.isLandscape && !grid.isLargeTablet()) {
-            mContent.setGridSize(1, (int) grid.numHotseatIcons);
+            mContent.setGridSize(1, (int) grid.inv.numHotseatIcons);
         } else {
-            mContent.setGridSize((int) grid.numHotseatIcons, 1);
+            mContent.setGridSize((int) grid.inv.numHotseatIcons, 1);
         }
         mContent.setIsHotseat(true);
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 8b5f747..0c91a71 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -94,7 +94,7 @@
 
     private final Handler mWorkerHandler;
 
-    public IconCache(Context context) {
+    public IconCache(Context context, InvariantDeviceProfile inv) {
         ActivityManager activityManager =
                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
@@ -374,20 +374,6 @@
     }
 
     /**
-     * Empty out the cache that aren't of the correct grid size
-     */
-    public synchronized void flushInvalidIcons(DeviceProfile grid) {
-        Iterator<Entry<ComponentKey, CacheEntry>> it = mCache.entrySet().iterator();
-        while (it.hasNext()) {
-            final CacheEntry e = it.next().getValue();
-            if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
-                    || e.icon.getHeight() < grid.iconSizePx)) {
-                it.remove();
-            }
-        }
-    }
-
-    /**
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
      * @return a request ID that can be used to cancel the request.
      */
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
new file mode 100644
index 0000000..fcd6d60
--- /dev/null
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.android.launcher3.util.Thunk;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class InvariantDeviceProfile {
+    private static final String TAG = "InvariantDeviceProfile";
+
+    // This is a static that we use for the default icon size on a 4/5-inch phone
+    static float DEFAULT_ICON_SIZE_DP = 60;
+
+
+    static ArrayList<InvariantDeviceProfile> sDeviceProfiles =
+            new ArrayList<InvariantDeviceProfile>();
+    static {
+        sDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
+                255, 300,  2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
+                255, 400,  3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
+                275, 420,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
+                255, 450,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
+                296, 491.33f,  4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
+                335, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
+                359, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
+                406, 694,  5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
+        // The tablet profile is odd in that the landscape orientation
+        // also includes the nav bar on the side
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
+                575, 904,  5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
+        // Larger tablet profiles always have system bars on the top & bottom
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
+                727, 1207,  5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
+        sDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
+                1527, 2527,  7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
+    }
+
+    class DeviceProfileQuery {
+        InvariantDeviceProfile profile;
+        float widthDps;
+        float heightDps;
+        float value;
+        PointF dimens;
+
+        DeviceProfileQuery(InvariantDeviceProfile p, float v) {
+            widthDps = p.minWidthDps;
+            heightDps = p.minHeightDps;
+            value = v;
+            dimens = new PointF(widthDps, heightDps);
+            profile = p;
+        }
+    }
+
+    // Profile-defining invariant properties
+    String name;
+    float minWidthDps;
+    float minHeightDps;
+    public int numRows;
+    public int numColumns;
+    public int numFolderRows;
+    public int numFolderColumns;
+    float iconSize;
+    float iconTextSize;
+    float numHotseatIcons;
+    float hotseatIconSize;
+    int defaultLayoutId;
+
+    // Derived invariant properties
+    int hotseatAllAppsRank;
+
+    InvariantDeviceProfile() {
+    }
+
+    InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
+            float is, float its, float hs, float his, int dlId) {
+        // Ensure that we have an odd number of hotseat items (since we need to place all apps)
+        if (hs % 2 == 0) {
+            throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
+        }
+
+        name = n;
+        minWidthDps = w;
+        minHeightDps = h;
+        numRows = r;
+        numColumns = c;
+        numFolderRows = fr;
+        numFolderColumns = fc;
+        iconSize = is;
+        iconTextSize = its;
+        numHotseatIcons = hs;
+        hotseatIconSize = his;
+        defaultLayoutId = dlId;
+    }
+
+    InvariantDeviceProfile(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+
+        Point smallestSize = new Point();
+        Point largestSize = new Point();
+        display.getCurrentSizeRange(smallestSize, largestSize);
+
+        minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
+        minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
+
+        ArrayList<DeviceProfileQuery> points =
+                new ArrayList<DeviceProfileQuery>();
+
+        // Find the closes profile given the width/height
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, 0f));
+        }
+
+        InvariantDeviceProfile closestProfile =
+                findClosestDeviceProfile(minWidthDps, minHeightDps, points);
+
+        // The following properties are inherited directly from the nearest archetypal profile
+        numRows = closestProfile.numRows;
+        numColumns = closestProfile.numColumns;
+        numHotseatIcons = closestProfile.numHotseatIcons;
+        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+        defaultLayoutId = closestProfile.defaultLayoutId;
+        numFolderRows = closestProfile.numFolderRows;
+        numFolderColumns = closestProfile.numFolderColumns;
+
+
+        // The following properties are interpolated based on proximity to nearby archetypal
+        // profiles
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.iconSize));
+        }
+        iconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.iconTextSize));
+        }
+        iconTextSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
+        }
+        hotseatIconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+
+        // If the partner customization apk contains any grid overrides, apply them
+        // Supported overrides: numRows, numColumns, iconSize
+        applyPartnerDeviceProfileOverrides(context, dm);
+    }
+
+    /**
+     * Apply any Partner customization grid overrides.
+     *
+     * Currently we support: all apps row / column count.
+     */
+    private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
+        Partner p = Partner.get(ctx.getPackageManager());
+        if (p != null) {
+            p.applyInvariantDeviceProfileOverrides(this, dm);
+        }
+    }
+
+    @Thunk float dist(PointF p0, PointF p1) {
+        return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
+                (p1.y-p0.y)*(p1.y-p0.y));
+    }
+
+    private float weight(PointF a, PointF b,
+                        float pow) {
+        float d = dist(a, b);
+        if (d == 0f) {
+            return Float.POSITIVE_INFINITY;
+        }
+        return (float) (1f / Math.pow(d, pow));
+    }
+
+    /** Returns the closest device profile given the width and height and a list of profiles */
+    private InvariantDeviceProfile findClosestDeviceProfile(float width, float height,
+                                                   ArrayList<DeviceProfileQuery> points) {
+        return findClosestDeviceProfiles(width, height, points).get(0).profile;
+    }
+
+    /** Returns the closest device profiles ordered by closeness to the specified width and height */
+    private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
+                                                   ArrayList<DeviceProfileQuery> points) {
+        final PointF xy = new PointF(width, height);
+
+        // Sort the profiles by their closeness to the dimensions
+        ArrayList<DeviceProfileQuery> pointsByNearness = points;
+        Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
+            public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
+                return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
+            }
+        });
+
+        return pointsByNearness;
+    }
+
+    private float invDistWeightedInterpolate(float width, float height,
+                ArrayList<DeviceProfileQuery> points) {
+        float sum = 0;
+        float weights = 0;
+        float pow = 5;
+        float kNearestNeighbors = 3;
+        final PointF xy = new PointF(width, height);
+
+        ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
+                points);
+
+        for (int i = 0; i < pointsByNearness.size(); ++i) {
+            DeviceProfileQuery p = pointsByNearness.get(i);
+            if (i < kNearestNeighbors) {
+                float w = weight(xy, p.dimens, pow);
+                if (w == Float.POSITIVE_INFINITY) {
+                    return p.value;
+                }
+                weights += w;
+            }
+        }
+
+        for (int i = 0; i < pointsByNearness.size(); ++i) {
+            DeviceProfileQuery p = pointsByNearness.get(i);
+            if (i < kNearestNeighbors) {
+                float w = weight(xy, p.dimens, pow);
+                sum += w * p.value / weights;
+            }
+        }
+
+        return sum;
+    }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a305d10..6c73739 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -430,8 +430,9 @@
         LauncherAppState app = LauncherAppState.getInstance();
         LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
 
-        // Lazy-initialize the dynamic grid
-        mDeviceProfile = app.initDynamicGrid(this);
+        // Load configuration-specific DeviceProfile
+        mDeviceProfile = new DeviceProfile(this, app.getInvariantDeviceProfile());
+        mDeviceProfile.addCallback(LauncherAppState.getInstance());
 
         // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
@@ -439,7 +440,7 @@
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
-        mIconCache.flushInvalidIcons(mDeviceProfile);
+
         mDragController = new DragController(this);
         mInflater = getLayoutInflater();
         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
@@ -1415,7 +1416,6 @@
         // Setup the hotseat
         mHotseat = (Hotseat) findViewById(R.id.hotseat);
         if (mHotseat != null) {
-            mHotseat.setup(this);
             mHotseat.setOnLongClickListener(this);
         }
 
@@ -1601,31 +1601,21 @@
         }
     }
 
-    static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
-            int minHeight) {
-        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
+    private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) {
+        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null);
         // We want to account for the extra amount of padding that we are adding to the widget
         // to ensure that it gets the full amount of space that it has requested
         int requiredWidth = minWidth + padding.left + padding.right;
         int requiredHeight = minHeight + padding.top + padding.bottom;
-        return CellLayout.rectToCell(requiredWidth, requiredHeight, null);
+        return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null);
     }
 
-    static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
-        return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
+    public int[] getSpanForWidget(AppWidgetProviderInfo info) {
+        return getSpanForWidget(info.provider, info.minWidth, info.minHeight);
     }
 
-    static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
-        return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
-    }
-
-    static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
-        return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
-    }
-
-    static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
-        return getSpanForWidget(context, info.componentName, info.minResizeWidth,
-                info.minResizeHeight);
+    public int[] getMinSpanForWidget(AppWidgetProviderInfo info) {
+        return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight);
     }
 
     /**
@@ -1916,6 +1906,10 @@
         return mSharedPrefs;
     }
 
+    public DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
     public void closeSystemDialogs() {
         getWindow().closeAllPanels();
 
@@ -4021,7 +4015,7 @@
             // Note: This assumes that the id remap broadcast is received before this step.
             // If that is not the case, the id remap will be ignored and user may see the
             // click to setup view.
-            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null);
+            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
             pendingInfo.spanX = item.spanX;
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index f540eb4..93753a2 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -57,7 +57,7 @@
 
     private static LauncherAppState INSTANCE;
 
-    private DynamicGrid mDynamicGrid;
+    private InvariantDeviceProfile mInvariantDeviceProfile;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
 
     public static LauncherAppState getInstance() {
@@ -96,8 +96,9 @@
         // set sIsScreenXLarge and mScreenDensity *before* creating icon cache
         mIsScreenLarge = isScreenLarge(sContext.getResources());
         mScreenDensity = sContext.getResources().getDisplayMetrics().density;
-        mIconCache = new IconCache(sContext);
-        mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
+        mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
+        mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
+        mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache);
 
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
         mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
@@ -172,49 +173,6 @@
         return LauncherFiles.SHARED_PREFERENCES_KEY;
     }
 
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    DeviceProfile initDynamicGrid(Context context) {
-        mDynamicGrid = createDynamicGrid(context, mDynamicGrid);
-        mDynamicGrid.getDeviceProfile().addCallback(this);
-        return mDynamicGrid.getDeviceProfile();
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    static DynamicGrid createDynamicGrid(Context context, DynamicGrid dynamicGrid) {
-        // Determine the dynamic grid properties
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        Display display = wm.getDefaultDisplay();
-
-        Point realSize = new Point();
-        display.getRealSize(realSize);
-        DisplayMetrics dm = new DisplayMetrics();
-        display.getMetrics(dm);
-
-        if (dynamicGrid == null) {
-            Point smallestSize = new Point();
-            Point largestSize = new Point();
-            display.getCurrentSizeRange(smallestSize, largestSize);
-
-            dynamicGrid = new DynamicGrid(context,
-                    context.getResources(),
-                    Math.min(smallestSize.x, smallestSize.y),
-                    Math.min(largestSize.x, largestSize.y),
-                    realSize.x, realSize.y,
-                    dm.widthPixels, dm.heightPixels);
-        }
-
-        // Update the icon size
-        DeviceProfile grid = dynamicGrid.getDeviceProfile();
-        grid.updateFromConfiguration(context, context.getResources(),
-                realSize.x, realSize.y,
-                dm.widthPixels, dm.heightPixels);
-        return dynamicGrid;
-    }
-
-    public DynamicGrid getDynamicGrid() {
-        return mDynamicGrid;
-    }
-
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
@@ -251,6 +209,10 @@
         return result;
     }
 
+    public InvariantDeviceProfile getInvariantDeviceProfile() {
+        return mInvariantDeviceProfile;
+    }
+
     @Override
     public void onAvailableSizeChanged(DeviceProfile grid) {
         Utilities.setIconSize(grid.iconSizePx);
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 954d2d7..71fb2d2 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -152,6 +152,10 @@
         return info;
     }
 
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
+        return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
+    }
+
     @Override
     public void onTouchComplete() {
         if (!mLongPressHelper.hasPerformedLongPress()) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index e19bc95..7ca4fe3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -18,10 +18,11 @@
 public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
 
     public boolean isCustomWidget = false;
-    public int spanX = -1;
-    public int spanY = -1;
-    public int minSpanX = -1;
-    public int minSpanY = -1;
+
+    private int mSpanX = -1;
+    private int mSpanY = -1;
+    private int mMinSpanX = -1;
+    private int mMinSpanY = -1;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
@@ -35,15 +36,6 @@
         p.setDataPosition(0);
         LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p);
         p.recycle();
-
-        int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi);
-        int[] span = Launcher.getSpanForWidget(context, lawpi);
-
-        lawpi.spanX = span[0];
-        lawpi.spanY = span[1];
-        lawpi.minSpanX = minResizeSpan[0];
-        lawpi.minSpanY = minResizeSpan[1];
-
         return lawpi;
     }
 
@@ -60,11 +52,6 @@
         previewImage = widget.getPreviewImage();
         initialLayout = widget.getWidgetLayout();
         resizeMode = widget.getResizeMode();
-
-        spanX = widget.getSpanX();
-        spanY = widget.getSpanY();
-        minSpanX = widget.getMinSpanX();
-        minSpanY = widget.getMinSpanY();
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -87,7 +74,39 @@
         if (isCustomWidget) {
             return "WidgetProviderInfo(" + provider + ")";
         }
-        return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)",
-                provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY);
+        return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s",
+                provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm));
+    }
+
+    public int getSpanX(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mSpanX;
+    }
+
+    public int getSpanY(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mSpanY;
+    }
+
+    public int getMinSpanX(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mMinSpanX;
+    }
+
+    public int getMinSpanY(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mMinSpanY;
+    }
+
+    private void lazyLoadSpans(Launcher launcher) {
+        if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) {
+            int[] minResizeSpan = launcher.getMinSpanForWidget(this);
+            int[] span = launcher.getSpanForWidget(this);
+
+            mSpanX = span[0];
+            mSpanY = span[1];
+            mMinSpanX = minResizeSpan[0];
+            mMinSpanY = minResizeSpan[1];
+        }
     }
  }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 92bbb40..af41012 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -141,13 +141,15 @@
     private final ItemTypeMatcher[] mItemTypeMatchers;
     private final long mUserSerial;
 
-    private IconCache mIconCache;
     private BackupManager mBackupManager;
     private byte[] mBuffer = new byte[512];
     private long mLastBackupRestoreTime;
     private boolean mBackupDataWasUpdated;
 
-    private DeviceProfieData mCurrentProfile;
+    private LauncherAppState mLauncherAppState;
+    private IconCache mIconCache;
+    private DeviceProfieData mDeviceProfileData;
+
     boolean restoreSuccessful;
     int restoredBackupVersion = 1;
 
@@ -197,10 +199,14 @@
 
         Journal in = readJournal(oldState);
         if (!launcherIsReady()) {
+            dataChanged();
             // Perform backup later.
             writeJournal(newState, in);
             return;
         }
+
+        lazyInitAppState(true /* noCreate */);
+
         Log.v(TAG, "lastBackupTime = " + in.t);
         mKeys.clear();
         applyJournal(in);
@@ -296,12 +302,7 @@
         if (!restoreSuccessful) {
             return;
         }
-        if (!initializeIconCache()) {
-            // During restore we do not need an initialized instance of IconCache. We can create
-            // a temporary icon cache here, as the process will be rebooted after restore
-            // is complete.
-            mIconCache = new IconCache(mContext);
-        }
+        lazyInitAppState(false /* noCreate */);
 
         int dataSize = data.size();
         if (mBuffer.length < dataSize) {
@@ -395,19 +396,34 @@
      * @return the current device profile information.
      */
     private DeviceProfieData getDeviceProfieData() {
-        if (mCurrentProfile != null) {
-            return mCurrentProfile;
-        }
-        final Context applicationContext = mContext.getApplicationContext();
-        DeviceProfile profile = LauncherAppState.createDynamicGrid(applicationContext, null)
-                .getDeviceProfile();
+        return mDeviceProfileData;
+    }
 
-        mCurrentProfile = new DeviceProfieData();
-        mCurrentProfile.desktopRows = profile.numRows;
-        mCurrentProfile.desktopCols = profile.numColumns;
-        mCurrentProfile.hotseatCount = profile.numHotseatIcons;
-        mCurrentProfile.allappsRank = profile.hotseatAllAppsRank;
-        return mCurrentProfile;
+    private void lazyInitAppState(boolean noCreate) {
+        if (mLauncherAppState != null) {
+            return;
+        }
+
+        if (noCreate) {
+            mLauncherAppState = LauncherAppState.getInstanceNoCreate();
+        } else {
+            LauncherAppState.setApplicationContext(mContext);
+            mLauncherAppState = LauncherAppState.getInstance();
+        }
+
+        mIconCache = mLauncherAppState.getIconCache();
+        InvariantDeviceProfile profile = mLauncherAppState.getInvariantDeviceProfile();
+
+        mDeviceProfileData = initDeviceProfileData(profile);
+    }
+
+    private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) {
+        DeviceProfieData data = new DeviceProfieData();
+        data.desktopRows = profile.numRows;
+        data.desktopCols = profile.numColumns;
+        data.hotseatCount = profile.numHotseatIcons;
+        data.allappsRank = profile.hotseatAllAppsRank;
+        return data;
     }
 
     /**
@@ -518,11 +534,6 @@
      */
     private void backupIcons(BackupDataOutput data) throws IOException {
         // persist icons that haven't been persisted yet
-        if (!initializeIconCache()) {
-            dataChanged(); // try again later
-            if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup");
-            return;
-        }
         final ContentResolver cr = mContext.getContentResolver();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
         final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
@@ -619,16 +630,9 @@
      */
     private void backupWidgets(BackupDataOutput data) throws IOException {
         // persist static widget info that hasn't been persisted yet
-        final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        if (appState == null || !initializeIconCache()) {
-            Log.w(TAG, "Failed to get icon cache during restore");
-            return;
-        }
         final ContentResolver cr = mContext.getContentResolver();
-        final WidgetPreviewLoader previewLoader = appState.getWidgetCache();
+        final WidgetPreviewLoader previewLoader = mLauncherAppState.getWidgetCache();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
-        final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile();
-        if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx);
         int backupWidgetCount = 0;
 
         String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
@@ -661,8 +665,7 @@
                         if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
                         UserHandleCompat user = UserHandleCompat.myUserHandle();
                         writeRowToBackup(key,
-                                packWidget(dpi, previewLoader,spanX * profile.cellWidthPx,
-                                        mIconCache, provider, user),
+                                packWidget(dpi, previewLoader, mIconCache, provider, user),
                                 data);
                         mKeys.add(key);
                         backupWidgetCount ++;
@@ -969,8 +972,7 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader,
-            int previewWidth, IconCache iconCache,
+    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
             ComponentName provider, UserHandleCompat user) {
         final LauncherAppWidgetProviderInfo info =
                 LauncherModel.getProviderInfo(mContext, provider, user);
@@ -985,12 +987,6 @@
             widget.icon.data = Utilities.flattenBitmap(icon);
             widget.icon.dpi = dpi;
         }
-        if (info.previewImage != 0) {
-            widget.preview = new Resource();
-            Bitmap preview = previewLoader.generateWidgetPreview(info, previewWidth, null);
-            widget.preview.data = Utilities.flattenBitmap(preview);
-            widget.preview.dpi = dpi;
-        }
         return widget;
     }
 
@@ -1136,25 +1132,6 @@
         return wrapper.payload;
     }
 
-    private boolean initializeIconCache() {
-        if (mIconCache != null) {
-            return true;
-        }
-
-        final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        if (appState == null) {
-            if (DEBUG) {
-                Throwable stackTrace = new Throwable();
-                stackTrace.fillInStackTrace();
-                Log.w(TAG, "Failed to get app state during backup/restore", stackTrace);
-            }
-            return false;
-        }
-        mIconCache = appState.getIconCache();
-        return mIconCache != null;
-    }
-
-
     /**
      * @return true if the launcher is in a state to support backup
      */
@@ -1167,9 +1144,8 @@
         }
         cursor.close();
 
-        if (!initializeIconCache()) {
+        if (LauncherAppState.getInstanceNoCreate() == null) {
             // launcher services are unavailable, try again later
-            dataChanged();
             return false;
         }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 1b92602..8d321e6 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -422,9 +422,9 @@
     private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
             int[] xy, int spanX, int spanY) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        final int xCount = (int) grid.numColumns;
-        final int yCount = (int) grid.numRows;
+        InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+        final int xCount = (int) profile.numColumns;
+        final int yCount = (int) profile.numRows;
         boolean[][] occupied = new boolean[xCount][yCount];
         if (occupiedPos != null) {
             for (ItemInfo r : occupiedPos) {
@@ -1663,9 +1663,9 @@
         // check & update map of what's occupied; used to discard overlapping/invalid items
         private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item) {
             LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-            final int countX = (int) grid.numColumns;
-            final int countY = (int) grid.numRows;
+            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            final int countX = (int) profile.numColumns;
+            final int countY = (int) profile.numRows;
 
             long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -1681,10 +1681,10 @@
                 final ItemInfo[][] hotseatItems =
                         occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
-                if (item.screenId >= grid.numHotseatIcons) {
+                if (item.screenId >= profile.numHotseatIcons) {
                     Log.e(TAG, "Error loading shortcut " + item
                             + " into hotseat position " + item.screenId
-                            + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
+                            + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
                             + ")");
                     return false;
                 }
@@ -1702,7 +1702,7 @@
                         return true;
                     }
                 } else {
-                    final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
+                    final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
                     items[(int) item.screenId][0] = item;
                     occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
                     return true;
@@ -1776,9 +1776,9 @@
                     new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
 
             LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-            int countX = (int) grid.numColumns;
-            int countY = (int) grid.numRows;
+            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            int countX = (int) profile.numColumns;
+            int countY = (int) profile.numRows;
 
             if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
                 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
@@ -2190,13 +2190,6 @@
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                                 provider.provider);
 
-                                        if (!customWidget) {
-                                            int[] minSpan =
-                                                    Launcher.getMinSpanForWidget(context, provider);
-                                            appWidgetInfo.minSpanX = minSpan[0];
-                                            appWidgetInfo.minSpanY = minSpan[1];
-                                        }
-
                                         int status = restoreStatus;
                                         if (!wasProviderReady) {
                                             // If provider was not previously ready, update the
@@ -2244,12 +2237,6 @@
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
 
-                                    if (!customWidget) {
-                                        int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
-                                        appWidgetInfo.minSpanX = minSpan[0];
-                                        appWidgetInfo.minSpanY = minSpan[1];
-                                    }
-
                                     container = c.getInt(containerIndex);
                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                         container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -2512,13 +2499,13 @@
          * right) */
         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
             final LauncherAppState app = LauncherAppState.getInstance();
-            final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
             // XXX: review this
             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
                 @Override
                 public int compare(ItemInfo lhs, ItemInfo rhs) {
-                    int cellCountX = (int) grid.numColumns;
-                    int cellCountY = (int) grid.numRows;
+                    int cellCountX = (int) profile.numColumns;
+                    int cellCountY = (int) profile.numRows;
                     int screenOffset = cellCountX * cellCountY;
                     int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
                     long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8fef32d..2751152 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -364,7 +364,7 @@
 
     private DefaultLayoutParser getDefaultLayoutParser() {
         int defaultLayout = LauncherAppState.getInstance()
-                .getDynamicGrid().getDeviceProfile().defaultLayoutId;
+                .getInvariantDeviceProfile().defaultLayoutId;
         return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
@@ -1042,10 +1042,10 @@
                         int curY = 0;
 
                         final LauncherAppState app = LauncherAppState.getInstance();
-                        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-                        final int width = (int) grid.numColumns;
-                        final int height = (int) grid.numRows;
-                        final int hotseatWidth = (int) grid.numHotseatIcons;
+                        final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+                        final int width = (int) profile.numColumns;
+                        final int height = (int) profile.numRows;
+                        final int hotseatWidth = (int) profile.numHotseatIcons;
 
                         final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
 
@@ -1187,7 +1187,7 @@
                             int hotseatX = hotseat.keyAt(idx);
                             ContentValues values = hotseat.valueAt(idx);
 
-                            if (hotseatX == grid.hotseatAllAppsRank) {
+                            if (hotseatX == profile.hotseatAllAppsRank) {
                                 // let's drop this in the next available hole in the hotseat
                                 while (++hotseatX < hotseatWidth) {
                                     if (hotseat.get(hotseatX) == null) {
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 3243754..ce13da6 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -156,8 +156,7 @@
             }
             @Override
             public float getMaterialRevealViewStartFinalRadius() {
-                int allAppsButtonSize = LauncherAppState.getInstance().
-                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
                 return allAppsButtonSize / 2;
             }
             @Override
@@ -456,8 +455,7 @@
             }
             @Override
             float getMaterialRevealViewStartFinalRadius() {
-                int allAppsButtonSize = LauncherAppState.getInstance().
-                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
                 return allAppsButtonSize / 2;
             }
             @Override
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index e191319..c9e8379 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -116,56 +116,70 @@
         return resId != 0 && getResources().getBoolean(resId);
     }
 
-    public DeviceProfile getDeviceProfileOverride(DisplayMetrics dm) {
-        boolean containsProfileOverrides = false;
-
-        DeviceProfile dp = new DeviceProfile();
-
-        // We initialize customizable fields to be invalid
-        dp.numRows = -1;
-        dp.numColumns = -1;
-        dp.allAppsShortEdgeCount = -1;
-        dp.allAppsLongEdgeCount = -1;
+    public void applyInvariantDeviceProfileOverrides(InvariantDeviceProfile inv, DisplayMetrics dm) {
+        int numRows = -1;
+        int numColumns = -1;
+        float iconSize = -1;
 
         try {
             int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS,
                     "integer", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.numRows = getResources().getInteger(resId);
+                numRows = getResources().getInteger(resId);
             }
 
             resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS,
                     "integer", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.numColumns = getResources().getInteger(resId);
-            }
-
-            resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT,
-                    "integer", getPackageName());
-            if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.allAppsShortEdgeCount = getResources().getInteger(resId);
-            }
-
-            resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT,
-                    "integer", getPackageName());
-            if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.allAppsLongEdgeCount = getResources().getInteger(resId);
+                numColumns = getResources().getInteger(resId);
             }
 
             resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP,
                     "dimen", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
                 int px = getResources().getDimensionPixelSize(resId);
-                dp.iconSize = DynamicGrid.dpiFromPx(px, dm);
+                iconSize = Utilities.dpiFromPx(px, dm);
             }
         } catch (Resources.NotFoundException ex) {
             Log.e(TAG, "Invalid Partner grid resource!", ex);
+            return;
         }
-        return containsProfileOverrides ? dp : null;
+
+        if (numRows > 0 && numColumns > 0) {
+            inv.numRows = numRows;
+            inv.numColumns = numColumns;
+        }
+
+        if (iconSize > 0) {
+            inv.iconSize = iconSize;
+        }
+    }
+
+    public void applyDeviceProfileOverrides(DeviceProfile dp) {
+        int allAppsShortEdgeCount = -1;
+        int allAppsLongEdgeCount = -1;
+
+        try {
+            int resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                allAppsShortEdgeCount = getResources().getInteger(resId);
+            }
+
+            resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                allAppsLongEdgeCount = getResources().getInteger(resId);
+            }
+
+        } catch (Resources.NotFoundException ex) {
+            Log.e(TAG, "Invalid Partner grid resource!", ex);
+            return;
+        }
+
+        if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {
+            dp.allAppsShortEdgeCount = allAppsShortEdgeCount;
+            dp.allAppsLongEdgeCount = allAppsLongEdgeCount;
+        }
     }
 }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 179c60a..c8b27ef 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -42,6 +42,7 @@
     private final int mStartState;
     private final Intent mIconLookupIntent;
     private final boolean mDisabledForSafeMode;
+    private Launcher mLauncher;
 
     private Bitmap mIcon;
 
@@ -56,6 +57,8 @@
     public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
             boolean disabledForSafeMode) {
         super(context);
+
+        mLauncher = (Launcher) context;
         mInfo = info;
         mStartState = info.restoreStatus;
         mIconLookupIntent = new Intent().setComponent(info.providerName);
@@ -64,7 +67,7 @@
         mPaint = new TextPaint();
         mPaint.setColor(0xFFFFFFFF);
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
-                getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+                mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.quantum_panel_dark);
         setWillNotDraw(false);
     }
@@ -173,12 +176,12 @@
             return;
         }
 
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (mTopCornerDrawable == null) {
             if (mDrawableSizeChanged) {
                 int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
                         ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
-                int maxSize = LauncherAppState.getInstance().getDynamicGrid()
-                        .getDeviceProfile().iconSizePx + 2 * outset;
+                int maxSize = grid.iconSizePx + 2 * outset;
                 int size = Math.min(maxSize, Math.min(
                         getWidth() - getPaddingLeft() - getPaddingRight(),
                         getHeight() - getPaddingTop() - getPaddingBottom()));
@@ -193,7 +196,6 @@
         } else  {
             // Draw the top corner icon and "Setup" text is possible
             if (mDrawableSizeChanged) {
-                DeviceProfile grid = getDeviceProfile();
                 int iconSize = grid.iconSizePx;
                 int paddingTop = getPaddingTop();
                 int paddingBottom = getPaddingBottom();
@@ -251,8 +253,4 @@
             }
         }
     }
-
-    private DeviceProfile getDeviceProfile() {
-        return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
-    }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 490ed6a..157b48a 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -45,10 +45,13 @@
     private int mCountX;
     private int mCountY;
 
+    private Launcher mLauncher;
+
     private boolean mInvertIfRtl = false;
 
     public ShortcutAndWidgetContainer(Context context) {
         super(context);
+        mLauncher = (Launcher) context;
         mWallpaperManager = WallpaperManager.getInstance(context);
     }
 
@@ -125,22 +128,19 @@
     }
 
     int getCellContentWidth() {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
                 grid.hotseatCellWidthPx: grid.cellWidthPx);
     }
 
     int getCellContentHeight() {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
                 grid.hotseatCellHeightPx : grid.cellHeightPx);
     }
 
     public void measureChild(View child) {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9f47e13..6c4b720 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -46,9 +46,11 @@
 import android.os.Build;
 import android.os.Process;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.Toast;
 
@@ -669,4 +671,17 @@
                 && launchIntent.getExtras() == null
                 && TextUtils.isEmpty(launchIntent.getDataString());
     }
+
+    public static float dpiFromPx(int size, DisplayMetrics metrics){
+        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (size / densityRatio);
+    }
+    public static int pxFromDp(float size, DisplayMetrics metrics) {
+        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                size, metrics));
+    }
+    public static int pxFromSp(float size, DisplayMetrics metrics) {
+        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                size, metrics));
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 5ee1f26..1cf3bc4 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -66,17 +66,19 @@
     private final UserManagerCompat mUserManager;
     private final AppWidgetManagerCompat mManager;
     private final CacheDb mDb;
+    private final InvariantDeviceProfile mDeviceProfile;
 
     private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
     private final Handler mWorkerHandler;
 
-    public WidgetPreviewLoader(Context context, IconCache iconCache) {
+    public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) {
         mContext = context;
         mIconCache = iconCache;
         mManager = AppWidgetManagerCompat.getInstance(context);
         mUserManager = UserManagerCompat.getInstance(context);
         mDb = new CacheDb(context);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
+        mDeviceProfile = inv;
     }
 
     /**
@@ -86,8 +88,8 @@
      * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo}
      * @return a request id which can be used to cancel the request.
      */
-    public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
-            WidgetCell caller) {
+    public PreviewLoadRequest getPreview(final Object o, int previewWidth,
+            int previewHeight, WidgetCell caller) {
         String size = previewWidth + "x" + previewHeight;
         WidgetCacheKey key = getObjectKey(o, size);
 
@@ -329,23 +331,18 @@
         return null;
     }
 
-    private Bitmap generatePreview(Object info, Bitmap recycle, int previewWidth, int previewHeight) {
+    private Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle,
+            int previewWidth, int previewHeight) {
         if (info instanceof LauncherAppWidgetProviderInfo) {
-            return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, previewWidth, recycle);
+            return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info,
+                    previewWidth, recycle, null);
         } else {
-            return generateShortcutPreview(
+            return generateShortcutPreview(launcher,
                     (ResolveInfo) info, previewWidth, previewHeight, recycle);
         }
     }
 
-    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
-            int previewWidth, Bitmap preview) {
-        int maxWidth = Math.min(previewWidth, info.spanX
-                * LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().cellWidthPx);
-        return generateWidgetPreview(info, maxWidth, preview, null);
-    }
-
-    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
+    public Bitmap generateWidgetPreview(Launcher launcher, LauncherAppWidgetProviderInfo info,
             int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
         // Load the preview image if possible
         if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
@@ -362,8 +359,8 @@
         }
 
         final boolean widgetPreviewExists = (drawable != null);
-        final int spanX = info.spanX < 1 ? 1 : info.spanX;
-        final int spanY = info.spanY < 1 ? 1 : info.spanY;
+        final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher);
+        final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher);
 
         int previewWidth;
         int previewHeight;
@@ -413,8 +410,7 @@
         } else {
             final Paint p = new Paint();
             p.setFilterBitmap(true);
-            int appIconSize = LauncherAppState.getInstance().getDynamicGrid()
-                    .getDeviceProfile().iconSizePx;
+            int appIconSize = launcher.getDeviceProfile().iconSizePx;
 
             // draw the spanX x spanY tiles
             final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight());
@@ -455,7 +451,7 @@
     }
 
     private Bitmap generateShortcutPreview(
-            ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
+            Launcher launcher, ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
         final Canvas c = new Canvas();
         if (preview == null) {
             preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
@@ -488,8 +484,8 @@
 
         // Draw the final icon at top left corner.
         // TODO: use top right for RTL
-        int appIconSize = LauncherAppState.getInstance().getDynamicGrid()
-                .getDeviceProfile().iconSizePx;
+        int appIconSize = launcher.getDeviceProfile().iconSizePx;
+
         icon.setAlpha(255);
         icon.setColorFilter(null);
         icon.setBounds(0, 0, appIconSize, appIconSize);
@@ -626,8 +622,10 @@
                 // which would gets re-written next time.
                 mVersions = getPackageVersion(mKey.componentName.getPackageName());
 
+                Launcher launcher = (Launcher) mCaller.getContext();
+
                 // it's not in the db... we need to generate it
-                preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
+                preview = generatePreview(launcher, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
             }
             return preview;
         }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 55742f6..67155d0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -307,13 +307,11 @@
         mLauncher = (Launcher) context;
         mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
         final Resources res = getResources();
-        mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
-                getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
         mFadeInAdjacentScreens = false;
         mWallpaperManager = WallpaperManager.getInstance(context);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.Workspace, defStyle, 0);
         mSpringLoadedShrinkFactor =
@@ -422,7 +420,7 @@
     protected void initWorkspace() {
         mCurrentPage = mDefaultPage;
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         mIconCache = app.getIconCache();
         setWillNotDraw(false);
         setClipChildren(false);
@@ -1901,8 +1899,7 @@
 
     public void onExternalDragStartedWithItem(View v) {
         // Compose a drag bitmap with the view scaled to the icon size
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int iconSize = grid.iconSizePx;
         int bmpWidth = v.getMeasuredWidth();
         int bmpHeight = v.getMeasuredHeight();
@@ -1984,7 +1981,7 @@
 
     int getOverviewModeTranslationY() {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Rect overviewBar = grid.getOverviewModeButtonBarRect();
 
         int availableHeight = getViewportHeight();
@@ -2285,8 +2282,7 @@
         int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
                         - padding.get() / 2);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
@@ -2343,7 +2339,7 @@
 
     public void beginExternalDragShared(View child, DragSource source) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int iconSize = grid.iconSizePx;
 
         // Notify launcher of drag start
@@ -2852,14 +2848,14 @@
      * widthGap/heightGap (right, bottom) */
     static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = launcher.getDeviceProfile();
 
         Display display = launcher.getWindowManager().getDefaultDisplay();
         Point smallestSize = new Point();
         Point largestSize = new Point();
         display.getCurrentSizeRange(smallestSize, largestSize);
-        int countX = (int) grid.numColumns;
-        int countY = (int) grid.numRows;
+        int countX = (int) grid.inv.numColumns;
+        int countY = (int) grid.inv.numRows;
         if (orientation == CellLayout.LANDSCAPE) {
             if (mLandscapeCellLayoutMetrics == null) {
                 Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
@@ -3023,7 +3019,7 @@
        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
 
        LauncherAppState app = LauncherAppState.getInstance();
-       DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+       DeviceProfile grid = mLauncher.getDeviceProfile();
        r = grid.getHotseatRect();
        if (r.contains(mTempPt[0], mTempPt[1])) {
            return true;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 61a64e3..5744531 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -162,7 +162,7 @@
         mWorkspace = workspace;
 
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Resources res = launcher.getResources();
         mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index a84e7df..696eabe 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -23,6 +23,7 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 
@@ -80,8 +81,8 @@
                 keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL);
     }
 
-    public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map,
-            int iconIdx, int pageIndex, int pageCount) {
+    public static int handleKeyEvent(int keyCode, int cntX, int cntY,
+            int [][] map, int iconIdx, int pageIndex, int pageCount, boolean isRtl) {
 
         if (DEBUG) {
             Log.v(TAG, String.format(
@@ -89,23 +90,21 @@
                     cntX, cntY, iconIdx, pageIndex, pageCount));
         }
 
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid()
-                .getDeviceProfile();
         int newIndex = NOOP;
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_LEFT:
                 newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/);
-                if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
+                if (isRtl && newIndex == NOOP && pageIndex > 0) {
                     newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
-                } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+                } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
                     newIndex = NEXT_PAGE_RIGHT_COLUMN;
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/);
-                if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+                if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
                     newIndex = NEXT_PAGE_LEFT_COLUMN;
-                } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
+                } else if (isRtl && newIndex == NOOP && pageIndex > 0) {
                     newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
                 }
                 break;
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index db16998..36cc2b1 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
@@ -39,7 +40,7 @@
     public AppWidgetHostView boundWidget;
     public Bundle bindOptions = null;
 
-    public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
+    public PendingAddWidgetInfo(Launcher launcher, LauncherAppWidgetProviderInfo i, Parcelable data) {
         if (i.isCustomWidget) {
             itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
         } else {
@@ -54,10 +55,10 @@
         previewImage = i.previewImage;
         icon = i.icon;
 
-        spanX = i.spanX;
-        spanY = i.spanY;
-        minSpanX = i.minSpanX;
-        minSpanY = i.minSpanY;
+        spanX = i.getSpanX(launcher);
+        spanY = i.getSpanY(launcher);
+        minSpanX = i.getMinSpanX(launcher);
+        minSpanY = i.getMinSpanY(launcher);
     }
 
     public boolean isCustomWidget() {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index dcaf1f2..3ec1645 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -29,7 +29,9 @@
 import android.widget.TextView;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
@@ -72,6 +74,8 @@
     private WidgetPreviewLoader mWidgetPreviewLoader;
     private PreviewLoadRequest mActiveRequest;
 
+    private Launcher mLauncher;
+
     public WidgetCell(Context context) {
         this(context, null);
     }
@@ -84,8 +88,9 @@
         super(context, attrs, defStyle);
 
         final Resources r = context.getResources();
-        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
+        mLauncher = (Launcher) context;
 
+        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
@@ -93,9 +98,9 @@
     }
 
     private void setContainerWidth() {
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile profile = mLauncher.getDeviceProfile();
         cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
-        mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE);
+        mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE);
     }
 
     @Override
@@ -130,14 +135,14 @@
      */
     public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
             WidgetPreviewLoader loader) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
+        InvariantDeviceProfile profile =
+                LauncherAppState.getInstance().getInvariantDeviceProfile();
         mInfo = info;
         // TODO(hyunyoungs): setup a cache for these labels.
         mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        int hSpan = Math.min(info.spanX, grid.numColumns);
-        int vSpan = Math.min(info.spanY, grid.numRows);
+        int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns);
+        int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows);
         mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         mWidgetPreviewLoader = loader;
     }
@@ -192,8 +197,7 @@
     public int getActualItemWidth() {
         ItemInfo info = (ItemInfo) getTag();
         int[] size = getPreviewSize();
-        int cellWidth = LauncherAppState.getInstance()
-                .getDynamicGrid().getDeviceProfile().cellWidthPx;
+        int cellWidth = mLauncher.getDeviceProfile().cellWidthPx;
 
         return Math.min(size[0], info.spanX * cellWidth);
     }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 5a879fa..05e842e 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -109,7 +109,7 @@
         mView.setLayoutManager(new LinearLayoutManager(getContext()) {
             @Override
             protected int getExtraLayoutSpace(State state) {
-                DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+                DeviceProfile grid = mLauncher.getDeviceProfile();
                 return super.getExtraLayoutSpace(state)
                         + grid.availableHeightPx * PRELOAD_SCREEN_HEIGHT_MULTIPLE;
             }
@@ -230,8 +230,8 @@
             int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]);
 
             int[] previewSizeBeforeScale = new int[1];
-            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
-                    maxWidth, null, previewSizeBeforeScale);
+            preview = getWidgetPreviewLoader().generateWidgetPreview(mLauncher,
+                    createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
 
             if (previewSizeBeforeScale[0] < icon.getWidth()) {
                 // The icon has extra padding around it.
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 918ec1b..7439a44 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -32,11 +32,12 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DynamicGrid;
+import com.android.launcher3.IconCache;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetsModel;
@@ -56,7 +57,6 @@
     private static final String TAG = "WidgetsListAdapter";
     private static final boolean DEBUG = true;
 
-    private Context mContext;
     private Launcher mLauncher;
     private LayoutInflater mLayoutInflater;
 
@@ -74,7 +74,6 @@
             View.OnLongClickListener iconLongClickListener,
             Launcher launcher) {
         mLayoutInflater = LayoutInflater.from(context);
-        mContext = context;
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
@@ -141,7 +140,7 @@
             WidgetCell widget = (WidgetCell) row.getChildAt(i);
             if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
                 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
-                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
+                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(mLauncher, info, null);
                 widget.setTag(pawi);
                 widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader);
             } else if (infoList.get(i) instanceof ResolveInfo) {
@@ -206,10 +205,10 @@
     }
 
     private void setContainerHeight() {
-        Resources r = mContext.getResources();
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        Resources r = mLauncher.getResources();
+        DeviceProfile profile = mLauncher.getDeviceProfile();
         if (profile.isLargeTablet || profile.isTablet) {
-            mIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
+            mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
         }
     }
 }
diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java
index 86431d9..0732fe8 100644
--- a/util/com/android/launcher3/DecoderRing.java
+++ b/util/com/android/launcher3/DecoderRing.java
@@ -220,9 +220,6 @@
                     if (widget.icon != null) {
                         writeImageData(widget.icon.data, prefix + "_icon.png");
                     }
-                    if (widget.preview != null) {
-                        writeImageData(widget.preview.data, prefix + "_preview.png");
-                    }
                 }
             }
         }