Merge "Removing some methods from the DragSource" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index d41e184..4cc5e42 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
     android-support-v7-recyclerview \
-    android-support-v7-palette \
     android-support-dynamic-animation
 
 LOCAL_SRC_FILES := \
@@ -72,7 +71,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
     android-support-v7-recyclerview \
-    android-support-v7-palette \
     android-support-dynamic-animation
 
 LOCAL_SRC_FILES := \
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 94098e1..bb03f50 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -81,12 +81,6 @@
             </intent-filter>
         </receiver>
 
-        <service android:name="com.android.launcher3.dynamicui.ColorExtractionService"
-            android:exported="false"
-            android:process=":wallpaper_chooser"
-            android:permission="android.permission.BIND_JOB_SERVICE">
-        </service>
-
         <service
             android:name="com.android.launcher3.compat.WallpaperManagerCompatVL$ColorExtractionService"
             android:exported="false"
diff --git a/build.gradle b/build.gradle
index 886ccac..2376146 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,12 +13,12 @@
 apply plugin: 'com.google.protobuf'
 
 android {
-    compileSdkVersion 26
-    buildToolsVersion '26.0.0'
+    compileSdkVersion 27
+    buildToolsVersion '27.0.0'
 
     defaultConfig {
         minSdkVersion 21
-        targetSdkVersion 26
+        targetSdkVersion 27
         versionCode 1
         versionName "1.0"
 
@@ -86,12 +86,11 @@
     jcenter()
 }
 
-final String SUPPORT_LIBS_VERSION = '26.0.0-SNAPSHOT'
+final String SUPPORT_LIBS_VERSION = '27.0.0-SNAPSHOT'
 dependencies {
     compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
     compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
     compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
-    compile "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
     compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
 
     testCompile 'junit:junit:4.12'
diff --git a/go/res/values/dimens.xml b/go/res/values/dimens.xml
new file mode 100644
index 0000000..f1b1053
--- /dev/null
+++ b/go/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<resources>
+    <!-- Dynamic Grid -->
+    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 094fc74..487c026 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -25,7 +25,7 @@
         launcher:numColumns="4"
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
-        launcher:iconSize="56"
+        launcher:iconSize="60"
         launcher:iconTextSize="14.0"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b403fbd..481199e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -232,8 +232,4 @@
     <dimen name="horizontal_ellipsis_offset">19dp</dimen>
     <dimen name="popup_item_divider_height">0.5dp</dimen>
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
-
-<!-- Other -->
-    <!-- Approximates the system status bar height. Not guaranteed to be always be correct. -->
-    <dimen name="status_bar_height">24dp</dimen>
 </resources>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index f835748..d92b934 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -355,10 +355,6 @@
         mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
     }
 
-    public void buildHardwareLayer() {
-        mShortcutsAndWidgets.buildLayer();
-    }
-
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 824a554..8f7e882 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -354,9 +354,18 @@
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
-        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
+        int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
+        if (iconDrawablePaddingPx > cellYPadding && !isVerticalBarLayout()
+                && !inMultiWindowMode()) {
+            // Ensures that the label is closer to its corresponding icon. This is not an issue
+            // with vertical bar layout or multi-window mode since the issue is handled separately
+            // with their calls to {@link #adjustToHideWorkspaceLabels}.
+            cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
+            iconDrawablePaddingPx = cellYPadding;
+        }
+        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
 
         // All apps
         allAppsIconTextSizePx = iconTextSizePx;
@@ -759,11 +768,14 @@
         return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
     }
 
+    public boolean inMultiWindowMode() {
+        return this != inv.landscapeProfile && this != inv.portraitProfile;
+    }
+
     public boolean shouldIgnoreLongPressToOverview(float touchX) {
-        boolean inMultiWindowMode = this != inv.landscapeProfile && this != inv.portraitProfile;
         boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
         boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
-        return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
+        return !inMultiWindowMode() && (touchedLhsEdge || touchedRhsEdge);
     }
 
     private static Context getContext(Context c, int orientation) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index a6d80e3..09f9e82 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,16 +16,9 @@
 
 package com.android.launcher3;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -35,11 +28,9 @@
 import android.widget.TextView;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.Themes;
 
 public class Hotseat extends FrameLayout
         implements UserEventDispatcher.LogContainerProvider {
@@ -51,12 +42,6 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private final boolean mHasVerticalHotseat;
 
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private int mBackgroundColor;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private ColorDrawable mBackground;
-    private ValueAnimator mBackgroundColorAnimator;
-
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -69,12 +54,6 @@
         super(context, attrs, defStyle);
         mLauncher = Launcher.getLauncher(context);
         mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
-        mBackgroundColor = ColorUtils.setAlphaComponent(
-                Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
-        mBackground = new ColorDrawable(mBackgroundColor);
-        if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            setBackground(mBackground);
-        }
     }
 
     public CellLayout getLayout() {
@@ -177,49 +156,4 @@
         target.gridY = info.cellY;
         targetParent.containerType = ContainerType.HOTSEAT;
     }
-
-    public void updateColor(ExtractedColors extractedColors, boolean animate) {
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            // not hotseat visible
-            return;
-        }
-        if (!mHasVerticalHotseat) {
-            int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
-            if (mBackgroundColorAnimator != null) {
-                mBackgroundColorAnimator.cancel();
-            }
-            if (!animate) {
-                setBackgroundColor(color);
-            } else {
-                mBackgroundColorAnimator = ValueAnimator.ofInt(mBackgroundColor, color);
-                mBackgroundColorAnimator.setEvaluator(new ArgbEvaluator());
-                mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        mBackground.setColor((Integer) animation.getAnimatedValue());
-                    }
-                });
-                mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mBackgroundColorAnimator = null;
-                    }
-                });
-                mBackgroundColorAnimator.start();
-            }
-            mBackgroundColor = color;
-        }
-    }
-
-    public void setBackgroundTransparent(boolean enable) {
-        if (enable) {
-            mBackground.setAlpha(0);
-        } else {
-            mBackground.setAlpha(255);
-        }
-    }
-
-    public int getBackgroundDrawableColor() {
-        return mBackgroundColor;
-    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4388eb4..ba31926 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -74,7 +74,6 @@
 import android.view.View;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -99,7 +98,6 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.PinItemDragListener;
-import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.dynamicui.WallpaperColorInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -221,8 +219,6 @@
     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
 
-    private final ExtractedColors mExtractedColors = new ExtractedColors();
-
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
     @Thunk DragLayer mDragLayer;
@@ -302,14 +298,6 @@
     // simply unregister this runnable.
     private Runnable mExitSpringLoadedModeRunnable;
 
-    @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
-        public void run() {
-            if (mWorkspace != null) {
-                mWorkspace.buildPageHardwareLayers();
-            }
-        }
-    };
-
     // Activity result which needs to be processed after workspace has loaded.
     private ActivityResultInfo mPendingActivityResult;
     /**
@@ -403,7 +391,6 @@
 
         setupViews();
         mDeviceProfile.layout(this, false /* notifyListeners */);
-        loadExtractedColorsAndColorItems();
 
         mPopupDataProvider = new PopupDataProvider(this);
 
@@ -489,27 +476,12 @@
     }
 
     @Override
-    public void onExtractedColorsChanged() {
-        loadExtractedColorsAndColorItems();
-        mExtractedColors.notifyChange();
-    }
-
-    @Override
     public void onAppWidgetHostReset() {
         if (mAppWidgetHost != null) {
             mAppWidgetHost.startListening();
         }
     }
 
-    private void loadExtractedColorsAndColorItems() {
-        // TODO: do this in pre-N as well, once the extraction part is complete.
-        if (Utilities.ATLEAST_NOUGAT) {
-            mExtractedColors.load(this);
-            mHotseat.updateColor(mExtractedColors, !mPaused);
-            mWorkspace.getPageIndicator().updateColor(mExtractedColors);
-        }
-    }
-
     private LauncherCallbacks mLauncherCallbacks;
 
     public void onPostCreate(Bundle savedInstanceState) {
@@ -895,17 +867,10 @@
         if (mOnResumeState == State.WORKSPACE) {
             showWorkspace(false);
         } else if (mOnResumeState == State.APPS) {
-            boolean launchedFromApp = (mWaitingForResume != null);
-            // Don't update the predicted apps if the user is returning to launcher in the apps
-            // view after launching an app, as they may be depending on the UI to be static to
-            // switch to another app, otherwise, if it was
-            showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
+            showAppsView(false /* animated */);
         } else if (mOnResumeState == State.WIDGETS) {
             showWidgetsView(false, false);
         }
-        if (mOnResumeState != State.APPS) {
-            tryAndUpdatePredictedApps();
-        }
         mOnResumeState = State.NONE;
 
         mPaused = false;
@@ -936,16 +901,7 @@
             mWaitingForResume.setStayPressed(false);
         }
 
-        // It is possible that widgets can receive updates while launcher is not in the foreground.
-        // Consequently, the widgets will be inflated in the orientation of the foreground activity
-        // (framework issue). On resuming, we ensure that any widgets are inflated for the current
-        // orientation.
-        if (!isWorkspaceLoading()) {
-            getWorkspace().reinflateWidgetsIfNecessary();
-        }
-
         updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
-        mWorkspace.onResume();
 
         // Process any items that were added while Launcher was away.
         InstallShortcutReceiver.disableAndFlushInstallQueue(
@@ -961,6 +917,8 @@
             mLauncherCallbacks.onResume();
         }
 
+        clearTypedText();
+
         TraceHelper.endSection("ON_RESUME");
     }
 
@@ -1462,44 +1420,6 @@
         }
     }
 
-    public void onWindowVisibilityChanged(int visibility) {
-        // The following code used to be in onResume, but it turns out onResume is called when
-        // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
-        // is a more appropriate event to handle
-        if (visibility == View.VISIBLE) {
-            if (!mWorkspaceLoading) {
-                final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
-                // We want to let Launcher draw itself at least once before we force it to build
-                // layers on all the workspace pages, so that transitioning to Launcher from other
-                // apps is nice and speedy.
-                observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
-                    private boolean mStarted = false;
-                    public void onDraw() {
-                        if (mStarted) return;
-                        mStarted = true;
-                        // We delay the layer building a bit in order to give
-                        // other message processing a time to run.  In particular
-                        // this avoids a delay in hiding the IME if it was
-                        // currently shown, because doing that may involve
-                        // some communication back with the app.
-                        mWorkspace.postDelayed(mBuildLayersRunnable, 500);
-                        final ViewTreeObserver.OnDrawListener listener = this;
-                        mWorkspace.post(new Runnable() {
-                            public void run() {
-                                if (mWorkspace != null &&
-                                        mWorkspace.getViewTreeObserver() != null) {
-                                    mWorkspace.getViewTreeObserver().
-                                            removeOnDrawListener(listener);
-                                }
-                            }
-                        });
-                    }
-                });
-            }
-            clearTypedText();
-        }
-    }
-
     public DragLayer getDragLayer() {
         return mDragLayer;
     }
@@ -1676,7 +1596,6 @@
         super.onDestroy();
 
         unregisterReceiver(mReceiver);
-        mWorkspace.removeCallbacks(mBuildLayersRunnable);
         mWorkspace.removeFolderListeners();
 
         // Stop callbacks from LauncherModel
@@ -2173,7 +2092,7 @@
         if (!isAppsViewVisible()) {
             getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.ALL_APPS_BUTTON);
-            showAppsView(true /* animated */, true /* updatePredictedApps */);
+            showAppsView(true /* animated */);
         } else {
             showWorkspace(true);
         }
@@ -2738,11 +2657,8 @@
     /**
      * Shows the apps view.
      */
-    public void showAppsView(boolean animated, boolean updatePredictedApps) {
+    public void showAppsView(boolean animated) {
         markAppsViewShown();
-        if (updatePredictedApps) {
-            tryAndUpdatePredictedApps();
-        }
         showAppsOrWidgets(State.APPS, animated);
     }
 
@@ -2862,7 +2778,7 @@
 
     public void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
-            showAppsView(true /* animated */, false /* updatePredictedApps */);
+            showAppsView(true /* animated */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
         } else if (mState == State.WORKSPACE_SPRING_LOADED) {
@@ -2870,19 +2786,6 @@
         }
     }
 
-    /**
-     * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
-     * resumed.
-     */
-    public void tryAndUpdatePredictedApps() {
-        if (mLauncherCallbacks != null) {
-            List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
-            if (apps != null) {
-                mAppsView.setPredictedApps(apps);
-            }
-        }
-    }
-
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -3322,7 +3225,10 @@
             info.pendingItemInfo = null;
         }
 
-        mWorkspace.reinflateWidgetsIfNecessary();
+        if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) {
+            view.reinflate();
+        }
+
         getModelWriter().updateItemInDatabase(info);
         return info;
     }
@@ -3597,7 +3503,6 @@
         // Update AllApps
         if (mAppsView != null) {
             mAppsView.removeApps(appInfos);
-            tryAndUpdatePredictedApps();
         }
     }
 
@@ -3761,7 +3666,7 @@
             switch (keyCode) {
                 case KeyEvent.KEYCODE_A:
                     if (mState == State.WORKSPACE) {
-                        showAppsView(true, true);
+                        showAppsView(true);
                         return true;
                     }
                     break;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 7bebf44..dfb30fd 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -28,7 +28,6 @@
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.Preconditions;
@@ -109,18 +108,11 @@
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
-        // For extracting colors from the wallpaper
-        if (Utilities.ATLEAST_NOUGAT) {
-            // TODO: add a broadcast entry to the manifest for pre-N.
-            filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
-        }
 
         mContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(mContext).enableAndResetCache();
         new ConfigMonitor(mContext).register();
 
-        ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
-
         if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
             mNotificationBadgingObserver = null;
         } else {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 819f23f..70440fa 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -122,7 +122,6 @@
                     context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             inflater.inflate(appWidget.initialLayout, lahv);
             lahv.setAppWidget(0, appWidget);
-            lahv.updateLastInflationOrientation();
             return lahv;
         } else {
             try {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 7fe1308..6f953e5 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -58,10 +59,14 @@
 
     private final CheckLongPressHelper mLongPressHelper;
     private final StylusEventHelper mStylusEventHelper;
-    private final Context mContext;
+    private final Launcher mLauncher;
+
+    private static final int DONT_REINFLATE = 0;
+    private static final int REINFLATE_ON_RESUME = 1;
+    private static final int REINFLATE_ON_CONFIG_CHANGE = 2;
 
     @ViewDebug.ExportedProperty(category = "launcher")
-    private int mPreviousOrientation;
+    private int mReinflateStatus;
 
     private float mSlop;
 
@@ -85,11 +90,11 @@
 
     public LauncherAppWidgetHostView(Context context) {
         super(context);
-        mContext = context;
+        mLauncher = Launcher.getLauncher(context);
         mLongPressHelper = new CheckLongPressHelper(this, this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
         mInflater = LayoutInflater.from(context);
-        setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
+        setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
 
         if (Utilities.ATLEAST_OREO) {
@@ -112,18 +117,28 @@
         return mInflater.inflate(R.layout.appwidget_error, this, false);
     }
 
-    public void updateLastInflationOrientation() {
-        mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
-    }
-
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
-        // Store the orientation in which the widget was inflated
-        updateLastInflationOrientation();
         super.updateAppWidget(remoteViews);
 
         // The provider info or the views might have changed.
         checkIfAutoAdvance();
+
+        // It is possible that widgets can receive updates while launcher is not in the foreground.
+        // Consequently, the widgets will be inflated for the orientation of the foreground activity
+        // (framework issue). On resuming, we ensure that any widgets are inflated for the current
+        // orientation.
+        if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) {
+            mReinflateStatus = REINFLATE_ON_RESUME;
+            if (!mLauncher.waitUntilResume(new ReInflateRunnable())) {
+                mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
+            }
+        }
+    }
+
+    private boolean isSameOrientation() {
+        return mLauncher.getResources().getConfiguration().orientation ==
+                mLauncher.getOrientation();
     }
 
     private boolean checkScrollableRecursively(ViewGroup viewGroup) {
@@ -142,14 +157,6 @@
         return false;
     }
 
-    public boolean isReinflateRequired(int orientation) {
-        // Re-inflate is required if the orientation has changed since last inflated.
-        if (mPreviousOrientation != orientation) {
-           return true;
-       }
-       return false;
-    }
-
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // Just in case the previous long press hasn't been cleared, we make sure to start fresh
         // on touch down.
@@ -473,4 +480,45 @@
     public PointF getTranslationForCentering() {
         return mTranslationForCentering;
     }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) {
+            // We are finally in the same orientation
+            reinflateIfNecessary();
+        }
+    }
+
+    private void reinflateIfNecessary() {
+        if (!isSameOrientation()) {
+            // We cannot reinflate yet, wait until next config change
+            mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
+            return;
+        }
+
+        mReinflateStatus = DONT_REINFLATE;
+        if (isAttachedToWindow()) {
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+            reinflate();
+        }
+    }
+
+    public void reinflate() {
+        LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+        // Remove and rebind the current widget (which was inflated in the wrong
+        // orientation), but don't delete it from the database
+        mLauncher.removeItem(this, info, false  /* deleteFromDb */);
+        mLauncher.bindAppWidget(info);
+    }
+
+    private class ReInflateRunnable implements Runnable {
+        @Override
+        public void run() {
+            if (mReinflateStatus == REINFLATE_ON_RESUME) {
+                reinflateIfNecessary();
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index d1e2b62..2c9a23f 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -19,14 +19,10 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.Menu;
-import android.view.View;
-
-import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
@@ -87,5 +83,4 @@
      */
     boolean shouldMoveToDefaultScreenOnHomeIntent();
     boolean hasSettings();
-    List<ComponentKeyMapper<AppInfo>> getPredictedApps();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a906b00..74a5bac 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -37,7 +37,6 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.BgDataModel;
@@ -406,8 +405,6 @@
                     enqueueModelUpdateTask(new UserLockStateChangedTask(user));
                 }
             }
-        } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
-            ExtractionUtils.startColorExtractionServiceIfNecessary(context);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f222a97..25a698b 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -55,7 +55,6 @@
 import com.android.launcher3.LauncherSettings.WorkspaceScreens;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.graphics.IconShapeOverride;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.DbDowngradeHelper;
@@ -365,19 +364,6 @@
         createDbIfNotExists();
 
         switch (method) {
-            case LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID: {
-                String extractedColors = extras.getString(
-                        LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS);
-                int wallpaperId = extras.getInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID);
-                Utilities.getPrefs(getContext()).edit()
-                        .putString(ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, extractedColors)
-                        .putInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, wallpaperId)
-                        .apply();
-                mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED);
-                Bundle result = new Bundle();
-                result.putString(LauncherSettings.Settings.EXTRA_VALUE, extractedColors);
-                return result;
-            }
             case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
                 clearFlagEmptyDbCreated();
                 return null;
@@ -1153,8 +1139,7 @@
     private static class ChangeListenerWrapper implements Handler.Callback {
 
         private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1;
-        private static final int MSG_EXTRACTED_COLORS_CHANGED = 2;
-        private static final int MSG_APP_WIDGET_HOST_RESET = 3;
+        private static final int MSG_APP_WIDGET_HOST_RESET = 2;
 
         private LauncherProviderChangeListener mListener;
 
@@ -1165,9 +1150,6 @@
                     case MSG_LAUNCHER_PROVIDER_CHANGED:
                         mListener.onLauncherProviderChanged();
                         break;
-                    case MSG_EXTRACTED_COLORS_CHANGED:
-                        mListener.onExtractedColorsChanged();
-                        break;
                     case MSG_APP_WIDGET_HOST_RESET:
                         mListener.onAppWidgetHostReset();
                         break;
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 7044812..0243088 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -9,7 +9,5 @@
 
     void onLauncherProviderChanged();
 
-    void onExtractedColorsChanged();
-
     void onAppWidgetHostReset();
 }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 87f62eb..3b337ef 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -304,11 +304,6 @@
 
         public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
 
-        public static final String METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID =
-                "set_extracted_colors_and_wallpaper_id_setting";
-        public static final String EXTRA_EXTRACTED_COLORS = "extra_extractedColors";
-        public static final String EXTRA_WALLPAPER_ID = "extra_wallpaperId";
-
         public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
 
         public static final String EXTRA_VALUE = "value";
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index c2d5501..b86d413 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -110,9 +110,7 @@
         mClickListener = l;
     }
 
-    @Override
-    public boolean isReinflateRequired(int orientation) {
-        // Re inflate is required any time the widget restore status changes
+    public boolean isReinflateIfNeeded() {
         return mStartState != mInfo.restoreStatus;
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d003fa4..fff075e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -228,7 +228,6 @@
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
-    boolean mAnimatingViewIntoPlace = false;
     boolean mChildrenLayersEnabled = true;
 
     private boolean mStripScreensOnPageStopMoving = false;
@@ -406,7 +405,7 @@
             }
         }
 
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
         mLauncher.lockScreenOrientation();
         mLauncher.onInteractionBegin();
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
@@ -459,7 +458,7 @@
             removeExtraEmptyScreen(true, mDragSourceInternal != null);
         }
 
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
         mLauncher.unlockScreenOrientation(false);
 
         // Re-enable any Un/InstallShortcutReceiver and now process any queued items
@@ -1016,10 +1015,6 @@
                 || (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS);
     }
 
-    protected void onWindowVisibilityChanged (int visibility) {
-        mLauncher.onWindowVisibilityChanged(visibility);
-    }
-
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
         if (workspaceInModalState() || !isFinishedSwitchingState()) {
@@ -1048,30 +1043,6 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-    protected void reinflateWidgetsIfNecessary() {
-        final int clCount = getChildCount();
-        for (int i = 0; i < clCount; i++) {
-            CellLayout cl = (CellLayout) getChildAt(i);
-            ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
-            final int itemCount = swc.getChildCount();
-            for (int j = 0; j < itemCount; j++) {
-                View v = swc.getChildAt(j);
-
-                if (v instanceof LauncherAppWidgetHostView
-                        && v.getTag() instanceof LauncherAppWidgetInfo) {
-                    LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
-                    LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v;
-                    if (lahv.isReinflateRequired(mLauncher.getOrientation())) {
-                        // Remove and rebind the current widget (which was inflated in the wrong
-                        // orientation), but don't delete it from the database
-                        mLauncher.removeItem(lahv, info, false  /* deleteFromDb */);
-                        mLauncher.bindAppWidget(info);
-                    }
-                }
-            }
-        }
-    }
-
     @Override
     protected void determineScrollingStart(MotionEvent ev) {
         if (!isFinishedSwitchingState()) return;
@@ -1109,12 +1080,12 @@
 
     protected void onPageBeginTransition() {
         super.onPageBeginTransition();
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
     }
 
     protected void onPageEndTransition() {
         super.onPageEndTransition();
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
 
         if (mDragController.isDragging()) {
             if (workspaceInModalState()) {
@@ -1459,10 +1430,6 @@
         mWallpaperOffset.setWindowToken(null);
     }
 
-    protected void onResume() {
-        mWallpaperOffset.onResume();
-    }
-
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (mUnlockWallpaperFromDefaultPageOnLayout) {
@@ -1494,9 +1461,9 @@
         return mState == State.NORMAL || mState == State.SPRING_LOADED;
     }
 
-    @Thunk void updateChildrenLayersEnabled(boolean force) {
+    private void updateChildrenLayersEnabled() {
         boolean small = mState == State.OVERVIEW || mIsSwitchingState;
-        boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageInTransition();
+        boolean enableChildrenLayers = small || isPageInTransition();
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
             mChildrenLayersEnabled = enableChildrenLayers;
@@ -1562,19 +1529,6 @@
         }
     }
 
-    public void buildPageHardwareLayers() {
-        // force layers to be enabled just for the call to buildLayer
-        updateChildrenLayersEnabled(true);
-        if (getWindowToken() != null) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                CellLayout cl = (CellLayout) getChildAt(i);
-                cl.buildHardwareLayer();
-            }
-        }
-        updateChildrenLayersEnabled(false);
-    }
-
     protected void onWallpaperTap(MotionEvent ev) {
         final int[] position = mTempXY;
         getLocationOnScreen(position);
@@ -1768,12 +1722,12 @@
         }
         invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
 
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
     }
 
     public void onEndStateTransition() {
         mIsSwitchingState = false;
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
         mForceDrawAdjacentPages = false;
         mTransitionProgress = 1;
     }
@@ -2257,16 +2211,6 @@
             }
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
-            // Prepare it to be animated into its new position
-            // This must be called after the view has been re-parented
-            final Runnable onCompleteRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    mAnimatingViewIntoPlace = false;
-                    updateChildrenLayersEnabled(false);
-                }
-            };
-            mAnimatingViewIntoPlace = true;
             if (d.dragView.hasDrawn()) {
                 if (droppedOnOriginalCellDuringTransition) {
                     // Animate the item to its original position, while simultaneously exiting
@@ -2285,12 +2229,11 @@
                 if (isWidget) {
                     int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
                             ANIMATE_INTO_POSITION_AND_DISAPPEAR;
-                    animateWidgetDrop(info, parent, d.dragView,
-                            onCompleteRunnable, animationType, cell, false);
+                    animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
                 } else {
                     int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
                     mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
-                            onCompleteRunnable, this);
+                            null, this);
                 }
             } else {
                 d.deferDragViewCleanupPostAnimation = false;
@@ -3142,7 +3085,7 @@
 
         // hardware layers on children are enabled on startup, but should be disabled until
         // needed
-        updateChildrenLayersEnabled(false);
+        updateChildrenLayersEnabled();
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 03e9c39..d63ae41 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -115,13 +115,6 @@
     }
 
     /**
-     * Sets the current set of predicted apps.
-     */
-    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
-        mApps.setPredictedApps(apps);
-    }
-
-    /**
      * Sets the current set of apps.
      */
     public void setApps(List<AppInfo> apps) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 3364c61..2cc0781 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -6,14 +6,11 @@
 import android.animation.AnimatorSet;
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.graphics.Color;
 import android.support.animation.SpringAnimation;
-import android.support.v4.graphics.ColorUtils;
 import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -46,12 +43,8 @@
 public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener,
          SearchUiManager.OnScrollRangeChangeListener {
 
-    private static final String TAG = "AllAppsTrans";
-    private static final boolean DBG = false;
-
     private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f);
     private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
-    private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
     private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
     private final SwipeDetector.ScrollInterpolator mScrollInterpolator
             = new SwipeDetector.ScrollInterpolator();
@@ -60,10 +53,8 @@
     private static final int SINGLE_FRAME_MS = 16;
 
     private AllAppsContainerView mAppsView;
-    private int mAllAppsBackgroundColor;
     private Workspace mWorkspace;
     private Hotseat mHotseat;
-    private int mHotseatBackgroundColor;
 
     private AllAppsCaretController mCaretController;
 
@@ -112,7 +103,6 @@
         mProgress = 1f;
 
         mEvaluator = new ArgbEvaluator();
-        mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary);
         mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
     }
 
@@ -226,7 +216,7 @@
                             Action.Direction.UP,
                             containerType);
                 }
-                mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
+                mLauncher.showAppsView(true /* animated */);
                 if (hasSpringAnimationHandler()) {
                     mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */);
                     // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
@@ -249,7 +239,7 @@
                             Action.Direction.UP,
                             containerType);
                 }
-                mLauncher.showAppsView(true, /* animated */ false /* updatePredictedApps */);
+                mLauncher.showAppsView(true /* animated */);
             }
         }
     }
@@ -266,29 +256,13 @@
             // Initialize values that should not change until #onDragEnd
             mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
             mHotseat.setVisibility(View.VISIBLE);
-            mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor();
-            mHotseat.setBackgroundTransparent(true /* transparent */);
-            if (!mLauncher.isAllAppsVisible()) {
-                mLauncher.tryAndUpdatePredictedApps();
-                mAppsView.setVisibility(View.VISIBLE);
-                if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-                    mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
-                }
-            }
+            mAppsView.setVisibility(View.VISIBLE);
         }
     }
 
     private void updateLightStatusBar(float shift) {
-        // Do not modify status bar on landscape as all apps is not full bleed.
-        if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
-                && mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-            return;
-        }
-
         // Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
-        boolean forceChange = FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS ?
-                shift <= mShiftRange / 4 :
-                shift <= mStatusBarHeight / 2;
+        boolean forceChange = shift <= mShiftRange / 4;
         if (forceChange) {
             mLauncher.getSystemUiController().updateUiState(
                     SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
@@ -320,17 +294,7 @@
         float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha);
         float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
 
-        int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha),
-                mHotseatBackgroundColor, mAllAppsBackgroundColor);
-        int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha,
-                mHotseatBackgroundColor, mAllAppsBackgroundColor));
-
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            updateAllAppsBg(alpha);
-        } else {
-            mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
-        }
-
+        updateAllAppsBg(alpha);
         mAppsView.getContentView().setAlpha(alpha);
         mAppsView.setTranslationY(shiftCurrent);
 
@@ -530,7 +494,6 @@
 
     public void finishPullDown() {
         mAppsView.setVisibility(View.INVISIBLE);
-        mHotseat.setBackgroundTransparent(false /* transparent */);
         mHotseat.setVisibility(View.VISIBLE);
         mAppsView.reset();
         if (hasSpringAnimationHandler()) {
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 8e572ee..4cc70d3 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.compat;
 
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.job.JobInfo;
@@ -38,7 +42,6 @@
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.support.annotation.Nullable;
-import android.support.v7.graphics.Palette;
 import android.util.Log;
 import android.util.Pair;
 
@@ -46,12 +49,6 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-import static com.android.launcher3.Utilities.getDevicePrefs;
 
 public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
 
@@ -260,27 +257,8 @@
             String value = VERSION_PREFIX + wallpaperId;
 
             if (bitmap != null) {
-                Palette palette = Palette.from(bitmap).generate();
-                bitmap.recycle();
-
-                StringBuilder builder = new StringBuilder(value);
-                List<Pair<Integer,Integer>> colorsToOccurrences = new ArrayList<>();
-                for (Palette.Swatch swatch : palette.getSwatches()) {
-                    colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation()));
-                }
-
-                Collections.sort(colorsToOccurrences, new Comparator<Pair<Integer, Integer>>() {
-                    @Override
-                    public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
-                        return b.second - a.second;
-                    }
-                });
-
-                for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) {
-                    builder.append(',').append(colorsToOccurrences.get(i).first);
-                }
-
-                value = builder.toString();
+                int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+                value += "," + color;
             }
 
             // Send the result
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 5f6909c..8a1bc63 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -39,8 +39,6 @@
     public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
-    // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
-    public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
     // When enabled allows use of physics based motions in the Launcher.
     public static final boolean LAUNCHER3_PHYSICS = true;
     // When enabled allows use of spring motions on the icons.
@@ -50,16 +48,12 @@
     public static final boolean QSB_ON_FIRST_SCREEN = true;
     // When enabled the all-apps icon is not added to the hotseat.
     public static final boolean NO_ALL_APPS_ICON = true;
-    // When enabled the status bar may show dark icons based on the top of the wallpaper.
-    public static final boolean LIGHT_STATUS_BAR = false;
     // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
     public static final boolean LEGACY_ICON_TREATMENT = true;
     // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
     public static final boolean ADAPTIVE_ICON_SHADOW = true;
     // When enabled, app discovery will be enabled if service is implemented
     public static final boolean DISCOVERY_ENABLED = false;
-    // When enabled, the qsb will be moved to the hotseat.
-    public static final boolean QSB_IN_HOTSEAT = true;
 
     // When true, custom widgets are loaded using CustomWidgetParser.
     public static final boolean ENABLE_CUSTOM_WIDGETS = false;
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
deleted file mode 100644
index b9dd3b5..0000000
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3.dynamicui;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperManager;
-import android.app.job.JobParameters;
-import android.app.job.JobService;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
-import android.support.v7.graphics.Palette;
-import android.util.Log;
-
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.io.IOException;
-
-/**
- * Extracts colors from the wallpaper, and saves results to {@link LauncherProvider}.
- */
-public class ColorExtractionService extends JobService {
-
-    private static final String TAG = "ColorExtractionService";
-    private static final boolean DEBUG = false;
-
-    /** The fraction of the wallpaper to extract colors for use on the hotseat. */
-    private static final float HOTSEAT_FRACTION = 1f / 4;
-
-    private HandlerThread mWorkerThread;
-    private Handler mWorkerHandler;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mWorkerThread = new HandlerThread("ColorExtractionService");
-        mWorkerThread.start();
-        mWorkerHandler = new Handler(mWorkerThread.getLooper());
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mWorkerThread.quit();
-    }
-
-    @Override
-    public boolean onStartJob(final JobParameters jobParameters) {
-        if (DEBUG) Log.d(TAG, "onStartJob");
-        mWorkerHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                WallpaperManager wallpaperManager = WallpaperManager.getInstance(
-                        ColorExtractionService.this);
-                int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager);
-
-                ExtractedColors extractedColors = new ExtractedColors();
-                if (wallpaperManager.getWallpaperInfo() != null) {
-                    // We can't extract colors from live wallpapers; always use the default color.
-                    extractedColors.updateHotseatPalette(null);
-
-                    if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-                        extractedColors.updateWallpaperThemePalette(null);
-                    }
-                } else {
-                    // We extract colors for the hotseat and status bar separately,
-                    // since they only consider part of the wallpaper.
-                    extractedColors.updateHotseatPalette(getHotseatPalette());
-
-                    if (FeatureFlags.LIGHT_STATUS_BAR) {
-                        extractedColors.updateStatusBarPalette(getStatusBarPalette());
-                    }
-
-                    if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-                        extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
-                    }
-                }
-
-                // Save the extracted colors and wallpaper id to LauncherProvider.
-                String colorsString = extractedColors.encodeAsString();
-                Bundle extras = new Bundle();
-                extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId);
-                extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString);
-                getContentResolver().call(
-                        LauncherSettings.Settings.CONTENT_URI,
-                        LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID,
-                        null, extras);
-                jobFinished(jobParameters, false /* needsReschedule */);
-                if (DEBUG) Log.d(TAG, "job finished!");
-            }
-        });
-        return true;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters jobParameters) {
-        if (DEBUG) Log.d(TAG, "onStopJob");
-        mWorkerHandler.removeCallbacksAndMessages(null);
-        return true;
-    }
-
-    @TargetApi(Build.VERSION_CODES.N)
-    private Palette getHotseatPalette() {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
-        if (Utilities.ATLEAST_NOUGAT) {
-            try (ParcelFileDescriptor fd = wallpaperManager
-                    .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
-                BitmapRegionDecoder decoder = BitmapRegionDecoder
-                        .newInstance(fd.getFileDescriptor(), false);
-                int height = decoder.getHeight();
-                Rect decodeRegion = new Rect(0, (int) (height * (1f - HOTSEAT_FRACTION)),
-                        decoder.getWidth(), height);
-                Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
-                decoder.recycle();
-                if (bitmap != null) {
-                    return Palette.from(bitmap).clearFilters().generate();
-                }
-            } catch (IOException | NullPointerException e) {
-                Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
-            }
-        }
-
-        Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
-        return Palette.from(wallpaper)
-                .setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
-                        wallpaper.getWidth(), wallpaper.getHeight())
-                .clearFilters()
-                .generate();
-    }
-
-    @TargetApi(Build.VERSION_CODES.N)
-    private Palette getStatusBarPalette() {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
-        int statusBarHeight = getResources()
-                .getDimensionPixelSize(R.dimen.status_bar_height);
-
-        if (Utilities.ATLEAST_NOUGAT) {
-            try (ParcelFileDescriptor fd = wallpaperManager
-                    .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
-                BitmapRegionDecoder decoder = BitmapRegionDecoder
-                        .newInstance(fd.getFileDescriptor(), false);
-                Rect decodeRegion = new Rect(0, 0,
-                        decoder.getWidth(), statusBarHeight);
-                Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
-                decoder.recycle();
-                if (bitmap != null) {
-                    return Palette.from(bitmap).clearFilters().generate();
-                }
-            } catch (IOException | NullPointerException e) {
-                Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
-            }
-        }
-
-        Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
-        return Palette.from(wallpaper)
-                .setRegion(0, 0, wallpaper.getWidth(), statusBarHeight)
-                .clearFilters()
-                .generate();
-    }
-
-    @TargetApi(Build.VERSION_CODES.N)
-    private Palette getWallpaperPalette() {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
-        if (Utilities.ATLEAST_NOUGAT) {
-            try (ParcelFileDescriptor fd = wallpaperManager
-                    .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
-                Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
-                if (bitmap != null) {
-                    return Palette.from(bitmap).clearFilters().generate();
-                }
-            } catch (IOException | NullPointerException e) {
-                Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
-            }
-        }
-
-        Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
-        return Palette.from(wallpaper).clearFilters().generate();
-    }
-}
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
deleted file mode 100644
index 2d8bb86..0000000
--- a/src/com/android/launcher3/dynamicui/ExtractedColors.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3.dynamicui;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.graphics.Color;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.graphics.Palette;
-import android.util.Log;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id.
- */
-public class ExtractedColors {
-    private static final String TAG = "ExtractedColors";
-
-    public static final int DEFAULT_LIGHT = Color.WHITE;
-    public static final int DEFAULT_DARK = Color.BLACK;
-
-    // These color profile indices should NOT be changed, since they are used when saving and
-    // loading extracted colors. New colors should always be added at the end.
-    public static final int VERSION_INDEX = 0;
-    public static final int HOTSEAT_INDEX = 1;
-    public static final int STATUS_BAR_INDEX = 2;
-    public static final int WALLPAPER_VIBRANT_INDEX = 3;
-    public static final int ALLAPPS_GRADIENT_MAIN_INDEX = 4;
-    public static final int ALLAPPS_GRADIENT_SECONDARY_INDEX = 5;
-
-    private static final int VERSION;
-    private static final int[] DEFAULT_VALUES;
-
-    static {
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            VERSION = 3;
-            DEFAULT_VALUES = new int[] {
-                    VERSION,            // VERSION_INDEX
-                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
-                    DEFAULT_DARK,       // STATUS_BAR_INDEX
-                    0xFFCCCCCC,         // WALLPAPER_VIBRANT_INDEX
-                    0xFF000000,         // ALLAPPS_GRADIENT_MAIN_INDEX
-                    0xFF000000          // ALLAPPS_GRADIENT_SECONDARY_INDEX
-            };
-        } else if (FeatureFlags.QSB_IN_HOTSEAT) {
-            VERSION = 2;
-            DEFAULT_VALUES = new int[] {
-                    VERSION,            // VERSION_INDEX
-                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
-                    DEFAULT_DARK,       // STATUS_BAR_INDEX
-                    0xFFCCCCCC,         // WALLPAPER_VIBRANT_INDEX
-            };
-        } else {
-            VERSION = 1;
-            DEFAULT_VALUES = new int[] {
-                    VERSION,            // VERSION_INDEX
-                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
-                    DEFAULT_DARK,       // STATUS_BAR_INDEX
-            };
-        }
-    }
-
-    private static final String COLOR_SEPARATOR = ",";
-
-    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
-    private final int[] mColors;
-
-    public ExtractedColors() {
-        // The first entry is reserved for the version number.
-        mColors = Arrays.copyOf(DEFAULT_VALUES, DEFAULT_VALUES.length);
-    }
-
-    public void setColorAtIndex(int index, int color) {
-        if (index > VERSION_INDEX && index < mColors.length) {
-            mColors[index] = color;
-        } else {
-            Log.e(TAG, "Attempted to set a color at an invalid index " + index);
-        }
-    }
-
-    /**
-     * Encodes {@link #mColors} as a comma-separated String.
-     */
-    String encodeAsString() {
-        StringBuilder colorsStringBuilder = new StringBuilder();
-        for (int color : mColors) {
-            colorsStringBuilder.append(color).append(COLOR_SEPARATOR);
-        }
-        return colorsStringBuilder.toString();
-    }
-
-    /**
-     * Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}.
-     * These were saved there in {@link ColorExtractionService}.
-     */
-    public void load(Context context) {
-        String encodedString = Utilities.getPrefs(context).getString(
-                ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
-
-        String[] splitColorsString = encodedString.split(COLOR_SEPARATOR);
-        if (splitColorsString.length == DEFAULT_VALUES.length &&
-                Integer.parseInt(splitColorsString[VERSION_INDEX]) == VERSION) {
-            // Parse and apply the saved values.
-            for (int i = 0; i < mColors.length; i++) {
-                mColors[i] = Integer.parseInt(splitColorsString[i]);
-            }
-        } else {
-            // Leave the values as default values as the saved values may not be compatible.
-            ExtractionUtils.startColorExtractionService(context);
-        }
-    }
-
-    /** @param index must be one of the index values defined at the top of this class. */
-    public int getColor(int index) {
-        return mColors[index];
-    }
-
-    /**
-     * The hotseat's color is defined as follows:
-     * - 12% black for super light wallpaper
-     * - 18% white for super dark
-     * - 25% white otherwise
-     */
-    public void updateHotseatPalette(Palette hotseatPalette) {
-        int hotseatColor;
-        if (hotseatPalette != null && ExtractionUtils.isSuperLight(hotseatPalette)) {
-            hotseatColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.12f * 255));
-        } else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
-            hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
-        } else {
-            hotseatColor = DEFAULT_VALUES[HOTSEAT_INDEX];
-        }
-        setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
-    }
-
-    public void updateStatusBarPalette(Palette statusBarPalette) {
-        setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ?
-                DEFAULT_LIGHT : DEFAULT_DARK);
-    }
-
-    public void updateWallpaperThemePalette(@Nullable Palette wallpaperPalette) {
-        int defaultColor = DEFAULT_VALUES[WALLPAPER_VIBRANT_INDEX];
-        setColorAtIndex(WALLPAPER_VIBRANT_INDEX, wallpaperPalette == null
-                ? defaultColor : wallpaperPalette.getVibrantColor(defaultColor));
-    }
-
-    public void addOnChangeListener(OnChangeListener listener) {
-        mListeners.add(listener);
-    }
-
-    public void notifyChange() {
-        for (OnChangeListener listener : mListeners) {
-            listener.onExtractedColorsChanged();
-        }
-    }
-
-    /**
-     * Interface for listening for extracted color changes
-     */
-    public interface OnChangeListener {
-
-        void onExtractedColorsChanged();
-    }
-}
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
deleted file mode 100644
index cc0e0be..0000000
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3.dynamicui;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperManager;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.Color;
-import android.os.Build;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.graphics.Palette;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.List;
-
-/**
- * Contains helper fields and methods related to extracting colors from the wallpaper.
- */
-public class ExtractionUtils {
-    public static final String EXTRACTED_COLORS_PREFERENCE_KEY = "pref_extractedColors";
-    public static final String WALLPAPER_ID_PREFERENCE_KEY = "pref_wallpaperId";
-
-    private static final float MIN_CONTRAST_RATIO = 2f;
-
-    /**
-     * Extract colors in the :wallpaper-chooser process, if the wallpaper id has changed.
-     * When the new colors are saved in the LauncherProvider,
-     * Launcher will be notified in Launcher#onSettingsChanged(String, String).
-     */
-    public static void startColorExtractionServiceIfNecessary(final Context context) {
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            return;
-        }
-        // Run on a background thread, since the service is asynchronous anyway.
-        Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
-            @Override
-            public void run() {
-                if (hasWallpaperIdChanged(context)) {
-                    startColorExtractionService(context);
-                }
-            }
-        });
-    }
-
-    /** Starts the {@link ColorExtractionService} without checking the wallpaper id */
-    public static void startColorExtractionService(Context context) {
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            return;
-        }
-        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
-                Context.JOB_SCHEDULER_SERVICE);
-        jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
-                new ComponentName(context, ColorExtractionService.class))
-                .setMinimumLatency(0).build());
-    }
-
-    private static boolean hasWallpaperIdChanged(Context context) {
-        if (!Utilities.ATLEAST_NOUGAT) {
-            // TODO: update an id in sharedprefs in onWallpaperChanged broadcast, and read it here.
-            return false;
-        }
-        final SharedPreferences sharedPrefs = Utilities.getPrefs(context);
-        int wallpaperId = getWallpaperId(WallpaperManager.getInstance(context));
-        int savedWallpaperId = sharedPrefs.getInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, -1);
-        return wallpaperId != savedWallpaperId;
-    }
-
-    @TargetApi(Build.VERSION_CODES.N)
-    public static int getWallpaperId(WallpaperManager wallpaperManager) {
-        return Utilities.ATLEAST_NOUGAT ?
-                wallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM) : -1;
-    }
-
-    public static boolean isSuperLight(Palette p) {
-        return !isLegibleOnWallpaper(Color.WHITE, p.getSwatches());
-    }
-
-    public static boolean isSuperDark(Palette p) {
-        return !isLegibleOnWallpaper(Color.BLACK, p.getSwatches());
-    }
-
-    /**
-     * Given a color, returns true if that color is legible on
-     * the given wallpaper color swatches, else returns false.
-     */
-    private static boolean isLegibleOnWallpaper(int color, List<Palette.Swatch> wallpaperSwatches) {
-        int legiblePopulation = 0;
-        int illegiblePopulation = 0;
-        for (Palette.Swatch swatch : wallpaperSwatches) {
-            if (isLegible(color, swatch.getRgb())) {
-                legiblePopulation += swatch.getPopulation();
-            } else {
-                illegiblePopulation += swatch.getPopulation();
-            }
-        }
-        return legiblePopulation > illegiblePopulation;
-    }
-
-    /** @return Whether the foreground color is legible on the background color. */
-    private static boolean isLegible(int foreground, int background) {
-        background = ColorUtils.setAlphaComponent(background, 255);
-        return ColorUtils.calculateContrast(foreground, background) >= MIN_CONTRAST_RATIO;
-    }
-
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 47c2ffb..d6ef5b4 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -20,8 +20,6 @@
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.dynamicui.ExtractedColors;
-
 /**
  * Base class for a page indicator.
  */
@@ -74,8 +72,6 @@
 
     public void setShouldAutoHide(boolean shouldAutoHide) {}
 
-    public void updateColor(ExtractedColors extractedColors) {}
-
     @Override
     protected boolean verifyDrawable(Drawable who) {
         return super.verifyDrawable(who) || who == getCaretDrawable();
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 6281fec..5eedd92 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -11,9 +11,7 @@
 import android.graphics.Paint;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Property;
 import android.view.ViewConfiguration;
 import android.widget.ImageView;
@@ -21,8 +19,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.dynamicui.WallpaperColorInfo;
 
 /**
@@ -31,9 +27,6 @@
  * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
  */
 public class PageIndicatorLineCaret extends PageIndicator {
-    private static final String TAG = "PageIndicatorLine";
-
-    private static final int[] sTempCoords = new int[2];
 
     private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
     private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -218,32 +211,6 @@
         }
     }
 
-    /**
-     * The line's color will be:
-     * - mostly opaque white if the hotseat is white (ignoring alpha)
-     * - mostly opaque black if the hotseat is black (ignoring alpha)
-     */
-    public void updateColor(ExtractedColors extractedColors) {
-        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
-            return;
-        }
-        int originalLineAlpha = mLinePaint.getAlpha();
-        int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
-        if (color != Color.TRANSPARENT) {
-            color = ColorUtils.setAlphaComponent(color, 255);
-            if (color == Color.BLACK) {
-                mActiveAlpha = BLACK_ALPHA;
-            } else if (color == Color.WHITE) {
-                mActiveAlpha = WHITE_ALPHA;
-            } else {
-                Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
-                        + Integer.toHexString(color));
-            }
-            mLinePaint.setColor(color);
-            mLinePaint.setAlpha(originalLineAlpha);
-        }
-    }
-
     private void animateLineToAlpha(int alpha) {
         if (alpha == mToAlpha) {
             // Ignore the new animation if it is going to the same alpha as the current animation.
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 355963b..a1a4d75 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -7,13 +7,10 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * This class represents a very trivial LauncherExtension. It primarily serves as a simple
@@ -150,12 +147,6 @@
         }
 
         @Override
-        public List<ComponentKeyMapper<AppInfo>> getPredictedApps() {
-            // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
-            return new ArrayList<>();
-        }
-
-        @Override
         public void onAttachedToWindow() {
         }
 
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index f99efce..ec494f1 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -1,9 +1,15 @@
 package com.android.launcher3.util;
 
 import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
+import android.os.SystemClock;
 import android.util.Log;
-import android.view.Choreographer;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
@@ -13,60 +19,30 @@
 /**
  * Utility class to handle wallpaper scrolling along with workspace.
  */
-public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
+public class WallpaperOffsetInterpolator extends BroadcastReceiver {
+
+    private static final int[] sTempInt = new int[2];
     private static final String TAG = "WPOffsetInterpolator";
     private static final int ANIMATION_DURATION = 250;
 
     // Don't use all the wallpaper for parallax until you have at least this many pages
     private static final int MIN_PARALLAX_PAGE_SPAN = 4;
 
-    private final Choreographer mChoreographer;
-    private final Interpolator mInterpolator;
-    private final WallpaperManager mWallpaperManager;
     private final Workspace mWorkspace;
     private final boolean mIsRtl;
+    private final Handler mHandler;
 
+    private boolean mRegistered = false;
     private IBinder mWindowToken;
     private boolean mWallpaperIsLiveWallpaper;
-    private float mLastSetWallpaperOffsetSteps = 0;
 
-    private float mFinalOffset = 0.0f;
-    private float mCurrentOffset = 0.5f; // to force an initial update
-    private boolean mWaitingForUpdate;
     private boolean mLockedToDefaultPage;
-
-    private boolean mAnimating;
-    private long mAnimationStartTime;
-    private float mAnimationStartOffset;
-    int mNumScreens;
-    int mNumPagesForWallpaperParallax;
+    private int mNumScreens;
 
     public WallpaperOffsetInterpolator(Workspace workspace) {
-        mChoreographer = Choreographer.getInstance();
-        mInterpolator = new DecelerateInterpolator(1.5f);
-
         mWorkspace = workspace;
-        mWallpaperManager = WallpaperManager.getInstance(workspace.getContext());
         mIsRtl = Utilities.isRtl(workspace.getResources());
-    }
-
-    @Override
-    public void doFrame(long frameTimeNanos) {
-        updateOffset(false);
-    }
-
-    private void updateOffset(boolean force) {
-        if (mWaitingForUpdate || force) {
-            mWaitingForUpdate = false;
-            if (computeScrollOffset() && mWindowToken != null) {
-                try {
-                    mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f);
-                    setWallpaperOffsetSteps();
-                } catch (IllegalArgumentException e) {
-                    Log.e(TAG, "Error updating wallpaper offset: " + e);
-                }
-            }
-        }
+        mHandler = new OffsetHandler(workspace.getContext());
     }
 
     /**
@@ -80,46 +56,25 @@
         return mLockedToDefaultPage;
     }
 
-    public boolean computeScrollOffset() {
-        final float oldOffset = mCurrentOffset;
-        if (mAnimating) {
-            long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
-            float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
-            float t1 = mInterpolator.getInterpolation(t0);
-            mCurrentOffset = mAnimationStartOffset +
-                    (mFinalOffset - mAnimationStartOffset) * t1;
-            mAnimating = durationSinceAnimation < ANIMATION_DURATION;
-        } else {
-            mCurrentOffset = mFinalOffset;
-        }
-
-        if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
-            scheduleUpdate();
-        }
-        if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
-            return true;
-        }
-        return false;
-    }
-
     /**
+     * Computes the wallpaper offset as an int ratio (out[0] / out[1])
+     *
      * TODO: do different behavior if it's  a live wallpaper?
      */
-    public float wallpaperOffsetForScroll(int scroll) {
+    private void wallpaperOffsetForScroll(int scroll, int numScrollingPages, final int[] out) {
+        out[1] = 1;
+
         // To match the default wallpaper behavior in the system, we default to either the left
         // or right edge on initialization
-        int numScrollingPages = getNumScreensExcludingEmpty();
         if (mLockedToDefaultPage || numScrollingPages <= 1) {
-            return mIsRtl ? 1f : 0f;
+            out[0] =  mIsRtl ? 1 : 0;
+            return;
         }
 
         // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace
         // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN)
-        if (mWallpaperIsLiveWallpaper) {
-            mNumPagesForWallpaperParallax = numScrollingPages;
-        } else {
-            mNumPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
-        }
+        int numPagesForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollingPages :
+                        Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
 
         // Offset by the custom screen
         int leftPageIndex;
@@ -136,106 +91,184 @@
         int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex);
         int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex);
         int scrollRange = rightPageScrollX - leftPageScrollX;
-        if (scrollRange == 0) {
-            return 0f;
+        if (scrollRange <= 0) {
+            out[0] = 0;
+            return;
         }
 
         // Sometimes the left parameter of the pages is animated during a layout transition;
         // this parameter offsets it to keep the wallpaper from animating as well
         int adjustedScroll = scroll - leftPageScrollX -
                 mWorkspace.getLayoutTransitionOffsetForPage(0);
-        float offset = Utilities.boundToRange((float) adjustedScroll / scrollRange, 0f, 1f);
+        adjustedScroll = Utilities.boundToRange(adjustedScroll, 0, scrollRange);
+        out[1] = (numPagesForWallpaperParallax - 1) * scrollRange;
 
         // The offset is now distributed 0..1 between the left and right pages that we care about,
         // so we just map that between the pages that we are using for parallax
-        float rtlOffset = 0;
+        int rtlOffset = 0;
         if (mIsRtl) {
             // In RTL, the pages are right aligned, so adjust the offset from the end
-            rtlOffset = (float) ((mNumPagesForWallpaperParallax - 1) - (numScrollingPages - 1)) /
-                    (mNumPagesForWallpaperParallax - 1);
+            rtlOffset = out[1] - (numScrollingPages - 1) * scrollRange;
         }
-        return rtlOffset + offset *
-                ((float) (numScrollingPages - 1) / (mNumPagesForWallpaperParallax - 1));
+        out[0] = rtlOffset + adjustedScroll * (numScrollingPages - 1);
     }
 
-    private float wallpaperOffsetForCurrentScroll() {
-        return wallpaperOffsetForScroll(mWorkspace.getScrollX());
-    }
-
-    private int numEmptyScreensToIgnore() {
-        int numScrollingPages = mWorkspace.getChildCount();
-        if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
-            return 1;
-        } else {
-            return 0;
-        }
+    public float wallpaperOffsetForScroll(int scroll) {
+        wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt);
+        return ((float) sTempInt[0]) / sTempInt[1];
     }
 
     private int getNumScreensExcludingEmpty() {
-        return mWorkspace.getChildCount() - numEmptyScreensToIgnore();
+        int numScrollingPages = mWorkspace.getChildCount();
+        if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
+            return numScrollingPages - 1;
+        } else {
+            return numScrollingPages;
+        }
     }
 
     public void syncWithScroll() {
-        float offset = wallpaperOffsetForCurrentScroll();
-        setFinalX(offset);
-        updateOffset(true);
-    }
-
-    public float getCurrX() {
-        return mCurrentOffset;
-    }
-
-    public float getFinalX() {
-        return mFinalOffset;
-    }
-
-    private void animateToFinal() {
-        mAnimating = true;
-        mAnimationStartOffset = mCurrentOffset;
-        mAnimationStartTime = System.currentTimeMillis();
-    }
-
-    private void setWallpaperOffsetSteps() {
-        // Set wallpaper offset steps (1 / (number of screens - 1))
-        float xOffset = 1.0f / (mNumPagesForWallpaperParallax - 1);
-        if (xOffset != mLastSetWallpaperOffsetSteps) {
-            mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
-            mLastSetWallpaperOffsetSteps = xOffset;
-        }
-    }
-
-    public void setFinalX(float x) {
-        scheduleUpdate();
-        mFinalOffset = Math.max(0f, Math.min(x, 1f));
-        if (getNumScreensExcludingEmpty() != mNumScreens) {
-            if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) {
-                // Don't animate if we're going from 0 screens, or if the final offset is the same
-                // as the current offset
-                animateToFinal();
+        int numScreens = getNumScreensExcludingEmpty();
+        wallpaperOffsetForScroll(mWorkspace.getScrollX(), numScreens, sTempInt);
+        Message msg = Message.obtain(mHandler, MSG_UPDATE_OFFSET, sTempInt[0], sTempInt[1],
+                mWindowToken);
+        if (numScreens != mNumScreens) {
+            if (mNumScreens > 0) {
+                // Don't animate if we're going from 0 screens
+                msg.what = MSG_START_ANIMATION;
             }
-            mNumScreens = getNumScreensExcludingEmpty();
+            mNumScreens = numScreens;
+            updateOffset();
         }
+        msg.sendToTarget();
     }
 
-    private void scheduleUpdate() {
-        if (!mWaitingForUpdate) {
-            mChoreographer.postFrameCallback(this);
-            mWaitingForUpdate = true;
+    private void updateOffset() {
+        int numPagesForWallpaperParallax;
+        if (mWallpaperIsLiveWallpaper) {
+            numPagesForWallpaperParallax = mNumScreens;
+        } else {
+            numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
         }
+        Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0,
+                mWindowToken).sendToTarget();
     }
 
     public void jumpToFinal() {
-        mCurrentOffset = mFinalOffset;
-    }
-
-    public void onResume() {
-        mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
-        // Force the wallpaper offset steps to be set again, because another app might have changed
-        // them
-        mLastSetWallpaperOffsetSteps = 0f;
+        Message.obtain(mHandler, MSG_JUMP_TO_FINAL, mWindowToken).sendToTarget();
     }
 
     public void setWindowToken(IBinder token) {
         mWindowToken = token;
+        if (mWindowToken == null && mRegistered) {
+            mWorkspace.getContext().unregisterReceiver(this);
+            mRegistered = false;
+        } else if (mWindowToken != null && !mRegistered) {
+            mWorkspace.getContext()
+                    .registerReceiver(this, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+            onReceive(mWorkspace.getContext(), null);
+            mRegistered = true;
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mWallpaperIsLiveWallpaper =
+                WallpaperManager.getInstance(mWorkspace.getContext()).getWallpaperInfo() != null;
+        updateOffset();
+    }
+
+    private static final int MSG_START_ANIMATION = 1;
+    private static final int MSG_UPDATE_OFFSET = 2;
+    private static final int MSG_APPLY_OFFSET = 3;
+    private static final int MSG_SET_NUM_PARALLAX = 4;
+    private static final int MSG_JUMP_TO_FINAL = 5;
+
+    private static class OffsetHandler extends Handler {
+
+        private final Interpolator mInterpolator;
+        private final WallpaperManager mWM;
+
+        private float mCurrentOffset = 0.5f; // to force an initial update
+        private boolean mAnimating;
+        private long mAnimationStartTime;
+        private float mAnimationStartOffset;
+
+        private float mFinalOffset;
+        private float mOffsetX;
+
+        public OffsetHandler(Context context) {
+            super(UiThreadHelper.getBackgroundLooper());
+            mInterpolator = new DecelerateInterpolator(1.5f);
+            mWM = WallpaperManager.getInstance(context);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final IBinder token = (IBinder) msg.obj;
+            if (token == null) {
+                return;
+            }
+
+            switch (msg.what) {
+                case MSG_START_ANIMATION: {
+                    mAnimating = true;
+                    mAnimationStartOffset = mCurrentOffset;
+                    mAnimationStartTime = msg.getWhen();
+                    // Follow through
+                }
+                case MSG_UPDATE_OFFSET:
+                    mFinalOffset = ((float) msg.arg1) / msg.arg2;
+                    // Follow through
+                case MSG_APPLY_OFFSET: {
+                    float oldOffset = mCurrentOffset;
+                    if (mAnimating) {
+                        long durationSinceAnimation = SystemClock.uptimeMillis()
+                                - mAnimationStartTime;
+                        float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
+                        float t1 = mInterpolator.getInterpolation(t0);
+                        mCurrentOffset = mAnimationStartOffset +
+                                (mFinalOffset - mAnimationStartOffset) * t1;
+                        mAnimating = durationSinceAnimation < ANIMATION_DURATION;
+                    } else {
+                        mCurrentOffset = mFinalOffset;
+                    }
+
+                    if (Float.compare(mCurrentOffset, oldOffset) != 0) {
+                        setOffsetSafely(token);
+                        // Force the wallpaper offset steps to be set again, because another app
+                        // might have changed them
+                        mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f);
+                    }
+                    if (mAnimating) {
+                        // If we are animating, keep updating the offset
+                        Message.obtain(this, MSG_APPLY_OFFSET, token).sendToTarget();
+                    }
+                    return;
+                }
+                case MSG_SET_NUM_PARALLAX: {
+                    // Set wallpaper offset steps (1 / (number of screens - 1))
+                    mOffsetX = 1.0f / (msg.arg1 - 1);
+                    mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f);
+                    return;
+                }
+                case MSG_JUMP_TO_FINAL: {
+                    if (Float.compare(mCurrentOffset, mFinalOffset) != 0) {
+                        mCurrentOffset = mFinalOffset;
+                        setOffsetSafely(token);
+                    }
+                    mAnimating = false;
+                    return;
+                }
+            }
+        }
+
+        private void setOffsetSafely(IBinder token) {
+            try {
+                mWM.setWallpaperOffsets(token, mCurrentOffset, 0.5f);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Error updating wallpaper offset: " + e);
+            }
+        }
     }
 }
\ No newline at end of file