Merge "Mergin motorola patch to avoind 0 id for all apps button" into ub-now-master
diff --git a/res/drawable-xxhdpi/apps_customize_bg.png b/res/drawable-xxhdpi/apps_customize_bg.png
new file mode 100644
index 0000000..a51cc11
--- /dev/null
+++ b/res/drawable-xxhdpi/apps_customize_bg.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/quantum_panel.9.png b/res/drawable-xxxhdpi/quantum_panel.9.png
deleted file mode 100644
index a15aab4..0000000
--- a/res/drawable-xxxhdpi/quantum_panel.9.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index 19db373..007c536 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -15,75 +15,40 @@
 -->
 <com.android.launcher3.AppsCustomizeTabHost
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    android:background="#80FFFFFF">
+    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+
     <LinearLayout
-        android:id="@+id/apps_customize_content"
-        android:orientation="vertical"
+        android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:visibility="gone">
-        <!-- The layout_width of the tab bar gets overriden to align the content
-             with the text in the tabs in AppsCustomizeTabHost. -->
-        <FrameLayout
-            android:id="@+id/tabs_container"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/apps_customize_tab_bar_height"
-            android:layout_marginTop="@dimen/apps_customize_tab_bar_margin_top"
-            android:layout_gravity="center_horizontal"
-            android:visibility="gone">
-            <com.android.launcher3.FocusOnlyTabWidget
-                android:id="@android:id/tabs"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:gravity="start"
-                android:background="@drawable/tab_unselected_holo"
-                android:tabStripEnabled="false"
-                android:divider="@null" />
-            <include
-                android:id="@+id/market_button"
-                layout="@layout/market_button"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_gravity="end" />
-        </FrameLayout>
-        <FrameLayout
-            android:id="@android:id/tabcontent"
+        android:clipChildren="false"
+        android:orientation="vertical">
+
+        <com.android.launcher3.AppsCustomizePagedView
+            android:id="@+id/apps_customize_pane_content"
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="vertical">
-
-                <com.android.launcher3.AppsCustomizePagedView
-                    android:id="@+id/apps_customize_pane_content"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
-                    launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
-                    launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
-                    launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"
-                    launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"
-                    launcher:maxGap="@dimen/workspace_max_gap"
-                    launcher:pageIndicator="@+id/apps_customize_page_indicator" />
-
-                <include
-                    android:id="@+id/apps_customize_page_indicator"
-                    layout="@layout/page_indicator"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center" />
-            </LinearLayout>
-
-            <FrameLayout
-                android:id="@+id/animation_buffer"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="#FF000000"
-                android:visibility="gone" />
-        </FrameLayout>
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
+            launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
+            launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"
+            launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"
+            launcher:maxGap="@dimen/workspace_max_gap"
+            launcher:pageIndicator="@+id/apps_customize_page_indicator" />
+        <include
+            android:id="@+id/apps_customize_page_indicator"
+            layout="@layout/page_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center" />
     </LinearLayout>
+
+    <include
+        android:id="@+id/market_button"
+        layout="@layout/market_button"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="end"
+        android:visibility="gone"/>
+
 </com.android.launcher3.AppsCustomizeTabHost>
diff --git a/res/values/config.xml b/res/values/config.xml
index b512ffe..3a862c5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -22,7 +22,7 @@
 
 <!-- AllApps/Customize/AppsCustomize -->
     <!-- The alpha of the AppsCustomize bg in spring loaded mode -->
-    <integer name="config_appsCustomizeSpringLoadedBgAlpha">65</integer>
+    <integer name="config_workspaceScrimAlpha">55</integer>
     <integer name="config_workspaceUnshrinkTime">300</integer>
     <integer name="config_overviewTransitionTime">250</integer>
 
@@ -31,6 +31,7 @@
 
     <!-- Fade/zoom in/out duration & scale in the AllApps transition.
          Note: This should be less than the workspaceShrinkTime as they happen together. -->
+    <integer name="config_appsCustomizeRevealTime">350</integer>
     <integer name="config_appsCustomizeZoomInTime">350</integer>
     <integer name="config_appsCustomizeZoomOutTime">600</integer>
     <integer name="config_appsCustomizeZoomScaleFactor">7</integer>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3bf6c8d..7c5ee9c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,6 +24,9 @@
     <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
     <string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
 
+    <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent -->
+    <string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
+
     <!-- Application name -->
     <string name="application_name">Launcher3</string>
     <!-- Accessibility-facing application name -->
@@ -34,6 +37,8 @@
     <string name="folder_name"></string>
     <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
     <string name="activity_not_found">App isn\'t installed.</string>
+    <!-- SafeMode shortcut error string -->
+    <string name="safemode_shortcut_error">Downloaded app disabled in Safe mode</string>
     <!--  Labels for the tabs in the customize drawer -->
     <string name="widgets_tab_label">Widgets</string>
 
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 2520b8a..0e99696 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -377,8 +377,7 @@
         int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
         mWidgetSpacingLayout.measure(widthSpec, heightSpec);
 
-        AppsCustomizeTabHost host = (AppsCustomizeTabHost) getTabHost();
-        final boolean hostIsTransitioning = host.isTransitioning();
+        final boolean hostIsTransitioning = getTabHost().isInTransition();
 
         // Restore the page
         int page = getPageForComponent(mSaveInstanceStateItemIndex);
@@ -1617,12 +1616,8 @@
         // If we have reset, then we should not continue to restore the previous state
         mSaveInstanceStateItemIndex = -1;
 
-        AppsCustomizeTabHost tabHost = getTabHost();
-        String tag = tabHost.getCurrentTabTag();
-        if (tag != null) {
-            if (!tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) {
-                tabHost.setCurrentTabFromContent(ContentType.Applications);
-            }
+        if (mContentType != ContentType.Applications) {
+            setContentType(ContentType.Applications);
         }
 
         if (mCurrentPage != 0) {
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index c6455c2..334d8b6 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -38,35 +38,20 @@
 
 import java.util.ArrayList;
 
-public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable,
-        TabHost.OnTabChangeListener, Insettable  {
+public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable  {
     static final String LOG_TAG = "AppsCustomizeTabHost";
 
     private static final String APPS_TAB_TAG = "APPS";
     private static final String WIDGETS_TAB_TAG = "WIDGETS";
 
-    private final LayoutInflater mLayoutInflater;
-    private ViewGroup mTabs;
-    private ViewGroup mTabsContainer;
-    private AppsCustomizePagedView mAppsCustomizePane;
-    private FrameLayout mAnimationBuffer;
-    private LinearLayout mContent;
+    private AppsCustomizePagedView mPagedView;
+    private View mContent;
+    private boolean mInTransition = false;
 
-    private boolean mInTransition;
-    private boolean mTransitioningToWorkspace;
-    private boolean mResetAfterTransition;
-    private Runnable mRelayoutAndMakeVisible;
     private final Rect mInsets = new Rect();
 
     public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLayoutInflater = LayoutInflater.from(context);
-        mRelayoutAndMakeVisible = new Runnable() {
-                public void run() {
-                    mTabs.requestLayout();
-                    mTabsContainer.setAlpha(1f);
-                }
-            };
     }
 
     /**
@@ -76,17 +61,17 @@
      * tabs manually).
      */
     void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) {
-        setOnTabChangedListener(null);
-        onTabChangedStart();
-        onTabChangedEnd(type);
-        setCurrentTabByTag(getTabTagForContentType(type));
-        setOnTabChangedListener(this);
+        mPagedView.setContentType(type);
+    }
+
+    public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
+        setContentTypeImmediate(type);
     }
 
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
-        FrameLayout.LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
+        LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
         flp.topMargin = insets.top;
         flp.bottomMargin = insets.bottom;
         flp.leftMargin = insets.left;
@@ -99,212 +84,12 @@
      */
     @Override
     protected void onFinishInflate() {
-        // Setup the tab host
-        setup();
-
-        final ViewGroup tabsContainer = (ViewGroup) findViewById(R.id.tabs_container);
-        final TabWidget tabs = getTabWidget();
-        final AppsCustomizePagedView appsCustomizePane = (AppsCustomizePagedView)
-                findViewById(R.id.apps_customize_pane_content);
-        mTabs = tabs;
-        mTabsContainer = tabsContainer;
-        mAppsCustomizePane = appsCustomizePane;
-        mAnimationBuffer = (FrameLayout) findViewById(R.id.animation_buffer);
-        mContent = (LinearLayout) findViewById(R.id.apps_customize_content);
-        if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException();
-
-        // Configure the tabs content factory to return the same paged view (that we change the
-        // content filter on)
-        TabContentFactory contentFactory = new TabContentFactory() {
-            public View createTabContent(String tag) {
-                return appsCustomizePane;
-            }
-        };
-
-        // Create the tabs
-        TextView tabView;
-        String label;
-        label = getContext().getString(R.string.all_apps_button_label);
-        tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
-        tabView.setText(label);
-        tabView.setContentDescription(label);
-        addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
-        label = getContext().getString(R.string.widgets_tab_label);
-        tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
-        tabView.setText(label);
-        tabView.setContentDescription(label);
-        addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
-        setOnTabChangedListener(this);
-
-        // Setup the key listener to jump between the last tab view and the market icon
-        AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener();
-        View lastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1);
-        lastTab.setOnKeyListener(keyListener);
-        View shopButton = findViewById(R.id.market_button);
-        shopButton.setOnKeyListener(keyListener);
-
-        // Hide the tab bar until we measure
-        mTabsContainer.setAlpha(0f);
+        mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content);
+        mContent = findViewById(R.id.content);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        boolean remeasureTabWidth = (mTabs.getLayoutParams().width <= 0);
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        // Set the width of the tab list to the content width
-        if (remeasureTabWidth) {
-            int contentWidth = mAppsCustomizePane.getPageContentWidth();
-            if (contentWidth > 0 && mTabs.getLayoutParams().width != contentWidth) {
-                // Set the width and show the tab bar
-                mTabs.getLayoutParams().width = contentWidth;
-                mRelayoutAndMakeVisible.run();
-            }
-
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-     public boolean onInterceptTouchEvent(MotionEvent ev) {
-         // If we are mid transitioning to the workspace, then intercept touch events here so we
-         // can ignore them, otherwise we just let all apps handle the touch events.
-         if (mInTransition && mTransitioningToWorkspace) {
-             return true;
-         }
-         return super.onInterceptTouchEvent(ev);
-     };
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // Allow touch events to fall through to the workspace if we are transitioning there
-        if (mInTransition && mTransitioningToWorkspace) {
-            return super.onTouchEvent(event);
-        }
-
-        // Intercept all touch events up to the bottom of the AppsCustomizePane so they do not fall
-        // through to the workspace and trigger showWorkspace()
-        if (event.getY() < mAppsCustomizePane.getBottom()) {
-            return true;
-        }
-        return super.onTouchEvent(event);
-    }
-
-    private void onTabChangedStart() {
-    }
-
-    private void reloadCurrentPage() {
-        mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
-        mAppsCustomizePane.requestFocus();
-    }
-
-    private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) {
-        int bgAlpha = (int) (255 * (getResources().getInteger(
-            R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f));
-        setBackgroundColor(Color.argb(bgAlpha, 0, 0, 0));
-        mAppsCustomizePane.setContentType(type);
-    }
-
-    @Override
-    public void onTabChanged(String tabId) {
-        final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId);
-
-        // Animate the changing of the tab content by fading pages in and out
-        final Resources res = getResources();
-        final int duration = res.getInteger(R.integer.config_tabTransitionDuration);
-
-        // We post a runnable here because there is a delay while the first page is loading and
-        // the feedback from having changed the tab almost feels better than having it stick
-        post(new Runnable() {
-            @Override
-            public void run() {
-                if (mAppsCustomizePane.getMeasuredWidth() <= 0 ||
-                        mAppsCustomizePane.getMeasuredHeight() <= 0) {
-                    reloadCurrentPage();
-                    return;
-                }
-
-                // Take the visible pages and re-parent them temporarily to mAnimatorBuffer
-                // and then cross fade to the new pages
-                int[] visiblePageRange = new int[2];
-                mAppsCustomizePane.getVisiblePages(visiblePageRange);
-                if (visiblePageRange[0] == -1 && visiblePageRange[1] == -1) {
-                    // If we can't get the visible page ranges, then just skip the animation
-                    reloadCurrentPage();
-                    return;
-                }
-                ArrayList<View> visiblePages = new ArrayList<View>();
-                for (int i = visiblePageRange[0]; i <= visiblePageRange[1]; i++) {
-                    visiblePages.add(mAppsCustomizePane.getPageAt(i));
-                }
-
-                // We want the pages to be rendered in exactly the same way as they were when
-                // their parent was mAppsCustomizePane -- so set the scroll on mAnimationBuffer
-                // to be exactly the same as mAppsCustomizePane, and below, set the left/top
-                // parameters to be correct for each of the pages
-                mAnimationBuffer.scrollTo(mAppsCustomizePane.getScrollX(), 0);
-
-                // mAppsCustomizePane renders its children in reverse order, so
-                // add the pages to mAnimationBuffer in reverse order to match that behavior
-                for (int i = visiblePages.size() - 1; i >= 0; i--) {
-                    View child = visiblePages.get(i);
-                    if (child instanceof AppsCustomizeCellLayout) {
-                        ((AppsCustomizeCellLayout) child).resetChildrenOnKeyListeners();
-                    } else if (child instanceof PagedViewGridLayout) {
-                        ((PagedViewGridLayout) child).resetChildrenOnKeyListeners();
-                    }
-                    PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(false);
-                    mAppsCustomizePane.removeView(child);
-                    PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(true);
-                    mAnimationBuffer.setAlpha(1f);
-                    mAnimationBuffer.setVisibility(View.VISIBLE);
-                    LayoutParams p = new FrameLayout.LayoutParams(child.getMeasuredWidth(),
-                            child.getMeasuredHeight());
-                    p.setMargins((int) child.getLeft(), (int) child.getTop(), 0, 0);
-                    mAnimationBuffer.addView(child, p);
-                }
-
-                // Toggle the new content
-                onTabChangedStart();
-                onTabChangedEnd(type);
-
-                // Animate the transition
-                ObjectAnimator outAnim = LauncherAnimUtils.ofFloat(mAnimationBuffer, "alpha", 0f);
-                outAnim.addListener(new AnimatorListenerAdapter() {
-                    private void clearAnimationBuffer() {
-                        mAnimationBuffer.setVisibility(View.GONE);
-                        PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(false);
-                        mAnimationBuffer.removeAllViews();
-                        PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(true);
-                    }
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        clearAnimationBuffer();
-                    }
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        clearAnimationBuffer();
-                    }
-                });
-                ObjectAnimator inAnim = LauncherAnimUtils.ofFloat(mAppsCustomizePane, "alpha", 1f);
-                inAnim.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        reloadCurrentPage();
-                    }
-                });
-
-                final AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet();
-                animSet.playTogether(outAnim, inAnim);
-                animSet.setDuration(duration);
-                animSet.start();
-            }
-        });
-    }
-
-    public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
-        setOnTabChangedListener(null);
-        setCurrentTabByTag(getTabTagForContentType(type));
-        setOnTabChangedListener(this);
+    public String getContentTag() {
+        return getTabTagForContentType(mPagedView.getContentType());
     }
 
     /**
@@ -343,44 +128,41 @@
     }
 
     void reset() {
-        if (mInTransition) {
-            // Defer to after the transition to reset
-            mResetAfterTransition = true;
-        } else {
-            // Reset immediately
-            mAppsCustomizePane.reset();
+        // Reset immediately
+        mPagedView.reset();
+    }
+
+    public void onWindowVisible() {
+        if (getVisibility() == VISIBLE) {
+            mContent.setVisibility(VISIBLE);
+            // We unload the widget previews when the UI is hidden, so need to reload pages
+            // Load the current page synchronously, and the neighboring pages asynchronously
+            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
+            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
         }
     }
 
-    private void enableAndBuildHardwareLayer() {
-        // isHardwareAccelerated() checks if we're attached to a window and if that
-        // window is HW accelerated-- we were sometimes not attached to a window
-        // and buildLayer was throwing an IllegalStateException
-        if (isHardwareAccelerated()) {
-            // Turn on hardware layers for performance
-            setLayerType(LAYER_TYPE_HARDWARE, null);
-
-            // force building the layer, so you don't get a blip early in an animation
-            // when the layer is created layer
-            buildLayer();
-        }
+    public void onTrimMemory() {
+        mContent.setVisibility(GONE);
+        // Clear the widget pages of all their subviews - this will trigger the widget previews
+        // to delete their bitmaps
+        mPagedView.clearAllWidgetPages();
     }
 
     @Override
     public View getContent() {
-        View appsCustomizeContent = mAppsCustomizePane.getContent();
-        if (appsCustomizeContent != null) {
-            return appsCustomizeContent;
-        }
-        return mContent;
+        return mPagedView;
+    }
+
+    public boolean isInTransition() {
+        return mInTransition;
     }
 
     /* LauncherTransitionable overrides */
     @Override
     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        mAppsCustomizePane.onLauncherTransitionPrepare(l, animated, toWorkspace);
+        mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace);
         mInTransition = true;
-        mTransitioningToWorkspace = toWorkspace;
 
         if (toWorkspace) {
             // Going from All Apps -> Workspace
@@ -391,52 +173,43 @@
 
             // Make sure the current page is loaded (we start loading the side pages after the
             // transition to prevent slowing down the animation)
-            mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
-        }
-
-        if (mResetAfterTransition) {
-            mAppsCustomizePane.reset();
-            mResetAfterTransition = false;
+            // TODO: revisit this
+            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
         }
     }
 
     @Override
     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
-        mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace);
-        if (animated) {
+        mPagedView.onLauncherTransitionStart(l, animated, toWorkspace);
+        if (animated && !Utilities.isLmp()) {
             enableAndBuildHardwareLayer();
         }
-
-        // Dismiss the workspace cling
-        l.getLauncherClings().dismissWorkspaceCling(null);
     }
 
     @Override
     public void onLauncherTransitionStep(Launcher l, float t) {
-        mAppsCustomizePane.onLauncherTransitionStep(l, t);
+        mPagedView.onLauncherTransitionStep(l, t);
     }
 
     @Override
     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        mAppsCustomizePane.onLauncherTransitionEnd(l, animated, toWorkspace);
+        mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace);
         mInTransition = false;
-        if (animated) {
+        if (animated && !Utilities.isLmp()) {
             setLayerType(LAYER_TYPE_NONE, null);
         }
 
         if (!toWorkspace) {
-            // Show the all apps cling (if not already shown)
-            mAppsCustomizePane.showAllAppsCling();
             // Make sure adjacent pages are loaded (we wait until after the transition to
             // prevent slowing down the animation)
-            mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
+            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
 
             // Opening apps, need to announce what page we are on.
             AccessibilityManager am = (AccessibilityManager)
                     getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
             if (am.isEnabled()) {
                 // Notify the user when the page changes
-                announceForAccessibility(mAppsCustomizePane.getCurrentPageDescription());
+                announceForAccessibility(mPagedView.getCurrentPageDescription());
             }
 
             // Going from Workspace -> All Apps
@@ -469,24 +242,19 @@
         }
     }
 
-    public void onWindowVisible() {
-        if (getVisibility() == VISIBLE) {
-            mContent.setVisibility(VISIBLE);
-            // We unload the widget previews when the UI is hidden, so need to reload pages
-            // Load the current page synchronously, and the neighboring pages asynchronously
-            mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
-            mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
+    private void enableAndBuildHardwareLayer() {
+        // isHardwareAccelerated() checks if we're attached to a window and if that
+        // window is HW accelerated-- we were sometimes not attached to a window
+        // and buildLayer was throwing an IllegalStateException
+        if (isHardwareAccelerated()) {
+            // Turn on hardware layers for performance
+            setLayerType(LAYER_TYPE_HARDWARE, null);
+
+            // force building the layer, so you don't get a blip early in an animation
+            // when the layer is created layer
+            buildLayer();
         }
     }
 
-    public void onTrimMemory() {
-        mContent.setVisibility(GONE);
-        // Clear the widget pages of all their subviews - this will trigger the widget previews
-        // to delete their bitmaps
-        mAppsCustomizePane.clearAllWidgetPages();
-    }
 
-    boolean isTransitioning() {
-        return mInTransition;
-    }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3f619a8..ffa7ec3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -51,8 +51,6 @@
 
     private static final boolean DEBUG = false;
 
-    private int mPrevAlpha = -1;
-
     private HolographicOutlineHelper mOutlineHelper;
     private final Canvas mTempCanvas = new Canvas();
     private final Rect mTempRect = new Rect();
@@ -124,14 +122,22 @@
         }
     }
 
-    public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
+    public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
+            boolean setDefaultPadding) {
         Bitmap b = info.getIcon(iconCache);
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
-        Drawable iconDrawable = Utilities.createIconDrawable(b);
+        FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b);
+        if (info.isDisabled) {
+            iconDrawable.setSaturation(0);
+            iconDrawable.setBrightness(20);
+        }
+
         setCompoundDrawables(null, iconDrawable, null, null);
-        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+        if (setDefaultPadding) {
+            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+        }
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
         }
@@ -417,10 +423,6 @@
 
     @Override
     protected boolean onSetAlpha(int alpha) {
-        if (mPrevAlpha != alpha) {
-            mPrevAlpha = alpha;
-            super.onSetAlpha(alpha);
-        }
         return true;
     }
 
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index c54db01..8bcc407 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -77,6 +77,10 @@
     private int mTopViewIndex;
     private int mChildCountOnLastUpdate = -1;
 
+    // Darkening scrim
+    private Drawable mBackground;
+    private float mBackgroundAlpha = 0;
+
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -91,8 +95,10 @@
         setChildrenDrawingOrderEnabled(true);
         setOnHierarchyChangeListener(this);
 
-        mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo);
-        mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo);
+        final Resources res = getResources();
+        mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left_holo);
+        mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right_holo);
+        mBackground = res.getDrawable(R.drawable.apps_customize_bg);
     }
 
     public void setup(Launcher launcher, DragController controller) {
@@ -862,8 +868,17 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        // Draw the background gradient below children.
+        if (mBackground != null && mBackgroundAlpha > 0.0f) {
+            int alpha = (int) (mBackgroundAlpha * 255);
+            mBackground.setAlpha(alpha);
+            mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
+            mBackground.draw(canvas);
+        }
+
         super.dispatchDraw(canvas);
 
+        // Draw screen hover indicators above children.
         if (mInScrollArea && !LauncherAppState.getInstance().isScreenLarge()) {
             Workspace workspace = mLauncher.getWorkspace();
             int width = getMeasuredWidth();
@@ -887,6 +902,17 @@
         }
     }
 
+    public void setBackgroundAlpha(float alpha) {
+        if (alpha != mBackgroundAlpha) {
+            mBackgroundAlpha = alpha;
+            invalidate();
+        }
+    }
+
+    public float getBackgroundAlpha() {
+        return mBackgroundAlpha;
+    }
+
     public void setTouchCompleteListener(TouchCompleteListener listener) {
         mTouchCompleteListener = listener;
     }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 85e9020..ef8d097 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -19,15 +19,24 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
 class FastBitmapDrawable extends Drawable {
+
+    private static final ColorMatrix sTempSaturationMatrix = new ColorMatrix();
+    private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix();
+
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
     private Bitmap mBitmap;
     private int mAlpha;
-    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+    private float mSatutation = 1;
+    private int mBrightness = 0;
 
     FastBitmapDrawable(Bitmap b) {
         mAlpha = 255;
@@ -44,7 +53,7 @@
 
     @Override
     public void setColorFilter(ColorFilter cf) {
-        mPaint.setColorFilter(cf);
+        // No op
     }
 
     @Override
@@ -58,6 +67,7 @@
         mPaint.setAlpha(alpha);
     }
 
+    @Override
     public void setFilterBitmap(boolean filterBitmap) {
         mPaint.setFilterBitmap(filterBitmap);
         mPaint.setAntiAlias(filterBitmap);
@@ -90,4 +100,51 @@
     public Bitmap getBitmap() {
         return mBitmap;
     }
+
+    public float getSaturation() {
+        return mSatutation;
+    }
+
+    public void setSaturation(float saturation) {
+        mSatutation = saturation;
+        updateFilter();
+    }
+
+    public int getBrightness() {
+        return mBrightness;
+    }
+
+    public void addBrightness(int amount) {
+        mBrightness += amount;
+        updateFilter();
+    }
+
+    public void setBrightness(int brightness) {
+        mBrightness = brightness;
+        updateFilter();
+    }
+
+    private void updateFilter() {
+        if (mSatutation != 1 || mBrightness != 0) {
+            sTempSaturationMatrix.setSaturation(mSatutation);
+
+            if (mBrightness != 0) {
+                // Brightness: C-new = C-old*(1-amount) + amount
+                float scale = 1 - mBrightness / 255.0f;
+                sTempBrightnessMatrix.setScale(scale, scale, scale, 1);
+                float[] array = sTempBrightnessMatrix.getArray();
+
+                // Add the amount to RGB components of the matrix, as per the above formula.
+                // Fifth elements in the array correspond to the constant being added to
+                // red, blue, green, and alpha channel respectively.
+                array[4] = mBrightness;
+                array[9] = mBrightness;
+                array[14] = mBrightness;
+                sTempSaturationMatrix.preConcat(sTempBrightnessMatrix);
+            }
+            mPaint.setColorFilter(new ColorMatrixColorFilter(sTempSaturationMatrix));
+        } else {
+            mPaint.setColorFilter(null);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 47c8a4a..655e5c3 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -566,15 +566,10 @@
     }
 
     protected View createAndAddShortcut(ShortcutInfo item) {
-        final TextView textView =
-            (TextView) mInflater.inflate(R.layout.folder_application, this, false);
-        textView.setCompoundDrawables(null,
-                Utilities.createIconDrawable(item.getIcon(mIconCache)), null, null);
-        textView.setText(item.title);
-        if (item.contentDescription != null) {
-            textView.setContentDescription(item.contentDescription);
-        }
-        textView.setTag(item);
+        final BubbleTextView textView =
+            (BubbleTextView) mInflater.inflate(R.layout.folder_application, this, false);
+        textView.applyFromShortcutInfo(item, mIconCache, false);
+
         textView.setOnClickListener(this);
         textView.setOnLongClickListener(this);
 
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 4f674f5..5b49fb8 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -133,7 +133,7 @@
         final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
         final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
         final Workspace workspace = (Workspace) cellLayout.getParent();
-        return !workspace.isSmall();
+        return !workspace.workspaceInModalState();
     }
 
     static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -580,10 +580,17 @@
         if (d != null) {
             mOldBounds.set(d.getBounds());
             d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
-            d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
-                    PorterDuff.Mode.SRC_ATOP);
-            d.draw(canvas);
-            d.clearColorFilter();
+            if (d instanceof FastBitmapDrawable) {
+                FastBitmapDrawable fd = (FastBitmapDrawable) d;
+                fd.addBrightness(params.overlayAlpha);
+                d.draw(canvas);
+                fd.addBrightness(-params.overlayAlpha);
+            } else {
+                d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
+                        PorterDuff.Mode.SRC_ATOP);
+                d.draw(canvas);
+                d.clearColorFilter();
+            }
             d.setBounds(mOldBounds);
         }
         canvas.restore();
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 8ed2230..79cac8f 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -175,7 +175,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.
-        if (mLauncher.getWorkspace().isSmall()) {
+        if (mLauncher.getWorkspace().workspaceInModalState()) {
             return true;
         }
         return false;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 951b5d4..d3a4362 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,6 +82,7 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
+import android.view.ViewAnimationUtils;
 import android.view.Window;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
@@ -91,19 +92,23 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Advanceable;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.Toast;
+
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
+
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
@@ -198,7 +203,6 @@
     // Type: int[]
     private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
 
-
     static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
 
@@ -218,6 +222,8 @@
     private State mState = State.WORKSPACE;
     private AnimatorSet mStateAnimation;
 
+    private boolean mIsSafeModeEnabled;
+
     static final int APPWIDGET_HOST_ID = 1024;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
@@ -421,6 +427,7 @@
         // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
                 Context.MODE_PRIVATE);
+        mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
         mIconCache.flushInvalidIcons(grid);
@@ -1395,7 +1402,7 @@
      */
     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
-        favorite.applyFromShortcutInfo(info, mIconCache);
+        favorite.applyFromShortcutInfo(info, mIconCache, true);
         favorite.setOnClickListener(this);
         return favorite;
     }
@@ -2775,6 +2782,7 @@
                 // Could be launching some bookkeeping activity
                 startActivity(intent, optsBundle);
             } else {
+                // TODO Component can be null when shortcuts are supported for secondary user
                 launcherApps.startActivityForProfile(intent.getComponent(), user,
                         intent.getSourceBounds(), optsBundle);
             }
@@ -2791,6 +2799,10 @@
 
     boolean startActivitySafely(View v, Intent intent, Object tag) {
         boolean success = false;
+        if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+            Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+            return false;
+        }
         try {
             success = startActivity(v, intent, tag);
         } catch (ActivityNotFoundException e) {
@@ -3146,6 +3158,7 @@
         AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
         showAppsCustomizeHelper(animated, springLoaded, contentType);
     }
+
     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
                                          final AppsCustomizePagedView.ContentType contentType) {
         if (mStateAnimation != null) {
@@ -3153,10 +3166,15 @@
             mStateAnimation.cancel();
             mStateAnimation = null;
         }
+
+        boolean material = Utilities.isLmp();
+
         final Resources res = getResources();
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
+        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+
         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mWorkspace;
         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
@@ -3165,9 +3183,10 @@
 
         setPivotsForZoom(toView, scale);
 
-        // Shrink workspaces away if going to AppsCustomize from workspace
+        Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
+                Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
         Animator workspaceAnim =
-                mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
+                mWorkspace.getChangeStateAnimation(workspaceState, animated);
         if (!LauncherAppState.isDisableAllApps()
                 || contentType == AppsCustomizePagedView.ContentType.Widgets) {
             // Set the content type for the all apps/widgets space
@@ -3175,65 +3194,151 @@
         }
 
         if (animated) {
-            toView.setScaleX(scale);
-            toView.setScaleY(scale);
-            final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
-            scaleAnim.
-                scaleX(1f).scaleY(1f).
-                setDuration(duration).
-                setInterpolator(new Workspace.ZoomOutInterpolator());
+            if (!material) {
+                toView.setScaleX(scale);
+                toView.setScaleY(scale);
+                final LauncherViewPropertyAnimator scaleAnim =
+                        new LauncherViewPropertyAnimator(toView);
+                scaleAnim.
+                    scaleX(1f).scaleY(1f).
+                    setDuration(duration).
+                    setInterpolator(new Workspace.ZoomOutInterpolator());
 
-            toView.setVisibility(View.VISIBLE);
-            toView.setAlpha(0f);
-            final ObjectAnimator alphaAnim = LauncherAnimUtils
-                .ofFloat(toView, "alpha", 0f, 1f)
-                .setDuration(fadeDuration);
-            alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
-            alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    if (animation == null) {
-                        throw new RuntimeException("animation is null");
+                toView.setVisibility(View.VISIBLE);
+                toView.setAlpha(0f);
+                final ObjectAnimator alphaAnim = LauncherAnimUtils
+                    .ofFloat(toView, "alpha", 0f, 1f)
+                    .setDuration(fadeDuration);
+                alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
+                alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        if (animation == null) {
+                            throw new RuntimeException("animation is null");
+                        }
+                        float t = (Float) animation.getAnimatedValue();
+                        dispatchOnLauncherTransitionStep(fromView, t);
+                        dispatchOnLauncherTransitionStep(toView, t);
                     }
-                    float t = (Float) animation.getAnimatedValue();
-                    dispatchOnLauncherTransitionStep(fromView, t);
-                    dispatchOnLauncherTransitionStep(toView, t);
-                }
-            });
+                });
 
-            // toView should appear right at the end of the workspace shrink
-            // animation
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
-            mStateAnimation.play(scaleAnim).after(startDelay);
-            mStateAnimation.play(alphaAnim).after(startDelay);
+                // toView should appear right at the end of the workspace shrink
+                // animation
+                mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+                mStateAnimation.play(scaleAnim).after(startDelay);
+                mStateAnimation.play(alphaAnim).after(startDelay);
 
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Prepare the position
-                    toView.setTranslationX(0.0f);
-                    toView.setTranslationY(0.0f);
-                    toView.setVisibility(View.VISIBLE);
-                    toView.bringToFront();
-                }
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
-                    dispatchOnLauncherTransitionEnd(toView, animated, false);
-
-                    // Hide the search bar
-                    if (mSearchDropTargetBar != null) {
-                        mSearchDropTargetBar.hideSearchBar(false);
+                mStateAnimation.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        // Prepare the position
+                        toView.setTranslationX(0.0f);
+                        toView.setTranslationY(0.0f);
+                        toView.setVisibility(View.VISIBLE);
+                        toView.bringToFront();
                     }
-                }
-            });
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                        dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-            if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
+                        // Hide the search bar
+                        if (mSearchDropTargetBar != null) {
+                            mSearchDropTargetBar.hideSearchBar(false);
+                        }
+                    }
+                });
+            } else {
+                int width = toView.getMeasuredWidth();
+                int height = toView.getMeasuredHeight();
+                float revealRadius = (float) Math.sqrt((width * width) / 4 + height * height);
+
+                mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+
+                AppsCustomizePagedView content = (AppsCustomizePagedView)
+                        toView.findViewById(R.id.apps_customize_pane_content);
+
+                View page = content.getPageAt(content.getCurrentPage());
+                View revealView = content;
+
+                float yDrift = height / 2f - 400;
+
+                LauncherViewPropertyAnimator panelAlphaAndDrift =
+                        new LauncherViewPropertyAnimator(revealView);
+                revealView.setTranslationY(yDrift);
+                revealView.setAlpha(0.3f);
+                panelAlphaAndDrift.alpha(1)
+                    .translationY(0)
+                    .setDuration(revealDuration)
+                    .setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+                mStateAnimation.play(panelAlphaAndDrift);
+
+                if (page instanceof CellLayout) {
+                    CellLayout cellLayout = (CellLayout) page;
+                    cellLayout.enableHardwareLayer(true);
+
+                    View iconsView = cellLayout.getShortcutsAndWidgets();
+                    iconsView.setAlpha(0f);
+
+                    LauncherViewPropertyAnimator iconsAlpha =
+                            new LauncherViewPropertyAnimator(iconsView);
+                    iconsAlpha.alpha(1f)
+                        .setDuration(revealDuration - 100)
+                        .setInterpolator(new LogDecelerateInterpolator(100, 0));
+                    mStateAnimation.play(iconsAlpha);
+                }
+
+                View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
+                pageIndicators.setAlpha(0f);
+                final LauncherViewPropertyAnimator indicatorsAlpha =
+                        new LauncherViewPropertyAnimator(pageIndicators);
+                indicatorsAlpha.alpha(1f);
+                indicatorsAlpha.setDuration(revealDuration);
+                mStateAnimation.play(indicatorsAlpha);
+
+                width = revealView.getMeasuredWidth();
+
+                ValueAnimator reveal =
+                        ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+                                height / 2 + 100, 0f, revealRadius);
+                reveal.setDuration(revealDuration);
+                reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+                toView.setTranslationX(0);
+                toView.setTranslationY(0);
+                toView.setAlpha(1f);
+                // toView should appear right at the end of the workspace shrink
+                // animation
+                mStateAnimation.play(reveal);
+
+                reveal.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        // Prepare the position
+                        toView.bringToFront();
+                        toView.setVisibility(View.VISIBLE);
+                    }
+                });
+
+                mStateAnimation.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                        dispatchOnLauncherTransitionEnd(toView, animated, false);
+
+                        // Hide the search bar
+                        if (mSearchDropTargetBar != null) {
+                            mSearchDropTargetBar.hideSearchBar(false);
+                        }
+                    }
+                });
             }
 
             boolean delayAnim = false;
-
+            if (workspaceAnim != null) {
+                mStateAnimation.play(workspaceAnim);
+            }
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
 
@@ -3305,11 +3410,15 @@
             mStateAnimation.cancel();
             mStateAnimation = null;
         }
+
+        boolean material = Utilities.isLmp();
+
         Resources res = getResources();
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
-        final int fadeOutDuration =
-                res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+        final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+
         final float scaleFactor = (float)
                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mAppsCustomizeTabHost;
@@ -3328,31 +3437,116 @@
         setPivotsForZoom(fromView, scaleFactor);
         showHotseat(animated);
         if (animated) {
-            final LauncherViewPropertyAnimator scaleAnim =
-                    new LauncherViewPropertyAnimator(fromView);
-            scaleAnim.
-                scaleX(scaleFactor).scaleY(scaleFactor).
-                setDuration(duration).
-                setInterpolator(new Workspace.ZoomInInterpolator());
+            if (!material) {
+                final LauncherViewPropertyAnimator scaleAnim =
+                        new LauncherViewPropertyAnimator(fromView);
+                scaleAnim.
+                    scaleX(scaleFactor).scaleY(scaleFactor).
+                    setDuration(duration).
+                    setInterpolator(new Workspace.ZoomInInterpolator());
 
-            final ObjectAnimator alphaAnim = LauncherAnimUtils
-                .ofFloat(fromView, "alpha", 1f, 0f)
-                .setDuration(fadeOutDuration);
-            alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
-            alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float t = 1f - (Float) animation.getAnimatedValue();
-                    dispatchOnLauncherTransitionStep(fromView, t);
-                    dispatchOnLauncherTransitionStep(toView, t);
+                final ObjectAnimator alphaAnim = LauncherAnimUtils
+                    .ofFloat(fromView, "alpha", 1f, 0f)
+                    .setDuration(fadeOutDuration);
+                alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
+                alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        float t = 1f - (Float) animation.getAnimatedValue();
+                        dispatchOnLauncherTransitionStep(fromView, t);
+                        dispatchOnLauncherTransitionStep(toView, t);
+                    }
+                });
+
+                mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+
+                dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+                dispatchOnLauncherTransitionPrepare(toView, animated, true);
+                mAppsCustomizeContent.stopScrolling();
+
+                mStateAnimation.playTogether(scaleAnim, alphaAnim);
+            } else {
+                mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+
+                int width = fromView.getMeasuredWidth();
+                int height = fromView.getMeasuredHeight();
+                float revealRadius = (float) Math.sqrt((width * width) / 4 + height * height);
+
+                AppsCustomizePagedView content = (AppsCustomizePagedView)
+                        fromView.findViewById(R.id.apps_customize_pane_content);
+
+                final View page = content.getPageAt(content.getNextPage());
+                View revealView = page;
+
+                float yDrift = height / 2f - 400;
+
+                LauncherViewPropertyAnimator panelAlphaAndDrift =
+                        new LauncherViewPropertyAnimator(revealView);
+                revealView.setTranslationY(0);
+                revealView.setAlpha(1);
+                panelAlphaAndDrift.alpha(0)
+                    .translationY(yDrift)
+                    .setDuration(revealDuration)
+                    .setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+                mStateAnimation.play(panelAlphaAndDrift);
+
+                if (page instanceof CellLayout) {
+                    final CellLayout cellLayout = (CellLayout) page;
+                    cellLayout.enableHardwareLayer(true);
+
+                    final View iconsView = cellLayout.getShortcutsAndWidgets();
+
+                    LauncherViewPropertyAnimator iconsAlpha =
+                            new LauncherViewPropertyAnimator(iconsView);
+                    iconsAlpha.alpha(0f)
+                        .setDuration(revealDuration - 100)
+                        .setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+                    mStateAnimation.play(iconsAlpha);
+
+                    mStateAnimation.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            cellLayout.setTranslationY(0);
+                            cellLayout.setAlpha(1f);
+                            iconsView.setAlpha(1f);
+                        }
+                    });
                 }
-            });
 
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+                View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
+                final LauncherViewPropertyAnimator indicatorsAlpha =
+                        new LauncherViewPropertyAnimator(pageIndicators);
+                indicatorsAlpha.alpha(0f);
+                indicatorsAlpha.setDuration(revealDuration);
+                indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
+                mStateAnimation.play(indicatorsAlpha);
 
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
-            mAppsCustomizeContent.stopScrolling();
+                width = revealView.getMeasuredWidth();
+
+                ValueAnimator reveal =
+                        ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+                                height / 2 + 100, revealRadius, 0f);
+                reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+                reveal.setDuration(revealDuration);
+
+                reveal.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        fromView.setVisibility(View.GONE);
+                    }
+                });
+
+                dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+                dispatchOnLauncherTransitionPrepare(toView, animated, true);
+                mAppsCustomizeContent.stopScrolling();
+
+                mStateAnimation.play(reveal);
+            }
+            if (workspaceAnim != null) {
+                mStateAnimation.play(workspaceAnim);
+            }
 
             mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -3367,10 +3561,6 @@
                 }
             });
 
-            mStateAnimation.playTogether(scaleAnim, alphaAnim);
-            if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
-            }
             dispatchOnLauncherTransitionStart(fromView, animated, true);
             dispatchOnLauncherTransitionStart(toView, animated, true);
             LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
@@ -3835,7 +4025,7 @@
         text.clear();
         // Populate event with a fake title based on the current state.
         if (mState == State.APPS_CUSTOMIZE) {
-            text.add(mAppsCustomizeTabHost.getCurrentTabView().getContentDescription());
+            text.add(mAppsCustomizeTabHost.getContentTag());
         } else {
             text.add(getString(R.string.all_apps_home_button_label));
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b01db71..a8bace8 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2047,6 +2047,8 @@
                                     info.spanX = 1;
                                     info.spanY = 1;
                                     info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+                                    info.isDisabled = isSafeMode
+                                            && !Utilities.isSystemApp(context, intent);
 
                                     // check & update map of what's occupied
                                     deleteOnInvalidPlacement.set(false);
diff --git a/src/com/android/launcher3/LogAccelerateInterpolator.java b/src/com/android/launcher3/LogAccelerateInterpolator.java
new file mode 100644
index 0000000..c3bbfa5
--- /dev/null
+++ b/src/com/android/launcher3/LogAccelerateInterpolator.java
@@ -0,0 +1,25 @@
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+
+public class LogAccelerateInterpolator implements TimeInterpolator {
+
+    int mBase;
+    int mDrift;
+    final float mLogScale;
+
+    public LogAccelerateInterpolator(int base, int drift) {
+        mBase = base;
+        mDrift = drift;
+        mLogScale = 1f / computeLog(1, mBase, mDrift);
+    }
+
+    static float computeLog(float t, int base, int drift) {
+        return (float) -Math.pow(base, -t) + 1 + (drift * t);
+    }
+
+    @Override
+    public float getInterpolation(float t) {
+        return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+    }
+}
diff --git a/src/com/android/launcher3/LogDecelerateInterpolator.java b/src/com/android/launcher3/LogDecelerateInterpolator.java
new file mode 100644
index 0000000..4c5f6f0
--- /dev/null
+++ b/src/com/android/launcher3/LogDecelerateInterpolator.java
@@ -0,0 +1,26 @@
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+
+public class LogDecelerateInterpolator implements TimeInterpolator {
+
+    int mBase;
+    int mDrift;
+    final float mLogScale;
+
+    public LogDecelerateInterpolator(int base, int drift) {
+        mBase = base;
+        mDrift = drift;
+
+        mLogScale = 1f / computeLog(1, mBase, mDrift);
+    }
+
+    static float computeLog(float t, int base, int drift) {
+        return (float) -Math.pow(base, -t) + 1 + (drift * t);
+    }
+
+    @Override
+    public float getInterpolation(float t) {
+        return computeLog(t, mBase, mDrift) * mLogScale;
+    }
+}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 7e1f0d6..266e9e0 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -79,6 +79,12 @@
     private Bitmap mIcon;
 
     /**
+     * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
+     * sd-card is not available).
+     */
+    boolean isDisabled = false;
+
+    /**
      * The installation state of the package that this shortcut represents.
      */
     protected int mState;
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index 882fb04..f3977e4 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -32,7 +32,6 @@
     private static final boolean LOCAL_LAUNCH_LOG = true;
 
     public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
-    public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
     public static final String EXTRA_INTENT = "intent";
     public static final String EXTRA_CONTAINER = "container";
     public static final String EXTRA_SCREEN = "screen";
@@ -53,6 +52,8 @@
 
     private final Launcher mLauncher;
 
+    private final String mLaunchBroadcastPermission;
+
     DataOutputStream mLog;
 
     ArrayList<String> mIntents;
@@ -61,6 +62,9 @@
     public Stats(Launcher launcher) {
         mLauncher = launcher;
 
+        mLaunchBroadcastPermission =
+                launcher.getResources().getString(R.string.receive_launch_broadcasts_permission);
+
         loadStats();
 
         if (LOCAL_LAUNCH_LOG) {
@@ -87,7 +91,7 @@
                         }
                     },
                     new IntentFilter(ACTION_LAUNCH),
-                    PERM_LAUNCH,
+                    mLaunchBroadcastPermission,
                     null
             );
         }
@@ -120,7 +124,7 @@
                     .putExtra(EXTRA_CELLX, shortcut.cellX)
                     .putExtra(EXTRA_CELLY, shortcut.cellY);
         }
-        mLauncher.sendBroadcast(broadcastIntent, PERM_LAUNCH);
+        mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
 
         incrementLaunch(flat);
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 74b6e47..0a711c5 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,14 +18,17 @@
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -52,10 +55,6 @@
     public static int sIconTextureWidth = -1;
     public static int sIconTextureHeight = -1;
 
-    private static final Paint sBlurPaint = new Paint();
-    private static final Paint sGlowColorPressedPaint = new Paint();
-    private static final Paint sGlowColorFocusedPaint = new Paint();
-    private static final Paint sDisabledPaint = new Paint();
     private static final Rect sOldBounds = new Rect();
     private static final Canvas sCanvas = new Canvas();
 
@@ -75,7 +74,7 @@
     /**
      * Returns a FastBitmapDrawable with the icon, accurately sized.
      */
-    static Drawable createIconDrawable(Bitmap icon) {
+    static FastBitmapDrawable createIconDrawable(Bitmap icon) {
         FastBitmapDrawable d = new FastBitmapDrawable(icon);
         d.setFilterBitmap(true);
         resizeIconDrawable(d);
@@ -332,15 +331,6 @@
 
         sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size);
         sIconTextureWidth = sIconTextureHeight = sIconWidth;
-
-        sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL));
-        sGlowColorPressedPaint.setColor(0xffffc300);
-        sGlowColorFocusedPaint.setColor(0xffff8e00);
-
-        ColorMatrix cm = new ColorMatrix();
-        cm.setSaturation(0.2f);
-        sDisabledPaint.setColorFilter(new ColorMatrixColorFilter(cm));
-        sDisabledPaint.setAlpha(0x88);
     }
 
     public static void setIconSize(int widthPx) {
@@ -378,4 +368,29 @@
                     "or use the exported attribute for this activity.", e);
         }
     }
+
+    static boolean isSystemApp(Context context, Intent intent) {
+        PackageManager pm = context.getPackageManager();
+        ComponentName cn = intent.getComponent();
+        String packageName = null;
+        if (cn == null) {
+            ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+            if ((info != null) && (info.activityInfo != null)) {
+                packageName = info.activityInfo.packageName;
+            }
+        } else {
+            packageName = cn.getPackageName();
+        }
+        if (packageName != null) {
+            try {
+                PackageInfo info = pm.getPackageInfo(packageName, 0);
+                return (info != null) && (info.applicationInfo != null) &&
+                        ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+            } catch (NameNotFoundException e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a8e7580..267dde8 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -109,9 +109,6 @@
     // These properties refer to the background protection gradient used for AllApps and Customize
     private ValueAnimator mBackgroundFadeInAnimation;
     private ValueAnimator mBackgroundFadeOutAnimation;
-    private Drawable mBackground;
-    boolean mDrawBackground = true;
-    private float mBackgroundAlpha = 0;
 
     private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
     private long mTouchDownTime = -1;
@@ -191,7 +188,7 @@
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
 
-    enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW};
+    enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
@@ -445,13 +442,6 @@
         setMinScale(mOverviewModeShrinkFactor);
         setupLayoutTransition();
 
-        final Resources res = getResources();
-        try {
-            mBackground = res.getDrawable(R.drawable.apps_customize_bg);
-        } catch (Resources.NotFoundException e) {
-            // In this case, we will skip drawing background protection
-        }
-
         mWallpaperOffset = new WallpaperOffsetInterpolator();
         Display display = mLauncher.getWindowManager().getDefaultDisplay();
         display.getSize(mDisplaySize);
@@ -1068,8 +1058,8 @@
      */
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        return (isSmall() || !isFinishedSwitchingState())
-                || (!isSmall() && indexOfChild(v) != mCurrentPage);
+        return (workspaceInModalState() || !isFinishedSwitchingState())
+                || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
     }
 
     public boolean isSwitchingState() {
@@ -1088,7 +1078,7 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
-        if (isSmall() || !isFinishedSwitchingState()) {
+        if (workspaceInModalState() || !isFinishedSwitchingState()) {
             // when the home screens are shrunken, shouldn't allow side-scrolling
             return false;
         }
@@ -1226,7 +1216,7 @@
         }
 
         if (mDragController.isDragging()) {
-            if (isSmall()) {
+            if (workspaceInModalState()) {
                 // If we are in springloaded mode, then force an event to check if the current touch
                 // is under a new page (to scroll to)
                 mDragController.forceTouchMove();
@@ -1500,7 +1490,7 @@
     }
 
     void showOutlines() {
-        if (!isSmall() && !mIsSwitchingState) {
+        if (!workspaceInModalState() && !mIsSwitchingState) {
             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
             mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
@@ -1510,7 +1500,7 @@
     }
 
     void hideOutlines() {
-        if (!isSmall() && !mIsSwitchingState) {
+        if (!workspaceInModalState() && !mIsSwitchingState) {
             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
             mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
@@ -1538,15 +1528,9 @@
         return mChildrenOutlineAlpha;
     }
 
-    void disableBackground() {
-        mDrawBackground = false;
-    }
-    void enableBackground() {
-        mDrawBackground = true;
-    }
-
     private void animateBackgroundGradient(float finalAlpha, boolean animated) {
-        if (mBackground == null) return;
+        final DragLayer dragLayer = mLauncher.getDragLayer();
+
         if (mBackgroundFadeInAnimation != null) {
             mBackgroundFadeInAnimation.cancel();
             mBackgroundFadeInAnimation = null;
@@ -1555,36 +1539,26 @@
             mBackgroundFadeOutAnimation.cancel();
             mBackgroundFadeOutAnimation = null;
         }
-        float startAlpha = getBackgroundAlpha();
+        float startAlpha = dragLayer.getBackgroundAlpha();
         if (finalAlpha != startAlpha) {
             if (animated) {
                 mBackgroundFadeOutAnimation =
                         LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
                 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
                     public void onAnimationUpdate(ValueAnimator animation) {
-                        setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
+                        dragLayer.setBackgroundAlpha(
+                                ((Float)animation.getAnimatedValue()).floatValue());
                     }
                 });
                 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
                 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
                 mBackgroundFadeOutAnimation.start();
             } else {
-                setBackgroundAlpha(finalAlpha);
+                dragLayer.setBackgroundAlpha(finalAlpha);
             }
         }
     }
 
-    public void setBackgroundAlpha(float alpha) {
-        if (alpha != mBackgroundAlpha) {
-            mBackgroundAlpha = alpha;
-            invalidate();
-        }
-    }
-
-    public float getBackgroundAlpha() {
-        return mBackgroundAlpha;
-    }
-
     float backgroundAlphaInterpolator(float r) {
         float pivotA = 0.1f;
         float pivotB = 0.4f;
@@ -1656,13 +1630,13 @@
         if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
 
         CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
-        if (progress > 0 && cc.getVisibility() != VISIBLE && !isSmall()) {
+        if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
             cc.setVisibility(VISIBLE);
         }
 
         mLastCustomContentScrollProgress = progress;
 
-        setBackgroundAlpha(progress * 0.8f);
+        mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f);
 
         if (mLauncher.getHotseat() != null) {
             mLauncher.getHotseat().setTranslationX(translationX);
@@ -1792,25 +1766,12 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        // Draw the background gradient if necessary
-        if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
-            int alpha = (int) (mBackgroundAlpha * 255);
-            mBackground.setAlpha(alpha);
-            mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
-                    getMeasuredHeight());
-            mBackground.draw(canvas);
-        }
-
         super.onDraw(canvas);
 
         // Call back to LauncherModel to finish binding after the first draw
         post(mBindPages);
     }
 
-    boolean isDrawingBackgroundGradient() {
-        return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
-    }
-
     @Override
     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
         if (!mLauncher.isAllAppsVisible()) {
@@ -1826,7 +1787,7 @@
 
     @Override
     public int getDescendantFocusability() {
-        if (isSmall()) {
+        if (workspaceInModalState()) {
             return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
         }
         return super.getDescendantFocusability();
@@ -1844,8 +1805,8 @@
         }
     }
 
-    public boolean isSmall() {
-        return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW;
+    public boolean workspaceInModalState() {
+        return mState != State.NORMAL;
     }
 
     void enableChildrenCache(int fromPage, int toPage) {
@@ -1880,7 +1841,7 @@
     }
 
     private void updateChildrenLayersEnabled(boolean force) {
-        boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState;
+        boolean small = mState == State.OVERVIEW || mIsSwitchingState;
         boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
@@ -2223,6 +2184,8 @@
         setImportantForAccessibility(accessible);
     }
 
+    private static final int HIDE_WORKSPACE_DURATION = 100;
+
     Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
         if (mState == state) {
             return null;
@@ -2236,21 +2199,25 @@
         final State oldState = mState;
         final boolean oldStateIsNormal = (oldState == State.NORMAL);
         final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
-        final boolean oldStateIsSmall = (oldState == State.SMALL);
+        final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN);
+        final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN);
         final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
         setState(state);
         final boolean stateIsNormal = (state == State.NORMAL);
         final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
-        final boolean stateIsSmall = (state == State.SMALL);
+        final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN);
+        final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN);
         final boolean stateIsOverview = (state == State.OVERVIEW);
         float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
-        float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
+        float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f;
         float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
         float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
-        float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0;
+        float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ?
+                getOverviewModeTranslationY() : 0;
 
-        boolean workspaceToAllApps = (oldStateIsNormal && stateIsSmall);
-        boolean allAppsToWorkspace = (oldStateIsSmall && stateIsNormal);
+        boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
+        boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
+        boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
         boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
         boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
 
@@ -2265,10 +2232,8 @@
         if (state != State.NORMAL) {
             if (stateIsSpringLoaded) {
                 mNewScale = mSpringLoadedShrinkFactor;
-            } else if (stateIsOverview) {
+            } else if (stateIsOverview || stateIsOverviewHidden) {
                 mNewScale = mOverviewModeShrinkFactor;
-            } else if (stateIsSmall){
-                mNewScale = mOverviewModeShrinkFactor - 0.3f;
             }
             if (workspaceToAllApps) {
                 updateChildrenLayersEnabled(false);
@@ -2276,8 +2241,8 @@
         }
 
         final int duration;
-        if (workspaceToAllApps) {
-            duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
+        if (workspaceToAllApps || overviewToAllApps) {
+            duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
         } else if (workspaceToOverview || overviewToWorkspace) {
             duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
         } else {
@@ -2294,7 +2259,7 @@
             boolean isCurrentPage = (i == snapPage);
             float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
             float finalAlpha;
-            if (stateIsSmall) {
+            if (stateIsNormalHidden || stateIsOverviewHidden) {
                 finalAlpha = 0f;
             } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
                 finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
@@ -2331,11 +2296,11 @@
         final View hotseat = mLauncher.getHotseat();
         final View pageIndicator = getPageIndicator();
         if (animated) {
-            anim.setDuration(duration);
             LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
             scale.scaleX(mNewScale)
                 .scaleY(mNewScale)
                 .translationY(finalWorkspaceTranslationY)
+                .setDuration(duration)
                 .setInterpolator(mZoomInInterpolator);
             anim.play(scale);
             for (int index = 0; index < getChildCount(); index++) {
@@ -2350,6 +2315,7 @@
                         LauncherViewPropertyAnimator alphaAnim =
                             new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
                         alphaAnim.alpha(mNewAlphas[i])
+                            .setDuration(duration)
                             .setInterpolator(mZoomInInterpolator);
                         anim.play(alphaAnim);
                     }
@@ -2358,6 +2324,7 @@
                         ValueAnimator bgAnim =
                                 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
                         bgAnim.setInterpolator(mZoomInInterpolator);
+                        bgAnim.setDuration(duration);
                         bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
                                 public void onAnimationUpdate(float a, float b) {
                                     cl.setBackgroundAlpha(
@@ -2400,7 +2367,11 @@
                 hotseatAlpha.setInterpolator(null);
                 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
             }
-            searchBarAlpha.setInterpolator(null);
+
+            overviewPanelAlpha.setDuration(duration);
+            pageIndicatorAlpha.setDuration(duration);
+            hotseatAlpha.setDuration(duration);
+            searchBarAlpha.setDuration(duration);
 
             anim.play(overviewPanelAlpha);
             anim.play(hotseatAlpha);
@@ -2425,18 +2396,11 @@
         }
         mLauncher.updateVoiceButtonProxyVisible(false);
 
-        if (stateIsSpringLoaded) {
-            // Right now we're covered by Apps Customize
-            // Show the background gradient immediately, so the gradient will
-            // be showing once AppsCustomize disappears
-            animateBackgroundGradient(getResources().getInteger(
-                    R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
-        } else if (stateIsOverview) {
-            animateBackgroundGradient(getResources().getInteger(
-                    R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, true);
-        } else {
-            // Fade the background gradient away
+        if (stateIsNormal) {
             animateBackgroundGradient(0f, animated);
+        } else {
+            animateBackgroundGradient(getResources().getInteger(
+                    R.integer.config_workspaceScrimAlpha) / 100f, animated);
         }
         return anim;
     }
@@ -2822,7 +2786,8 @@
     }
 
     public boolean transitionStateShouldAllowDrop() {
-        return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
+        return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
+                (mState == State.NORMAL || mState == State.SPRING_LOADED));
     }
 
     /**
@@ -3591,7 +3556,7 @@
 
     public void onDragOver(DragObject d) {
         // Skip drag over events while we are dragging over side pages
-        if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
+        if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
 
         Rect r = new Rect();
         CellLayout layout = null;
@@ -3604,7 +3569,7 @@
 
         final View child = (mDragInfo == null) ? null : mDragInfo.cell;
         // Identify whether we have dragged over a side page
-        if (isSmall()) {
+        if (workspaceInModalState()) {
             if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
                 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
                     layout = mLauncher.getHotseat().getLayout();
@@ -4482,7 +4447,7 @@
 
     @Override
     public void scrollLeft() {
-        if (!isSmall() && !mIsSwitchingState) {
+        if (!workspaceInModalState() && !mIsSwitchingState) {
             super.scrollLeft();
         }
         Folder openFolder = getOpenFolder();
@@ -4493,7 +4458,7 @@
 
     @Override
     public void scrollRight() {
-        if (!isSmall() && !mIsSwitchingState) {
+        if (!workspaceInModalState() && !mIsSwitchingState) {
             super.scrollRight();
         }
         Folder openFolder = getOpenFolder();
@@ -4515,7 +4480,7 @@
         }
 
         boolean result = false;
-        if (!isSmall() && !mIsSwitchingState && getOpenFolder() == null) {
+        if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
             mInScrollArea = true;
 
             final int page = getNextPage() +
@@ -4790,7 +4755,7 @@
                 shortcutInfo.updateIcon(mIconCache);
                 shortcutInfo.title = appInfo.title.toString();
                 shortcutInfo.contentDescription = appInfo.contentDescription;
-                shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
+                shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true);
             }
         }
     }
@@ -4895,7 +4860,7 @@
     }
 
     private void moveToScreen(int page, boolean animate) {
-        if (!isSmall()) {
+        if (!workspaceInModalState()) {
             if (animate) {
                 snapToPage(page);
             } else {