diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index 6b27559534..33041dbca2 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -63,6 +63,7 @@ enum ItemType { FOLDER_ICON = 4; DEEPSHORTCUT = 5; SEARCHBOX = 6; + EDITTEXT = 7; } // Used to define what type of container a Target would represent. @@ -91,7 +92,9 @@ enum ControlType { APPINFO_TARGET = 7; RESIZE_HANDLE = 8; VERTICAL_SCROLL = 9; - // HOME, BACK, GO_TO_PLAYSTORE + HOME_INTENT = 10; // Deprecated, use enum Command instead + BACK_BUTTON = 11; // Deprecated, use enum Command instead + // GO_TO_PLAYSTORE } // Used to define the action component of the LauncherEvent. @@ -99,6 +102,7 @@ message Action { enum Type { TOUCH = 0; AUTOMATED = 1; + COMMAND = 2; // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST } enum Touch { @@ -116,9 +120,16 @@ message Action { LEFT = 3; RIGHT = 4; } + enum Command { + HOME_INTENT = 0; + BACK = 1; + } optional Type type = 1; optional Touch touch = 2; optional Direction dir = 3; + optional Command command = 4; + // Log if the action was performed on outside of the container + optional bool is_outside = 5; } // diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9a5e1868a5..9160a012e7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1681,9 +1681,27 @@ public class Launcher extends Activity // Can be cases where mWorkspace is null, this prevents a NPE return; } - // In all these cases, only animate if we're already on home + + // Note: There should be at most one log per method call. This is enforced implicitly + // by using if-else statements. + UserEventDispatcher ued = getUserEventDispatcher(); + + // TODO: Log this case. mWorkspace.exitWidgetResizeMode(); + AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this); + if (topOpenView instanceof DeepShortcutsContainer) { + ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT, + topOpenView.getExtendedTouchView(), LauncherLogProto.DEEPSHORTCUTS); + } else if (topOpenView instanceof Folder) { + ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT, + ((Folder) topOpenView).getFolderIcon(), LauncherLogProto.FOLDER); + } else if (alreadyOnHome) { + ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT, + mWorkspace.getState().containerType, mWorkspace.getCurrentPage()); + } + + // In all these cases, only animate if we're already on home AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome); exitSpringLoadedDragMode(); @@ -2187,20 +2205,34 @@ public class Launcher extends Activity return; } + // Note: There should be at most one log per method call. This is enforced implicitly + // by using if-else statements. + UserEventDispatcher ued = getUserEventDispatcher(); AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { if (topView.getActiveTextView() != null) { topView.getActiveTextView().dispatchBackKey(); } else { + if (topView instanceof DeepShortcutsContainer) { + ued.logActionCommand(LauncherLogProto.Action.BACK, + topView.getExtendedTouchView(), LauncherLogProto.DEEPSHORTCUTS); + } else if (topView instanceof Folder) { + ued.logActionCommand(LauncherLogProto.Action.BACK, + ((Folder) topView).getFolderIcon(), LauncherLogProto.FOLDER); + } topView.close(true); } } else if (isAppsViewVisible()) { + ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.ALLAPPS); showWorkspace(true); } else if (isWidgetsViewVisible()) { + ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.WIDGETS); showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { + ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.OVERVIEW); showWorkspace(true); } else { + // TODO: Log this case. mWorkspace.exitWidgetResizeMode(); // Back button is a no-op here, but give at least some feedback for the button press diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index f6b5072569..7758769b3e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -183,18 +183,20 @@ public class Workspace extends PagedView // in all apps or customize mode) public enum State { - NORMAL (false, false), - NORMAL_HIDDEN (false, false), - SPRING_LOADED (false, true), - OVERVIEW (true, true), - OVERVIEW_HIDDEN (true, false); + NORMAL (false, false, LauncherLogProto.WORKSPACE), + NORMAL_HIDDEN (false, false, LauncherLogProto.ALLAPPS), + SPRING_LOADED (false, true, LauncherLogProto.WORKSPACE), + OVERVIEW (true, true, LauncherLogProto.OVERVIEW), + OVERVIEW_HIDDEN (true, false, LauncherLogProto.WIDGETS); public final boolean shouldUpdateWidget; public final boolean hasMultipleVisiblePages; + public final int containerType; - State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages) { + State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages, int containerType) { this.shouldUpdateWidget = shouldUpdateWidget; this.hasMultipleVisiblePages = hasMultipleVisiblePages; + this.containerType = containerType; } } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 53c12b5b1a..e813bb450b 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -79,7 +79,6 @@ import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.pageindicators.PageIndicatorDots; -import com.android.launcher3.shortcuts.DeepShortcutsContainer; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.CircleRevealOutlineProvider; @@ -388,6 +387,10 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC return isEditingName() ? mFolderName : null; } + public FolderIcon getFolderIcon() { + return mFolderIcon; + } + /** * We need to handle touch events to prevent them from falling through to the workspace below. */ diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index c2b97eb2d8..395daa5f09 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -20,8 +20,16 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Target; public class LoggerUtils { private static final String TAG = "LoggerUtils"; - public static String getActionStr(LauncherLogProto.Action action) { - switch(action.touch) { + private static String getCommandStr(Action action) { + switch (action.command) { + case Action.HOME_INTENT: return "HOME_INTENT"; + case Action.BACK: return "BACK"; + default: return "UNKNOWN"; + } + } + + private static String getTouchStr(Action action) { + switch (action.touch) { case Action.TAP: return "TAP"; case Action.LONGPRESS: return "LONGPRESS"; case Action.DRAGDROP: return "DRAGDROP"; @@ -32,6 +40,14 @@ public class LoggerUtils { } } + public static String getActionStr(LauncherLogProto.Action action) { + switch (action.type) { + case Action.TOUCH: return getTouchStr(action); + case Action.COMMAND: return getCommandStr(action); + default: return "UNKNOWN"; + } + } + public static String getTargetStr(Target t) { String typeStr = ""; if (t == null){ @@ -61,6 +77,7 @@ public class LoggerUtils { case LauncherLogProto.DEEPSHORTCUT: typeStr = "DEEPSHORTCUT"; break; case LauncherLogProto.FOLDER_ICON: typeStr = "FOLDERICON"; break; case LauncherLogProto.SEARCHBOX: typeStr = "SEARCHBOX"; break; + case LauncherLogProto.EDITTEXT: typeStr = "EDITTEXT"; break; default: typeStr = "UNKNOWN"; } @@ -172,24 +189,42 @@ public class LoggerUtils { return event; } + /** + * Used for commands. + */ + public static LauncherLogProto.LauncherEvent initLauncherEvent(int command, + boolean createSrcTarget) { + LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent(); + event.action = new LauncherLogProto.Action(); + event.action.type = Action.COMMAND; + event.action.command = command; + event.srcTarget = null; + + if (createSrcTarget) { + event.srcTarget = new LauncherLogProto.Target[1]; + event.srcTarget[0] = new LauncherLogProto.Target(); + event.srcTarget[0].type = Target.CONTAINER; + } + return event; + } + /** * Used for drag and drop interaction. */ public static LauncherLogProto.LauncherEvent initLauncherEvent( int actionType, - View v, ItemInfo info, int parentSrcTargetType, View parentDestTargetType){ LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent(); event.srcTarget = new LauncherLogProto.Target[2]; - event.srcTarget[0] = initTarget(v, info); + event.srcTarget[0] = initTarget(info); event.srcTarget[1] = new LauncherLogProto.Target(); event.srcTarget[1].type = parentSrcTargetType; event.destTarget = new LauncherLogProto.Target[2]; - event.destTarget[0] = initTarget(v, info); + event.destTarget[0] = initTarget(info); event.destTarget[1] = initDropTarget(parentDestTargetType); event.action = new LauncherLogProto.Action(); @@ -197,7 +232,7 @@ public class LoggerUtils { return event; } - private static Target initTarget(View v, ItemInfo info) { + private static Target initTarget(ItemInfo info) { Target t = new LauncherLogProto.Target(); t.type = Target.ITEM; switch (info.itemType) { @@ -243,6 +278,6 @@ public class LoggerUtils { if (!(v.getTag() instanceof ItemInfo)) { return t; } - return initTarget(v, (ItemInfo) v.getTag()); + return initTarget((ItemInfo) v.getTag()); } } diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index abe8f42358..2fcdd3970d 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -117,29 +117,35 @@ public class UserEventDispatcher { Action.TOUCH, v, Target.CONTAINER); event.action.touch = Action.TAP; - // Fill in grid(x,y), pageIndex of the child and container type of the parent - // TODO: make this percolate up the view hierarchy if needed. + // TODO: make idx percolate up the view hierarchy if needed. int idx = 0; - LogContainerProvider provider = getLaunchProviderRecursive(v); - if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) { - return null; - } - ItemInfo itemInfo = (ItemInfo) v.getTag(); - provider.fillInLogContainerData(v, itemInfo, event.srcTarget[idx], event.srcTarget[idx + 1]); - - event.srcTarget[idx].intentHash = intent.hashCode(); - ComponentName cn = intent.getComponent(); - if (cn != null) { - event.srcTarget[idx].packageNameHash = cn.getPackageName().hashCode(); - event.srcTarget[idx].componentHash = cn.hashCode(); - if (mPredictedApps != null) { - event.srcTarget[idx].predictedRank = mPredictedApps.indexOf( - new ComponentKey(cn, itemInfo.user)); + if (fillInLogContainerData(event, v)) { + ItemInfo itemInfo = (ItemInfo) v.getTag(); + event.srcTarget[idx].intentHash = intent.hashCode(); + ComponentName cn = intent.getComponent(); + if (cn != null) { + event.srcTarget[idx].packageNameHash = cn.getPackageName().hashCode(); + event.srcTarget[idx].componentHash = cn.hashCode(); + if (mPredictedApps != null) { + event.srcTarget[idx].predictedRank = mPredictedApps.indexOf( + new ComponentKey(cn, itemInfo.user)); + } } } return event; } + public boolean fillInLogContainerData(LauncherEvent event, View v) { + // Fill in grid(x,y), pageIndex of the child and container type of the parent + LogContainerProvider provider = getLaunchProviderRecursive(v); + if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) { + return false; + } + ItemInfo itemInfo = (ItemInfo) v.getTag(); + provider.fillInLogContainerData(v, itemInfo, event.srcTarget[0], event.srcTarget[1]); + return true; + } + public void logAppLaunch(View v, Intent intent) { LauncherEvent ev = createLauncherEvent(v, intent); if (ev == null) { @@ -148,10 +154,30 @@ public class UserEventDispatcher { dispatchUserEvent(ev, intent); } - public void logActionOnItem(int action, int itemType) { - LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.ITEM); - event.action.touch = action; - event.srcTarget[0].itemType = itemType; + public void logActionCommand(int command, int containerType) { + logActionCommand(command, containerType, 0); + } + + public void logActionCommand(int command, int containerType, int pageIndex) { + LauncherEvent event = LoggerUtils.initLauncherEvent(command, true); + event.srcTarget[0].containerType = containerType; + event.srcTarget[0].pageIndex = pageIndex; + 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) { + LauncherEvent event = LoggerUtils.initLauncherEvent(Action.COMMAND, itemView, + Target.CONTAINER); + event.action.command = command; + if (fillInLogContainerData(event, itemView)) { + // TODO: Remove the following two lines once fillInLogContainerData can take in a + // container view. + event.srcTarget[0].type = Target.CONTAINER; + event.srcTarget[0].containerType = containerType; + } dispatchUserEvent(event, null); } @@ -215,7 +241,6 @@ public class UserEventDispatcher { } public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) { LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, - dragObj.dragView, dragObj.originalDragInfo, Target.CONTAINER, dropTargetAsView);