Merge "Don't let AllAppsTransitionController intercept when a floating view is open." into ub-launcher3-master
diff --git a/res/layout/folder_page.xml b/res/layout/folder_page.xml
new file mode 100644
index 0000000..084e8fd
--- /dev/null
+++ b/res/layout/folder_page.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.CellLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:hapticFeedbackEnabled="false"
+    android:importantForAccessibility="no"
+    launcher:containerType="folder" />
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 7bef889..f5b5bbf 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -20,5 +20,6 @@
         android:id="@+id/layout"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_gravity="center" />
+        android:layout_gravity="center"
+        launcher:containerType="hotseat" />
 </com.android.launcher3.Hotseat>
diff --git a/res/layout/workspace_screen.xml b/res/layout/workspace_screen.xml
index faf6885..94bdedb 100644
--- a/res/layout/workspace_screen.xml
+++ b/res/layout/workspace_screen.xml
@@ -19,4 +19,5 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:hapticFeedbackEnabled="false" />
+    android:hapticFeedbackEnabled="false"
+    launcher:containerType="workspace" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 32bccb8..1e34c0f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -112,4 +112,12 @@
         <attr name="defaultLayoutId" format="reference" />
     </declare-styleable>
 
+    <declare-styleable name="CellLayout">
+        <attr name="containerType" format="integer">
+            <enum name="workspace" value="0" />
+            <enum name="hotseat" value="1" />
+            <enum name="folder" value="2" />
+        </attr>
+    </declare-styleable>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d5ce786..ccbae58 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -40,6 +40,7 @@
     <color name="all_apps_caret_shadow_color">#22000000</color>
     <color name="all_apps_container_color">#FFF2F2F2</color>
     <color name="all_apps_navbar_color">#28000000</color>
+    <color name="all_apps_light_navbar_color">#AAF2F2F2</color>
 
     <color name="spring_loaded_panel_color">#40FFFFFF</color>
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index b13c20b..bbc8650 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -66,7 +66,7 @@
      * If the app is already in the list, doesn't add it.
      */
     public void add(AppInfo info) {
-        if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {
+        if (!mAppFilter.shouldShowApp(info.componentName)) {
             return;
         }
         if (findActivity(data, info.componentName, info.user)) {
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index e01436d..db8f5dd 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -1,35 +1,10 @@
 package com.android.launcher3;
 
 import android.content.ComponentName;
-import android.text.TextUtils;
-import android.util.Log;
 
-public abstract class AppFilter {
+public class AppFilter {
 
-    private static final boolean DBG = false;
-    private static final String TAG = "AppFilter";
-
-    public abstract boolean shouldShowApp(ComponentName app);
-
-    public static AppFilter loadByName(String className) {
-        if (TextUtils.isEmpty(className)) return null;
-        if (DBG) Log.d(TAG, "Loading AppFilter: " + className);
-        try {
-            Class<?> cls = Class.forName(className);
-            return (AppFilter) cls.newInstance();
-        } catch (ClassNotFoundException e) {
-            Log.e(TAG, "Bad AppFilter class", e);
-            return null;
-        } catch (InstantiationException e) {
-            Log.e(TAG, "Bad AppFilter class", e);
-            return null;
-        } catch (IllegalAccessException e) {
-            Log.e(TAG, "Bad AppFilter class", e);
-            return null;
-        } catch (ClassCastException e) {
-            Log.e(TAG, "Bad AppFilter class", e);
-            return null;
-        }
+    public boolean shouldShowApp(ComponentName app) {
+        return true;
     }
-
 }
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index c45ff7b..54faca3 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -354,19 +354,18 @@
     }
 
     public void snapToWidget(boolean animate) {
-        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         DeviceProfile profile = mLauncher.getDeviceProfile();
-        int newWidth = (int) (mWidgetView.getWidth() * profile.appWidgetScale.x)
-                + 2 * mBackgroundPadding - mWidgetPadding.left - mWidgetPadding.right;
-        int newHeight = (int) (mWidgetView.getHeight() * profile.appWidgetScale.y)
-                + 2 * mBackgroundPadding - mWidgetPadding.top - mWidgetPadding.bottom;
+        float scale = Math.min(profile.appWidgetScale.x, profile.appWidgetScale.y);
 
-        mTmpPt[0] = mWidgetView.getLeft();
-        mTmpPt[1] = mWidgetView.getTop();
-        mDragLayer.getDescendantCoordRelativeToSelf(mCellLayout.getShortcutsAndWidgets(), mTmpPt);
+        mDragLayer.getViewRectRelativeToSelf(mWidgetView, sTmpRect);
 
-        int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPadding.left;
-        int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPadding.top;
+        int newWidth = 2 * mBackgroundPadding
+                + (int) (scale * (sTmpRect.width() - mWidgetPadding.left - mWidgetPadding.right));
+        int newHeight = 2 * mBackgroundPadding
+                + (int) (scale * (sTmpRect.height() - mWidgetPadding.top - mWidgetPadding.bottom));
+
+        int newX = (int) (sTmpRect.left - mBackgroundPadding + scale * mWidgetPadding.left);
+        int newY = (int) (sTmpRect.top - mBackgroundPadding + scale * mWidgetPadding.top);
 
         // We need to make sure the frame's touchable regions lie fully within the bounds of the
         // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
@@ -384,6 +383,7 @@
             mBottomTouchRegionAdjustment = 0;
         }
 
+        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         if (!animate) {
             lp.width = newWidth;
             lp.height = newHeight;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 866d239..c0087c4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -24,6 +24,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -35,6 +36,7 @@
 import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
+import android.support.annotation.IntDef;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -60,6 +62,8 @@
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Thunk;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -145,8 +149,16 @@
     private TimeInterpolator mEaseOutInterpolator;
     private ShortcutAndWidgetContainer mShortcutsAndWidgets;
 
-    private boolean mIsHotseat = false;
-    private float mHotseatScale = 1f;
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({WORKSPACE, HOTSEAT, FOLDER})
+    public @interface ContainerType{}
+    public static final int WORKSPACE = 0;
+    public static final int HOTSEAT = 1;
+    public static final int FOLDER = 2;
+
+    @ContainerType private final int mContainerType;
+
+    private final float mChildScale;
 
     public static final int MODE_SHOW_REORDER_HINT = 0;
     public static final int MODE_DRAG_OVER = 1;
@@ -158,7 +170,7 @@
 
     private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
     private static final int REORDER_ANIMATION_DURATION = 150;
-    @Thunk float mReorderPreviewAnimationMagnitude;
+    @Thunk final float mReorderPreviewAnimationMagnitude;
 
     private ArrayList<View> mIntersectingViews = new ArrayList<View>();
     private Rect mOccupiedRect = new Rect();
@@ -184,6 +196,9 @@
 
     public CellLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
+        mContainerType = a.getInteger(R.styleable.CellLayout_containerType, WORKSPACE);
+        a.recycle();
 
         // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
         // the user where a dragged item will land when dropped.
@@ -207,9 +222,10 @@
         mFolderLeaveBehind.delegateCellX = -1;
         mFolderLeaveBehind.delegateCellY = -1;
 
+        mChildScale = mContainerType == HOTSEAT ? grid.inv.hotseatScale : 1f;
+
         setAlwaysDrawnWithCacheEnabled(false);
         final Resources res = getResources();
-        mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
 
         mBackground = (TransitionDrawable) res.getDrawable(
                 FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? R.drawable.bg_screenpanel
@@ -217,8 +233,7 @@
         mBackground.setCallback(this);
         mBackground.setAlpha((int) (mBackgroundAlpha * 255));
 
-        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE *
-                grid.iconSizePx);
+        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);
 
         // Initialize the data structures used for the drag visualization.
         mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out
@@ -276,7 +291,7 @@
             mDragOutlineAnims[i] = anim;
         }
 
-        mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
+        mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
 
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
@@ -355,10 +370,6 @@
         mShortcutsAndWidgets.buildLayer();
     }
 
-    public float getChildrenScale() {
-        return mIsHotseat ? mHotseatScale : 1.0f;
-    }
-
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
@@ -603,15 +614,8 @@
         return mCountY;
     }
 
-    public void setIsHotseat(boolean isHotseat) {
-        mIsHotseat = isHotseat;
-        mShortcutsAndWidgets.setContainerType(isHotseat
-                ? ShortcutAndWidgetContainer.HOTSEAT
-                : ShortcutAndWidgetContainer.DEFAULT);
-    }
-
-    public boolean isHotseat() {
-        return mIsHotseat;
+    public boolean acceptsWidget() {
+        return mContainerType == WORKSPACE;
     }
 
     public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
@@ -621,11 +625,11 @@
         // Hotseat icons - remove text
         if (child instanceof BubbleTextView) {
             BubbleTextView bubbleChild = (BubbleTextView) child;
-            bubbleChild.setTextVisibility(!mIsHotseat);
+            bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
         }
 
-        child.setScaleX(getChildrenScale());
-        child.setScaleY(getChildrenScale());
+        child.setScaleX(mChildScale);
+        child.setScaleY(mChildScale);
 
         // Generate an id for each view, this assumes we have at most 256x256 cells
         // per workspace screen
@@ -1061,24 +1065,26 @@
                 r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
             }
 
-            Utilities.scaleRectAboutCenter(r, getChildrenScale());
+            Utilities.scaleRectAboutCenter(r, mChildScale);
             mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
             mDragOutlineAnims[mDragOutlineCurrent].animateIn();
 
             if (dragObject.stateAnnouncer != null) {
-                String msg;
-                if (isHotseat()) {
-                    msg = getContext().getString(R.string.move_to_hotseat_position,
-                            Math.max(cellX, cellY) + 1);
-                } else {
-                    msg = getContext().getString(R.string.move_to_empty_cell,
-                            cellY + 1, cellX + 1);
-                }
-                dragObject.stateAnnouncer.announce(msg);
+                dragObject.stateAnnouncer.announce(getItemMoveDescription(cellX, cellY));
             }
         }
     }
 
+    public String getItemMoveDescription(int cellX, int cellY) {
+        if (mContainerType == HOTSEAT) {
+            return getContext().getString(R.string.move_to_hotseat_position,
+                    Math.max(cellX, cellY) + 1);
+        } else {
+            return getContext().getString(R.string.move_to_empty_cell,
+                    cellY + 1, cellX + 1);
+        }
+    }
+
     public void clearDragOutlines() {
         final int oldIndex = mDragOutlineCurrent;
         mDragOutlineAnims[oldIndex].animateOut();
@@ -2011,7 +2017,7 @@
             this.mode = mode;
             initDeltaX = child.getTranslationX();
             initDeltaY = child.getTranslationY();
-            finalScale = getChildrenScale() - 4.0f / child.getWidth();
+            finalScale = mChildScale - 4.0f / child.getWidth();
             initScale = child.getScaleX();
             this.child = child;
         }
@@ -2061,7 +2067,7 @@
                     // We make sure to end only after a full period
                     initDeltaX = 0;
                     initDeltaY = 0;
-                    initScale = getChildrenScale();
+                    initScale = mChildScale;
                     repeating = true;
                 }
             });
@@ -2081,8 +2087,8 @@
             }
 
             a = new LauncherViewPropertyAnimator(child)
-                .scaleX(getChildrenScale())
-                .scaleY(getChildrenScale())
+                .scaleX(mChildScale)
+                .scaleY(mChildScale)
                 .translationX(0)
                 .translationY(0)
                 .setDuration(REORDER_ANIMATION_DURATION);
@@ -2104,7 +2110,7 @@
         long screenId = mLauncher.getWorkspace().getIdForScreen(this);
         int container = Favorites.CONTAINER_DESKTOP;
 
-        if (mLauncher.isHotseatLayout(this)) {
+        if (mContainerType == HOTSEAT) {
             screenId = -1;
             container = Favorites.CONTAINER_HOTSEAT;
         }
@@ -2127,7 +2133,7 @@
                 info.spanY = lp.cellVSpan;
 
                 if (requiresDbUpdate) {
-                    LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId,
+                    LauncherModel.modifyItemInDatabase(getContext(), info, container, screenId,
                             info.cellX, info.cellY, info.spanX, info.spanY);
                 }
             }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index eb1db1c..f79d666 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
@@ -314,19 +315,28 @@
                 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
                 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
 
-        updateFolderCellSize(1f, dm, res, folderBottomPanelSize);
+        updateFolderCellSize(1f, dm, res);
 
-        // Check to see if the icons fit within the available height.  If not, then scale down.
-        float usedHeight = (folderCellHeightPx * inv.numFolderRows) + folderBottomPanelSize;
-        int maxHeight = availableHeightPx - getTotalWorkspacePadding().y - (2 * edgeMarginPx);
-        if (usedHeight > maxHeight) {
-            float scale = maxHeight / usedHeight;
-            updateFolderCellSize(scale, dm, res, folderBottomPanelSize);
+        // Don't let the folder get too close to the edges of the screen.
+        int folderMargin = 4 * edgeMarginPx;
+
+        // Check if the icons fit within the available height.
+        float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
+        int maxHeight = availableHeightPx - getTotalWorkspacePadding().y - folderMargin;
+        float scaleY = maxHeight / usedHeight;
+
+        // Check if the icons fit within the available width.
+        float usedWidth = folderCellWidthPx * inv.numFolderColumns;
+        int maxWidth = availableWidthPx - getTotalWorkspacePadding().x - folderMargin;
+        float scaleX = maxWidth / usedWidth;
+
+        float scale = Math.min(scaleX, scaleY);
+        if (scale < 1f) {
+            updateFolderCellSize(scale, dm, res);
         }
     }
 
-    private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res,
-             int folderBottomPanelSize) {
+    private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) {
         folderChildIconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
         folderChildTextSizePx =
                 (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
@@ -335,11 +345,8 @@
         int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale);
         int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding) * scale);
 
-        // Don't let the folder get too close to the edges of the screen.
-        folderCellWidthPx = Math.min(folderChildIconSizePx + 2 * cellPaddingX,
-                (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
-        folderCellHeightPx = Math.min(folderChildIconSizePx + 2 * cellPaddingY + textHeight,
-                (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows);
+        folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
+        folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
         folderChildDrawablePaddingPx = Math.max(0,
                 (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3);
     }
@@ -620,6 +627,19 @@
                 : Math.max(widthPx, heightPx);
     }
 
+    public int getCellHeight(@ContainerType int containerType) {
+        switch (containerType) {
+            case CellLayout.WORKSPACE:
+                return cellHeightPx;
+            case CellLayout.FOLDER:
+                return folderCellHeightPx;
+            case CellLayout.HOTSEAT:
+                return hotseatCellHeightPx;
+            default:
+                // ??
+                return 0;
+        }
+    }
 
     /**
      * @return the left/right paddings for all containers.
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 5c96dde..3648fb7 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -113,12 +113,11 @@
         super.onFinishInflate();
         DeviceProfile grid = mLauncher.getDeviceProfile();
         mContent = (CellLayout) findViewById(R.id.layout);
-        if (grid.isLandscape && !grid.isLargeTablet) {
-            mContent.setGridSize(1, (int) grid.inv.numHotseatIcons);
+        if (grid.isVerticalBarLayout()) {
+            mContent.setGridSize(1, grid.inv.numHotseatIcons);
         } else {
-            mContent.setGridSize((int) grid.inv.numHotseatIcons, 1);
+            mContent.setGridSize(grid.inv.numHotseatIcons, 1);
         }
-        mContent.setIsHotseat(true);
 
         resetLayout();
     }
@@ -168,7 +167,7 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // We don't want any clicks to go through to the hotseat unless the workspace is in
         // the normal state or an accessible drag is in progress.
-        return mLauncher.getWorkspace().workspaceInModalState() &&
+        return !mLauncher.getWorkspace().workspaceIconsCanBeDragged() &&
                 !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index db72b2f..4b09bf8 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -123,9 +123,8 @@
         mLowResCanvas = new Canvas();
         mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
 
-        mIconProvider = IconProvider.loadByName(context.getString(R.string.icon_provider_class),
-                context);
-
+        mIconProvider = Utilities.getOverrideObject(
+                IconProvider.class, context, R.string.icon_provider_class);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
 
         mActivityBgColor = context.getResources().getColor(R.color.quantum_panel_bg_color);
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 0a273bb..005bbaa 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,13 +1,9 @@
 package com.android.launcher3;
 
-import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 
-import java.lang.reflect.InvocationTargetException;
 import java.util.Locale;
 
 public class IconProvider {
@@ -21,19 +17,6 @@
         updateSystemStateString();
     }
 
-    public static IconProvider loadByName(String className, Context context) {
-        if (TextUtils.isEmpty(className)) return new IconProvider();
-        if (DBG) Log.d(TAG, "Loading IconProvider: " + className);
-        try {
-            Class<?> cls = Class.forName(className);
-            return (IconProvider) cls.getDeclaredConstructor(Context.class).newInstance(context);
-        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
-                | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
-            Log.e(TAG, "Bad IconProvider class", e);
-            return new IconProvider();
-        }
-    }
-
     public void updateSystemStateString() {
         mSystemState = Locale.getDefault().toString();
     }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 38545e2..1b0e5b3 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -85,6 +85,7 @@
      */
     public int numHotseatIcons;
     float hotseatIconSize;
+    public float hotseatScale;
     int defaultLayoutId;
 
     DeviceProfile landscapeProfile;
@@ -117,6 +118,8 @@
         numHotseatIcons = hs;
         hotseatIconSize = his;
         defaultLayoutId = dlId;
+
+        hotseatScale = hotseatIconSize / iconSize;
     }
 
     @TargetApi(23)
@@ -158,6 +161,8 @@
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, dm);
 
+        hotseatScale = hotseatIconSize / iconSize;
+
         Point realSize = new Point();
         display.getRealSize(realSize);
         // The real size never changes. smallSide and largeSide will remain the
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7fce079..accc3cd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -475,25 +475,37 @@
             mWorkspace.getPageIndicator().updateColor(mExtractedColors);
             // It's possible that All Apps is visible when this is run,
             // so always use light status bar in that case.
-            activateLightStatusBar(isAllAppsVisible());
+            activateLightStatusBar(isAllAppsVisible(), false);
         }
     }
 
+    // TODO: use platform flag on API >= 26
+    private static final int SYSTEM_UI_FLAG_LIGHT_NAV_BAR = 0x10;
+
     /**
      * Sets the status bar to be light or not. Light status bar means dark icons.
      * @param activate if true, make sure the status bar is light, otherwise base on wallpaper.
+     * @param changeNavBar make sure the nav bar is light only if this param and {@param activate}
+     *                     is also true.
      */
-    public void activateLightStatusBar(boolean activate) {
+    public void activateLightStatusBar(boolean activate, boolean changeNavBar) {
         boolean lightStatusBar = activate || (FeatureFlags.LIGHT_STATUS_BAR
                 && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
                 ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
         int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
         int newSystemUiFlags = oldSystemUiFlags;
         if (lightStatusBar) {
-            newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR ;
         } else {
             newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
         }
+        if (Utilities.isAtLeastO() && activate) {
+            if (changeNavBar) {
+                newSystemUiFlags |= SYSTEM_UI_FLAG_LIGHT_NAV_BAR;
+            } else {
+                newSystemUiFlags &= ~(SYSTEM_UI_FLAG_LIGHT_NAV_BAR);
+            }
+        }
         if (newSystemUiFlags != oldSystemUiFlags) {
             getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
         }
@@ -2808,6 +2820,7 @@
     }
 
     boolean isHotseatLayout(View layout) {
+        // TODO: Remove this method
         return mHotseat != null && layout != null &&
                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
     }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 5937d78..ca5cd74 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -38,7 +38,6 @@
 
     public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
 
-    private final AppFilter mAppFilter;
     @Thunk final LauncherModel mModel;
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
@@ -96,8 +95,8 @@
         mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
 
-        mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
-        mModel = new LauncherModel(this, mIconCache, mAppFilter);
+        mModel = new LauncherModel(this, mIconCache,
+                Utilities.getOverrideObject(AppFilter.class, sContext, R.string.app_filter_class));
 
         LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
 
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 342479f..6c73762 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -20,29 +20,19 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.support.annotation.IntDef;
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import com.android.launcher3.CellLayout.ContainerType;
 
 public class ShortcutAndWidgetContainer extends ViewGroup {
     static final String TAG = "ShortcutAndWidgetContainer";
 
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({DEFAULT, HOTSEAT, FOLDER})
-    public @interface ContainerType{}
-    public static final int DEFAULT = 0;
-    public static final int HOTSEAT = 1;
-    public static final int FOLDER = 2;
-
-    private int mContainerType = DEFAULT;
-
     // These are temporary variables to prevent having to allocate a new object just to
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private final int[] mTmpCellXY = new int[2];
 
+    @ContainerType private final int mContainerType;
     private final WallpaperManager mWallpaperManager;
 
     private int mCellWidth;
@@ -51,13 +41,13 @@
     private int mCountX;
 
     private Launcher mLauncher;
-
     private boolean mInvertIfRtl = false;
 
-    public ShortcutAndWidgetContainer(Context context) {
+    public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
         super(context);
         mLauncher = Launcher.getLauncher(context);
         mWallpaperManager = WallpaperManager.getInstance(context);
+        mContainerType = containerType;
     }
 
     public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY) {
@@ -105,19 +95,9 @@
         mInvertIfRtl = invert;
     }
 
-    public void setContainerType(@ContainerType int containerType) {
-        mContainerType = containerType;
-    }
-
     int getCellContentHeight() {
-        final DeviceProfile grid = mLauncher.getDeviceProfile();
-        int cellContentHeight = grid.cellHeightPx;
-        if (mContainerType == HOTSEAT) {
-            cellContentHeight = grid.hotseatCellHeightPx;
-        } else if (mContainerType == FOLDER) {
-            cellContentHeight = grid.folderCellHeightPx;
-        }
-        return Math.min(getMeasuredHeight(), cellContentHeight);
+        return Math.min(getMeasuredHeight(),
+                mLauncher.getDeviceProfile().getCellHeight(mContainerType));
     }
 
     public void measureChild(View child) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 416ca8e..197aaf7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -60,6 +60,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Locale;
@@ -655,4 +656,23 @@
         return e.getCause() instanceof TransactionTooLargeException
                 || e.getCause() instanceof DeadObjectException;
     }
+
+    public static <T> T getOverrideObject(Class<T> clazz, Context context, int resId) {
+        String className = context.getString(resId);
+        if (!TextUtils.isEmpty(className)) {
+            try {
+                Class<?> cls = Class.forName(className);
+                return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
+            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                    | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
+                Log.e(TAG, "Bad overriden class", e);
+            }
+        }
+
+        try {
+            return clazz.newInstance();
+        } catch (InstantiationException|IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 10680b4..13bea20 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -100,6 +100,14 @@
         Insettable, DropTargetSource {
     private static final String TAG = "Launcher.Workspace";
 
+    /** The value that {@link #mTransitionProgress} must be greater than for
+     * {@link #transitionStateShouldAllowDrop()} to return true. */
+    private static final float ALLOW_DROP_TRANSITION_PROGRESS = 0.25f;
+
+    /** The value that {@link #mTransitionProgress} must be greater than for
+     * {@link #isFinishedSwitchingState()} ()} to return true. */
+    private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
+
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
 
     private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
@@ -1160,7 +1168,7 @@
     }
 
     private boolean shouldConsumeTouch(View v) {
-        return (workspaceInModalState() || !isFinishedSwitchingState())
+        return !workspaceIconsCanBeDragged()
                 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
     }
 
@@ -1171,7 +1179,8 @@
     /** This differs from isSwitchingState in that we take into account how far the transition
      *  has completed. */
     public boolean isFinishedSwitchingState() {
-        return !mIsSwitchingState || (mTransitionProgress > 0.5f);
+        return !mIsSwitchingState
+                || (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS);
     }
 
     protected void onWindowVisibilityChanged (int visibility) {
@@ -1826,6 +1835,11 @@
         return mState != State.NORMAL;
     }
 
+    /** Returns whether a drag should be allowed to be started from the current workspace state. */
+    public boolean workspaceIconsCanBeDragged() {
+        return mState == State.NORMAL || mState == State.SPRING_LOADED;
+    }
+
     @Thunk void updateChildrenLayersEnabled(boolean force) {
         boolean small = mState == State.OVERVIEW || mIsSwitchingState;
         boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageInTransition();
@@ -2273,8 +2287,8 @@
         return dv;
     }
 
-    public boolean transitionStateShouldAllowDrop() {
-        return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
+    private boolean transitionStateShouldAllowDrop() {
+        return ((!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
                 (mState == State.NORMAL || mState == State.SPRING_LOADED));
     }
 
@@ -2563,10 +2577,19 @@
                         && item.screenId == screenId && item.container == container
                         && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
 
+                // When quickly moving an item, a user may accidentally rearrange their
+                // workspace. So instead we move the icon back safely to its original position.
+                boolean returnToOriginalCellToPreventShuffling = !isFinishedSwitchingState()
+                        && !droppedOnOriginalCellDuringTransition && !dropTargetLayout
+                        .isRegionVacant(mTargetCell[0], mTargetCell[1], spanX, spanY);
                 int[] resultSpan = new int[2];
-                mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
-                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
-                        mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
+                if (returnToOriginalCellToPreventShuffling) {
+                    mTargetCell[0] = mTargetCell[1] = -1;
+                } else {
+                    mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
+                            (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
+                            mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
+                }
 
                 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
 
@@ -2632,7 +2655,9 @@
                     LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
                             lp.cellY, item.spanX, item.spanY);
                 } else {
-                    onNoCellFound(dropTargetLayout);
+                    if (!returnToOriginalCellToPreventShuffling) {
+                        onNoCellFound(dropTargetLayout);
+                    }
 
                     // If we can't find a drop location, we return the item to its original position
                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index c71307d..9a23aa8 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -57,7 +57,7 @@
         int y = id / mCountX;
         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
 
-        if (dragInfo.dragType == DragType.WIDGET && mView.isHotseat()) {
+        if (dragInfo.dragType == DragType.WIDGET && !mView.acceptsWidget()) {
             return INVALID_POSITION;
         }
 
@@ -161,11 +161,7 @@
 
         View child = mView.getChildAt(x, y);
         if (child == null || child == dragInfo.item) {
-            if (mView.isHotseat()) {
-                return mContext.getString(R.string.move_to_hotseat_position, id + 1);
-            } else {
-                return mContext.getString(R.string.move_to_empty_cell, y + 1, x + 1);
-            }
+            return mView.getItemMoveDescription(x, y);
         } else {
             return getDescriptionForDropOver(child, mContext);
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 6650aba..e0d145c 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -484,6 +484,10 @@
             setLayoutParams(mlp);
         } else {
             View navBarBg = findViewById(R.id.nav_bar_bg);
+            if (Utilities.isAtLeastO()) {
+                navBarBg.setBackgroundColor(getResources().getColor(
+                        R.color.all_apps_light_navbar_color));
+            }
             ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
             navBarBgLp.height = insets.bottom;
             navBarBg.setLayoutParams(navBarBgLp);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 8ab4a61..4667806 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -286,7 +286,7 @@
         // Use a light status bar (dark icons) if all apps is behind at least half of the status
         // bar. If the status bar is already light due to wallpaper extraction, keep it that way.
         boolean forceLight = shift <= mStatusBarHeight / 2;
-        mLauncher.activateLightStatusBar(forceLight);
+        mLauncher.activateLightStatusBar(forceLight, forceLight);
     }
 
     /**
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index e71c5e9..8aaeb9e 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -253,11 +253,9 @@
 
     private CellLayout createAndAddNewPage() {
         DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
-        CellLayout page = new CellLayout(getContext());
+        CellLayout page = (CellLayout) mInflater.inflate(R.layout.folder_page, this, false);
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
-        page.getShortcutsAndWidgets().setContainerType(ShortcutAndWidgetContainer.FOLDER);
-        page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         page.setInvertIfRtl(true);
         page.setGridSize(mGridCountX, mGridCountY);
 
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 2926a29..f6a4b84 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -18,13 +18,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.text.TextUtils;
 
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
-
-import java.lang.reflect.InvocationTargetException;
+import com.android.launcher3.Utilities;
 
 /**
  * Factory for creating new drawables.
@@ -37,27 +35,13 @@
     public static DrawableFactory get(Context context) {
         synchronized (LOCK) {
             if (sInstance == null) {
-                context = context.getApplicationContext();
-                sInstance = loadByName(context.getString(R.string.drawable_factory_class), context);
+                sInstance = Utilities.getOverrideObject(DrawableFactory.class,
+                        context.getApplicationContext(), R.string.drawable_factory_class);
             }
             return sInstance;
         }
     }
 
-    public static DrawableFactory loadByName(String className, Context context) {
-        if (!TextUtils.isEmpty(className)) {
-            try {
-                Class<?> cls = Class.forName(className);
-                return (DrawableFactory)
-                        cls.getDeclaredConstructor(Context.class).newInstance(context);
-            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
-                    | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
-                return new DrawableFactory();
-            }
-        }
-        return new DrawableFactory();
-    }
-
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 64043f4..5ad6f0f 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -117,7 +117,7 @@
                 }
             }
 
-            if (mAppFilter != null && !mAppFilter.shouldShowApp(item.componentName)) {
+            if (!mAppFilter.shouldShowApp(item.componentName)) {
                 if (DEBUG) {
                     Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
                             item.componentName));
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 293585d..07f59a5 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -29,11 +29,9 @@
 import android.widget.TextView;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.R;
+import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
@@ -182,14 +180,6 @@
         ensurePreview();
     }
 
-    public int getActualItemWidth() {
-        ItemInfo info = (ItemInfo) getTag();
-        int[] size = getPreviewSize();
-        int cellWidth = mLauncher.getDeviceProfile().cellWidthPx;
-
-        return Math.min(size[0], info.spanX * cellWidth);
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = super.onTouchEvent(ev);