Merge "Fix app open animation when in overview mode." into ub-launcher3-edmonton
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 179f21d..cd404d6 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -55,6 +55,7 @@
   optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
   optional int32 predictedRank = 15;
   optional TargetExtension extension = 16;
+  optional TipType tip_type = 17;
 }
 
 // Used to define what type of item a Target would represent.
@@ -88,6 +89,7 @@
   NAVBAR = 11;
   TASKSWITCHER = 12; // Recents UI Container (QuickStep)
   APP = 13; // Foreground activity is another app (QuickStep)
+  TIP = 14; // Onboarding texts (QuickStep)
 }
 
 // Used to define what type of control a Target would represent.
@@ -109,14 +111,24 @@
   CANCEL_TARGET = 14;
 }
 
+enum TipType {
+  DEFAULT_NONE = 0;
+  BOUNCE = 1;
+  SWIPE_UP_TEXT = 2;
+  QUICK_SCRUB_TEXT = 3;
+  PREDICTION_TEXT = 4;
+}
+
 // Used to define the action component of the LauncherEvent.
 message Action {
   enum Type {
     TOUCH = 0;
     AUTOMATED = 1;
     COMMAND = 2;
+    TIP = 3;
     // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
   }
+
   enum Touch {
     TAP = 0;
     LONGPRESS = 1;
@@ -125,7 +137,8 @@
     FLING = 4;
     PINCH = 5;
   }
- enum Direction {
+
+  enum Direction {
     NONE = 0;
     UP = 1;
     DOWN = 2;
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index b343cc2..5e25fd8 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index 0451653..a11625a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -28,7 +28,7 @@
             | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
 
     public FastOverviewState(int id) {
-        super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS);
+        super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index febe360..79e3c42 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -17,6 +17,8 @@
 
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
 import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
@@ -24,12 +26,14 @@
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.os.Build;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
@@ -69,7 +73,13 @@
         PropertySetter setter = config.getPropertySetter(builder);
         float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
         setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], LINEAR);
-        setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1], LINEAR);
+        Interpolator transYInterpolator = LINEAR;
+        if (toState == LauncherState.FAST_OVERVIEW) {
+            transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
+                    QUICK_SCRUB_TRANSLATION_Y_FACTOR);
+        }
+        setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
+                transYInterpolator);
         setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
                 AGGRESSIVE_EASE_IN_OUT);
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 06099b9..e6030d1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -24,7 +24,9 @@
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 
+import android.app.Activity;
 import android.content.Context;
+import android.util.Base64;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
@@ -32,13 +34,19 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.ActivityCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
 public class UiFactory {
 
     public static TouchController[] createTouchControllers(Launcher launcher) {
@@ -167,6 +175,32 @@
         }
     }
 
+    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+        if (!Utilities.IS_DEBUG_DEVICE) {
+            return false;
+        }
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+            return false;
+        }
+
+        Deflater deflater = new Deflater();
+        deflater.setInput(out.toByteArray());
+        deflater.finish();
+
+        out.reset();
+        byte[] buffer = new byte[1024];
+        while (!deflater.finished()) {
+            int count = deflater.deflate(buffer); // returns the generated code... index
+            out.write(buffer, 0, count);
+        }
+
+        writer.println("--encoded-view-dump-v0--");
+        writer.println(Base64.encodeToString(
+                out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+        return true;
+    }
+
     private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
 
         public LauncherTaskViewController(Launcher activity) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index c146246..81a73fc 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -48,8 +48,10 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
 import com.android.quickstep.ActivityControlHelper.AnimationFactory;
 import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
@@ -189,6 +191,17 @@
         mMainThreadExecutor.execute(new ShowRecentsCommand());
     }
 
+    public void onTip(int actionType, int viewType) {
+        mMainThreadExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                UserEventDispatcher.newInstance(mContext,
+                        new InvariantDeviceProfile(mContext).getDeviceProfile(mContext))
+                        .logActionTip(actionType, viewType);
+            }
+        });
+    }
+
     public ActivityControlHelper getActivityControlHelper() {
         return mActivityControlHelper;
     }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index ae7de87..5ddd904 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -16,16 +16,18 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.Alarm;
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
@@ -37,7 +39,11 @@
  */
 public class QuickScrubController implements OnAlarmListener {
 
-    public static final int QUICK_SCRUB_START_DURATION = 210;
+    public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
+    public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 150;
+    // We want the translation y to finish faster than the rest of the animation.
+    public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
+    public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
 
     /**
      * Snap to a new page when crossing these thresholds. The first and last auto-advance.
@@ -117,9 +123,9 @@
                 mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
             }
         }
-        mActivity.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
-                ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
-                        ContainerType.WORKSPACE : ContainerType.APP);
+        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
+                LauncherLogProto.Action.Direction.NONE, page,
+                TaskUtils.getComponentKeyForTask(mRecentsView.getPageAt(page).getTask().key));
     }
 
     /**
@@ -168,23 +174,27 @@
 
     public void snapToNextTaskIfAvailable() {
         if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
+            int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
+                    : QUICK_SCRUB_FROM_APP_START_DURATION;
             int pageToGoTo = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
-            goToPageWithHaptic(pageToGoTo, QUICK_SCRUB_START_DURATION, true /* forceHaptic */);
+            goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
+                    QUICK_SCRUB_START_INTERPOLATOR);
         }
     }
 
     private void goToPageWithHaptic(int pageToGoTo) {
-        goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */);
+        goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */, null);
     }
 
-    private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic) {
+    private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
+            Interpolator interpolator) {
         pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
         boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
         if (snappingToPage) {
             int duration = overrideDuration > -1 ? overrideDuration
                     : Math.abs(pageToGoTo - mRecentsView.getNextPage())
                             * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
-            mRecentsView.snapToPage(pageToGoTo, duration);
+            mRecentsView.snapToPage(pageToGoTo, duration, interpolator);
         }
         if (snappingToPage || forceHaptic) {
             mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7496b47..bd05b6d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -147,6 +147,11 @@
             TraceHelper.endSection("SysUiBinder", "onQuickStep");
 
         }
+
+        @Override
+        public void onTip(int actionType, int viewType) {
+            mOverviewCommandHelper.onTip(actionType, viewType);
+        }
     };
 
     private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 911a602..807dae8 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,11 +17,9 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
 
@@ -454,7 +452,7 @@
         setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
 
         // Start the window animation without waiting for launcher.
-        animateToProgress(1f, QUICK_SCRUB_START_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
+        animateToProgress(1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR);
     }
 
     @WorkerThread
@@ -501,10 +499,7 @@
 
         RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
         if (controller != null) {
-            Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
-                    ? ACCEL_2 : LINEAR;
-            float interpolated = interpolator.getInterpolation(shift);
-            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, interpolated);
+            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift);
 
             // TODO: This logic is spartanic!
             boolean passedThreshold = shift > 0.12f;
@@ -792,7 +787,8 @@
             }
             mClipAnimationHelper.offsetTarget(
                     firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask,
-                    mActivityControlHelper.getTranslationYForQuickScrub(mActivity));
+                    mActivityControlHelper.getTranslationYForQuickScrub(mActivity),
+                    QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index d4cdd35..04153cc 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -15,15 +15,32 @@
  */
 package com.android.quickstep.logging;
 
+import android.util.Log;
+
+import static com.android.launcher3.logging.LoggerUtils.newAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
+import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
+import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
+import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
+import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
+
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.nano.LauncherDumpProto;
+import com.android.launcher3.userevent.nano.LauncherLogExtensions;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.systemui.shared.system.LauncherEventUtil;
 import com.android.systemui.shared.system.MetricsLoggerCompat;
 
 /**
- * This class handles AOSP MetricsLogger function calls.
+ * This class handles AOSP MetricsLogger function calls and logging around
+ * quickstep interactions.
  */
 public class UserEventDispatcherExtension extends UserEventDispatcher {
 
+    private static final String TAG = "UserEventDispatcher";
+
     public void logStateChangeAction(int action, int dir, int srcChildTargetType,
                                      int srcParentContainerType, int dstContainerType,
                                      int pageIndex) {
@@ -32,4 +49,37 @@
         super.logStateChangeAction(action, dir, srcChildTargetType, srcParentContainerType,
                 dstContainerType, pageIndex);
     }
+
+    public void logActionTip(int actionType, int viewType) {
+        LauncherLogProto.Action action = new LauncherLogProto.Action();
+        LauncherLogProto.Target target = new LauncherLogProto.Target();
+        switch(actionType) {
+            case VISIBLE:
+                action.type = LauncherLogProto.Action.Type.TIP;
+                target.type = LauncherLogProto.Target.Type.CONTAINER;
+                target.containerType = LauncherLogProto.ContainerType.TIP;
+                break;
+            case DISMISS:
+                action.type = LauncherLogProto.Action.Type.TOUCH;
+                action.touch = LauncherLogProto.Action.Touch.TAP;
+                target.type = LauncherLogProto.Target.Type.CONTROL;
+                target.controlType = CANCEL_TARGET;
+                break;
+            default:
+                Log.e(TAG, "Unexpected action type = " + actionType);
+        }
+
+        switch(viewType) {
+            case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
+                target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
+                break;
+            case RECENTS_SWIPE_UP_ONBOARDING_TIP:
+                target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
+                break;
+            default:
+                Log.e(TAG, "Unexpected viewType = " + viewType);
+        }
+        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+        dispatchUserEvent(event, null);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 14b4046..c28a6bd 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -16,7 +16,7 @@
 package com.android.quickstep.util;
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.SCROLL;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -30,11 +30,13 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.support.annotation.Nullable;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
@@ -78,6 +80,9 @@
     private final RectF mTmpRectF = new RectF();
 
     private float mTargetScale = 1f;
+    private Interpolator mInterpolator = LINEAR;
+    // We translate y slightly faster than the rest of the animation for quick scrub.
+    private Interpolator mOffsetYInterpolator = LINEAR;
 
     // Whether to boost the opening animation target layers, or the closing
     private int mBoostModeTargetLayers = -1;
@@ -134,12 +139,13 @@
         RectF currentRect;
         mTmpRectF.set(mTargetRect);
         Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
+        float offsetYProgress = mOffsetYInterpolator.getInterpolation(progress);
+        progress = mInterpolator.getInterpolation(progress);
         currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
 
         synchronized (mTargetOffset) {
             // Stay lined up with the center of the target, since it moves for quick scrub.
-            currentRect.offset(mTargetOffset.x * SCROLL.getInterpolation(progress),
-                    mTargetOffset.y  * LINEAR.getInterpolation(progress));
+            currentRect.offset(mTargetOffset.x * progress, mTargetOffset.y  * offsetYProgress);
         }
 
         mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
@@ -180,11 +186,14 @@
         mTaskTransformCallback = callback;
     }
 
-    public void offsetTarget(float scale, float offsetX, float offsetY) {
+    public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
         synchronized (mTargetOffset) {
-            mTargetScale = scale;
             mTargetOffset.set(offsetX, offsetY);
         }
+        mTargetScale = scale;
+        mInterpolator = interpolator;
+        mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
+                QUICK_SCRUB_TRANSLATION_Y_FACTOR);
     }
 
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 05793f1..68432ab 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -418,6 +418,7 @@
         final int requiredChildCount = tasks.size();
         for (int i = getChildCount(); i < requiredChildCount; i++) {
             final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
+            taskView.setOnClickListener(this::onTaskClicked);
             addView(taskView);
         }
         while (getChildCount() > requiredChildCount) {
@@ -443,6 +444,17 @@
         onTaskStackUpdated();
     }
 
+    private void onTaskClicked(View v) {
+        TaskView taskView = (TaskView) v;
+        if (taskView.getTask() == null) {
+            return;
+        }
+        taskView.launchTask(true /* animate */);
+        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+                Touch.TAP, Direction.NONE, indexOfChild(taskView),
+                TaskUtils.getComponentKeyForTask(taskView.getTask().key));
+    }
+
     protected void onTaskStackUpdated() { }
 
     public void resetTaskVisuals() {
@@ -745,13 +757,13 @@
                 duration, LINEAR, anim);
     }
 
-    private void removeTask(Task task, PendingAnimation.OnEndListener onEndListener,
-            boolean shouldLog) {
+    private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
+                            boolean shouldLog) {
         if (task != null) {
             ActivityManagerWrapper.getInstance().removeTask(task.key.id);
             if (shouldLog) {
                 mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                        onEndListener.logAction, Direction.UP,
+                        onEndListener.logAction, Direction.UP, index,
                         TaskUtils.getComponentKeyForTask(task.key));
             }
         }
@@ -836,7 +848,7 @@
         mPendingAnimation.addEndListener((onEndListener) -> {
            if (onEndListener.isSuccess) {
                if (shouldRemoveTask) {
-                   removeTask(taskView.getTask(), onEndListener, true);
+                   removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
                }
                int pageToSnapTo = mCurrentPage;
                if (draggedIndex < pageToSnapTo) {
@@ -872,7 +884,7 @@
             if (onEndListener.isSuccess) {
                 while (getChildCount() != 0) {
                     TaskView taskView = getPageAt(getChildCount() - 1);
-                    removeTask(taskView.getTask(), onEndListener, false);
+                    removeTask(taskView.getTask(), -1, onEndListener, false);
                     removeView(taskView);
                 }
                 onAllTasksRemoved();
@@ -1204,7 +1216,7 @@
                 Task task = tv.getTask();
                 if (task != null) {
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                            onEndListener.logAction, Direction.DOWN,
+                            onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
                             TaskUtils.getComponentKeyForTask(task.key));
                 }
             } else {
@@ -1228,6 +1240,7 @@
         if (currChild != null) {
             currChild.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         }
+        loadVisibleTaskData();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 69b77b4..24afd48 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -28,6 +28,9 @@
 import android.graphics.Path;
 import android.graphics.Path.Direction;
 import android.graphics.Path.Op;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.util.AttributeSet;
 
 import com.android.launcher3.DeviceProfile;
@@ -48,6 +51,7 @@
     private static final int THRESHOLD_ALPHA_DARK = 102;
     private static final int THRESHOLD_ALPHA_LIGHT = 46;
     private static final int THRESHOLD_ALPHA_SUPER_LIGHT = 128;
+    private static final int CLEAR_ALL_TASKS = R.string.recents_clear_all;
 
     // In transposed layout, we simply draw a flat color.
     private boolean mDrawingFlatColor;
@@ -213,4 +217,43 @@
                 mRadius, mRadius, mPaint);
         return minTop - mDragHandleSize - top;
     }
+
+    @NonNull
+    @Override
+    protected AccessibilityHelper createAccessibilityHelper() {
+        return new ShelfScrimAccessibilityHelper();
+    }
+
+    protected class ShelfScrimAccessibilityHelper extends AccessibilityHelper {
+        @Override
+        protected void onPopulateNodeForVirtualView(int virtualViewId,
+                AccessibilityNodeInfoCompat node) {
+            super.onPopulateNodeForVirtualView(virtualViewId, node);
+
+            if (mLauncher.isInState(OVERVIEW)) {
+                final RecentsView overviewPanel = mLauncher.getOverviewPanel();
+                if (overviewPanel.getChildCount() != 0) {
+                    node.addAction(
+                            new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                                    CLEAR_ALL_TASKS,
+                                    getContext().getText(CLEAR_ALL_TASKS)));
+                }
+            }
+        }
+
+        @Override
+        protected boolean onPerformActionForVirtualView(
+                int virtualViewId, int action, Bundle arguments) {
+            if (super.onPerformActionForVirtualView(virtualViewId, action, arguments)) return true;
+
+            if (action == CLEAR_ALL_TASKS) {
+                if (mLauncher.isInState(OVERVIEW)) {
+                    mLauncher.<RecentsView>getOverviewPanel().dismissAllTasks();
+                }
+                return true;
+            }
+
+            return false;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index af7a735..128a19e 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,7 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
 import android.content.Context;
@@ -52,16 +51,16 @@
 
     private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
 
-    public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
-            new FloatProperty<TaskThumbnailView>("dimAlpha") {
+    public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
+            new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
                 @Override
-                public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
-                    thumbnail.setDimAlpha(dimAlpha);
+                public void setValue(TaskThumbnailView thumbnail, float dimAlphaMultiplier) {
+                    thumbnail.setDimAlphaMultipler(dimAlphaMultiplier);
                 }
 
                 @Override
                 public Float get(TaskThumbnailView thumbnailView) {
-                    return thumbnailView.mDimAlpha;
+                    return thumbnailView.mDimAlphaMultiplier;
                 }
             };
 
@@ -81,6 +80,7 @@
     protected BitmapShader mBitmapShader;
 
     private float mDimAlpha = 1f;
+    private float mDimAlphaMultiplier = 1f;
 
     public TaskThumbnailView(Context context) {
         this(context, null);
@@ -128,6 +128,11 @@
         updateThumbnailPaintFilter();
     }
 
+    public void setDimAlphaMultipler(float dimAlphaMultipler) {
+        mDimAlphaMultiplier = dimAlphaMultipler;
+        setDimAlpha(mDimAlpha);
+    }
+
     /**
      * Sets the alpha of the dim layer on top of this view.
      *
@@ -191,7 +196,7 @@
     }
 
     private void updateThumbnailPaintFilter() {
-        int mul = (int) ((1 - mDimAlpha) * 255);
+        int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
         if (mBitmapShader != null) {
             LightingColorFilter filter = getLightingColorFilter(mul);
             mPaint.setColorFilter(filter);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 4f447b1..0ddeae7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -17,7 +17,7 @@
 package com.android.quickstep.views;
 
 import static android.widget.Toast.LENGTH_SHORT;
-import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
+import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA_MULTIPLIER;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -78,6 +78,7 @@
     private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
 
     public static final long SCALE_ICON_DURATION = 120;
+    private static final long DIM_ANIM_DURATION = 700;
 
     public static final Property<TaskView, Float> ZOOM_SCALE =
             new FloatProperty<TaskView>("zoomScale") {
@@ -97,7 +98,6 @@
     private IconView mIconView;
     private float mCurveScale;
     private float mZoomScale;
-    private float mCurveDimAlpha;
     private Animator mDimAlphaAnim;
 
     public TaskView(Context context) {
@@ -110,13 +110,6 @@
 
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        setOnClickListener((view) -> {
-            if (mTask != null) {
-                launchTask(true /* animate */);
-                BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
-                        Touch.TAP, Direction.NONE, TaskUtils.getComponentKeyForTask(mTask.key));
-            }
-        });
         setOutlineProvider(new TaskOutlineProvider(getResources()));
     }
 
@@ -200,8 +193,9 @@
 
     public void animateIconToScaleAndDim(float scale) {
         mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
-        mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA, scale * mCurveDimAlpha);
-        mDimAlphaAnim.setDuration(SCALE_ICON_DURATION);
+        mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA_MULTIPLIER, 1 - scale,
+                scale);
+        mDimAlphaAnim.setDuration(DIM_ANIM_DURATION);
         mDimAlphaAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -218,7 +212,7 @@
         if (mDimAlphaAnim != null) {
             mDimAlphaAnim.cancel();
         }
-        mSnapshotView.setDimAlpha(iconScale * mCurveDimAlpha);
+        mSnapshotView.setDimAlphaMultipler(iconScale);
     }
 
     public void resetVisualProperties() {
@@ -235,11 +229,7 @@
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
 
-        mCurveDimAlpha = curveInterpolation * MAX_PAGE_SCRIM_ALPHA;
-        if (mDimAlphaAnim == null && mIconView.getScaleX() > 0) {
-            mSnapshotView.setDimAlpha(mCurveDimAlpha);
-        }
-
+        mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
         setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
     }
 
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 3e4589d..4219667 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -30,8 +30,10 @@
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.SystemUiController;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -224,6 +226,13 @@
         void onMultiWindowModeChanged(boolean isInMultiWindowMode);
     }
 
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!UiFactory.dumpActivity(this, writer)) {
+            super.dump(prefix, fd, writer, args);
+        }
+    }
+
     protected void dumpMisc(PrintWriter writer) {
         writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
         writer.println(" orientation=" + getResources().getConfiguration().orientation);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index bf73149..e851499 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -240,7 +240,6 @@
     private PendingRequestArgs mPendingRequestArgs;
 
     public ViewGroupFocusHelper mFocusHandler;
-    private boolean mAppLaunchSuccess;
 
     private RotationHelper mRotationHelper;
 
@@ -730,10 +729,8 @@
         }
         mAppWidgetHost.setListenIfResumed(false);
 
-        if (!mAppLaunchSuccess) {
-            getUserEventDispatcher().logActionCommand(Action.Command.STOP,
-                    mStateManager.getState().containerType, -1);
-        }
+        getUserEventDispatcher().logActionCommand(Action.Command.STOP,
+                mStateManager.getState().containerType, -1);
         NotificationListener.removeNotificationsChangedListener();
         getStateManager().moveToRestState();
 
@@ -760,7 +757,6 @@
         super.onResume();
         TraceHelper.partitionSection("ON_RESUME", "superCall");
 
-        mAppLaunchSuccess = false;
         getUserEventDispatcher().resetElapsedSessionMillis();
         setOnResumeCallback(null);
         // Process any items that were added while Launcher was away.
@@ -1634,8 +1630,8 @@
     }
 
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
-        mAppLaunchSuccess = super.startActivitySafely(v, intent, item);
-        if (mAppLaunchSuccess && v instanceof BubbleTextView) {
+        boolean success = super.startActivitySafely(v, intent, item);
+        if (success && v instanceof BubbleTextView) {
             // This is set to the view that launched the activity that navigated the user away
             // from launcher. Since there is no callback for when the activity has finished
             // launching, enable the press state and keep this reference to reset the press
@@ -1644,7 +1640,7 @@
             btv.setStayPressed(true);
             setOnResumeCallback(btv);
         }
-        return mAppLaunchSuccess;
+        return success;
     }
 
     boolean isHotseatLayout(View layout) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9c146b0..eb816c5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -27,6 +27,7 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -44,6 +45,7 @@
 import android.widget.ScrollView;
 
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.util.Thunk;
@@ -1429,7 +1431,7 @@
         return snapToPage(whichPage, duration, false, null);
     }
 
-    protected boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
+    public boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
         return snapToPage(whichPage, duration, false, interpolator);
     }
 
@@ -1448,6 +1450,12 @@
 
     protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
             TimeInterpolator interpolator) {
+
+        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            duration *= Settings.System.getFloat(getContext().getContentResolver(),
+                    Settings.System.WINDOW_ANIMATION_SCALE, 1);
+        }
+
         whichPage = validateNewPage(whichPage);
 
         mNextPage = whichPage;
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index e8c5f15..deaf8d3 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -18,6 +18,8 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
@@ -127,6 +129,7 @@
                 AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce));
         view.mIsOpen = true;
         launcher.getDragLayer().addView(view);
+        launcher.getUserEventDispatcher().logActionBounceTip(HOTSEAT);
     }
 
     public static void showForOverviewIfNeeded(Launcher launcher) {
@@ -173,5 +176,6 @@
         DiscoveryBounce view = new DiscoveryBounce(launcher, animator);
         view.mIsOpen = true;
         launcher.getDragLayer().addView(view);
+        launcher.getUserEventDispatcher().logActionBounceTip(PREDICTION);
     }
 }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 0d388fe..8374f98 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -115,4 +115,24 @@
     public static Interpolator scrollInterpolatorForVelocity(float velocity) {
         return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
     }
+
+    /**
+     * Runs the given interpolator such that the entire progress is set between the given bounds.
+     * That is, we set the interpolation to 0 until lowerBound and reach 1 by upperBound.
+     */
+    public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
+            float upperBound) {
+        if (upperBound <= lowerBound) {
+            throw new IllegalArgumentException("lowerBound must be less than upperBound");
+        }
+        return t -> {
+            if (t < lowerBound) {
+                return 0;
+            }
+            if (t > upperBound) {
+                return 1;
+            }
+            return interpolator.getInterpolation((t - lowerBound) / (upperBound - lowerBound));
+        };
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 9d97cb9..83593aa 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
 import com.android.launcher3.util.InstantAppResolver;
 
 import java.lang.reflect.Field;
@@ -76,7 +77,7 @@
                 }
                 return str;
             case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
-            default: return UNKNOWN;
+            default: return getFieldName(action.type, Action.Type.class);
         }
     }
 
@@ -84,23 +85,32 @@
         if (t == null){
             return "";
         }
+        String str = "";
         switch (t.type) {
             case Target.Type.ITEM:
-                return getItemStr(t);
+                str = getItemStr(t);
+                break;
             case Target.Type.CONTROL:
-                return getFieldName(t.controlType, ControlType.class);
+                str = getFieldName(t.controlType, ControlType.class);
+                break;
             case Target.Type.CONTAINER:
-                String str = getFieldName(t.containerType, ContainerType.class);
+                str = getFieldName(t.containerType, ContainerType.class);
                 if (t.containerType == ContainerType.WORKSPACE ||
                         t.containerType == ContainerType.HOTSEAT) {
                     str += " id=" + t.pageIndex;
                 } else if (t.containerType == ContainerType.FOLDER) {
                     str += " grid(" + t.gridX + "," + t.gridY+ ")";
                 }
-                return str;
+                break;
             default:
-                return "UNKNOWN TARGET TYPE";
+                str += "UNKNOWN TARGET TYPE";
         }
+
+        if (t.tipType != TipType.DEFAULT_NONE) {
+            str += " " + getFieldName(t.tipType, TipType.class);
+        }
+
+        return str;
     }
 
     private static String getItemStr(Target t) {
@@ -121,6 +131,9 @@
                     + "), pageIdx=" + t.pageIndex;
 
         }
+        if (t.itemType == ItemType.TASK) {
+            typeStr += ", pageIdx=" + t.pageIndex;
+        }
         return typeStr;
     }
 
@@ -186,6 +199,12 @@
         return t;
     }
 
+    public static Target newControlTarget(int controlType) {
+        Target t = newTarget(Target.Type.CONTROL);
+        t.controlType = controlType;
+        return t;
+    }
+
     public static Target newContainerTarget(int containerType) {
         Target t = newTarget(Target.Type.CONTAINER);
         t.containerType = containerType;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 2c1eb32..850c948 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -16,8 +16,10 @@
 
 package com.android.launcher3.logging;
 
+import static com.android.launcher3.logging.LoggerUtils.newAction;
 import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newControlTarget;
 import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
 import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
 import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
@@ -130,6 +132,7 @@
     private boolean mIsInLandscapeMode;
     private String mUuidStr;
     protected InstantAppResolver mInstantAppResolver;
+    private boolean mAppOrTaskLaunch;
 
     //                      APP_ICON    SHORTCUT    WIDGET
     // --------------------------------------------------------------
@@ -161,9 +164,13 @@
             fillIntentInfo(event.srcTarget[0], intent);
         }
         dispatchUserEvent(event, intent);
+        mAppOrTaskLaunch = true;
     }
 
-    public void logTaskLaunchOrDismiss(int action, int direction, ComponentKey componentKey) {
+    public void logActionTip(int actionType, int viewType) { }
+
+    public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex,
+            ComponentKey componentKey) {
         LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE or FLING
                 newTarget(Target.Type.ITEM));
         if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
@@ -171,8 +178,10 @@
             event.action.dir = direction;
         }
         event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+        event.srcTarget[0].pageIndex = taskIndex;
         fillComponentInfo(event.srcTarget[0], componentKey.componentName);
         dispatchUserEvent(event, null);
+        mAppOrTaskLaunch = true;
     }
 
     protected void fillIntentInfo(Target target, Intent intent) {
@@ -207,6 +216,11 @@
 
     public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
         LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
+        if (command == Action.Command.STOP && mAppOrTaskLaunch) {
+            // Prevent double logging by skipping STOP when app or task has been launched.
+            return;
+        }
+
         if (dstTarget != null) {
             event.destTarget = new Target[1];
             event.destTarget[0] = dstTarget;
@@ -243,6 +257,15 @@
         logActionOnControl(action, controlType, controlInContainer, -1);
     }
 
+    public void logActionOnControl(int action, int controlType, int parentContainer,
+                                   int grandParentContainer){
+        LauncherEvent event = newLauncherEvent(newTouchAction(action),
+                newControlTarget(controlType),
+                newContainerTarget(parentContainer),
+                newContainerTarget(grandParentContainer));
+        dispatchUserEvent(event, null);
+    }
+
     public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
                                    int parentContainerType) {
         final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
@@ -269,6 +292,13 @@
         dispatchUserEvent(event, null);
     }
 
+    public void logActionBounceTip(int containerType) {
+        LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
+                newContainerTarget(containerType));
+        event.srcTarget[0].tipType = LauncherLogProto.TipType.BOUNCE;
+        dispatchUserEvent(event, null);
+    }
+
     public void logActionOnContainer(int action, int dir, int containerType) {
         logActionOnContainer(action, dir, containerType, 0);
     }
@@ -385,6 +415,7 @@
     }
 
     public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+        mAppOrTaskLaunch = false;
         ev.isInLandscapeMode = mIsInLandscapeMode;
         ev.isInMultiWindowMode = mIsInMultiWindowMode;
         ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
@@ -393,7 +424,8 @@
         if (!IS_VERBOSE) {
             return;
         }
-        String log = "action:" + LoggerUtils.getActionStr(ev.action);
+        String log = "\n-----------------------------------------------------"
+                + "\naction:" + LoggerUtils.getActionStr(ev.action);
         if (ev.srcTarget != null && ev.srcTarget.length > 0) {
             log += "\n Source " + getTargetsStr(ev.srcTarget);
         }
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 5dcfe4a..49e96be 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -38,6 +38,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -113,12 +114,17 @@
                 .getDimensionPixelSize(R.dimen.vertical_drag_handle_size);
         mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize);
 
-        mAccessibilityHelper = new AccessibilityHelper();
+        mAccessibilityHelper = createAccessibilityHelper();
         ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
 
         mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
     }
 
+    @NonNull
+    protected AccessibilityHelper createAccessibilityHelper() {
+        return new AccessibilityHelper();
+    }
+
     @Override
     public void setInsets(Rect insets) {
         updateDragHandleBounds();
@@ -190,6 +196,9 @@
         if (mCurrentFlatColor != 0) {
             canvas.drawColor(mCurrentFlatColor);
         }
+        if (mDragHandle != null) {
+            mDragHandle.draw(canvas);
+        }
     }
 
     @Override
@@ -323,7 +332,7 @@
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
 
-    private class AccessibilityHelper extends ExploreByTouchHelper {
+    protected class AccessibilityHelper extends ExploreByTouchHelper {
 
         private static final int DRAG_HANDLE_ID = 1;
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index b8cd035..e9a47a7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,12 +16,15 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.app.Activity;
 import android.content.Context;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.util.TouchController;
 
+import java.io.PrintWriter;
+
 public class UiFactory {
 
     public static TouchController[] createTouchControllers(Launcher launcher) {
@@ -47,4 +50,9 @@
     public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
 
     public static void onTrimMemory(Launcher launcher, int level) { }
+
+    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+        return false;
+    }
+
 }