Merge "Deferring the move to the default screen until after we callback to the callbacks." into ub-launcher3-burnaby
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index f2bb509..f2459dd 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -223,14 +223,12 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     protected boolean isActivityDestroyed() {
-        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
-                && isDestroyed();
+        return Utilities.ATLEAST_JB_MR1 && isDestroyed();
     }
 
     @Thunk void addReusableBitmap(TileSource src) {
         synchronized (mReusableBitmaps) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
-                    && src instanceof BitmapRegionTileSource) {
+            if (Utilities.ATLEAST_KITKAT && src instanceof BitmapRegionTileSource) {
                 Bitmap preview = ((BitmapRegionTileSource) src).getBitmap();
                 if (preview != null && preview.isMutable()) {
                     mReusableBitmaps.add(preview);
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 75d8745..5985850 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -977,10 +977,8 @@
 
         if (partner == null || !partner.hideDefaultWallpaper()) {
             // Add an entry for the default wallpaper (stored in system resources)
-            WallpaperTileInfo defaultWallpaperInfo =
-                    (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
-                    ? getPreKKDefaultWallpaperInfo()
-                    : getDefaultWallpaper();
+            WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT
+                    ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo();
             if (defaultWallpaperInfo != null) {
                 bundled.add(0, defaultWallpaperInfo);
             }
diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml
index 0c183ea..69a66c8 100644
--- a/res/layout/all_apps_search_bar.xml
+++ b/res/layout/all_apps_search_bar.xml
@@ -38,7 +38,7 @@
             android:contentDescription="@string/all_apps_button_label"
             android:src="@drawable/ic_arrow_back_grey" />
 
-        <com.android.launcher3.allapps.AllAppsSearchEditView
+        <com.android.launcher3.ExtendedEditText
             android:id="@+id/search_box_input"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index ecf7def..275840d 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -53,7 +53,7 @@
         android:paddingLeft="8dp"
         android:paddingRight="8dp" >
 
-        <com.android.launcher3.FolderEditText
+        <com.android.launcher3.ExtendedEditText
             android:id="@+id/folder_name"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 856690d..84e1594 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -32,7 +32,7 @@
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accéder au service <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accéder à <xliff:g id="QUERY">%1$s</xliff:g>"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 46a2343..2900330 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -32,8 +32,7 @@
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
-    <!-- no translation found for all_apps_search_market_message (5470761048755751471) -->
-    <skip />
+    <string name="all_apps_search_market_message" msgid="5470761048755751471">"გადადი <xliff:g id="QUERY">%1$s</xliff:g>-ში"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 495a14b..77f07c9 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -32,8 +32,7 @@
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜索应用"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在加载应用…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
-    <!-- no translation found for all_apps_search_market_message (5470761048755751471) -->
-    <skip />
+    <string name="all_apps_search_market_message" msgid="5470761048755751471">"转到 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
     <string name="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"应用"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 589d696..b9fb6e2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -129,4 +129,8 @@
     <dimen name="blur_size_click_shadow">4dp</dimen>
     <dimen name="click_shadow_high_shift">2dp</dimen>
 
+<!-- Pending widget -->
+    <dimen name="pending_widget_min_padding">8dp</dimen>
+    <dimen name="pending_widget_elevation">2dp</dimen>
+
 </resources>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 5e7a012..b1d51ec 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -13,6 +13,7 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -48,7 +49,8 @@
             final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
             final int state;
             if (LauncherModel.isValidProvider(provider)) {
-                state = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+                // This will ensure that we show 'Click to setup' UI if required.
+                state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
             } else {
                 state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
             }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1bcaab5..5070878 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -510,7 +510,7 @@
             mIcon.setBounds(0, 0, iconSize, iconSize);
         }
         if (mLayoutHorizontal) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            if (Utilities.ATLEAST_JB_MR1) {
                 setCompoundDrawablesRelative(mIcon, null, null, null);
             } else {
                 setCompoundDrawables(mIcon, null, null, null);
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index b7f89d0..2baa6d8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -24,7 +24,6 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Configuration;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.PointF;
@@ -93,7 +92,7 @@
         // drawableLeft and drawableStart.
         mDrawable = getResources().getDrawable(resId);
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
         } else {
             setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
@@ -114,7 +113,7 @@
     @Override
     public final void onDragEnter(DragObject d) {
         d.dragView.setColor(mHoverColor);
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             animateTextColor(mHoverColor);
         } else {
             if (mCurrentFilter == null) {
@@ -131,8 +130,8 @@
         // Do nothing
     }
 
-	protected void resetHoverColor() {
-        if (Utilities.isLmpOrAbove()) {
+    protected void resetHoverColor() {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             animateTextColor(mOriginalTextColor.getDefaultColor());
         } else {
             mDrawable.setColorFilter(null);
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index dfa8202..2acfc61 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -131,7 +131,7 @@
         measure(ms, ms);
         mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
 
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             setElevation(getResources().getDimension(R.dimen.drag_elevation));
         }
     }
@@ -252,14 +252,14 @@
             setColorScale(color, m2);
             m1.postConcat(m2);
 
-            if (Utilities.isLmpOrAbove()) {
+            if (Utilities.ATLEAST_LOLLIPOP) {
                 animateFilterTo(m1.getArray());
             } else {
                 mPaint.setColorFilter(new ColorMatrixColorFilter(m1));
                 invalidate();
             }
         } else {
-            if (!Utilities.isLmpOrAbove() || mCurrentFilter == null) {
+            if (!Utilities.ATLEAST_LOLLIPOP || mCurrentFilter == null) {
                 mPaint.setColorFilter(null);
                 invalidate();
             } else {
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchEditView.java b/src/com/android/launcher3/ExtendedEditText.java
similarity index 74%
rename from src/com/android/launcher3/allapps/AllAppsSearchEditView.java
rename to src/com/android/launcher3/ExtendedEditText.java
index b7dcd66..c7b64ec 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchEditView.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps;
+package com.android.launcher3;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -22,28 +22,28 @@
 
 
 /**
- * The edit text for the search container
+ * The edit text that reports back when the back key has been pressed.
  */
-public class AllAppsSearchEditView extends EditText {
+public class ExtendedEditText extends EditText {
 
     /**
      * Implemented by listeners of the back key.
      */
     public interface OnBackKeyListener {
-        public void onBackKey();
+        public boolean onBackKey();
     }
 
     private OnBackKeyListener mBackKeyListener;
 
-    public AllAppsSearchEditView(Context context) {
-        this(context, null);
+    public ExtendedEditText(Context context) {
+        super(context);
     }
 
-    public AllAppsSearchEditView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
+    public ExtendedEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
     }
 
-    public AllAppsSearchEditView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public ExtendedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
@@ -56,7 +56,7 @@
         // If this is a back key, propagate the key back to the listener
         if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
             if (mBackKeyListener != null) {
-                mBackKeyListener.onBackKey();
+                return mBackKeyListener.onBackKey();
             }
             return false;
         }
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 5e713b2..c1aa356 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -124,7 +124,7 @@
 
     @Thunk FolderPagedView mContent;
     @Thunk View mContentWrapper;
-    FolderEditText mFolderName;
+    ExtendedEditText mFolderName;
 
     private View mFooter;
     private int mFooterHeight;
@@ -196,8 +196,15 @@
         mContent = (FolderPagedView) findViewById(R.id.folder_content);
         mContent.setFolder(this);
 
-        mFolderName = (FolderEditText) findViewById(R.id.folder_name);
-        mFolderName.setFolder(this);
+        mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
+        mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
+            @Override
+            public boolean onBackKey() {
+                // Close the activity on back key press
+                doneEditingFolderName(true);
+                return false;
+            }
+        });
         mFolderName.setOnFocusChangeListener(this);
 
         // We disable action mode for now since it messes up the view on phones
@@ -446,7 +453,7 @@
 
         Animator openFolderAnim = null;
         final Runnable onCompleteRunnable;
-        if (!Utilities.isLmpOrAbove()) {
+        if (!Utilities.ATLEAST_LOLLIPOP) {
             positionAndSizeAsIcon();
             centerAboutIcon();
 
@@ -561,7 +568,7 @@
                 public void onAnimationEnd(Animator animation) {
                     mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
                         .translationX(0)
-                        .setInterpolator(Utilities.isLmpOrAbove() ?
+                        .setInterpolator(Utilities.ATLEAST_LOLLIPOP ?
                                 AnimationUtils.loadInterpolator(mLauncher,
                                         android.R.interpolator.fast_out_slow_in)
                                 : new LogDecelerateInterpolator(100, 0));
diff --git a/src/com/android/launcher3/FolderEditText.java b/src/com/android/launcher3/FolderEditText.java
deleted file mode 100644
index c311008..0000000
--- a/src/com/android/launcher3/FolderEditText.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-public class FolderEditText extends EditText {
-
-    private Folder mFolder;
-
-    public FolderEditText(Context context) {
-        super(context);
-    }
-
-    public FolderEditText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public FolderEditText(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void setFolder(Folder folder) {
-        mFolder = folder;
-    }
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        // Catch the back button on the soft keyboard so that we can just close the activity
-        if (event.getKeyCode() == android.view.KeyEvent.KEYCODE_BACK) {
-            mFolder.doneEditingFolderName(true);
-        }
-        return super.onKeyPreIme(keyCode, event);
-    }
-}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 203e224..6f2458c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -116,8 +116,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -360,18 +358,6 @@
         }
     }
 
-    // TODO: remove this field and call method directly when Launcher3 can depend on M APIs
-    private static Method sClipRevealMethod = null;
-    static {
-        Class<?> activityOptionsClass = ActivityOptions.class;
-        try {
-            sClipRevealMethod = activityOptionsClass.getDeclaredMethod("makeClipRevealAnimation",
-                    View.class, int.class, int.class, int.class, int.class);
-        } catch (Exception e) {
-            // Earlier version
-        }
-    }
-
     @Thunk Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
@@ -667,7 +653,7 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static int generateViewId() {
-        if (Build.VERSION.SDK_INT >= 17) {
+        if (Utilities.ATLEAST_JB_MR1) {
             return View.generateViewId();
         } else {
             // View.generateViewId() is not available. The following fallback logic is a copy
@@ -1653,18 +1639,18 @@
         }
         registerReceiver(mReceiver, filter);
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
-        setupTransparentSystemBarsForLmp();
+        setupTransparentSystemBarsForLollipop();
         mAttached = true;
         mVisible = true;
     }
 
     /**
-     * Sets up transparent navigation and status bars in LMP.
+     * Sets up transparent navigation and status bars in Lollipop.
      * This method is a no-op for other platform versions.
      */
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    private void setupTransparentSystemBarsForLmp() {
-        if (Utilities.isLmpOrAbove()) {
+    private void setupTransparentSystemBarsForLollipop() {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             Window window = getWindow();
             window.getAttributes().systemUiVisibility |=
                     (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
@@ -2893,8 +2879,7 @@
             Bundle optsBundle = null;
             if (useLaunchAnimation) {
                 ActivityOptions opts = null;
-                if (sClipRevealMethod != null) {
-                    // TODO: call method directly when Launcher3 can depend on M APIs
+                if (Utilities.ATLEAST_MARSHMALLOW) {
                     int left = 0, top = 0;
                     int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
                     if (v instanceof TextView) {
@@ -2908,22 +2893,12 @@
                             height = bounds.height();
                         }
                     }
-                    try {
-                        opts = (ActivityOptions) sClipRevealMethod.invoke(null, v,
-                                left, top, width, height);
-                    } catch (IllegalAccessException e) {
-                        Log.d(TAG, "Could not call makeClipRevealAnimation: " + e);
-                        sClipRevealMethod = null;
-                    } catch (InvocationTargetException e) {
-                        Log.d(TAG, "Could not call makeClipRevealAnimation: " + e);
-                        sClipRevealMethod = null;
-                    }
-                }
-                if (opts == null && !Utilities.isLmpOrAbove()) {
+                    opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+                } else if (!Utilities.ATLEAST_LOLLIPOP) {
                     // Below L, we use a scale up animation
                     opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                                     v.getMeasuredWidth(), v.getMeasuredHeight());
-                } else if (opts == null && Utilities.isLmpMR1()) {
+                } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
                     // On L devices, we use the device default slide-up transition.
                     // On L MR1 devices, we a custom version of the slide-up transition which
                     // doesn't have the delay present in the device default.
@@ -3038,7 +3013,7 @@
 
         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
                 scaleX, scaleY);
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
         }
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
@@ -3899,7 +3874,8 @@
 
         if (!mIsSafeModeEnabled
                 && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
-                && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
+                && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+
             if (appWidgetInfo == null) {
                 if (DEBUG_WIDGETS) {
                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -3909,42 +3885,51 @@
                 LauncherModel.deleteItemFromDatabase(this, item);
                 return;
             }
-            // Note: This assumes that the id remap broadcast is received before this step.
-            // If that is not the case, the id remap will be ignored and user may see the
-            // click to setup view.
-            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
-            pendingInfo.spanX = item.spanX;
-            pendingInfo.spanY = item.spanY;
-            pendingInfo.minSpanX = item.minSpanX;
-            pendingInfo.minSpanY = item.minSpanY;
-            Bundle options = null;
-                    WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
 
-            int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
-            boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
-                    newWidgetId, appWidgetInfo, options);
+            // If we do not have a valid id, try to bind an id.
+            if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+                // Note: This assumes that the id remap broadcast is received before this step.
+                // If that is not the case, the id remap will be ignored and user may see the
+                // click to setup view.
+                PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
+                pendingInfo.spanX = item.spanX;
+                pendingInfo.spanY = item.spanY;
+                pendingInfo.minSpanX = item.minSpanX;
+                pendingInfo.minSpanY = item.minSpanY;
+                Bundle options = null;
+                        WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
 
-            // TODO consider showing a permission dialog when the widget is clicked.
-            if (!success) {
-                mAppWidgetHost.deleteAppWidgetId(newWidgetId);
-                if (DEBUG_WIDGETS) {
-                    Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
-                            + " belongs to component " + item.providerName
-                            + ", as the launcher is unable to bing a new widget id");
+                int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                        newWidgetId, appWidgetInfo, options);
+
+                // TODO consider showing a permission dialog when the widget is clicked.
+                if (!success) {
+                    mAppWidgetHost.deleteAppWidgetId(newWidgetId);
+                    if (DEBUG_WIDGETS) {
+                        Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+                                + " belongs to component " + item.providerName
+                                + ", as the launcher is unable to bing a new widget id");
+                    }
+                    LauncherModel.deleteItemFromDatabase(this, item);
+                    return;
                 }
-                LauncherModel.deleteItemFromDatabase(this, item);
-                return;
+
+                item.appWidgetId = newWidgetId;
+
+                // If the widget has a configure activity, it is still needs to set it up, otherwise
+                // the widget is ready to go.
+                item.restoreStatus = (appWidgetInfo.configure == null)
+                        ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+                        : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+                LauncherModel.updateItemInDatabase(this, item);
+            } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+                    && (appWidgetInfo.configure == null)) {
+                // If the ID is already valid, verify if we need to configure or not.
+                item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+                LauncherModel.updateItemInDatabase(this, item);
             }
-
-            item.appWidgetId = newWidgetId;
-
-            // If the widget has a configure activity, it is still needs to set it up, otherwise
-            // the widget is ready to go.
-            item.restoreStatus = (appWidgetInfo.configure == null)
-                    ? LauncherAppWidgetInfo.RESTORE_COMPLETED
-                    : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-
-            LauncherModel.updateItemInDatabase(this, item);
         }
 
         if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
@@ -4324,13 +4309,14 @@
         return oriMap[(d.getRotation() + indexOffset) % 4];
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     public void lockScreenOrientation() {
         if (mRotationEnabled) {
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            if (Utilities.ATLEAST_JB_MR2) {
+                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+            } else {
                 setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
                         .getConfiguration().orientation));
-            } else {
-                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
             }
         }
     }
@@ -4534,7 +4520,7 @@
     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
         // Called from search suggestion
         UserHandleCompat user = null;
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             UserHandle userHandle = appLaunchIntent.getParcelableExtra(Intent.EXTRA_USER);
             if (userHandle != null) {
                 user = UserHandleCompat.fromUser(userHandle);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 52f85ea..d87ad67 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -127,7 +127,7 @@
     LauncherModel setLauncher(Launcher launcher) {
         getLauncherProvider().setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
-        mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ?
+        mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
             new LauncherAccessibilityDelegate(launcher) : null;
         return mModel;
     }
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 3debef6..8eb4e63 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -65,7 +65,7 @@
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
             throws IOException {
-        if (!Utilities.isLmpOrAbove()) {
+        if (!Utilities.ATLEAST_LOLLIPOP) {
             // No restore for old devices.
             Log.i(TAG, "You shall not pass!!!");
             Log.d(TAG, "Restore is only supported on devices running Lollipop and above.");
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index c44ea6b..7470284 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.TargetApi;
@@ -36,6 +34,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.accessibility.AccessibilityManager;
+
 import com.android.launcher3.util.Thunk;
 
 class LauncherClings implements OnClickListener {
@@ -225,7 +224,7 @@
 
         // Restricted secondary users (child mode) will potentially have very few apps
         // seeded when they start up for the first time. Clings won't work well with that
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        if (Utilities.ATLEAST_JB_MR2) {
             UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
             Bundle restrictions = um.getUserRestrictions();
             if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 397db6d..2748682 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2195,7 +2195,7 @@
 
                                             // Id would be valid only if the widget restore broadcast was received.
                                             if (isIdValid) {
-                                                status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+                                                status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                                             } else {
                                                 status &= ~LauncherAppWidgetInfo
                                                         .FLAG_PROVIDER_NOT_READY;
@@ -3197,6 +3197,12 @@
                                 widgetInfo.restoreStatus &=
                                         ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
                                         ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+                                // adding this flag ensures that launcher shows 'click to setup'
+                                // if the widget has a config activity. In case there is no config
+                                // activity, it will be marked as 'restored' during bind.
+                                widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
                                 widgets.add(widgetInfo);
                                 updateItemInDatabase(context, widgetInfo);
                             }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6cbb267..20844ec 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -77,8 +77,6 @@
     static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME;
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
-    private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
-
     private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
 
     @Thunk LauncherProviderChangeListener mListener;
@@ -140,14 +138,21 @@
         return db.insert(table, nullColumnHack, values);
     }
 
+    private void reloadLauncherIfExternal() {
+        if (Binder.getCallingPid() != Process.myPid()) {
+            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+            if (app != null) {
+                app.reloadWorkspace();
+            }
+        }
+    }
+
     @Override
     public Uri insert(Uri uri, ContentValues initialValues) {
         SqlArguments args = new SqlArguments(uri);
 
-        // In very limited cases, we support system|signature permission apps to add to the db
-        String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD);
-        final boolean isExternalAll = externalAdd != null && "true".equals(externalAdd);
-        if (isExternalAll) {
+        // In very limited cases, we support system|signature permission apps to modify the db.
+        if (Binder.getCallingPid() != Process.myPid()) {
             if (!mOpenHelper.initializeExternalAdd(initialValues)) {
                 return null;
             }
@@ -161,13 +166,7 @@
         uri = ContentUris.withAppendedId(uri, rowId);
         notifyListeners();
 
-        if (isExternalAll) {
-            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-            if (app != null) {
-                app.reloadWorkspace();
-            }
-        }
-
+        reloadLauncherIfExternal();
         return uri;
     }
 
@@ -192,6 +191,7 @@
         }
 
         notifyListeners();
+        reloadLauncherIfExternal();
         return values.length;
     }
 
@@ -203,6 +203,7 @@
         try {
             ContentProviderResult[] result =  super.applyBatch(operations);
             db.setTransactionSuccessful();
+            reloadLauncherIfExternal();
             return result;
         } finally {
             db.endTransaction();
@@ -217,6 +218,7 @@
         int count = db.delete(args.table, args.where, args.args);
         if (count > 0) notifyListeners();
 
+        reloadLauncherIfExternal();
         return count;
     }
 
@@ -229,6 +231,7 @@
         int count = db.update(args.table, values, args.where, args.args);
         if (count > 0) notifyListeners();
 
+        reloadLauncherIfExternal();
         return count;
     }
 
@@ -399,7 +402,7 @@
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() {
         // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18
-        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        if (!Utilities.ATLEAST_JB_MR2) {
             return null;
         }
 
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 5a3f3dc..cdde8c1 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -205,7 +205,7 @@
             final boolean animated, final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
-        final boolean material = Utilities.isLmpOrAbove();
+        final boolean material = Utilities.ATLEAST_LOLLIPOP;
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -358,12 +358,11 @@
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
                     // Enable all necessary layers
-                    boolean isLmpOrAbove = Utilities.isLmpOrAbove();
                     for (View v : layerViews.keySet()) {
                         if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
                             v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                         }
-                        if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
+                        if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
                             v.buildLayer();
                         }
                     }
@@ -486,7 +485,7 @@
             final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
-        final boolean material = Utilities.isLmpOrAbove();
+        final boolean material = Utilities.ATLEAST_LOLLIPOP;
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -668,12 +667,11 @@
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
                     // Enable all necessary layers
-                    boolean isLmpOrAbove = Utilities.isLmpOrAbove();
                     for (View v : layerViews.keySet()) {
                         if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
                             v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                         }
-                        if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
+                        if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
                             v.buildLayer();
                         }
                     }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 218c1a3..05f0a05 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -2293,7 +2293,7 @@
         // Besides disabling the accessibility long-click, this also prevents this view from getting
         // accessibility focus.
         info.setLongClickable(false);
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
         }
     }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 08f8e56..40eadab 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -16,13 +16,17 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources.Theme;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.Layout;
 import android.text.StaticLayout;
@@ -32,6 +36,8 @@
 import android.view.View.OnClickListener;
 
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
+    private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
+    private static final float MIN_SATUNATION = 0.7f;
 
     private static Theme sPreloaderTheme;
 
@@ -47,13 +53,14 @@
     private Bitmap mIcon;
 
     private Drawable mCenterDrawable;
-    private Drawable mTopCornerDrawable;
+    private Drawable mSettingIconDrawable;
 
     private boolean mDrawableSizeChanged;
 
     private final TextPaint mPaint;
     private Layout mSetupTextLayout;
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
             boolean disabledForSafeMode) {
         super(context);
@@ -70,6 +77,10 @@
                 mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.quantum_panel_dark);
         setWillNotDraw(false);
+
+        if (Utilities.ATLEAST_LOLLIPOP) {
+            setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
+        }
     }
 
     @Override
@@ -124,10 +135,12 @@
                 FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon);
                 disabledIcon.setGhostModeEnabled(true);
                 mCenterDrawable = disabledIcon;
-                mTopCornerDrawable = null;
+                mSettingIconDrawable = null;
             } else if (isReadyForClickSetup()) {
-                mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
-                mTopCornerDrawable = new FastBitmapDrawable(mIcon);
+                mCenterDrawable = new FastBitmapDrawable(mIcon);
+                mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
+
+                updateSettingColor();
             } else {
                 if (sPreloaderTheme == null) {
                     sPreloaderTheme = getResources().newTheme();
@@ -137,13 +150,25 @@
                 FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon);
                 mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
                 mCenterDrawable.setCallback(this);
-                mTopCornerDrawable = null;
+                mSettingIconDrawable = null;
                 applyState();
             }
             mDrawableSizeChanged = true;
         }
     }
 
+    private void updateSettingColor() {
+        int color = Utilities.findDominantColorByHue(mIcon, 20);
+        // Make the dominant color bright.
+        float[] hsv = new float[3];
+        Color.colorToHSV(color, hsv);
+        hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
+        hsv[2] = 1;
+        color = Color.HSVToColor(hsv);
+
+        mSettingIconDrawable.setColorFilter(color,  PorterDuff.Mode.SRC_IN);
+    }
+
     @Override
     protected boolean verifyDrawable(Drawable who) {
         return (who == mCenterDrawable) || super.verifyDrawable(who);
@@ -169,6 +194,83 @@
                 && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
     }
 
+    private void updateDrawableBounds() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        int paddingTop = getPaddingTop();
+        int paddingBottom = getPaddingBottom();
+        int paddingLeft = getPaddingLeft();
+        int paddingRight = getPaddingRight();
+
+        int minPadding = getResources()
+                .getDimensionPixelSize(R.dimen.pending_widget_min_padding);
+
+        int availableWidth = getWidth() - paddingLeft - paddingRight - 2 * minPadding;
+        int availableHeight = getHeight() - paddingTop - paddingBottom - 2 * minPadding;
+
+        if (mSettingIconDrawable == null) {
+            int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
+                    ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
+            int maxSize = grid.iconSizePx + 2 * outset;
+            int size = Math.min(maxSize, Math.min(availableWidth, availableHeight));
+
+            mRect.set(0, 0, size, size);
+            mRect.inset(outset, outset);
+            mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+            mCenterDrawable.setBounds(mRect);
+        } else  {
+            float iconSize = Math.min(availableWidth, availableHeight);
+
+            // Use twice the setting size factor, as the setting is drawn at a corner and the
+            // icon is drawn in the center.
+            float settingIconScaleFactor = 1 + SETUP_ICON_SIZE_FACTOR * 2;
+            int maxSize = Math.max(availableWidth, availableHeight);
+            if (iconSize * settingIconScaleFactor > maxSize) {
+                // There is an overlap
+                iconSize = maxSize / settingIconScaleFactor;
+            }
+
+            int actualIconSize = (int) Math.min(iconSize, grid.iconSizePx);
+
+            // Recreate the setup text.
+            mSetupTextLayout = new StaticLayout(
+                    getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
+                    Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+            int textHeight = mSetupTextLayout.getHeight();
+
+            // Extra icon size due to the setting icon
+            float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor
+                    + grid.iconDrawablePaddingPx;
+
+            int iconTop;
+            if (minHeightWithText < availableHeight) {
+                // We can draw the text as well
+                iconTop =  (getHeight() - textHeight -
+                        grid.iconDrawablePaddingPx - actualIconSize) / 2;
+
+            } else {
+                // The text will not fit. Only draw the icons.
+                iconTop = (getHeight() - actualIconSize) / 2;
+                mSetupTextLayout = null;
+            }
+
+            mRect.set(0, 0, actualIconSize, actualIconSize);
+            mRect.offset((getWidth() - actualIconSize) / 2, iconTop);
+            mCenterDrawable.setBounds(mRect);
+
+            mRect.left = paddingLeft + minPadding;
+            mRect.right = mRect.left + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
+            mRect.top = paddingTop + minPadding;
+            mRect.bottom = mRect.top + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
+            mSettingIconDrawable.setBounds(mRect);
+
+            if (mSetupTextLayout != null) {
+                // Set up position for dragging the text
+                mRect.left = paddingLeft + minPadding;
+                mRect.top = mCenterDrawable.getBounds().bottom + grid.iconDrawablePaddingPx;
+            }
+        }
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (mCenterDrawable == null) {
@@ -176,81 +278,21 @@
             return;
         }
 
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        if (mTopCornerDrawable == null) {
-            if (mDrawableSizeChanged) {
-                int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
-                        ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
-                int maxSize = grid.iconSizePx + 2 * outset;
-                int size = Math.min(maxSize, Math.min(
-                        getWidth() - getPaddingLeft() - getPaddingRight(),
-                        getHeight() - getPaddingTop() - getPaddingBottom()));
-
-                mRect.set(0, 0, size, size);
-                mRect.inset(outset, outset);
-                mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
-                mCenterDrawable.setBounds(mRect);
-                mDrawableSizeChanged = false;
-            }
-            mCenterDrawable.draw(canvas);
-        } else  {
-            // Draw the top corner icon and "Setup" text is possible
-            if (mDrawableSizeChanged) {
-                int iconSize = grid.iconSizePx;
-                int paddingTop = getPaddingTop();
-                int paddingBottom = getPaddingBottom();
-                int paddingLeft = getPaddingLeft();
-                int paddingRight = getPaddingRight();
-
-                int availableWidth = getWidth() - paddingLeft - paddingRight;
-                int availableHeight = getHeight() - paddingTop - paddingBottom;
-
-                // Recreate the setup text.
-                mSetupTextLayout = new StaticLayout(
-                        getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
-                        Layout.Alignment.ALIGN_CENTER, 1, 0, true);
-                if (mSetupTextLayout.getLineCount() == 1) {
-                    // The text fits in a single line. No need to draw the setup icon.
-                    int size = Math.min(iconSize, Math.min(availableWidth,
-                            availableHeight - mSetupTextLayout.getHeight()));
-                    mRect.set(0, 0, size, size);
-                    mRect.offsetTo((getWidth() - mRect.width()) / 2,
-                            (getHeight() - mRect.height() - mSetupTextLayout.getHeight()
-                                    - grid.iconDrawablePaddingPx) / 2);
-
-                    mTopCornerDrawable.setBounds(mRect);
-
-                    // Update left and top to indicate the position where the text will be drawn.
-                    mRect.left = paddingLeft;
-                    mRect.top = mRect.bottom + grid.iconDrawablePaddingPx;
-                } else {
-                    // The text can't be drawn in a single line. Draw a setup icon instead.
-                    mSetupTextLayout = null;
-                    int size = Math.min(iconSize, Math.min(
-                            getWidth() - paddingLeft - paddingRight,
-                            getHeight() - paddingTop - paddingBottom));
-                    mRect.set(0, 0, size, size);
-                    mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
-                    mCenterDrawable.setBounds(mRect);
-
-                    size = Math.min(size / 2,
-                            Math.max(mRect.top - paddingTop, mRect.left - paddingLeft));
-                    mTopCornerDrawable.setBounds(paddingLeft, paddingTop,
-                            paddingLeft + size, paddingTop + size);
-                }
-                mDrawableSizeChanged = false;
-            }
-
-            if (mSetupTextLayout == null) {
-                mCenterDrawable.draw(canvas);
-                mTopCornerDrawable.draw(canvas);
-            } else {
-                canvas.save();
-                canvas.translate(mRect.left, mRect.top);
-                mSetupTextLayout.draw(canvas);
-                canvas.restore();
-                mTopCornerDrawable.draw(canvas);
-            }
+        if (mDrawableSizeChanged) {
+            updateDrawableBounds();
+            mDrawableSizeChanged = false;
         }
+
+        mCenterDrawable.draw(canvas);
+        if (mSettingIconDrawable != null) {
+            mSettingIconDrawable.draw(canvas);
+        }
+        if (mSetupTextLayout != null) {
+            canvas.save();
+            canvas.translate(mRect.left, mRect.top);
+            mSetupTextLayout.draw(canvas);
+            canvas.restore();
+        }
+
     }
 }
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0819f8c..955d401 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -38,7 +38,7 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     public static boolean supportsDrop(Context context, Object info) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        if (Utilities.ATLEAST_JB_MR2) {
             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
             Bundle restrictions = userManager.getUserRestrictions();
             if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8fd298d..adedd33 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -87,6 +87,24 @@
     private static final int[] sLoc0 = new int[2];
     private static final int[] sLoc1 = new int[2];
 
+    // TODO: use Build.VERSION_CODES when available
+    public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23;
+
+    public static final boolean ATLEAST_LOLLIPOP_MR1 =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
+
+    public static final boolean ATLEAST_LOLLIPOP =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+
+    public static final boolean ATLEAST_KITKAT =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+    public static final boolean ATLEAST_JB_MR1 =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
+
+    public static final boolean ATLEAST_JB_MR2 =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
+
     // To turn on these properties, type
     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
     private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
@@ -110,23 +128,6 @@
         return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
     }
 
-    /**
-     * Indicates if the device is running LMP or higher.
-     */
-    public static boolean isLmpOrAbove() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
-    }
-
-    public static boolean isLmpMR1OrAbove() {
-        // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22;
-        return Build.VERSION.SDK_INT >= 22;
-    }
-
-    public static boolean isLmpMR1() {
-        // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22;
-        return Build.VERSION.SDK_INT == 22;
-    }
-
     public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
         byte[] data = c.getBlob(iconIndex);
         try {
@@ -517,7 +518,7 @@
 
     @TargetApi(Build.VERSION_CODES.KITKAT)
     public static boolean isViewAttachedToWindow(View v) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+        if (ATLEAST_KITKAT) {
             return v.isAttachedToWindow();
         } else {
             // A proxy call which returns null, if the view is not attached to the window.
@@ -544,7 +545,7 @@
         AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
         for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
             if (info.provider.getPackageName().equals(providerPkg)) {
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                if (ATLEAST_JB_MR1) {
                     if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
                         return info;
                     } else if (defaultWidgetForSearchPackage == null) {
@@ -655,7 +656,7 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static boolean isRtl(Resources res) {
-        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
+        return ATLEAST_JB_MR1 &&
                 (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 08e6429..61d09a9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2013,7 +2013,7 @@
     }
 
     public void updateAccessibilityFlags() {
-        if (Utilities.isLmpOrAbove()) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
             int total = getPageCount();
             for (int i = numCustomPages(); i < total; i++) {
                 updateAccessibilityFlags((CellLayout) getPageAt(i), i);
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
index e1a2b7f..3169f84 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -25,6 +25,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.TextView;
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
@@ -54,7 +55,8 @@
     @Thunk View mSearchBarContainerView;
     private View mSearchButtonView;
     private View mDismissSearchButtonView;
-    @Thunk AllAppsSearchEditView mSearchBarEditView;
+    @Thunk
+    ExtendedEditText mSearchBarEditView;
     @Thunk AllAppsRecyclerView mAppsRecyclerView;
     @Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
         @Override
@@ -82,21 +84,23 @@
         mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
         mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
         mDismissSearchButtonView.setOnClickListener(this);
-        mSearchBarEditView = (AllAppsSearchEditView)
+        mSearchBarEditView = (ExtendedEditText)
                 mSearchBarContainerView.findViewById(R.id.search_box_input);
         mSearchBarEditView.addTextChangedListener(this);
         mSearchBarEditView.setOnEditorActionListener(this);
         mSearchBarEditView.setOnBackKeyListener(
-                new AllAppsSearchEditView.OnBackKeyListener() {
+                new ExtendedEditText.OnBackKeyListener() {
                     @Override
-                    public void onBackKey() {
+                    public boolean onBackKey() {
                         // Only hide the search field if there is no query, or if there
                         // are no filtered results
                         String query = Utilities.trim(
                                 mSearchBarEditView.getEditableText().toString());
                         if (query.isEmpty() || mApps.hasNoFilteredResults()) {
                             hideSearchField(true, mFocusRecyclerViewRunnable);
+                            return true;
                         }
+                        return false;
                     }
                 });
         return mSearchView;
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 7aa36d4..434f13d 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -40,7 +40,7 @@
     public static AppWidgetManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isLmpOrAbove()) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
                 } else {
                     sInstance = new AppWidgetManagerCompatV16(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index f7f4b7e..463cf90 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -54,10 +54,10 @@
     @Override
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
             Bundle options) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
-        } else {
+        if (Utilities.ATLEAST_JB_MR1) {
             return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options);
+        } else {
+            return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
         }
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 5858bc8..95e3ba9 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -53,7 +53,7 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isLmpOrAbove()) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index ac3d252..339c457 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.provider.Settings;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -188,7 +189,7 @@
                 // when moving a package or mounting/un-mounting external storage. Assume that
                 // it is a replacing operation.
                 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
-                        Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
+                        !Utilities.ATLEAST_KITKAT);
                 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
                     callback.onPackagesAvailable(packages, user, replacing);
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index c499083..ec5014d 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -34,7 +34,7 @@
     public static PackageInstallerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isLmpOrAbove()) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new PackageInstallerCompatVL(context);
                 } else {
                     sInstance = new PackageInstallerCompatV16();
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index ab4b721..567022b 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -34,7 +34,7 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static UserHandleCompat myUserHandle() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             return new UserHandleCompat(android.os.Process.myUserHandle());
         } else {
             return new UserHandleCompat();
@@ -55,7 +55,7 @@
 
     @Override
     public String toString() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             return mUser.toString();
         } else {
             return "";
@@ -67,7 +67,7 @@
         if (!(other instanceof UserHandleCompat)) {
             return false;
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             return mUser.equals(((UserHandleCompat) other).mUser);
         } else {
             return true;
@@ -76,7 +76,7 @@
 
     @Override
     public int hashCode() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             return mUser.hashCode();
         } else {
             return 0;
@@ -89,7 +89,7 @@
      * profiles so this is a no-op.
      */
     public void addToIntent(Intent intent, String name) {
-        if (Utilities.isLmpOrAbove() && mUser != null) {
+        if (Utilities.ATLEAST_LOLLIPOP && mUser != null) {
             intent.putExtra(name, mUser);
         }
     }
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 2ff1e7b..f708004 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -34,9 +34,9 @@
     public static UserManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isLmpOrAbove()) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new UserManagerCompatVL(context.getApplicationContext());
-                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                } else if (Utilities.ATLEAST_JB_MR1) {
                     sInstance = new UserManagerCompatV17(context.getApplicationContext());
                 } else {
                     sInstance = new UserManagerCompatV16();
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index b37f447..74fc92a 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -68,7 +68,7 @@
     private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
 
     public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) {
-        if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) {
+        if (Utilities.ATLEAST_LOLLIPOP && !UserHandleCompat.myUserHandle().equals(user)) {
             return new ManagedProfileHeuristic(context, user);
         }
         return null;
@@ -296,7 +296,7 @@
      * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
      */
     public static void processAllUsers(List<UserHandleCompat> users, Context context) {
-        if (!Utilities.isLmpOrAbove()) {
+        if (!Utilities.ATLEAST_LOLLIPOP) {
             return;
         }
         UserManagerCompat userManager = UserManagerCompat.getInstance(context);
diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java
index 3ca3aee..f2b5e5e 100644
--- a/src/com/android/launcher3/util/UiThreadCircularReveal.java
+++ b/src/com/android/launcher3/util/UiThreadCircularReveal.java
@@ -47,7 +47,7 @@
                 float progress = arg0.getAnimatedFraction();
                 outlineProvider.setProgress(progress);
                 revealView.invalidateOutline();
-                if (!Utilities.isLmpMR1OrAbove()) {
+                if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
                     revealView.invalidate();
                 }
             }
diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java
index 53b2acd..b9fccbc 100644
--- a/src/com/android/launcher3/util/WallpaperUtils.java
+++ b/src/com/android/launcher3/util/WallpaperUtils.java
@@ -24,6 +24,8 @@
 import android.os.Build;
 import android.view.WindowManager;
 
+import com.android.launcher3.Utilities;
+
 /**
  * Utility methods for wallpaper management.
  */
@@ -99,7 +101,7 @@
             int maxDim = Math.max(maxDims.x, maxDims.y);
             int minDim = Math.max(minDims.x, minDims.y);
 
-            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            if (Utilities.ATLEAST_JB_MR1) {
                 Point realSize = new Point();
                 windowManager.getDefaultDisplay().getRealSize(realSize);
                 maxDim = Math.max(realSize.x, realSize.y);
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 30b3d58..461aebb 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -15,6 +15,7 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.util.Thunk;
 
@@ -131,7 +132,7 @@
     public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
         Bundle options = null;
         Rect rect = new Rect();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
             Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
                     info.componentName, null);
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index a54626a..f1cde29 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -169,7 +169,7 @@
 
         // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
         // the end of the linear layout width + the start padding and doesn't allow scrolling.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        if (Utilities.ATLEAST_JB_MR1) {
             cellList.setPaddingRelative(mIndent, 0, 1, 0);
         } else {
             cellList.setPadding(mIndent, 0, 1, 0);