Merge "Create a singleton HashMap instead of using Collections.singleton()" into ub-launcher3-master
diff --git a/res/drawable-hdpi/widget_tile.png b/res/drawable-hdpi/widget_tile.png
deleted file mode 100644
index 572bf6f..0000000
--- a/res/drawable-hdpi/widget_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_tile.png b/res/drawable-mdpi/widget_tile.png
deleted file mode 100644
index 9652ace..0000000
--- a/res/drawable-mdpi/widget_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_tile.png b/res/drawable-xhdpi/widget_tile.png
deleted file mode 100644
index be1748d..0000000
--- a/res/drawable-xhdpi/widget_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_tile.png b/res/drawable-xxhdpi/widget_tile.png
deleted file mode 100644
index c6237db..0000000
--- a/res/drawable-xxhdpi/widget_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/quantum_panel_dark.xml b/res/drawable/quantum_panel_dark.xml
deleted file mode 100644
index b113b37..0000000
--- a/res/drawable/quantum_panel_dark.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:drawable="@drawable/quantum_panel_shape_dark"
-       android:insetBottom="@dimen/quantum_panel_outer_padding"
-       android:insetLeft="@dimen/quantum_panel_outer_padding"
-       android:insetRight="@dimen/quantum_panel_outer_padding"
-       android:insetTop="@dimen/quantum_panel_outer_padding" />
diff --git a/res/drawable/quantum_panel_shape_dark.xml b/res/drawable/round_rect_primary.xml
similarity index 88%
rename from res/drawable/quantum_panel_shape_dark.xml
rename to res/drawable/round_rect_primary.xml
index b299eb8..2c47e06 100644
--- a/res/drawable/quantum_panel_shape_dark.xml
+++ b/res/drawable/round_rect_primary.xml
@@ -16,7 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/colorSecondary" />
-    <corners
-        android:radius="2dp" />
+    <solid android:color="?android:attr/colorPrimary" />
+    <corners android:radius="2dp" />
 </shape>
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index da079da..2aae5bc 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -22,10 +22,10 @@
     android:layout_height="match_parent">
 
     <TextView
-        android:paddingLeft="16dp"
-        android:paddingRight="16dp"
-        android:paddingTop="8dp"
-        android:paddingBottom="8dp"
+        android:paddingLeft="24dp"
+        android:paddingRight="24dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="20dp"
         android:text="@string/add_item_request_drag_hint"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
@@ -33,7 +33,7 @@
     <FrameLayout
         android:theme="@style/WidgetContainerTheme"
         android:layout_width="match_parent"
-        android:background="?android:attr/colorPrimary"
+        android:background="?android:attr/colorPrimaryDark"
         android:layout_height="wrap_content">
 
         <include
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 1909f3b..5f5b38b 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -55,7 +55,7 @@
             android:focusable="true"
             android:paddingStart="@dimen/container_fastscroll_thumb_max_width"
             android:paddingEnd="@dimen/container_fastscroll_thumb_max_width"
-            android:theme="@style/CustomOverscroll.Light" />
+            android:theme="@style/AllAppsOverscroll" />
 
         <!-- Fast scroller popup -->
         <TextView
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
index d6bd0c5..6a459c3 100644
--- a/res/layout/appwidget_error.xml
+++ b/res/layout/appwidget_error.xml
@@ -19,9 +19,10 @@
     android:layout_height="match_parent"
     android:gravity="center"
     android:elevation="2dp"
+    android:padding="4dp"
     android:theme="@style/WidgetContainerTheme"
-    android:background="@drawable/quantum_panel_dark"
+    android:background="@drawable/round_rect_primary"
     android:textAppearance="?android:attr/textAppearanceMediumInverse"
-    android:textColor="@color/widgets_view_item_text_color"
+    android:textColor="?android:attr/textColorSecondary"
     android:text="@string/gadget_error_text"
     />
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index c5a6753..b9b098c 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -20,7 +20,7 @@
     android:layout_weight="1"
     android:orientation="vertical"
     android:focusable="true"
-    android:background="?android:attr/colorPrimary"
+    android:background="?android:attr/colorPrimaryDark"
     android:gravity="center_horizontal">
 
     <LinearLayout
@@ -42,10 +42,9 @@
             android:fadingEdge="horizontal"
             android:fontFamily="sans-serif-condensed"
             android:gravity="start"
-            android:shadowColor="#B0000000"
-            android:shadowRadius="2.0"
             android:singleLine="true"
-            android:textColor="@color/widgets_view_item_text_color"
+            android:maxLines="1"
+            android:textColor="?android:attr/textColorSecondary"
             android:textSize="14sp" />
 
         <!-- The original dimensions of the widget (can't be the same text as above due to different
@@ -56,11 +55,10 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="5dp"
             android:layout_marginLeft="5dp"
-            android:textColor="@color/widgets_view_item_text_color"
+            android:textColor="?android:attr/textColorSecondary"
             android:textSize="14sp"
             android:fontFamily="sans-serif-condensed"
-            android:shadowRadius="2.0"
-            android:shadowColor="#B0000000" />
+            android:alpha="0.8" />
     </LinearLayout>
 
     <!-- The image of the widget. This view does not support padding. Any placement adjustment
diff --git a/res/drawable/widgets_row_divider.xml b/res/layout/widget_list_divider.xml
similarity index 69%
rename from res/drawable/widgets_row_divider.xml
rename to res/layout/widget_list_divider.xml
index 2c3c7a2..68c9a45 100644
--- a/res/drawable/widgets_row_divider.xml
+++ b/res/layout/widget_list_divider.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size android:width="@dimen/widget_row_divider" />
-    <solid android:color="?attr/colorSecondary" />
-</shape>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/widget_row_divider"
+    android:layout_height="match_parent"
+    android:background="?android:attr/colorPrimary" />
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 530e856..5c2e230 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -19,7 +19,6 @@
     android:id="@+id/widgets_cell_list_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="?android:attr/colorPrimary"
     android:orientation="vertical"
     android:focusable="true"
     android:descendantFocusability="afterDescendants">
@@ -30,7 +29,7 @@
         android:id="@+id/section"
         android:layout_width="match_parent"
         android:layout_height="@dimen/widget_section_height"
-        android:background="?attr/colorSecondary"
+        android:background="?android:attr/colorPrimary"
         android:drawablePadding="@dimen/widget_section_horizontal_padding"
         android:ellipsize="end"
         android:focusable="true"
@@ -51,9 +50,9 @@
 
     <HorizontalScrollView
         android:id="@+id/widgets_scroll_container"
-        android:theme="@style/CustomOverscroll.Dark"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:background="?android:attr/colorPrimaryDark"
         android:scrollbars="none">
         <LinearLayout
             android:id="@+id/widgets_cell_list"
@@ -62,7 +61,6 @@
             android:paddingStart="@dimen/widget_row_padding"
             android:paddingEnd="0dp"
             android:orientation="horizontal"
-            android:divider="@drawable/widgets_row_divider"
-            android:showDividers="middle"/>
+            android:showDividers="none"/>
     </HorizontalScrollView>
 </LinearLayout>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index d193a5e..2f11c28 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -23,7 +23,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:descendantFocusability="afterDescendants"
-    launcher:revealBackground="@drawable/quantum_panel_shape_dark"
+    launcher:revealBackground="@drawable/round_rect_primary"
     android:theme="@style/WidgetContainerTheme">
 
     <View
@@ -45,7 +45,6 @@
 
         <com.android.launcher3.widget.WidgetsRecyclerView
             android:id="@+id/widgets_list_view"
-            android:theme="@style/CustomOverscroll.Dark"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
diff --git a/res/values-v25/styles.xml b/res/values-v26/styles.xml
similarity index 84%
rename from res/values-v25/styles.xml
rename to res/values-v26/styles.xml
index ed670a9..6877623 100644
--- a/res/values-v25/styles.xml
+++ b/res/values-v26/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2016 The Android Open Source Project
+* 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.
@@ -19,6 +19,6 @@
 <resources>
     <!-- Theme for the widget container. -->
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="colorSecondary">?android:attr/colorSecondary</item>
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a259362..18f409f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -80,9 +80,6 @@
         <attr name="hideParentOnDisable" format="boolean" />
     </declare-styleable>
 
-    <!-- Fallback attr for pre-API 25 support -->
-    <attr name="colorSecondary" format="reference|color" />
-
     <declare-styleable name="InvariantDeviceProfile">
         <attr name="name" format="string" />
         <attr name="minWidthDps" format="float" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index a7b507c..d166a79 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -45,11 +45,5 @@
     <color name="spring_loaded_panel_color">#40FFFFFF</color>
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
-    <!-- Widgets view -->
-    <color name="widgets_view_item_text_color">#3B3B3B</color>
-
-    <!-- Used as a fallback since colorSecondary doesn't exist pre-API 25 -->
-    <color name="fallback_secondary_color">#FF37474F</color>
-
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f00a00d..70f5b32 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -85,6 +85,11 @@
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
 
+    <dimen name="widget_preview_shadow_blur">0.5dp</dimen>
+    <dimen name="widget_preview_key_shadow_distance">1dp</dimen>
+    <dimen name="widget_preview_corner_radius">2dp</dimen>
+    <dimen name="widget_preview_cell_divider_width">0.5dp</dimen>
+
     <dimen name="widget_section_height">56dp</dimen>
     <dimen name="widget_section_icon_size">40dp</dimen>
     <dimen name="widget_section_vertical_padding">8dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8ffc53f..879c7d8 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -46,20 +46,16 @@
 
     <!-- Theme for the widget container. Overridden on API 25. -->
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="colorSecondary">@color/fallback_secondary_color</item>
+        <item name="android:colorEdgeEffect">@color/workspace_edge_effect_color</item>
+        <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
+        <item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
     </style>
 
     <!-- Overscroll effect -->
-    <style name="CustomOverscroll" />
-
-    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault.Light">
+    <style name="AllAppsOverscroll" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorEdgeEffect">@color/folder_edge_effect_color</item>
     </style>
 
-    <style name="CustomOverscroll.Dark">
-        <item name="android:colorEdgeEffect">@color/workspace_edge_effect_color</item>
-    </style>
-
     <!-- Different icons -->
     <style name="Icon">
         <item name="android:layout_width">match_parent</item>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index c834c6b..52a83dc 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -121,4 +121,6 @@
     public static AbstractFloatingView getTopOpenView(Launcher launcher) {
         return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW);
     }
+
+    public abstract int getLogContainerType();
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 5b487b6..6e9820c 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -45,6 +45,7 @@
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -132,9 +133,9 @@
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
 
         mActivityBgColor = context.getResources().getColor(R.color.quantum_panel_bg_color);
-        TypedArray ta = context.obtainStyledAttributes(new int[]{R.attr.colorSecondary});
-        mPackageBgColor = ta.getColor(0, 0);
-        ta.recycle();
+        mPackageBgColor = Utilities.getAttrColor(
+                new ContextThemeWrapper(context, R.style.WidgetContainerTheme),
+                android.R.attr.colorPrimary);
         mLowResOptions = new BitmapFactory.Options();
         // Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
         // automatically be loaded as ALPHA_8888.
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d963f43..826f686 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -111,6 +111,7 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
@@ -2272,7 +2273,11 @@
 
         if (v instanceof CellLayout) {
             if (mWorkspace.isInOverviewMode()) {
-                mWorkspace.snapToPageFromOverView(mWorkspace.indexOfChild(v));
+                int page = mWorkspace.indexOfChild(v);
+                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+                        LauncherLogProto.Action.Direction.NONE,
+                        LauncherLogProto.ContainerType.OVERVIEW, page);
+                mWorkspace.snapToPageFromOverView(page);
                 showWorkspace(true);
             }
             return;
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 354cf20..815fd10 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -69,10 +69,10 @@
         mDisabledForSafeMode = disabledForSafeMode;
 
         mPaint = new TextPaint();
-        mPaint.setColor(0xFFFFFFFF);
+        mPaint.setColor(Utilities.getAttrColor(getContext(), android.R.attr.textColorPrimary));
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
                 mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
-        setBackgroundResource(R.drawable.quantum_panel_dark);
+        setBackgroundResource(R.drawable.round_rect_primary);
         setWillNotDraw(false);
 
         setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index be3297c..78774f3 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -609,7 +609,11 @@
     }
 
     public static int getColorAccent(Context context) {
-        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        return getAttrColor(context, android.R.attr.colorAccent);
+    }
+
+    public static int getAttrColor(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
         int colorAccent = ta.getColor(0, 0);
         ta.recycle();
         return colorAccent;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 091ae99..3512210 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -18,9 +18,8 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.Rect;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -31,6 +30,7 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.ShadowGenerator;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Preconditions;
@@ -52,8 +52,6 @@
     private static final String TAG = "WidgetPreviewLoader";
     private static final boolean DEBUG = false;
 
-    private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
-
     private final HashMap<String, long[]> mPackageVersions = new HashMap<>();
 
     /**
@@ -104,7 +102,7 @@
      * sizes (landscape vs portrait).
      */
     private static class CacheDb extends SQLiteCacheHelper {
-        private static final int DB_VERSION = 5;
+        private static final int DB_VERSION = 6;
 
         private static final String TABLE_NAME = "shortcut_and_widget_previews";
         private static final String COLUMN_COMPONENT = "componentName";
@@ -299,7 +297,7 @@
         Drawable drawable = null;
         if (info.previewImage != 0) {
             try {
-                drawable = info.loadPreviewImage(launcher.getApplicationContext(), 0);
+                drawable = info.loadPreviewImage(mContext, 0);
             } catch (OutOfMemoryError e) {
                 Log.w(TAG, "Error loading widget preview for: " + info.provider, e);
                 // During OutOfMemoryError, the previous heap stack is not affected. Catching
@@ -321,17 +319,14 @@
         int previewWidth;
         int previewHeight;
 
-        Bitmap tileBitmap = null;
-
         if (widgetPreviewExists) {
             previewWidth = drawable.getIntrinsicWidth();
             previewHeight = drawable.getIntrinsicHeight();
         } else {
-            // Generate a preview image if we couldn't load one
-            tileBitmap = ((BitmapDrawable) mContext.getResources().getDrawable(
-                    R.drawable.widget_tile)).getBitmap();
-            previewWidth = tileBitmap.getWidth() * spanX;
-            previewHeight = tileBitmap.getHeight() * spanY;
+            DeviceProfile dp = launcher.getDeviceProfile();
+            int tileSize = Math.min(dp.cellWidthPx, dp.cellHeightPx);
+            previewWidth = tileSize * spanX;
+            previewHeight = tileSize * spanY;
         }
 
         // Scale to fit width only - let the widget preview be clipped in the
@@ -371,45 +366,61 @@
             drawable.setBounds(x, 0, x + previewWidth, previewHeight);
             drawable.draw(c);
         } else {
-            final Paint p = new Paint();
-            p.setFilterBitmap(true);
-            int appIconSize = launcher.getDeviceProfile().iconSizePx;
+            Resources res = mContext.getResources();
+            float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
+            float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
+            float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
 
-            // draw the spanX x spanY tiles
-            final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight());
+            RectF boxRect = new RectF(shadowBlur, shadowBlur,
+                    previewWidth - shadowBlur, previewHeight - shadowBlur - keyShadowDistance);
 
-            float tileW = scale * tileBitmap.getWidth();
-            float tileH = scale * tileBitmap.getHeight();
-            final RectF dst = new RectF(0, 0, tileW, tileH);
+            final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
+            p.setColor(0xFFFFFFFF);
 
-            float tx = x;
-            for (int i = 0; i < spanX; i++, tx += tileW) {
-                float ty = 0;
-                for (int j = 0; j < spanY; j++, ty += tileH) {
-                    dst.offsetTo(tx, ty);
-                    c.drawBitmap(tileBitmap, src, dst, p);
-                }
+            // Key shadow
+            p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
+                    ShadowGenerator.KEY_SHADOW_ALPHA << 24);
+            c.drawRoundRect(boxRect, corner, corner, p);
+
+            // Ambient shadow
+            p.setShadowLayer(shadowBlur, 0, 0, ShadowGenerator.AMBIENT_SHADOW_ALPHA << 24);
+            c.drawRoundRect(boxRect, corner, corner, p);
+
+            // Draw horizontal and vertical lines to represent individual columns.
+            p.clearShadowLayer();
+            p.setStyle(Paint.Style.STROKE);
+            p.setStrokeWidth(res.getDimension(R.dimen.widget_preview_cell_divider_width));
+            p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+            float t = boxRect.left;
+            float tileSize = boxRect.width() / spanX;
+            for (int i = 1; i < spanX; i++) {
+                t += tileSize;
+                c.drawLine(t, 0, t, previewHeight, p);
             }
 
-            // Draw the icon in the top left corner
-            // TODO: use top right for RTL
-            int minOffset = (int) (appIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
-            int smallestSide = Math.min(previewWidth, previewHeight);
-            float iconScale = Math.min((float) smallestSide / (appIconSize + 2 * minOffset), scale);
+            t = boxRect.top;
+            tileSize = boxRect.height() / spanY;
+            for (int i = 1; i < spanY; i++) {
+                t += tileSize;
+                c.drawLine(0, t, previewWidth, t, p);
+            }
 
+            // Draw icon in the center.
             try {
                 Drawable icon = info.getIcon(launcher, mIconCache);
                 if (icon != null) {
+                    int appIconSize = launcher.getDeviceProfile().iconSizePx;
+                    int iconSize = (int) Math.min(appIconSize * scale,
+                            Math.min(boxRect.width(), boxRect.height()));
+
                     icon = mutateOnMainThread(icon);
-                    int hoffset = (int) ((tileW - appIconSize * iconScale) / 2) + x;
-                    int yoffset = (int) ((tileH - appIconSize * iconScale) / 2);
-                    icon.setBounds(hoffset, yoffset,
-                            hoffset + (int) (appIconSize * iconScale),
-                            yoffset + (int) (appIconSize * iconScale));
+                    int hoffset = (previewWidth - iconSize) / 2;
+                    int yoffset = (previewHeight - iconSize) / 2;
+                    icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
                     icon.draw(c);
                 }
-            } catch (Resources.NotFoundException e) {
-            }
+            } catch (Resources.NotFoundException e) { }
             c.setBitmap(null);
         }
         return preview;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 1be40f7..23a2577 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -57,6 +57,8 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
 
@@ -192,6 +194,8 @@
                         return true;
                     }
                 } else {
+                    mLauncher.getUserEventDispatcher().logActionTapOutside(
+                            LoggerUtils.newContainerTarget(topView.getLogContainerType()));
                     topView.close(true);
 
                     // We let touches on the original icon go through so that users can launch
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 876c3c4..eacea4a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -74,6 +74,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.CircleRevealOutlineProvider;
@@ -1532,4 +1533,9 @@
     public static Folder getOpen(Launcher launcher) {
         return getOpenView(launcher, TYPE_FOLDER);
     }
+
+    @Override
+    public int getLogContainerType() {
+        return ContainerType.FOLDER;
+    }
 }
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index 9c39721..c9873d9 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -86,33 +86,26 @@
      * bitmap.
      */
     public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, true);
-    }
-
-    public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas,
-            boolean clipAlpha) {
         if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
             throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
         }
 
         // We start by removing most of the alpha channel so as to ignore shadows, and
         // other types of partial transparency when defining the shape of the object
-        if (clipAlpha) {
-            byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()];
-            ByteBuffer buffer = ByteBuffer.wrap(pixels);
-            buffer.rewind();
-            srcDst.copyPixelsToBuffer(buffer);
+        byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()];
+        ByteBuffer buffer = ByteBuffer.wrap(pixels);
+        buffer.rewind();
+        srcDst.copyPixelsToBuffer(buffer);
 
-            for (int i = 0; i < pixels.length; i++) {
-                if ((pixels[i] & 0xFF) < 188) {
-                    pixels[i] = 0;
-                }
+        for (int i = 0; i < pixels.length; i++) {
+            if ((pixels[i] & 0xFF) < 188) {
+                pixels[i] = 0;
             }
-
-            buffer.rewind();
-            srcDst.copyPixelsFromBuffer(buffer);
         }
 
+        buffer.rewind();
+        srcDst.copyPixelsFromBuffer(buffer);
+
         // calculate the outer blur first
         mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
         int[] outerBlurOffset = new int[2];
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 8aea5a0..31276ec 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -39,9 +39,9 @@
 
     // Percent of actual icon size
     private static final float KEY_SHADOW_DISTANCE = 1f/48;
-    private static final int KEY_SHADOW_ALPHA = 61;
+    public static final int KEY_SHADOW_ALPHA = 61;
 
-    private static final int AMBIENT_SHADOW_ALPHA = 30;
+    public static final int AMBIENT_SHADOW_ALPHA = 30;
 
     private static final Object LOCK = new Object();
     // Singleton object guarded by {@link #LOCK}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index b4b62a8..5f45c61 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
@@ -196,6 +197,13 @@
         dispatchUserEvent(event, null);
     }
 
+    public void logActionTapOutside(Target target) {
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Type.TOUCH),
+                target);
+        event.action.isOutside = true;
+        dispatchUserEvent(event, null);
+    }
+
     public void logActionOnContainer(int action, int dir, int containerType) {
         logActionOnContainer(action, dir, containerType, 0);
     }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b8e2dc8..7fda8b5 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -825,4 +825,9 @@
     public static PopupContainerWithArrow getOpen(Launcher launcher) {
         return getOpenView(launcher, TYPE_POPUP_CONTAINER_WITH_ARROW);
     }
+
+    @Override
+    public int getLogContainerType() {
+        return ContainerType.DEEPSHORTCUTS;
+    }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index a4698c2..c723f9e 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -178,12 +178,8 @@
         dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
 
         canvas.drawBitmap(mPreviewBitmap, src, dst, null);
-
-        // Don't clip alpha values for the drag outline if we're using the default widget preview
-        boolean clipAlpha = !(mAddInfo instanceof PendingAddWidgetInfo &&
-                (((PendingAddWidgetInfo) mAddInfo).previewImage == 0));
         HolographicOutlineHelper.getInstance(mView.getContext())
-                .applyExpensiveOutlineWithBlur(b, canvas, clipAlpha);
+                .applyExpensiveOutlineWithBlur(b, canvas);
         canvas.setBitmap(null);
 
         return b;
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 60e6e2a..38210fc 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -113,20 +113,27 @@
 
         // Add more views.
         // if there are too many, hide them.
-        int diff = infoList.size() - row.getChildCount();
+        int expectedChildCount = infoList.size() + Math.max(0, infoList.size() - 1);
+        int childCount = row.getChildCount();
 
-        if (diff > 0) {
-            for (int i = 0; i < diff; i++) {
-                WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
-                        R.layout.widget_cell, row, false);
+        if (expectedChildCount > childCount) {
+            for (int i = childCount ; i < expectedChildCount; i++) {
+                if ((i & 1) == 1) {
+                    // Add a divider for odd index
+                    mLayoutInflater.inflate(R.layout.widget_list_divider, row);
+                } else {
+                    // Add cell for even index
+                    WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+                            R.layout.widget_cell, row, false);
 
-                // set up touch.
-                widget.setOnClickListener(mIconClickListener);
-                widget.setOnLongClickListener(mIconLongClickListener);
-                row.addView(widget);
+                    // set up touch.
+                    widget.setOnClickListener(mIconClickListener);
+                    widget.setOnLongClickListener(mIconLongClickListener);
+                    row.addView(widget);
+                }
             }
-        } else if (diff < 0) {
-            for (int i=infoList.size() ; i < row.getChildCount(); i++) {
+        } else if (expectedChildCount < childCount) {
+            for (int i = expectedChildCount ; i < childCount; i++) {
                 row.getChildAt(i).setVisibility(View.GONE);
             }
         }
@@ -136,10 +143,14 @@
 
         // Bind the view in the widget horizontal tray region.
         for (int i=0; i < infoList.size(); i++) {
-            WidgetCell widget = (WidgetCell) row.getChildAt(i);
+            WidgetCell widget = (WidgetCell) row.getChildAt(2*i);
             widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
             widget.ensurePreview();
             widget.setVisibility(View.VISIBLE);
+
+            if (i > 0) {
+                row.getChildAt(2*i - 1).setVisibility(View.VISIBLE);
+            }
         }
     }
 
@@ -151,11 +162,10 @@
 
         ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
                 R.layout.widgets_list_row_view, parent, false);
-        LinearLayout cellList = (LinearLayout) container.findViewById(R.id.widgets_cell_list);
 
         // 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.
-        cellList.setPaddingRelative(mIndent, 0, 1, 0);
+        container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
 
         return new WidgetsRowViewHolder(container);
     }
@@ -163,7 +173,7 @@
     @Override
     public void onViewRecycled(WidgetsRowViewHolder holder) {
         int total = holder.cellContainer.getChildCount();
-        for (int i = 0; i < total; i++) {
+        for (int i = 0; i < total; i+=2) {
             WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
             widget.clear();
         }