Support user event logging for drag and drop

b/30039490

Supported in this CL:
- DnD: drag from container [WORKSPACE|HOTSEAT|FOLDER|ALLAPPS|WIDGETS|DEEPSHORTCUTS]
       drag to container [HOTSEAT,WORKSPACE,FOLDER,DROPTARGETS]
- Source and target can be [FOLDER_ICON, ICON, DEEPSHORTCUT, WIDGET]
- $ adb shell setprop log.tag.UserEvent DEBUG will turn on debugging

Change-Id: I0b8b879b80e6dce85bbde6e7794f9e0677832603
(cherry picked from commit 59a238095e)
This commit is contained in:
Hyunyoung Song
2016-09-01 12:47:12 -07:00
parent 6ff1ef17a2
commit 551e5abfe1
11 changed files with 173 additions and 59 deletions

View File

@@ -19,11 +19,12 @@ package com.android.launcher3;
import android.view.View;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
/**
* Interface defining an object that can originate a drag.
*/
public interface DragSource {
public interface DragSource extends LaunchSourceProvider {
/**
* @return whether items dragged from this source supports

View File

@@ -48,9 +48,12 @@ public interface DropTarget {
/** The view that moves around while you drag. */
public DragView dragView = null;
/** The data associated with the object being dragged */
/** The data associated with the object, after item is dropped. */
public ItemInfo dragInfo = null;
/** The data associated with the object being dragged */
public ItemInfo originalDragInfo = null;
/** Where the drag originated */
public DragSource dragSource = null;

View File

@@ -100,7 +100,7 @@ import java.util.HashSet;
public class Workspace extends PagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
Insettable, DropTargetSource, AccessibilityDragSource, UserEventDispatcher.LaunchSourceProvider {
Insettable, DropTargetSource, AccessibilityDragSource {
private static final String TAG = "Launcher.Workspace";
private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
@@ -3453,6 +3453,7 @@ public class Workspace extends PagedView
if (info.container == NO_ID && info instanceof AppInfo) {
// Came from all apps -- make a copy
info = ((AppInfo) info).makeShortcut();
d.dragInfo = info;
}
view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
break;
@@ -4322,6 +4323,12 @@ public class Workspace extends PagedView
target.gridY = info.cellY;
target.pageIndex = getCurrentPage();
targetParent.containerType = LauncherLogProto.WORKSPACE;
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
target.rank = info.rank;
targetParent.containerType = LauncherLogProto.HOTSEAT;
} else if (info.container >= 0) {
targetParent.containerType = LauncherLogProto.FOLDER;
}
}
/**

View File

@@ -51,6 +51,8 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
import java.nio.charset.Charset;
@@ -700,4 +702,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mSearchQueryBuilder.clearSpans();
Selection.setSelection(mSearchQueryBuilder, 0);
}
@Override
public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = mAppsRecyclerView.getContainerType(v);
}
}

View File

@@ -27,20 +27,16 @@ import android.view.View;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import java.util.List;
/**
* A RecyclerView with custom fast scroll support for the all apps view.
*/
public class AllAppsRecyclerView extends BaseRecyclerView
implements LaunchSourceProvider {
public class AllAppsRecyclerView extends BaseRecyclerView {
private AlphabeticalAppsList mApps;
private AllAppsFastScrollHelper mFastScrollHelper;
@@ -207,10 +203,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView
updateEmptySearchBackgroundBounds();
}
@Override
public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
public int getContainerType(View v) {
if (mApps.hasFilter()) {
targetParent.containerType = LauncherLogProto.SEARCHRESULT;
return LauncherLogProto.SEARCHRESULT;
} else {
if (v instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) v;
@@ -219,12 +214,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
AlphabeticalAppsList.AdapterItem item = items.get(position);
if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
targetParent.containerType = LauncherLogProto.PREDICTION;
return;
return LauncherLogProto.PREDICTION;
}
}
}
targetParent.containerType = LauncherLogProto.ALLAPPS;
return LauncherLogProto.ALLAPPS;
}
}

View File

@@ -43,7 +43,6 @@ import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -51,7 +50,6 @@ import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Class for initiating a drag within a view or across multiple views.
@@ -271,6 +269,8 @@ public class DragController implements DragDriver.EventListener, TouchController
mDragObject.dragSource = source;
mDragObject.dragInfo = dragInfo;
mDragObject.originalDragInfo = new ItemInfo();
mDragObject.originalDragInfo.copyFrom(dragInfo);
if (dragOffset != null) {
dragView.setDragVisualizeOffset(new Point(dragOffset));
@@ -285,6 +285,7 @@ public class DragController implements DragDriver.EventListener, TouchController
mLastTouch[0] = mMotionDownX;
mLastTouch[1] = mMotionDownY;
handleMoveEvent(mMotionDownX, mMotionDownY);
mLauncher.getUserEventDispatcher().resetActionDurationMillis();
return dragView;
}
@@ -507,7 +508,7 @@ public class DragController implements DragDriver.EventListener, TouchController
*/
public void setMoveTarget(View view) {
mMoveTarget = view;
}
}
public boolean dispatchUnhandledMove(View focused, int direction) {
return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
@@ -734,6 +735,7 @@ public class DragController implements DragDriver.EventListener, TouchController
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
mDragObject.dragSource.onDropCompleted(
dropTargetAsView, mDragObject, flingVel != null, accepted);
mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
}
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {

View File

@@ -92,8 +92,7 @@ import java.util.Comparator;
*/
public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource,
LaunchSourceProvider {
View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource {
private static final String TAG = "Launcher.Folder";
/**

View File

@@ -2,8 +2,12 @@ package com.android.launcher3.logging;
import android.view.View;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -164,14 +168,35 @@ public class LoggerUtils {
return event;
}
private static Target initTarget(View v) {
/**
* 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[1] = new LauncherLogProto.Target();
event.srcTarget[1].type = parentSrcTargetType;
event.destTarget = new LauncherLogProto.Target[2];
event.destTarget[0] = initTarget(v, info);
event.destTarget[1] = initDropTarget(parentDestTargetType);
event.action = new LauncherLogProto.Action();
event.action.type = actionType;
return event;
}
private static Target initTarget(View v, ItemInfo info) {
Target t = new LauncherLogProto.Target();
t.type = Target.ITEM;
if (!(v.getTag() instanceof ItemInfo)) {
return t;
}
ItemInfo itemInfo = (ItemInfo) v.getTag();
switch (itemInfo.itemType) {
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
t.itemType = LauncherLogProto.APP_ICON;
break;
@@ -190,4 +215,30 @@ public class LoggerUtils {
}
return t;
}
private static Target initDropTarget(View v) {
Target t = new LauncherLogProto.Target();
t.type = (v instanceof ButtonDropTarget)? Target.CONTROL : Target.CONTAINER;
if (t.type == Target.CONTAINER) {
return t;
}
if (v instanceof InfoDropTarget) {
t.controlType = LauncherLogProto.APPINFO_TARGET;
} else if (v instanceof UninstallDropTarget) {
t.controlType = LauncherLogProto.UNINSTALL_TARGET;
} else if (v instanceof DeleteDropTarget) {
t.controlType = LauncherLogProto.REMOVE_TARGET;
}
return t;
}
private static Target initTarget(View v) {
Target t = new LauncherLogProto.Target();
t.type = Target.ITEM;
if (!(v.getTag() instanceof ItemInfo)) {
return t;
}
return initTarget(v, (ItemInfo) v.getTag());
}
}

View File

@@ -18,12 +18,15 @@ package com.android.launcher3.logging;
import android.content.ComponentName;
import android.content.Intent;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -37,10 +40,15 @@ import java.util.Locale;
*/
public class UserEventDispatcher {
private static final boolean DEBUG_LOGGING = false;
private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
private final boolean mIsVerbose;
/**
* TODO: change the name of this interface to LogContainerProvider
* and the method name to fillInLogContainerData. Not changed to minimize CL diff
* in this branch.
*
* Implemented by containers to provide a launch source for a given child.
*/
public interface LaunchSourceProvider {
@@ -61,6 +69,7 @@ public class UserEventDispatcher {
*/
public static LaunchSourceProvider getLaunchProviderRecursive(View v) {
ViewParent parent = null;
if (v != null) {
parent = v.getParent();
} else {
@@ -88,6 +97,14 @@ public class UserEventDispatcher {
// Used for filling in predictedRank on {@link Target}s.
private List<ComponentKey> mPredictedApps;
public UserEventDispatcher() {
if (ProviderConfig.IS_DOGFOOD_BUILD) {
mIsVerbose = Utilities.isPropertyEnabled(TAG);
} else {
mIsVerbose = false;
}
}
// APP_ICON SHORTCUT WIDGET
// --------------------------------------------------------------
// packageNameHash required optional required
@@ -104,7 +121,7 @@ public class UserEventDispatcher {
// TODO: make this percolate up the view hierarchy if needed.
int idx = 0;
LaunchSourceProvider provider = getLaunchProviderRecursive(v);
if (!(v.getTag() instanceof ItemInfo)) {
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
return null;
}
ItemInfo itemInfo = (ItemInfo) v.getTag();
@@ -122,8 +139,8 @@ public class UserEventDispatcher {
}
// Fill in the duration of time spent navigating in Launcher and the container.
event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
event.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
return event;
}
@@ -139,8 +156,8 @@ public class UserEventDispatcher {
LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTROL);
event.action.touch = action;
event.srcTarget[0].controlType = controlType;
event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
event.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
dispatchUserEvent(event, null);
}
@@ -149,8 +166,8 @@ public class UserEventDispatcher {
event.action.touch = action;
event.action.dir = dir;
event.srcTarget[0].containerType = containerType;
event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
event.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
dispatchUserEvent(event, null);
}
@@ -158,14 +175,14 @@ public class UserEventDispatcher {
LauncherEvent event = LoggerUtils.initLauncherEvent(
Action.TOUCH, icon, Target.CONTAINER);
LaunchSourceProvider provider = getLaunchProviderRecursive(icon);
if (!(icon.getTag() instanceof ItemInfo)) {
if (icon == null && !(icon.getTag() instanceof ItemInfo)) {
return;
}
ItemInfo info = (ItemInfo) icon.getTag();
provider.fillInLaunchSourceData(icon, info, event.srcTarget[0], event.srcTarget[1]);
event.action.touch = Action.LONGPRESS;
event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
event.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
dispatchUserEvent(event, null);
}
@@ -173,39 +190,66 @@ public class UserEventDispatcher {
mPredictedApps = predictedApps;
}
public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH,
dragObj.dragView,
dragObj.originalDragInfo,
Target.CONTAINER,
dropTargetAsView);
event.action.touch = Action.DRAGDROP;
dragObj.dragSource.fillInLaunchSourceData(null, dragObj.originalDragInfo,
event.srcTarget[0], event.srcTarget[1]);
if (dropTargetAsView instanceof LaunchSourceProvider) {
((LaunchSourceProvider) dropTargetAsView).fillInLaunchSourceData(null,
dragObj.dragInfo, event.destTarget[0], event.destTarget[1]);
}
event.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
event.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
dispatchUserEvent(event, null);
}
/**
* Currently logs following containers: workspace, allapps, widget tray.
*/
public final void resetElapsedContainerMillis() {
mElapsedContainerMillis = System.currentTimeMillis();
mElapsedContainerMillis = SystemClock.uptimeMillis();
}
public final void resetElapsedSessionMillis() {
mElapsedSessionMillis = System.currentTimeMillis();
mElapsedContainerMillis = System.currentTimeMillis();
mElapsedSessionMillis = SystemClock.uptimeMillis();
mElapsedContainerMillis = SystemClock.uptimeMillis();
}
public final void resetActionDurationMillis() {
mActionDurationMillis = System.currentTimeMillis();
mActionDurationMillis = SystemClock.uptimeMillis();
}
public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
if (DEBUG_LOGGING) {
Log.d(TAG, String.format(Locale.US,
"\naction:%s\n Source child:%s\tparent:%s",
LoggerUtils.getActionStr(ev.action),
LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
LoggerUtils.getTargetStr(ev.srcTarget.length > 1 ? ev.srcTarget[1] : null)));
if (ev.destTarget != null && ev.destTarget.length > 0) {
Log.d(TAG, String.format(Locale.US,
" Destination child:%s\tparent:%s",
LoggerUtils.getTargetStr(ev.destTarget != null ? ev.destTarget[0] : null),
LoggerUtils.getTargetStr(ev.destTarget.length > 1 ? ev.destTarget[1] : null)));
}
Log.d(TAG, String.format(Locale.US,
" Elapsed container %d ms session %d ms",
ev.elapsedContainerMillis,
ev.elapsedSessionMillis));
if (!mIsVerbose) {
return;
}
Log.d(TAG, String.format(Locale.US,
"\naction:%s\n Source child:%s\tparent:%s",
LoggerUtils.getActionStr(ev.action),
LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
LoggerUtils.getTargetStr(ev.srcTarget != null && ev.srcTarget.length > 1 ?
ev.srcTarget[1] : null)));
if (ev.destTarget != null && ev.destTarget.length > 0) {
Log.d(TAG, String.format(Locale.US,
" Destination child:%s\tparent:%s",
LoggerUtils.getTargetStr(ev.destTarget != null ? ev.destTarget[0] : null),
LoggerUtils.getTargetStr(ev.destTarget != null && ev.destTarget.length > 1 ?
ev.destTarget[1] : null)));
}
Log.d(TAG, String.format(Locale.US,
" Elapsed container %d ms session %d ms action %d ms",
ev.elapsedContainerMillis,
ev.elapsedSessionMillis,
ev.actionDurationMillis));
}
}

View File

@@ -76,8 +76,7 @@ import java.util.List;
*/
@TargetApi(Build.VERSION_CODES.N)
public class DeepShortcutsContainer extends LinearLayout implements View.OnLongClickListener,
View.OnTouchListener, DragSource, DragController.DragListener,
UserEventDispatcher.LaunchSourceProvider {
View.OnTouchListener, DragSource, DragController.DragListener {
private static final String TAG = "ShortcutsContainer";
private final Point mIconShift = new Point();

View File

@@ -45,6 +45,8 @@ import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Thunk;
/**
@@ -354,4 +356,9 @@ public class WidgetsContainerView extends BaseContainerView
}
return mWidgetPreviewLoader;
}
@Override
public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = LauncherLogProto.WIDGETS;
}
}