Merge "Optimizing open shortcuts container lookep" into ub-launcher3-calgary
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index eae02ca..3730555 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -61,6 +61,7 @@
   SHORTCUT = 2;
   WIDGET = 3;
   FOLDER_ICON = 4;
+  DEEPSHORTCUT = 5;
 }
 
 // Used to define what type of container a Target would represent.
@@ -74,6 +75,7 @@
   OVERVIEW = 6;
   PREDICTION = 7;
   SEARCHRESULT = 8;
+  DEEPSHORTCUTS = 9;
 }
 
 // Used to define what type of control a Target would represent.
@@ -106,8 +108,16 @@
     FLING = 4;
     PINCH = 5;
   }
+ enum Direction {
+    NONE = 0;
+    UP = 1;
+    DOWN = 2;
+    LEFT = 3;
+    RIGHT = 4;
+  }
   optional Type type = 1;
   optional Touch touch = 2;
+  optional Direction dir = 3;
 }
 
 //
@@ -126,4 +136,4 @@
   optional int64 action_duration_millis = 4;
   optional int64 elapsed_container_millis = 5;
   optional int64 elapsed_session_millis = 6;
-}
\ No newline at end of file
+}
diff --git a/res/drawable/bg_white_pill.xml b/res/drawable/bg_white_pill.xml
index 186b06b..f92f739 100644
--- a/res/drawable/bg_white_pill.xml
+++ b/res/drawable/bg_white_pill.xml
@@ -16,6 +16,6 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/quantum_panel_bg_color" />
+    <solid android:color="#FFFFFF" />
     <corners android:radius="@dimen/bg_pill_radius" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/deep_shortcuts_drag_handle.xml
new file mode 100644
index 0000000..d5fca2e
--- /dev/null
+++ b/res/drawable/deep_shortcuts_drag_handle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:pathData="M20 9H4v2h16V9zM4 15h16v-2H4v2z"
+        android:fillColor="#757575"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 0199212..3f19b25 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -18,7 +18,7 @@
     android:id="@+id/search_market_text"
     android:layout_width="match_parent"
     android:layout_height="48dp"
-    android:gravity="start|center_vertical"
+    android:gravity="center"
     android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
     android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
     android:fontFamily="sans-serif-medium"
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 0895460..4fcbbd2 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -21,7 +21,7 @@
     android:elevation="@dimen/deep_shortcuts_elevation"
     android:background="@drawable/bg_white_pill">
 
-    <com.android.launcher3.BubbleTextView
+    <com.android.launcher3.shortcuts.DeepShortcutTextView
         android:id="@+id/deep_shortcut"
         style="@style/Icon.DeepShortcut"
         android:focusable="true"/>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 54b3d09..6897269 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -28,7 +28,7 @@
     <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
     <dimen name="dynamic_grid_overview_bar_spacer_width">25dp</dimen>
     <dimen name="dynamic_grid_hotseat_height">88dp</dimen>
-    <dimen name="dynamic_grid_hotseat_top_padding">12dp</dimen>
+    <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_gutter_width">24dp</dimen>
     <dimen name="dynamic_grid_workspace_top_padding">12dp</dimen>
     <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 532b701..627c433 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -81,7 +81,8 @@
         <item name="android:gravity">start|center_vertical</item>
         <item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
         <item name="android:paddingLeft">7dp</item>
-        <item name="android:drawablePadding">12dp</item>
+        <item name="android:paddingRight">12dp</item>
+        <item name="android:drawablePadding">9dp</item>
         <item name="android:textColor">@color/quantum_panel_text_color</item>
         <item name="android:shadowRadius">0</item>
         <item name="customShadows">false</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 00ec4c2..c0c6673 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -526,19 +526,23 @@
      * Sets the icon for this view based on the layout direction.
      */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public void setIcon(Drawable icon) {
+    private void setIcon(Drawable icon) {
         mIcon = icon;
         if (mIconSize != -1) {
             mIcon.setBounds(0, 0, mIconSize, mIconSize);
         }
+        applyCompoundDrawables(mIcon);
+    }
+
+    protected void applyCompoundDrawables(Drawable icon) {
         if (mLayoutHorizontal) {
             if (Utilities.ATLEAST_JB_MR1) {
-                setCompoundDrawablesRelative(mIcon, null, null, null);
+                setCompoundDrawablesRelative(icon, null, null, null);
             } else {
-                setCompoundDrawables(mIcon, null, null, null);
+                setCompoundDrawables(icon, null, null, null);
             }
         } else {
-            setCompoundDrawables(null, mIcon, null, null);
+            setCompoundDrawables(null, icon, null, null);
         }
     }
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9030dae..77f6612 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -406,7 +406,8 @@
             mTouchFeedbackView.animate().cancel();
         } else {
             if (mTouchFeedbackView.setBitmap(background)) {
-                mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets);
+                mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets,
+                        null /* clipAgainstView */);
                 mTouchFeedbackView.animateShadow();
             }
         }
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
index e2bc6ba..aad1112 100644
--- a/src/com/android/launcher3/ClickShadowView.java
+++ b/src/com/android/launcher3/ClickShadowView.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
@@ -91,13 +92,27 @@
      * Aligns the shadow with {@param view}
      * @param viewParent immediate parent of {@param view}. It must be a sibling of this view.
      */
-    public void alignWithIconView(BubbleTextView view, ViewGroup viewParent) {
+    public void alignWithIconView(BubbleTextView view, ViewGroup viewParent, View clipAgainstView) {
         float leftShift = view.getLeft() + viewParent.getLeft() - getLeft();
         float topShift = view.getTop() + viewParent.getTop() - getTop();
         int iconWidth = view.getRight() - view.getLeft();
+        int iconHeight = view.getBottom() - view.getTop();
         int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
         float drawableWidth = view.getIcon().getBounds().width();
 
+        if (clipAgainstView != null) {
+            // Set the bounds to clip against
+            int[] coords = new int[] {0, 0};
+            Utilities.getDescendantCoordRelativeToAncestor(clipAgainstView, (View) getParent(),
+                    coords, false);
+            int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding);
+            int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ;
+            setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight));
+        } else {
+            // Reset the clip bounds
+            setClipBounds(null);
+        }
+
         setTranslationX(leftShift
                 + viewParent.getTranslationX()
                 + view.getCompoundPaddingLeft() * view.getScaleX()
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5828dfc..c9d5cff 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -460,7 +460,7 @@
         qsbContainer.setLayoutParams(lp);
 
         // Layout the hotseat
-        View hotseat = launcher.findViewById(R.id.hotseat);
+        Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
         // We want the edges of the hotseat to line up with the edges of the workspace, but the
         // icons in the hotseat are a different size, and so don't line up perfectly. To account for
@@ -475,27 +475,25 @@
             lp.gravity = Gravity.RIGHT;
             lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
-            hotseat.setPadding(mInsets.left, 0, mInsets.right, 0);
+            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
+                    workspacePadding.bottom);
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
             lp.height = hotseatBarHeightPx + mInsets.bottom;
-            hotseat.findViewById(R.id.layout).setPadding(
-                    hotseatAdjustment + workspacePadding.left, 0,
-                    hotseatAdjustment + workspacePadding.right, 2 * edgeMarginPx);
-            hotseat.setPadding(0, hotseatBarTopPaddingPx, 0, mInsets.bottom);
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
+                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
+                    mInsets.bottom);
         } else {
             // For phones, layout the hotseat without any bottom margin
             // to ensure that we have space for the folders
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
             lp.height = hotseatBarHeightPx + mInsets.bottom;
-            hotseat.findViewById(R.id.layout).setPadding(
-                    hotseatAdjustment + workspacePadding.left, 0,
-                    hotseatAdjustment + workspacePadding.right, 0);
-            hotseat.setPadding(0, hotseatBarTopPaddingPx, 0, mInsets.bottom);
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
+                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
+                    mInsets.bottom);
         }
         hotseat.setLayoutParams(lp);
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0571787..f8b7c27 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -117,6 +117,7 @@
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -2590,6 +2591,8 @@
     protected void onClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
         if (!isAppsViewVisible()) {
+            mUserEventDispatcher.logActionOnControl(LauncherLogProto.Action.TAP,
+                    LauncherLogProto.ALL_APPS_BUTTON);
             showAppsView(true /* animated */, false /* resetListToTop */,
                     true /* updatePredictedApps */, false /* focusSearchBar */);
         }
@@ -2598,6 +2601,8 @@
     protected void onLongClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
         if (!isAppsViewVisible()) {
+            mUserEventDispatcher.logActionOnControl(LauncherLogProto.Action.LONGPRESS,
+                    LauncherLogProto.ALL_APPS_BUTTON);
             showAppsView(true /* animated */, false /* resetListToTop */,
                     true /* updatePredictedApps */, true /* focusSearchBar */);
         }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7b1a16c..ef78195 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -113,8 +113,8 @@
                 && "NMR1".compareTo(VERSION.CODENAME) <= 0;
     }
 
-    // TODO: use Build.VERSION_CODES when available
-    public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23;
+    public static final boolean ATLEAST_MARSHMALLOW =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
 
     public static final boolean ATLEAST_LOLLIPOP_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 97c46b1..d8f9fb1 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -66,7 +66,7 @@
     public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
     // The message shown when there are no filtered results
     public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
-    // A divider that separates the apps list and the search market button
+    // The message to continue to a market search when there are no filtered results
     public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
 
     // We use various dividers for various purposes.  They share enough attributes to reuse layouts,
@@ -528,8 +528,6 @@
                 TextView searchView = (TextView) holder.mContent;
                 if (mMarketSearchIntent != null) {
                     searchView.setVisibility(View.VISIBLE);
-                    searchView.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
-                            Gravity.START | Gravity.CENTER_VERTICAL);
                 } else {
                     searchView.setVisibility(View.GONE);
                 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 09a7d59..1d5b209 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -26,6 +27,7 @@
 import com.android.launcher3.ClickShadowView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 
 /**
  * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
@@ -63,7 +65,8 @@
             mTouchFeedbackView.setBitmap(null);
             mTouchFeedbackView.animate().cancel();
         } else if (mTouchFeedbackView.setBitmap(background)) {
-            mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent());
+            View rv = findViewById(R.id.apps_list_view);
+            mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
             mTouchFeedbackView.animateShadow();
         }
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 03b3107..136308b 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.TouchController;
 
 /**
@@ -210,6 +211,10 @@
                 calculateDuration(velocity, mAppsView.getTranslationY());
 
                 if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.getUserEventDispatcher().logActionOnContainer(
+                            LauncherLogProto.Action.FLING,
+                            LauncherLogProto.Action.UP,
+                            LauncherLogProto.HOTSEAT);
                     mLauncher.showAppsView(true, true, false, false);
                 } else {
                     animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
@@ -234,6 +239,10 @@
             } else {
                 calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
                 if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.getUserEventDispatcher().logActionOnContainer(
+                            LauncherLogProto.Action.SWIPE,
+                            LauncherLogProto.Action.UP,
+                            LauncherLogProto.HOTSEAT);
                     mLauncher.showAppsView(true, true, false, false);
                 } else {
                     animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 584e38e..9e92721 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -1,15 +1,9 @@
 package com.android.launcher3.logging;
 
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
-
 /**
  * Debugging helper methods.
  * toString() cannot be overriden inside auto generated {@link LauncherLogProto}.
@@ -24,12 +18,17 @@
             case Action.LONGPRESS: return "LONGPRESS";
             case Action.DRAGDROP: return "DRAGDROP";
             case Action.PINCH: return "PINCH";
+            case Action.SWIPE: return "SWIPE";
+            case Action.FLING: return "FLING";
             default: return "UNKNOWN";
         }
     }
 
     public static String getTargetStr(Target t) {
-        String typeStr;
+        String typeStr = "";
+        if (t == null){
+            return typeStr;
+        }
         switch (t.type) {
             case Target.ITEM:
                 return getItemStr(t);
@@ -44,6 +43,9 @@
 
     private static String getItemStr(Target t) {
         String typeStr = "";
+        if (t == null){
+            return typeStr;
+        }
         switch(t.itemType){
             case LauncherLogProto.APP_ICON: typeStr = "ICON"; break;
             case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
@@ -58,6 +60,9 @@
     }
 
     private static String getControlStr(Target t) {
+        if (t == null){
+            return "";
+        }
         switch(t.controlType) {
             case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON";
             case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON";
@@ -72,8 +77,10 @@
     }
 
     private static String getContainerStr(LauncherLogProto.Target t) {
-        String str;
-        Log.d(TAG, "t.containerType" + t.containerType);
+        String str = "";
+        if (t == null) {
+            return str;
+        }
         switch (t.containerType) {
             case LauncherLogProto.WORKSPACE:
                 str = "WORKSPACE";
@@ -122,4 +129,18 @@
         event.action.type = actionType;
         return event;
     }
+
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            int childTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[1];
+        event.srcTarget[0] = new LauncherLogProto.Target();
+        event.srcTarget[0].type = childTargetType;
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 0deee57..f67f487 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -37,8 +37,8 @@
 public class UserEventDispatcher {
 
     private static final boolean DEBUG_LOGGING = false;
-
     private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+
     /**
      * Implemented by containers to provide a launch source for a given child.
      */
@@ -46,10 +46,11 @@
 
         /**
          * Copies data from the source to the destination proto.
-         * @param v                 source of the data
-         * @param info          source of the data
-         * @param target            dest of the data
-         * @param targetParent      dest of the data
+         *
+         * @param v            source of the data
+         * @param info         source of the data
+         * @param target       dest of the data
+         * @param targetParent dest of the data
          */
         void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent);
     }
@@ -126,12 +127,23 @@
         dispatchUserEvent(createLauncherEvent(v, intent), intent);
     }
 
-    public void logTap(View v) {
-        // TODO
+    public void logActionOnControl(int action, int controlType) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTROL);
+        event.action.touch = action;
+        event.srcTarget[0].controlType = controlType;
+        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
+        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
+        dispatchUserEvent(event, null);
     }
 
-    public void logLongPress() {
-        // TODO
+    public void logActionOnContainer(int action, int dir, int containerType) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTAINER);
+        event.action.touch = action;
+        event.action.dir = dir;
+        event.srcTarget[0].containerType = containerType;
+        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
+        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
+        dispatchUserEvent(event, null);
     }
 
     public void logDragNDrop() {
@@ -152,7 +164,6 @@
     public final void resetElapsedSessionMillis() {
         mElapsedSessionMillis = System.currentTimeMillis();
         mElapsedContainerMillis = System.currentTimeMillis();
-
     }
 
     public final void resetActionDurationMillis() {
@@ -164,8 +175,8 @@
             Log.d("UserEvent", String.format(Locale.US,
                     "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms",
                     LoggerUtils.getActionStr(ev.action),
-                    LoggerUtils.getTargetStr(ev.srcTarget[0]),
-                    LoggerUtils.getTargetStr(ev.srcTarget[1]),
+                    LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
+                    LoggerUtils.getTargetStr(ev.srcTarget.length > 1 ? ev.srcTarget[1] : null),
                     ev.elapsedContainerMillis,
                     ev.elapsedSessionMillis));
         }
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
new file mode 100644
index 0000000..c48d160
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+
+/**
+ * A {@link BubbleTextView} that has the shortcut icon on the left and drag handle on the right.
+ */
+public class DeepShortcutTextView extends BubbleTextView {
+
+    public DeepShortcutTextView(Context context) {
+        this(context, null, 0);
+    }
+
+    public DeepShortcutTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DeepShortcutTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    /** Use the BubbleTextView icon for the start and the drag handle for the end. */
+    protected void applyCompoundDrawables(Drawable icon) {
+        Drawable dragHandle = getResources().getDrawable(R.drawable.deep_shortcuts_drag_handle);
+        dragHandle.setBounds(0, 0, dragHandle.getIntrinsicWidth(), dragHandle.getIntrinsicHeight());
+        setCompoundDrawablesRelative(icon, null, dragHandle, null);
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 912f006..9f59e06 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -160,11 +160,10 @@
                     final ShortcutInfoCompat shortcut = shortcuts.get(i);
                     final ShortcutInfo launcherShortcutInfo = ShortcutInfo
                             .fromDeepShortcutInfo(shortcut, mLauncher);
-                    CharSequence label = shortcut.getLongLabel();
-                    if (TextUtils.isEmpty(label)) {
-                        label = shortcut.getShortLabel();
-                    }
-                    uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo, label));
+                    CharSequence shortLabel = shortcut.getShortLabel();
+                    CharSequence longLabel = shortcut.getLongLabel();
+                    uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo,
+                            shortLabel, longLabel));
                 }
             }
         });
@@ -174,13 +173,15 @@
     private class UpdateShortcutChild implements Runnable {
         private int mShortcutChildIndex;
         private ShortcutInfo mShortcutChildInfo;
-        private CharSequence mLabel;
+        private CharSequence mShortLabel;
+        private CharSequence mLongLabel;
 
         public UpdateShortcutChild(int shortcutChildIndex, ShortcutInfo shortcutChildInfo,
-                CharSequence label) {
+                CharSequence shortLabel, CharSequence longLabel) {
             mShortcutChildIndex = shortcutChildIndex;
             mShortcutChildInfo = shortcutChildInfo;
-            mLabel = label;
+            mShortLabel = shortLabel;
+            mLongLabel = longLabel;
         }
 
         @Override
@@ -188,7 +189,12 @@
             BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText();
             shortcutView.applyFromShortcutInfo(mShortcutChildInfo,
                     LauncherAppState.getInstance().getIconCache());
-            shortcutView.setText(mLabel);
+            // Use the long label as long as it exists and fits.
+            int availableWidth = shortcutView.getWidth() - shortcutView.getTotalPaddingLeft()
+                    - shortcutView.getTotalPaddingRight();
+            boolean usingLongLabel = !TextUtils.isEmpty(mLongLabel)
+                    && shortcutView.getPaint().measureText(mLongLabel.toString()) <= availableWidth;
+            shortcutView.setText(usingLongLabel ? mLongLabel : mShortLabel);
             shortcutView.setOnClickListener(mLauncher);
             shortcutView.setOnLongClickListener(DeepShortcutsContainer.this);
             shortcutView.setOnTouchListener(DeepShortcutsContainer.this);
@@ -480,10 +486,8 @@
 
     @Override
     public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
-        target.itemType = LauncherLogProto.SHORTCUT; // TODO: change to DYNAMIC_SHORTCUT
-        target.gridX = info.cellX;
-        target.gridY = info.cellY;
-        target.pageIndex = 0;
-        targetParent.containerType = LauncherLogProto.FOLDER; // TODO: change to DYNAMIC_SHORTCUTS
+        target.itemType = LauncherLogProto.DEEPSHORTCUT;
+        // TODO: add target.rank
+        targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
     }
 }