Merge "ItemInfo supports lite proto builder (1/n)" into ub-launcher3-master
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
new file mode 100644
index 0000000..a89fe5c
--- /dev/null
+++ b/protos/launcher_atom.proto
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtom";
+
+//
+// ItemInfos
+message ItemInfo {
+  oneof Item {
+    Application application = 1;
+    Task task= 2;
+    Shortcut shortcut = 3;
+    Widget widget = 4;
+  }
+  // When used for launch event, stores the global predictive rank
+  optional int32 rank = 5;
+
+  // Stores whether the Item belows to non primary user
+  optional bool is_work = 6;
+
+  // Item can be child node to parent container or parent containers (nested)
+  oneof Container {
+    WorkspaceContainer workspace = 7;
+    HotseatContainer hotseat = 8;
+    FolderContainer folder = 9;
+  }
+  // Stores the origin of the Item
+  optional Origin source = 10;
+}
+
+enum Origin {
+  UNKNOWN = 0;
+  DEFAULT_LAYOUT = 1;       // icon automatically placed in workspace, folder, hotseat
+  BACKUP_RESTORE = 2;       // icon layout restored from backup
+  PINITEM = 3;              // from another app (e.g., Chrome's "Add to Home screen")
+  ALLAPPS_ATOZ = 4;         // within launcher surface, all aps a-z
+  WIDGETS = 5;              // within launcher, widgets tray
+  ADD_TO_HOMESCREEN = 6;    // play install + launcher home setting
+  ALLAPPS_PREDICTION = 7;   // from prediction bar in all apps container
+  HOTSEAT_PREDICTION = 8;   // from prediction bar in hotseat container
+}
+
+// Main app icons
+message Application {
+  optional string package_name = 1;
+  optional string component_name = 2;
+}
+
+// Legacy shortcuts and shortcuts handled by ShortcutManager
+message Shortcut {
+  optional string shortcut_name = 1;
+}
+
+// AppWidgets handled by AppWidgetManager
+message Widget {
+  optional int32 span_x = 1;
+  optional int32 span_y = 2;
+  optional int32 app_widget_id = 3;
+  optional string package_name = 4; // only populated during snapshot if from workspace
+  optional string component_name = 5; // only populated during snapshot if from workspace
+}
+
+// Tasks handled by PackageManager
+message Task {
+  optional string package_name = 1;
+  optional string component_name = 2;
+  optional int32 index = 3;
+}
+
+//////////////////////////////////////////////
+// Containers
+
+message WorkspaceContainer {
+  optional int32 page_index = 1; // range [-1, l], 0 is the index of the main homescreen
+  optional int32 grid_x = 2;     // [0, m], m varies based on the display density and resolution
+  optional int32 grid_y = 3;     // [0, n], n varies based on the display density and resolution
+}
+
+message HotseatContainer {
+  optional int32 index = 1;
+}
+
+message FolderContainer {
+  optional int32 page_index = 1;
+  optional int32 grid_x = 2;
+  optional int32 grid_y = 3;
+  oneof Container {
+    WorkspaceContainer workspace = 4;
+    HotseatContainer hotseat = 5;
+  }
+}
+
+
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 57a9940..1c95a9e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -30,6 +30,8 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_DISMISS_SWIPE_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -1183,13 +1185,13 @@
                 verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
     }
 
-    private void removeTask(Task task, int index, EndState endState) {
-        if (task != null) {
-            ActivityManagerWrapper.getInstance().removeTask(task.key.id);
-            ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+    private void removeTask(TaskView taskView, int index, EndState endState) {
+        if (taskView.getTask() != null) {
+            ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+            ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                    endState.logAction, Direction.UP, index, componentKey);
-            mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
+                    endState.logAction, Direction.UP, index, compKey);
+            mActivity.getStatsLogManager().log(TASK_DISMISS_SWIPE_UP, taskView.buildProto());
         }
     }
 
@@ -1284,7 +1286,7 @@
             private void onEnd(EndState endState) {
                 if (endState.isSuccess) {
                     if (shouldRemoveTask) {
-                        removeTask(taskView.getTask(), draggedIndex, endState);
+                        removeTask(taskView, draggedIndex, endState);
                     }
 
                     int pageToSnapTo = mCurrentPage;
@@ -1733,6 +1735,8 @@
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                             endState.logAction, Direction.DOWN, indexOfChild(tv),
                             TaskUtils.getLaunchComponentKeyForTask(task.key));
+                    mActivity.getStatsLogManager().log(TASK_LAUNCH_SWIPE_DOWN, tv.buildProto()
+                    );
                 }
             } else {
                 onTaskLaunched(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 56e3632..8160aae 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -29,6 +29,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_TAP;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -43,6 +44,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Process;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -59,6 +61,7 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.states.RotationHelper;
@@ -68,6 +71,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TaskIconCache;
@@ -217,8 +221,7 @@
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            mActivity.getStatsLogManager().logTaskLaunch(getRecentsView(),
-                    TaskUtils.getLaunchComponentKeyForTask(getTask().key));
+            mActivity.getStatsLogManager().log(TASK_LAUNCH_TAP, buildProto());
         });
         mCornerRadius = TaskCornerRadius.get(context);
         mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
@@ -229,6 +232,17 @@
         setOutlineProvider(mOutlineProvider);
     }
 
+    /* Builds proto for logging */
+    protected LauncherAtom.ItemInfo buildProto() {
+        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
+        LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
+        itemBuilder.setIsWork(componentKey.user != Process.myUserHandle());
+        itemBuilder.setTask(LauncherAtom.Task.newBuilder()
+                .setComponentName(componentKey.componentName.flattenToShortString())
+                .setIndex(getRecentsView().indexOfChild(this)));
+        return itemBuilder.build();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 22fe2e1..58bb980 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -18,36 +18,22 @@
 
 import static android.stats.launcher.nano.Launcher.ALLAPPS;
 import static android.stats.launcher.nano.Launcher.BACKGROUND;
-import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
 import static android.stats.launcher.nano.Launcher.HOME;
-import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
-import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
 import static android.stats.launcher.nano.Launcher.OVERVIEW;
 
-import static com.android.launcher3.logging.UserEventDispatcher.makeTargetsList;
-
 import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.stats.launcher.nano.Launcher;
-import android.stats.launcher.nano.LauncherExtension;
-import android.stats.launcher.nano.LauncherTarget;
-import android.util.Log;
-import android.view.View;
 
-import androidx.annotation.Nullable;
-
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogUtils;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.systemui.shared.system.SysUiStatsLog;
-
-import com.google.protobuf.nano.MessageNano;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.IntSparseArrayMap;
 
 import java.util.ArrayList;
 
@@ -62,186 +48,17 @@
 public class StatsLogCompatManager extends StatsLogManager {
 
     private static final int SUPPORTED_TARGET_DEPTH = 2;
-    private static final String TAG = "StatsLogCompatManager";
+    private static final String TAG = "StatsLog";
     private static final boolean DEBUG = false;
+    private static Context sContext;
 
     public StatsLogCompatManager(Context context) {
+        sContext = context;
     }
 
     @Override
-    public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
-        LauncherExtension ext = new LauncherExtension();
-        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
-        int srcState = mStateProvider.getCurrentState();
-        fillInLauncherExtension(v, ext);
-        if (ext.srcTarget[0] != null) {
-            ext.srcTarget[0].item = LauncherTarget.APP_ICON;
-        }
-        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, LAUNCH_APP, srcState,
-                BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
-    }
-
-    @Override
-    public void logTaskLaunch(View v, ComponentKey componentKey) {
-        LauncherExtension ext = new LauncherExtension();
-        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
-        int srcState = OVERVIEW;
-        fillInLauncherExtension(v, ext);
-        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, LAUNCH_TASK, srcState,
-                BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
-    }
-
-    @Override
-    public void logTaskDismiss(View v, ComponentKey componentKey) {
-        LauncherExtension ext = new LauncherExtension();
-        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
-        int srcState = OVERVIEW;
-        fillInLauncherExtension(v, ext);
-        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, DISMISS_TASK, srcState,
-                BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
-    }
-
-    @Override
-    public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
-        LauncherExtension ext = new LauncherExtension();
-        ext.srcTarget = new LauncherTarget[1];
-        int srcState = mStateProvider.getCurrentState();
-        fillInLauncherExtensionWithPageId(ext, pageId);
-        int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
-        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, launcherAction, srcState, srcState,
-                MessageNano.toByteArray(ext), true);
-    }
-
-    public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
-        if (DEBUG) {
-            Log.d(TAG, "fillInLauncherExtension");
-        }
-
-        StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
-        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
-            if (DEBUG) {
-                Log.d(TAG, "View or provider is null, or view doesn't have an ItemInfo tag.");
-            }
-
-            return false;
-        }
-        Target child = new Target();
-        ArrayList<Target> targets = makeTargetsList(child);
-        targets.add(child);
-        provider.fillInLogContainerData((ItemInfo) v.getTag(), child, targets);
-
-        int maxDepth = Math.min(SUPPORTED_TARGET_DEPTH, targets.size());
-        extension.srcTarget = new LauncherTarget[maxDepth];
-        for (int i = 0; i < maxDepth; i++) {
-            extension.srcTarget[i] = new LauncherTarget();
-            copy(targets.get(i), extension.srcTarget[i]);
-        }
-        return true;
-    }
-
-    public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
-        if (DEBUG) {
-            Log.d(TAG, "fillInLauncherExtensionWithPageId, pageId = " + pageId);
-        }
-
-        Target target = new Target();
-        target.pageIndex = pageId;
-        ext.srcTarget[0] = new LauncherTarget();
-        copy(target, ext.srcTarget[0]);
-        return true;
-    }
-
-    private static void copy(Target src, LauncherTarget dst) {
-        if (DEBUG) {
-            Log.d(TAG, "copy target information from clearcut Target to LauncherTarget.");
-        }
-
-        // Fill in type
-        switch (src.type) {
-            case Target.Type.ITEM:
-                dst.type = LauncherTarget.ITEM_TYPE;
-                break;
-            case Target.Type.CONTROL:
-                dst.type = LauncherTarget.CONTROL_TYPE;
-                break;
-            case Target.Type.CONTAINER:
-                dst.type = LauncherTarget.CONTAINER_TYPE;
-                break;
-            default:
-                dst.type = LauncherTarget.NONE;
-                break;
-        }
-
-        // Fill in item
-        switch (src.itemType) {
-            case ItemType.APP_ICON:
-                dst.item = LauncherTarget.APP_ICON;
-                break;
-            case ItemType.SHORTCUT:
-                dst.item = LauncherTarget.SHORTCUT;
-                break;
-            case ItemType.WIDGET:
-                dst.item = LauncherTarget.WIDGET;
-                break;
-            case ItemType.FOLDER_ICON:
-                dst.item = LauncherTarget.FOLDER_ICON;
-                break;
-            case ItemType.DEEPSHORTCUT:
-                dst.item = LauncherTarget.DEEPSHORTCUT;
-                break;
-            case ItemType.SEARCHBOX:
-                dst.item = LauncherTarget.SEARCHBOX;
-                break;
-            case ItemType.EDITTEXT:
-                dst.item = LauncherTarget.EDITTEXT;
-                break;
-            case ItemType.NOTIFICATION:
-                dst.item = LauncherTarget.NOTIFICATION;
-                break;
-            case ItemType.TASK:
-                dst.item = LauncherTarget.TASK;
-                break;
-            default:
-                dst.item = LauncherTarget.DEFAULT_ITEM;
-                break;
-        }
-
-        // Fill in container
-        switch (src.containerType) {
-            case ContainerType.HOTSEAT:
-                dst.container = LauncherTarget.HOTSEAT;
-                break;
-            case ContainerType.FOLDER:
-                dst.container = LauncherTarget.FOLDER;
-                break;
-            case ContainerType.PREDICTION:
-                dst.container = LauncherTarget.PREDICTION;
-                break;
-            case ContainerType.SEARCHRESULT:
-                dst.container = LauncherTarget.SEARCHRESULT;
-                break;
-            default:
-                dst.container = LauncherTarget.DEFAULT_CONTAINER;
-                break;
-        }
-
-        // Fill in control
-        switch (src.controlType) {
-            case ControlType.UNINSTALL_TARGET:
-                dst.control = LauncherTarget.UNINSTALL;
-                break;
-            case ControlType.REMOVE_TARGET:
-                dst.control = LauncherTarget.REMOVE;
-                break;
-            default:
-                dst.control = LauncherTarget.DEFAULT_CONTROL;
-                break;
-        }
-
-        // Fill in other fields
-        dst.pageId = src.pageIndex;
-        dst.gridX = src.gridX;
-        dst.gridY = src.gridY;
+    public void log(LauncherEvent eventId, LauncherAtom.ItemInfo item) {
+        // Call StatsLog method
     }
 
     @Override
@@ -254,4 +71,36 @@
                     "StatsLogUtil constants doesn't match enums in launcher.proto");
         }
     }
+
+    /**
+     * Logs the workspace layout information on the model thread.
+     */
+    public void logSnapshot() {
+        LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+                new SnapshotWorker());
+    }
+
+    private class SnapshotWorker extends BaseModelUpdateTask {
+        @Override
+        public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+            IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
+            ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
+            ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
+
+            for (ItemInfo info : workspaceItems) {
+                LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
+                // call StatsLog method
+            }
+            for (FolderInfo fInfo : folders) {
+                for (ItemInfo info : fInfo.contents) {
+                    LauncherAtom.ItemInfo atomInfo = info.buildProto(null, fInfo);
+                    // call StatsLog method
+                }
+            }
+            for (ItemInfo info : appWidgets) {
+                LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
+                // call StatsLog method
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 9f3b48f..6fa3c28 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.APP_LAUNCH_TAP;
 import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
 
 import android.app.ActivityOptions;
@@ -181,7 +182,7 @@
                         sourceContainer);
             }
             getUserEventDispatcher().logAppLaunch(v, intent, user);
-            getStatsLogManager().logAppLaunch(v, intent, user);
+            getStatsLogManager().log(APP_LAUNCH_TAP, item.buildProto(null, null));
             return true;
         } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index c99465c..8c4e4a0 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -16,6 +16,13 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Intent;
@@ -24,13 +31,17 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.util.ContentWriter;
 
+
+
 /**
  * Represents an item in the launcher.
  */
 public class ItemInfo {
 
+    public static final boolean DEBUG = true;
     public static final int NO_ID = -1;
 
     /**
@@ -190,6 +201,7 @@
         return "id=" + id
                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
                 + " container=" + LauncherSettings.Favorites.containerToString((int)container)
+                + " targetComponent=" + getTargetComponent()
                 + " screen=" + screenId
                 + " cell(" + cellX + "," + cellY + ")"
                 + " span(" + spanX + "," + spanY + ")"
@@ -221,4 +233,70 @@
         return container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
                 || container == LauncherSettings.Favorites.CONTAINER_PREDICTION;
     }
+
+    /**
+     * Can be overridden by inherited classes to fill in {@link LauncherAtom.ItemInfo}
+     */
+    public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
+    }
+
+    /**
+     * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+     */
+    public LauncherAtom.ItemInfo buildProto(Intent intent, FolderInfo fInfo) {
+
+        LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
+        itemBuilder.setIsWork(user != Process.myUserHandle());
+        ComponentName cn = getTargetComponent();
+        switch (itemType) {
+            case ITEM_TYPE_APPLICATION:
+                itemBuilder.setApplication(LauncherAtom.Application.newBuilder()
+                        .setComponentName(cn.flattenToShortString())
+                        .setPackageName(cn.getPackageName()));
+                break;
+            case ITEM_TYPE_DEEP_SHORTCUT:
+            case ITEM_TYPE_SHORTCUT:
+                itemBuilder.setShortcut(LauncherAtom.Shortcut.newBuilder()
+                        .setShortcutName(cn.flattenToShortString()));
+                break;
+            case ITEM_TYPE_APPWIDGET:
+                setItemBuilder(itemBuilder);
+                break;
+            default:
+                break;
+
+        }
+        if (fInfo != null) {
+            LauncherAtom.FolderContainer.Builder folderBuilder =
+                    LauncherAtom.FolderContainer.newBuilder();
+            folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId);
+
+            switch (fInfo.container) {
+                case CONTAINER_HOTSEAT:
+                    folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
+                            .setIndex(fInfo.screenId));
+                    break;
+                case CONTAINER_DESKTOP:
+                    folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
+                            .setPageIndex(fInfo.screenId)
+                            .setGridX(fInfo.cellX).setGridY(fInfo.cellY));
+                    break;
+            }
+            itemBuilder.setFolder(folderBuilder);
+        } else {
+            switch (container) {
+                case CONTAINER_HOTSEAT:
+                    itemBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
+                            .setIndex(screenId));
+                    break;
+                case CONTAINER_DESKTOP:
+                    itemBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
+                            .setGridX(cellX)
+                            .setGridY(cellY)
+                            .setPageIndex(screenId));
+                    break;
+            }
+        }
+        return itemBuilder.build();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index b824301..3a478dd 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.Process;
 
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ContentWriter;
 
@@ -162,7 +163,9 @@
 
     @Override
     protected String dumpProperties() {
-        return super.dumpProperties() + " appWidgetId=" + appWidgetId;
+        return super.dumpProperties()
+                + " providerName=" + providerName
+                + " appWidgetId=" + appWidgetId;
     }
 
     public final boolean isWidgetIdAllocated() {
@@ -182,4 +185,13 @@
     public final boolean hasOptionFlag(int option) {
         return (options & option) != 0;
     }
+
+    @Override
+    public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
+        builder.setWidget(LauncherAtom.Widget.newBuilder()
+                .setSpanX(spanX)
+                .setSpanY(spanY)
+                .setComponentName(providerName.toString())
+                .setPackageName(providerName.getPackageName()));
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ee9c099..8bc0242 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -991,7 +991,6 @@
             if (!mOverlayShown) {
                 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
-                mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
             }
             mOverlayShown = true;
             // Not announcing the overlay page for accessibility since it announces itself.
@@ -1001,7 +1000,6 @@
                 if (!ued.isPreviousHomeGesture()) {
                     mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
-                    mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
                 }
             } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
                 // When arriving to 0 overscroll from non-zero overscroll, announce page for
diff --git a/src/com/android/launcher3/logging/LauncherUiEvent.java b/src/com/android/launcher3/logging/LauncherUiEvent.java
new file mode 100644
index 0000000..4507ff7
--- /dev/null
+++ b/src/com/android/launcher3/logging/LauncherUiEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.logging;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(SOURCE)
+@Target(FIELD)
+public @interface LauncherUiEvent {
+    /** An explanation, suitable for Android analysts, of the UI event that this log represents. */
+    String doc();
+}
+
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 9dfd7ab..2829951 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,23 +16,44 @@
 package com.android.launcher3.logging;
 
 import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.view.View;
-
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ItemInfo;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
- * Handles the user event logging in Q.
+ * Handles the user event logging in R+.
  */
 public class StatsLogManager implements ResourceBasedOverride {
 
+    interface EventEnum {
+        int getId();
+    }
+
+    public enum LauncherEvent implements EventEnum {
+        @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
+        APP_LAUNCH_TAP(1),
+        @LauncherUiEvent(doc = "Task launched from overview using TAP")
+        TASK_LAUNCH_TAP(2),
+        @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
+        TASK_LAUNCH_SWIPE_DOWN(2),
+        @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
+        TASK_DISMISS_SWIPE_UP(3);
+        // ADD MORE
+
+        private final int mId;
+        LauncherEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+    }
+
     protected LogStateProvider mStateProvider;
+
     public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
         StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
                 context.getApplicationContext(), R.string.stats_log_manager_class);
@@ -42,11 +63,14 @@
     }
 
     /**
-     * Logs app launches
+     * Logs an event and accompanying {@link ItemInfo}
      */
-    public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) { }
-    public void logTaskLaunch(View v, ComponentKey key) { }
-    public void logTaskDismiss(View v, ComponentKey key) { }
-    public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) { }
+    public void log(LauncherEvent eventId, LauncherAtom.ItemInfo itemInfo) { }
+
+    /**
+     * Logs snapshot, or impression of the current workspace.
+     */
+    public void logSnapshot() { }
+
     public void verify() {}     // TODO: should move into robo tests
 }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 2311dcc..695d2a6 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -35,6 +35,8 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
@@ -150,8 +152,10 @@
         }
     }
 
+    @VisibleForTesting
     public WorkspaceItemInfo loadSimpleWorkspaceItem() {
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
+        info.intent = new Intent();
         // Non-app shortcuts are only supported for current user.
         info.user = user;
         info.itemType = itemType;