Quick step/scrub/switch logging
- state transition happening due to Home and back is handled by
  specifying src target as 'from' container and dst target as the 'to'
  container
- Source and Destination container shows FROM and TO state for SWIPE/FLING
- event.isStateChange = true indicates that an action resulted in
  state transition
- Elapsed container millis is the screen time on the source container

Bug: 70181187

- logcat printout with setprop log.tag.UserEvent VERBOSE
1) State: WORKSPACE -> ALLAPPS
  action:FLING direction=UP
  Source child:HOTSEAT id=0	parent:WORKSPACE id=0
  Destination child:ALLAPPS
  Elapsed container 1225 ms, session 1225 ms, action 0 ms

2) ALLAPPS -> HOMESCREEN
  action:FLING direction=DOWN
  Source child:ALLAPPS	parent:ALLAPPS
  Destination child:WORKSPACE id=0
  Elapsed container 971 ms, session 2197 ms, action 0 ms

3) HOMESCREEN -> OVERVIEW
  action:FLING direction=UP
  Source child:NAVBAR	parent:WORKSPACE id=0
  Destination child:TASKSWITCHER
  Elapsed container 4834 ms, session 4834 ms, action 0 ms

4) OVERVIEW-> ALLAPPS
  action:FLING direction=UP
  Source child:TASK	parent:TASKSWITCHER
  Destination child:ALLAPPS
  Elapsed container 2176 ms, session 7010 ms, action 0 ms

5) ALLAPPS->OVERVIEW
  action:FLING direction=DOWN
  Source child:ALLAPPS	parent:ALLAPPS
  Destination child:TASKSWITCHER
  Elapsed container 3683 ms, session 10693 ms, action 0 ms

6) OVERVIEW-> HOMESCREEN
  action:FLING direction=DOWN
  Source child:TASK	parent:TASKSWITCHER
  Destination child:WORKSPACE id=0
  Elapsed container 2108 ms, session 12801 ms, action 0 ms

7) APPS-> OVERVIEW
  action:FLING direction=UP
  Source child:NAVBAR	parent:APP
  Destination child:TASKSWITCHER
  Elapsed container 104 ms, session 104 ms, action 0 ms

8) Quickscrub: action:DRAGANDDROP Source child: QUICK

9) Quickswitch: action:FLING Source child: QUICK

Change-Id: I5898230859ff600f48a2a873a40b670fe4d39a0d
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index de74fce..3b983d2 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -68,6 +68,7 @@
   SEARCHBOX = 6;
   EDITTEXT = 7;
   NOTIFICATION = 8;
+  TASK = 9;         // Each page of Recents UI (QuickStep)
 }
 
 // Used to define what type of container a Target would represent.
@@ -78,11 +79,14 @@
   FOLDER = 3;
   ALLAPPS = 4;
   WIDGETS = 5;
-  OVERVIEW = 6;
+  OVERVIEW = 6;   // Zoomed out workspace (without QuickStep)
   PREDICTION = 7;
   SEARCHRESULT = 8;
   DEEPSHORTCUTS = 9;
   PINITEM = 10;    // confirmation screen
+  NAVBAR = 11;
+  TASKSWITCHER = 12; // Recents UI Container (QuickStep)
+  APP = 13; // Foreground activity is another app (QuickStep)
 }
 
 // Used to define what type of control a Target would represent.
@@ -100,6 +104,7 @@
   HOME_INTENT = 10; // Deprecated, use enum Command instead
   BACK_BUTTON = 11; // Deprecated, use enum Command instead
   // GO_TO_PLAYSTORE
+  QUICK_SCRUB_BUTTON = 12;
 }
 
 // Used to define the action component of the LauncherEvent.
@@ -141,6 +146,7 @@
   optional Command command = 4;
   // Log if the action was performed on outside of the container
   optional bool is_outside = 5;
+  optional bool is_state_change = 6;
 }
 
 //
@@ -150,7 +156,6 @@
 //
 message LauncherEvent {
   required Action action = 1;
-
   // List of targets that touch actions can be operated on.
   repeated Target src_target = 2;
   repeated Target dest_target = 3;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
index bf55bd6..3d2830d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -30,8 +30,12 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.VerticalSwipeController;
 import com.android.quickstep.RecentsView;
 
@@ -100,6 +104,10 @@
         return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
     }
 
+    public EdgeSwipeController(Launcher l, LauncherState baseState) {
+        super(l, baseState);
+    }
+
     @Override
     protected boolean isTransitionFlipped() {
         return mLauncher.getDeviceProfile().isSeascape();
@@ -117,8 +125,21 @@
             builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/,
                     0/* zero time */);
             mMetricsLogger.write(builder);
+
+            // Add user event logging for launcher pipeline
+            int direction = Direction.UP;
+            if (mLauncher.getDeviceProfile().isLandscape) {
+                direction = Direction.LEFT;
+                if (mLauncher.getDeviceProfile().isSeascape()) {
+                    direction = Direction.RIGHT;
+                }
+            }
+            mLauncher.getUserEventDispatcher().logStateChangeAction(
+                    wasFling ? Touch.FLING : Touch.SWIPE, direction,
+                    ContainerType.NAVBAR, ContainerType.WORKSPACE, // src target
+                    ContainerType.TASKSWITCHER,                    // dst target
+                    mLauncher.getWorkspace().getCurrentPage());
         }
-        // TODO: Log something
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 5706d32..9ba2308 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -40,7 +40,7 @@
             | FLAG_DISABLE_RESTORE;
 
     public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+        super(id, ContainerType.TASKSWITCHER, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
index 1fd541a..468a561 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -31,6 +31,10 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsView;
 import com.android.quickstep.TaskView;
@@ -70,6 +74,7 @@
     private float mDisplacementShift;
     private float mProgressMultiplier;
     private float mEndDisplacement;
+    private int mStartingTarget;
 
     private TaskView mTaskBeingDragged;
 
@@ -120,7 +125,6 @@
             // calling the callbacks.
             final int directionsToDetectScroll;
             boolean ignoreSlopWhenSettling = false;
-
             if (mCurrentAnimation != null) {
                 directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
                 ignoreSlopWhenSettling = true;
@@ -139,12 +143,15 @@
                         // The tile can be dragged down to open the task.
                         mTaskBeingDragged = (TaskView) view;
                         directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+                        mStartingTarget = LauncherLogProto.ItemType.TASK;
                     } else if (isEventOverHotseat(ev)) {
                         // The hotseat is being dragged
                         directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
                         mSwipeDownEnabled = false;
+                        mStartingTarget = ContainerType.HOTSEAT;
                     } else {
                         mNoIntercept = true;
+                        mStartingTarget = ContainerType.WORKSPACE;
                         return false;
                     }
                 }
@@ -249,8 +256,9 @@
     @Override
     public void onDragEnd(float velocity, boolean fling) {
         final boolean goingToEnd;
-
+        final int logAction;
         if (fling) {
+            logAction = Touch.FLING;
             boolean goingUp = velocity < 0;
             if (!goingUp && !mSwipeDownEnabled) {
                 goingToEnd = false;
@@ -269,6 +277,7 @@
                 goingToEnd = true;
             }
         } else {
+            logAction = Touch.SWIPE;
             goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
         }
 
@@ -280,7 +289,7 @@
                 progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
 
 
-        mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd));
+        mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
 
         ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
         anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
@@ -289,19 +298,28 @@
         anim.start();
     }
 
-    private void onCurrentAnimationEnd(boolean wasSuccess) {
-        // TODO: Might be a good time to log something.
+    private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
         if (mTaskBeingDragged == null) {
             LauncherState state = wasSuccess ?
                     (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW;
             mLauncher.getStateManager().goToState(state, false);
+
         } else if (wasSuccess) {
             if (mCurrentAnimationIsGoingUp) {
                 mRecentsView.onTaskDismissed(mTaskBeingDragged);
             } else {
                 mTaskBeingDragged.launchTask(false);
+                mLauncher.getUserEventDispatcher().logTaskLaunch(logAction,
+                        Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent());
             }
         }
+        if (mTaskBeingDragged == null || (wasSuccess && mCurrentAnimationIsGoingUp)) {
+            mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
+                    mCurrentAnimationIsGoingUp ? Direction.UP : Direction.DOWN,
+                    mStartingTarget, ContainerType.TASKSWITCHER,
+                    mLauncher.getStateManager().getState().containerType,
+                    mRecentsView.getCurrentPage());
+        }
         mDetector.finishedScrolling();
         mTaskBeingDragged = null;
         mCurrentAnimation = null;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
index 3ae8f41..4fb3886 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
@@ -58,10 +58,12 @@
     protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
         if (stateChanged) {
             // Transition complete. log the action
-            mLauncher.getUserEventDispatcher().logActionOnContainer(
+            mLauncher.getUserEventDispatcher().logStateChangeAction(
                     wasFling ? Touch.FLING : Touch.SWIPE,
                     Direction.UP,
-                    ContainerType.OVERVIEW,
+                    ContainerType.HOTSEAT,
+                    ContainerType.TASKSWITCHER,
+                    ContainerType.ALLAPPS,
                     mLauncher.getWorkspace().getCurrentPage());
         }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index fb59946..2695054 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -50,10 +50,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.FloatRange;
 import com.android.launcher3.util.TouchController;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsView;
 import com.android.quickstep.TouchInteractionService;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 
 import java.util.ArrayList;
 
@@ -371,9 +368,12 @@
     private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         if (targetState != mFromState) {
             // Transition complete. log the action
-            mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
+            mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
                     mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
-                    mStartContainerType, mLauncher.getWorkspace().getCurrentPage());
+                    mStartContainerType,
+                    mFromState.containerType,
+                    mToState.containerType,
+                    mLauncher.getWorkspace().getCurrentPage());
         }
         clearState();
 
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index bda9688..f28d51c 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -19,8 +19,13 @@
 import android.view.HapticFeedbackConstants;
 
 import com.android.launcher3.Alarm;
+import com.android.launcher3.Launcher;
 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.ControlType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * Responds to quick scrub callbacks to page through and launch recent tasks.
@@ -42,13 +47,15 @@
 
     private final Alarm mAutoAdvanceAlarm;
     private final RecentsView mRecentsView;
+    private final Launcher mLauncher;
 
     private boolean mInQuickScrub;
     private int mQuickScrubSection;
     private int mStartPage;
     private boolean mHasAlarmRun;
 
-    public QuickScrubController(RecentsView recentsView) {
+    public QuickScrubController(Launcher launcher, RecentsView recentsView) {
+        mLauncher = launcher;
         mRecentsView = recentsView;
         if (ENABLE_AUTO_ADVANCE) {
             mAutoAdvanceAlarm = new Alarm();
@@ -61,6 +68,7 @@
         mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex();
         mQuickScrubSection = 0;
         mHasAlarmRun = false;
+        mLauncher.getUserEventDispatcher().resetActionDurationMillis();
     }
 
     public void onQuickScrubEnd() {
@@ -85,6 +93,9 @@
             // No page move needed, just launch it
             launchTaskRunnable.run();
         }
+        mLauncher.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
+                ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ?
+                        ContainerType.WORKSPACE : ContainerType.APP);
     }
 
     public void onQuickScrubProgress(float progress) {
@@ -119,6 +130,9 @@
                 break;
             }
         }
+        mLauncher.getUserEventDispatcher().logActionOnControl(Touch.FLING,
+                ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ?
+                        ContainerType.WORKSPACE : ContainerType.APP);
     }
 
     public void snapToPageForCurrentQuickScrubSection() {
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index cb510c8..8ff0a86 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -124,7 +124,7 @@
         setClipToOutline(true);
 
         mLauncher = Launcher.getLauncher(context);
-        mQuickScrubController = new QuickScrubController(this);
+        mQuickScrubController = new QuickScrubController(mLauncher, this);
         mModel = RecentsModel.getInstance(context);
 
         mScrollState.isRtl = mIsRtl;
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index ddddbf6..0e8dea6 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -59,6 +59,10 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
+
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -603,6 +607,30 @@
         }
 
         animateToProgress(endShift, duration);
+        int direction = Direction.UP;
+        if (mLauncher.getDeviceProfile().isLandscape) {
+            direction = Direction.LEFT;
+            if (mLauncher.getDeviceProfile().isSeascape()) {
+                direction = Direction.RIGHT;
+            }
+        }
+        int dstContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+        if (Float.compare(endShift, 0) == 0) {
+            direction = Direction.DOWN;
+            if (mLauncher.getDeviceProfile().isLandscape) {
+                direction = Direction.RIGHT;
+                if (mLauncher.getDeviceProfile().isSeascape()) {
+                    direction = Direction.LEFT;
+                }
+            }
+            dstContainerType = LauncherLogProto.ContainerType.APP;
+        }
+        mLauncher.getUserEventDispatcher().logStateChangeAction(
+                isFling ? Touch.FLING : Touch.SWIPE, direction,
+                LauncherLogProto.ContainerType.NAVBAR,
+                LauncherLogProto.ContainerType.APP,
+                dstContainerType,
+                0);
     }
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 12f022f..fc5ce8f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -92,7 +92,8 @@
     public final void close(boolean animate) {
         animate &= !Utilities.isPowerSaverOn(getContext());
         handleClose(animate);
-        Launcher.getLauncher(getContext()).getUserEventDispatcher().resetElapsedContainerMillis();
+        Launcher.getLauncher(getContext()).getUserEventDispatcher()
+                .resetElapsedContainerMillis("container closed");
     }
 
     protected abstract void handleClose(boolean animate);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b4093b7..f76d4eb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -783,7 +783,7 @@
 
         if (!mAppLaunchSuccess) {
             getUserEventDispatcher().logActionCommand(Action.Command.STOP,
-                    mStateManager.getState().containerType);
+                    mStateManager.getState().containerType, -1);
         }
         NotificationListener.removeNotificationsChangedListener();
         getStateManager().moveToRestState();
@@ -1280,7 +1280,8 @@
                 } else if (alreadyOnHome) {
                     Target target = newContainerTarget(mStateManager.getState().containerType);
                     target.pageIndex = mWorkspace.getCurrentPage();
-                    ued.logActionCommand(Action.Command.HOME_INTENT, target);
+                    ued.logActionCommand(Action.Command.HOME_INTENT, target,
+                            newContainerTarget(ContainerType.WORKSPACE));
                 }
 
                 // In all these cases, only animate if we're already on home
@@ -1656,7 +1657,8 @@
             topView.onBackPressed();
         } else if (!isInState(NORMAL)) {
             LauncherState lastState = mStateManager.getLastState();
-            ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType);
+            ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType,
+                    lastState.containerType);
             mStateManager.goToState(lastState);
         } else {
             // Back button is a no-op here, but give at least some feedback for the button press
@@ -1682,7 +1684,7 @@
 
         if (v instanceof Workspace) {
             if (isInState(OVERVIEW)) {
-                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
                         LauncherLogProto.Action.Direction.NONE,
                         LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
                 mStateManager.goToState(NORMAL);
@@ -1693,7 +1695,7 @@
         if (v instanceof CellLayout) {
             if (isInState(OVERVIEW)) {
                 int page = mWorkspace.indexOfChild(v);
-                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
                         LauncherLogProto.Action.Direction.NONE,
                         LauncherLogProto.ContainerType.OVERVIEW, page);
                 mWorkspace.snapToPageFromOverView(page);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 170fcd7..2f8687d 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -302,7 +302,6 @@
 
         state.onStateTransitionEnd(mLauncher);
         mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
-        mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
         mLauncher.finishAutoCancelActionMode();
 
         if (state == NORMAL) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 11523a5..1414946 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1679,7 +1679,7 @@
             if (popupContainer != null) {
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
 
-                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
             }
         }
 
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 8abafb0..993663e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -522,7 +522,7 @@
         onCompleteRunnable = new Runnable() {
             @Override
             public void run() {
-                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
             }
         };
         anim.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 00ee009..c608a23 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -71,7 +71,7 @@
         switch (action.type) {
             case Action.Type.TOUCH:
                 str += getFieldName(action.touch, Action.Touch.class);
-                if (action.touch == Action.Touch.SWIPE) {
+                if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
                     str += " direction=" + getFieldName(action.dir, Action.Direction.class);
                 }
                 return str;
@@ -114,11 +114,20 @@
         if (t.intentHash != 0) {
             typeStr += ", intentHash=" + t.intentHash;
         }
-        if (t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) {
-            typeStr += ", predictiveRank=" + t.predictedRank;
+        if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) &&
+                t.itemType != ItemType.TASK) {
+            typeStr += ", predictiveRank=" + t.predictedRank + ", grid(" + t.gridX + "," + t.gridY
+                    + "), span(" + t.spanX + "," + t.spanY
+                    + "), pageIdx=" + t.pageIndex;
+
         }
-        return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
-                + "), pageIdx=" + t.pageIndex;
+        return typeStr;
+    }
+
+    public static Target newItemTarget(int itemType) {
+        Target t = newTarget(Target.Type.ITEM);
+        t.itemType = itemType;
+        return t;
     }
 
     public static Target newItemTarget(View v) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 243dbea..b7de400 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+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;
@@ -158,9 +159,23 @@
         dispatchUserEvent(event, intent);
     }
 
+    public void logTaskLaunch(int action, int direction, ComponentName componentName){
+        LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE
+                newTarget(Target.Type.ITEM));
+        if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
+            event.action.dir = direction;
+        }
+        event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+        fillComponentInfo(event.srcTarget[0], componentName);
+        dispatchUserEvent(event, null);
+    }
+
     protected void fillIntentInfo(Target target, Intent intent) {
         target.intentHash = intent.hashCode();
-        ComponentName cn = intent.getComponent();
+        fillComponentInfo(target, intent.getComponent());
+    }
+
+    private void fillComponentInfo(Target target, ComponentName cn) {
         if (cn != null) {
             target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode();
             target.componentHash = (mUuidStr + cn.flattenToString()).hashCode();
@@ -176,19 +191,29 @@
         dispatchUserEvent(event, null);
     }
 
-    public void logActionCommand(int command, int containerType) {
-        logActionCommand(command, newContainerTarget(containerType));
+    public void logActionCommand(int command, Target srcTarget) {
+        logActionCommand(command, srcTarget, null);
     }
 
-    public void logActionCommand(int command, Target target) {
-        LauncherEvent event = newLauncherEvent(newCommandAction(command), target);
+    public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
+        logActionCommand(command, newContainerTarget(srcContainerType),
+                dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+    }
+
+    public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
+        LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
+        if (dstTarget != null) {
+            event.destTarget = new Target[1];
+            event.destTarget[0] = dstTarget;
+            event.action.isStateChange = true;
+        }
         dispatchUserEvent(event, null);
     }
 
     /**
      * TODO: Make this function work when a container view is passed as the 2nd param.
      */
-    public void logActionCommand(int command, View itemView, int containerType) {
+    public void logActionCommand(int command, View itemView, int srcContainerType) {
         LauncherEvent event = newLauncherEvent(newCommandAction(command),
                 newItemTarget(itemView), newTarget(Target.Type.CONTAINER));
 
@@ -196,22 +221,39 @@
             // TODO: Remove the following two lines once fillInLogContainerData can take in a
             // container view.
             event.srcTarget[0].type = Target.Type.CONTAINER;
-            event.srcTarget[0].containerType = containerType;
+            event.srcTarget[0].containerType = srcContainerType;
         }
         dispatchUserEvent(event, null);
     }
 
     public void logActionOnControl(int action, int controlType) {
-        logActionOnControl(action, controlType, null);
+        logActionOnControl(action, controlType, null, -1);
+    }
+
+    public void logActionOnControl(int action, int controlType, int parentContainerType) {
+        logActionOnControl(action, controlType, null, parentContainerType);
     }
 
     public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
+        logActionOnControl(action, controlType, controlInContainer, -1);
+    }
+
+    public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
+                                   int parentContainerType) {
         final LauncherEvent event = controlInContainer == null
                 ? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
                 : newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
                         newTarget(Target.Type.CONTAINER));
         event.srcTarget[0].controlType = controlType;
-        fillInLogContainerData(event, controlInContainer);
+        if (controlInContainer != null) {
+            fillInLogContainerData(event, controlInContainer);
+        }
+        if (parentContainerType >= 0) {
+            event.srcTarget[1].containerType = parentContainerType;
+        }
+        if (action == Action.Touch.DRAGDROP) {
+            event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
+        }
         dispatchUserEvent(event, null);
     }
 
@@ -232,10 +274,35 @@
         event.action.dir = dir;
         event.srcTarget[0].pageIndex = pageIndex;
         dispatchUserEvent(event, null);
+    }
 
-        if (action == Action.Touch.SWIPE) {
-            resetElapsedContainerMillis();
+    /**
+     * Used primarily for swipe up and down when state changes when swipe up happens from the
+     * navbar bezel, the {@param srcChildContainerType} is NAVBAR and
+     * {@param srcParentContainerType} is either one of the two
+     * (1) WORKSPACE: if the launcher the foreground activity
+     * (2) APP: if another app was the foreground activity
+     */
+    public void logStateChangeAction(int action, int dir, int srcChildTargetType,
+                                     int srcParentContainerType, int dstContainerType,
+                                     int pageIndex) {
+        LauncherEvent event;
+        if (srcChildTargetType == LauncherLogProto.ItemType.TASK) {
+            event = newLauncherEvent(newTouchAction(action),
+                    newItemTarget(srcChildTargetType),
+                    newContainerTarget(srcParentContainerType));
+        } else {
+            event = newLauncherEvent(newTouchAction(action),
+                    newContainerTarget(srcChildTargetType),
+                    newContainerTarget(srcParentContainerType));
         }
+        event.destTarget = new Target[1];
+        event.destTarget[0] = newContainerTarget(dstContainerType);
+        event.action.dir = dir;
+        event.action.isStateChange = true;
+        event.srcTarget[0].pageIndex = pageIndex;
+        dispatchUserEvent(event, null);
+        resetElapsedContainerMillis("state changed");
     }
 
     public void logActionOnItem(int action, int dir, int itemType) {
@@ -257,7 +324,7 @@
         provider.fillInLogContainerData(icon, info, event.srcTarget[0], event.srcTarget[1]);
         dispatchUserEvent(event, null);
 
-        resetElapsedContainerMillis();
+        resetElapsedContainerMillis("deep shortcut open");
     }
 
     /* Currently we are only interested in whether this event happens or not and don't
@@ -290,9 +357,15 @@
 
     /**
      * Currently logs following containers: workspace, allapps, widget tray.
+     * @param reason
      */
-    public final void resetElapsedContainerMillis() {
+    public final void resetElapsedContainerMillis(String reason) {
         mElapsedContainerMillis = SystemClock.uptimeMillis();
+        if (!IS_VERBOSE) {
+            return;
+        }
+        Log.d(TAG, "resetElapsedContainerMillis reason=" + reason);
+
     }
 
     public final void resetElapsedSessionMillis() {
@@ -321,13 +394,13 @@
             log += "\n Destination " + getTargetsStr(ev.destTarget);
         }
         log += String.format(Locale.US,
-                "\n Elapsed container %d ms session %d ms action %d ms",
+                "\n Elapsed container %d ms, session %d ms, action %d ms",
                 ev.elapsedContainerMillis,
                 ev.elapsedSessionMillis,
                 ev.actionDurationMillis);
         log += "\n isInLandscapeMode " + ev.isInLandscapeMode;
         log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode;
-        log += "\n";
+        log += "\n\n";
         Log.d(TAG, log);
     }