From ddec1c739ef37c3a042982b8943fe42e04b65f4c Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 12 Apr 2016 18:32:04 -0700 Subject: [PATCH] Refactor UserEventLogging, Add predictedRank, replace Bundle with Proto b/26494415 - Removed bundle object that became redundant now that we have LauncherEvent proto - Combined Stats and UserEventLogger as they are effectively doing same thing - Removed parent field inside Target - added predictedRank target inside Target b/27967359 - make com.android.launcher3.action.LAUNCH broadcast explicit Later CL: finish packageName/intent/componentHash/predictedRank fields Change-Id: I441fb46c834f73e58a4d2324e8da7971e8713ec8 --- protos/launcher_log.proto | 83 ++++--- res/values/config.xml | 4 + src/com/android/launcher3/Hotseat.java | 14 +- src/com/android/launcher3/Launcher.java | 29 ++- .../android/launcher3/LauncherCallbacks.java | 3 +- src/com/android/launcher3/LauncherFiles.java | 4 +- src/com/android/launcher3/Stats.java | 152 ------------ src/com/android/launcher3/Workspace.java | 15 +- .../allapps/AllAppsRecyclerView.java | 22 +- src/com/android/launcher3/folder/Folder.java | 18 +- .../launcher3/logging/LoggerUtils.java | 36 +-- .../launcher3/logging/UserEventLogger.java | 218 +++++++++++++----- .../launcher3/testing/LauncherExtension.java | 4 +- 13 files changed, 292 insertions(+), 310 deletions(-) delete mode 100644 src/com/android/launcher3/Stats.java diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index a7b6429e42..eae02ca074 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -32,59 +32,66 @@ message Target { // For container type and item type // Used mainly for ContainerType.FOLDER, ItemType.* - optional Target parent = 2; - optional int32 page_index = 3; - optional int32 rank = 4; - optional int32 grid_x = 5; - optional int32 grid_y = 6; + optional int32 page_index = 2; + optional int32 rank = 3; + optional int32 grid_x = 4; + optional int32 grid_y = 5; // For container types only - optional ContainerType container_type = 7; - optional int32 cardinality = 8; + optional ContainerType container_type = 6; + optional int32 cardinality = 7; // For control types only - optional ControlType control_type = 9; + optional ControlType control_type = 8; // For item types only - optional ItemType item_type = 10; - optional int32 package_name_hash = 11; - optional int32 component_hash = 12; // Used for ItemType.WIDGET - optional int32 intent_hash = 13; // Used for ItemType.SHORTCUT - optional int32 span_x = 14 [default = 1];// Used for ItemType.WIDGET - optional int32 span_y = 15 [default = 1];// Used for ItemType.WIDGET + optional ItemType item_type = 9; + optional int32 package_name_hash = 10; + optional int32 component_hash = 11; // Used for ItemType.WIDGET + optional int32 intent_hash = 12; // Used for ItemType.SHORTCUT + optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET + optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET + optional int32 predictedRank = 15; } +// Used to define what type of item a Target would represent. enum ItemType { - APP_ICON = 0; - SHORTCUT = 1; - WIDGET = 2; - FOLDER_ICON = 3; + DEFAULT_ITEMTYPE = 0; + APP_ICON = 1; + SHORTCUT = 2; + WIDGET = 3; + FOLDER_ICON = 4; } +// Used to define what type of container a Target would represent. enum ContainerType { - WORKSPACE = 0; - HOTSEAT = 1; - FOLDER = 2; - ALLAPPS = 3; - WIDGETS = 4; - OVERVIEW = 5; - PREDICTION = 6; - SEARCHRESULT = 7; + DEFAULT_CONTAINERTYPE = 0; + WORKSPACE = 1; + HOTSEAT = 2; + FOLDER = 3; + ALLAPPS = 4; + WIDGETS = 5; + OVERVIEW = 6; + PREDICTION = 7; + SEARCHRESULT = 8; } +// Used to define what type of control a Target would represent. enum ControlType { - ALL_APPS_BUTTON = 0; - WIDGETS_BUTTON = 1; - WALLPAPER_BUTTON = 2; - SETTINGS_BUTTON = 3; - REMOVE_TARGET = 4; - UNINSTALL_TARGET = 5; - APPINFO_TARGET = 6; - RESIZE_HANDLE = 7; - FAST_SCROLL_HANDLE = 8; + DEFAULT_CONTROLTYPE = 0; + ALL_APPS_BUTTON = 1; + WIDGETS_BUTTON = 2; + WALLPAPER_BUTTON = 3; + SETTINGS_BUTTON = 4; + REMOVE_TARGET = 5; + UNINSTALL_TARGET = 6; + APPINFO_TARGET = 7; + RESIZE_HANDLE = 8; + VERTICAL_SCROLL = 9; // HOME, BACK, GO_TO_PLAYSTORE } +// Used to define the action component of the LauncherEvent. message Action { enum Type { TOUCH = 0; @@ -113,10 +120,10 @@ message LauncherEvent { required Action action = 1; // List of targets that touch actions can be operated on. - optional Target src_target = 2; - optional Target dest_target = 3; + repeated Target src_target = 2; + repeated Target dest_target = 3; optional int64 action_duration_millis = 4; optional int64 elapsed_container_millis = 5; optional int64 elapsed_session_millis = 6; -} +} \ No newline at end of file diff --git a/res/values/config.xml b/res/values/config.xml index 8d69f9a0d0..d689f1b8fe 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -68,6 +68,10 @@ filter the activities shown in the launcher. Can be empty. --> + + + diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index e691b480a1..1af76682fa 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -19,7 +19,6 @@ package com.android.launcher3; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -28,8 +27,12 @@ import android.view.ViewDebug; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.launcher3.logging.UserEventLogger; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; + public class Hotseat extends FrameLayout - implements Stats.LaunchSourceProvider{ + implements UserEventLogger.LaunchSourceProvider{ private CellLayout mContent; @@ -157,7 +160,10 @@ public class Hotseat extends FrameLayout } @Override - public void fillInLaunchSourceData(View v, Bundle sourceData) { - sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT); + public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) { + target.itemType = LauncherLogProto.APP_ICON; + target.gridX = info.cellX; + target.gridY = info.cellY; + targetParent.containerType = LauncherLogProto.HOTSEAT; } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d2d07cc5a8..5873368028 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -148,6 +148,7 @@ public class Launcher extends Activity static final boolean DEBUG_WIDGETS = false; static final boolean DEBUG_STRICT_MODE = false; static final boolean DEBUG_RESUME_TIME = false; + static final boolean DEBUG_LOGGING = false; static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run @@ -174,7 +175,7 @@ public class Launcher extends Activity protected static final int REQUEST_LAST = 100; // To turn on these properties, type - // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] + // adb shell setprop logTap.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] static final String DUMP_STATE_PROPERTY = "launcher_dump_state"; // The Intent extra that defines whether to ignore the launch animation @@ -365,7 +366,6 @@ public class Launcher extends Activity int appWidgetId; } - private Stats mStats; private UserEventLogger mUserEventLogger; public FocusIndicatorView mFocusHandler; @@ -425,7 +425,6 @@ public class Launcher extends Activity mDragController = new DragController(this); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); - mStats = new Stats(this); initLogger(); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); @@ -638,10 +637,6 @@ public class Launcher extends Activity } } - public Stats getStats() { - return mStats; - } - /** * Logger object is a singleton and does not have to be coupled with the foreground activity. * Since most user event logging is done on the UI, the object is retrieved from the @@ -652,16 +647,19 @@ public class Launcher extends Activity mUserEventLogger = mLauncherCallbacks.getLogger(); } if (mUserEventLogger == null) { - mUserEventLogger = new UserEventLogger() { + mUserEventLogger = new UserEventLogger(this) { @Override public void processEvent(LauncherLogProto.LauncherEvent ev) { - if (ev.action.touch == LauncherLogProto.Action.TAP && ev.srcTarget.itemType == LauncherLogProto.APP_ICON) { - Log.d(TAG, String.format(Locale.US, "action:%s target:%s\n\telapsed container %d ms session %d ms", - LoggerUtils.getActionStr(ev.action), - LoggerUtils.getTargetStr(ev.srcTarget), - ev.elapsedContainerMillis, - ev.elapsedSessionMillis)); + if (!DEBUG_LOGGING) { + return; } + Log.d("UserEvent", String.format(Locale.US, + "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms", + LoggerUtils.getActionStr(ev.action), + LoggerUtils.getTargetStr(ev.srcTarget[0]), + LoggerUtils.getTargetStr(ev.srcTarget[1]), + ev.elapsedContainerMillis, + ev.elapsedSessionMillis)); } }; } @@ -2705,7 +2703,7 @@ public class Launcher extends Activity } boolean success = startActivitySafely(v, intent, tag); - mStats.recordLaunch(v, intent, shortcut); + mUserEventLogger.logLaunch(v, intent); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; @@ -3488,6 +3486,7 @@ public class Launcher extends Activity List apps = mLauncherCallbacks.getPredictedApps(); if (apps != null) { mAppsView.setPredictedApps(apps); + mUserEventLogger.setPredictedApps(apps); } } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 635d413ba7..f33cf83e84 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -1,12 +1,11 @@ package com.android.launcher3; -import android.content.ComponentName; import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; import android.view.Menu; import android.view.View; -import android.view.ViewGroup; + import com.android.launcher3.allapps.AllAppsSearchBarController; import com.android.launcher3.logging.UserEventLogger; import com.android.launcher3.util.ComponentKey; diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 6ce2293829..adb5031352 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -39,8 +39,8 @@ public class LauncherFiles { // TODO: Delete these files on upgrade public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( - "launches.log", - "stats.log", + "launches.logTap", + "stats.logTap", "launcher.preferences", "com.android.launcher3.compat.PackageInstallerCompatV16.queue")); } diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java deleted file mode 100644 index 10a26ad59b..0000000000 --- a/src/com/android/launcher3/Stats.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2012 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; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.view.ViewParent; - -import com.android.launcher3.config.ProviderConfig; - -public class Stats { - - /** - * Implemented by containers to provide a launch source for a given child. - */ - public interface LaunchSourceProvider { - void fillInLaunchSourceData(View v, Bundle sourceData); - } - - /** - * Helpers to add the source to a launch intent. - */ - public static class LaunchSourceUtils { - /** - * Create a default bundle for LaunchSourceProviders to fill in their data. - */ - public static Bundle createSourceData() { - Bundle sourceData = new Bundle(); - sourceData.putString(SOURCE_EXTRA_CONTAINER, CONTAINER_HOMESCREEN); - // Have default container/sub container pages - sourceData.putInt(SOURCE_EXTRA_CONTAINER_PAGE, 0); - sourceData.putInt(SOURCE_EXTRA_SUB_CONTAINER_PAGE, 0); - return sourceData; - } - - /** - * Finds the next launch source provider in the parents of the view hierarchy and populates - * the source data from that provider. - */ - public static void populateSourceDataFromAncestorProvider(View v, Bundle sourceData) { - if (v == null) { - return; - } - - Stats.LaunchSourceProvider provider = null; - ViewParent parent = v.getParent(); - while (parent != null && parent instanceof View) { - if (parent instanceof Stats.LaunchSourceProvider) { - provider = (Stats.LaunchSourceProvider) parent; - break; - } - parent = parent.getParent(); - } - - if (provider != null) { - provider.fillInLaunchSourceData(v, sourceData); - } else if (ProviderConfig.IS_DOGFOOD_BUILD) { - throw new RuntimeException("Expected LaunchSourceProvider"); - } - } - } - - private static final boolean DEBUG_BROADCASTS = false; - - public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; - public static final String EXTRA_INTENT = "intent"; - public static final String EXTRA_CONTAINER = "container"; - public static final String EXTRA_SCREEN = "screen"; - public static final String EXTRA_CELLX = "cellX"; - public static final String EXTRA_CELLY = "cellY"; - public static final String EXTRA_SOURCE = "source"; - - public static final String SOURCE_EXTRA_CONTAINER = "container"; - public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page"; - public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container"; - public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page"; - - public static final String CONTAINER_SEARCH_BOX = "search_box"; - public static final String CONTAINER_ALL_APPS = "all_apps"; - public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace - public static final String CONTAINER_HOTSEAT = "hotseat"; - - public static final String SUB_CONTAINER_FOLDER = "folder"; - public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z"; - public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction"; - public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search"; - - private final Launcher mLauncher; - private final String mLaunchBroadcastPermission; - - public Stats(Launcher launcher) { - mLauncher = launcher; - mLaunchBroadcastPermission = - launcher.getResources().getString(R.string.receive_launch_broadcasts_permission); - - if (DEBUG_BROADCASTS) { - launcher.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.v("Stats", "got broadcast: " + intent + " for launched intent: " - + intent.getStringExtra(EXTRA_INTENT)); - } - }, - new IntentFilter(ACTION_LAUNCH), - mLaunchBroadcastPermission, - null - ); - } - } - - public void recordLaunch(View v, Intent intent, ShortcutInfo shortcut) { - intent = new Intent(intent); - intent.setSourceBounds(null); - - final String flat = intent.toUri(0); - Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat); - if (shortcut != null) { - broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container) - .putExtra(EXTRA_SCREEN, shortcut.screenId) - .putExtra(EXTRA_CELLX, shortcut.cellX) - .putExtra(EXTRA_CELLY, shortcut.cellY); - } - - Bundle sourceExtras = LaunchSourceUtils.createSourceData(); - LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras); - broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras); - mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); - if (intent.getComponent() != null) { - mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras); - } - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 4aea85e6da..6b38f648c0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -39,7 +39,6 @@ import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; @@ -73,6 +72,9 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.SpringLoadedDragController; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.logging.UserEventLogger; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperOffsetInterpolator; @@ -92,7 +94,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Workspace extends PagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, - Insettable, DropTargetSource, AccessibilityDragSource, Stats.LaunchSourceProvider { + Insettable, DropTargetSource, AccessibilityDragSource, UserEventLogger.LaunchSourceProvider { private static final String TAG = "Launcher.Workspace"; private static boolean ENFORCE_DRAG_EVENT_ORDER = false; @@ -4264,9 +4266,12 @@ public class Workspace extends PagedView } @Override - public void fillInLaunchSourceData(View v, Bundle sourceData) { - sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN); - sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage()); + public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) { + target.itemType = LauncherLogProto.APP_ICON; + target.gridX = info.cellX; + target.gridY = info.cellY; + target.pageIndex = getCurrentPage(); + targetParent.containerType = LauncherLogProto.WORKSPACE; } /** diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 2b3d061e60..32d444d8f9 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -19,7 +19,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; @@ -27,17 +26,19 @@ 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.R; -import com.android.launcher3.Stats; import com.android.launcher3.Utilities; - +import com.android.launcher3.logging.UserEventLogger.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 Stats.LaunchSourceProvider { + implements LaunchSourceProvider { private AlphabeticalAppsList mApps; private AllAppsFastScrollHelper mFastScrollHelper; @@ -165,11 +166,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView } @Override - public void fillInLaunchSourceData(View v, Bundle sourceData) { - sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); + public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) { if (mApps.hasFilter()) { - sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, - Stats.SUB_CONTAINER_ALL_APPS_SEARCH); + targetParent.containerType = LauncherLogProto.SEARCHRESULT; } else { if (v instanceof BubbleTextView) { BubbleTextView icon = (BubbleTextView) v; @@ -178,14 +177,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView List items = mApps.getAdapterItems(); AlphabeticalAppsList.AdapterItem item = items.get(position); if (item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { - sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, - Stats.SUB_CONTAINER_ALL_APPS_PREDICTION); + targetParent.containerType = LauncherLogProto.PREDICTION; return; } } } - sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, - Stats.SUB_CONTAINER_ALL_APPS_A_Z); + + targetParent.containerType = LauncherLogProto.ALLAPPS; } } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 2b34a79e83..b6be8e04cc 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -29,7 +29,6 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; -import android.os.Bundle; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -70,7 +69,6 @@ import com.android.launcher3.LogDecelerateInterpolator; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; -import com.android.launcher3.Stats; import com.android.launcher3.UninstallDropTarget.DropTargetSource; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; @@ -79,6 +77,9 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.logging.UserEventLogger.LaunchSourceProvider; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.UiThreadCircularReveal; @@ -92,7 +93,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, - Stats.LaunchSourceProvider { + LaunchSourceProvider { private static final String TAG = "Launcher.Folder"; /** @@ -1416,11 +1417,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } @Override - public void fillInLaunchSourceData(View v, Bundle sourceData) { - // Fill in from the folder icon's launch source provider first - Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData); - sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER); - sourceData.putInt(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE, mContent.getCurrentPage()); + public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) { + target.itemType = LauncherLogProto.APP_ICON; + target.gridX = info.cellX; + target.gridY = info.cellY; + target.pageIndex = mContent.getCurrentPage(); + targetParent.containerType = LauncherLogProto.FOLDER; } private class OnScrollHintListener implements OnAlarmListener { diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index 4b30384f27..584e38e9e9 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -5,7 +5,6 @@ import android.util.Log; import com.android.launcher3.LauncherSettings; import com.android.launcher3.ShortcutInfo; -import com.android.launcher3.Stats; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -18,16 +17,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Target; */ public class LoggerUtils { private static final String TAG = "LoggerUtils"; - private static final boolean DEBUG = false; - - static int getContainerType(ShortcutInfo shortcut) { - switch ((int) shortcut.container) { - case LauncherSettings.Favorites.CONTAINER_DESKTOP: return LauncherLogProto.WORKSPACE; - case LauncherSettings.Favorites.CONTAINER_HOTSEAT: return LauncherLogProto.HOTSEAT; - default: - return (int) shortcut.container; - } - } public static String getActionStr(LauncherLogProto.Action action) { switch(action.touch) { @@ -62,8 +51,10 @@ public class LoggerUtils { default: typeStr = "UNKNOWN"; } - return typeStr + " " + t.packageNameHash + " grid=(" + t.gridX + "," + t.gridY + ") " - + getContainerStr(t.parent); + return typeStr + ", packageHash=" + t.packageNameHash + + ", componentHash=" + t.componentHash + + ", intentHash=" + t.intentHash + + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex; } private static String getControlStr(Target t) { @@ -76,7 +67,6 @@ public class LoggerUtils { case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET"; case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET"; case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE"; - case LauncherLogProto.FAST_SCROLL_HANDLE: return "FAST_SCROLL_HANDLE"; default: return "UNKNOWN"; } } @@ -114,4 +104,22 @@ public class LoggerUtils { } return str + " id=" + t.pageIndex; } + + + public static LauncherLogProto.LauncherEvent initLauncherEvent( + int actionType, + int childTargetType, + int parentTargetType){ + LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent(); + + event.srcTarget = new LauncherLogProto.Target[2]; + event.srcTarget[0] = new LauncherLogProto.Target(); + event.srcTarget[0].type = childTargetType; + event.srcTarget[1] = new LauncherLogProto.Target(); + event.srcTarget[1].type = parentTargetType; + + event.action = new LauncherLogProto.Action(); + event.action.type = actionType; + return event; + } } diff --git a/src/com/android/launcher3/logging/UserEventLogger.java b/src/com/android/launcher3/logging/UserEventLogger.java index 4e5b2c1b40..bc3afeb8a4 100644 --- a/src/com/android/launcher3/logging/UserEventLogger.java +++ b/src/com/android/launcher3/logging/UserEventLogger.java @@ -1,71 +1,182 @@ +/* + * Copyright (C) 2012 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 android.os.Bundle; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.util.Log; +import android.view.View; +import android.view.ViewParent; -import com.android.launcher3.ShortcutInfo; -import com.android.launcher3.Stats; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.util.ComponentKey; -import java.util.Locale; +import com.google.protobuf.nano.MessageNano; + +import java.util.List; public abstract class UserEventLogger { + private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5; + /** + * Implemented by containers to provide a launch source for a given child. + */ + public interface LaunchSourceProvider { + + /** + * Copies data from the source to the destination proto. + * @param v source of the data + * @param info source of the data + * @param target dest of the data + * @param targetParent dest of the data + */ + void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent); + } + + /** + * Recursively finds the parent of the given child which implements IconLogInfoProvider + */ + public static LaunchSourceProvider getLaunchProviderRecursive(View v) { + ViewParent parent = null; + if (v != null) { + parent = v.getParent(); + } else { + return null; + } + + // Optimization to only check up to 5 parents. + int count = MAXIMUM_VIEW_HIERARCHY_LEVEL; + while (parent != null && count-- > 0) { + if (parent instanceof LaunchSourceProvider) { + return (LaunchSourceProvider) parent; + } else { + parent = parent.getParent(); + } + } + return null; + } + private String TAG = "UserEventLogger"; - private boolean DEBUG = false; + private static final boolean DEBUG_BROADCASTS = true; + + public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; + public static final String EXTRA_INTENT = "intent";; + public static final String EXTRA_SOURCE = "source"; + + private final Launcher mLauncher; + private final String mLaunchBroadcastPermission; private long mElapsedContainerMillis; private long mElapsedSessionMillis; private long mActionDurationMillis; + // Used for filling in predictedRank on {@link Target}s. + private List mPredictedApps; - public final void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) { - if (FeatureFlags.LAUNCHER3_LEGACY_LOGGING) return; + public UserEventLogger(Launcher launcher) { + mLauncher = launcher; + mLaunchBroadcastPermission = + launcher.getResources().getString(R.string.receive_launch_broadcasts_permission); - LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent(); - event.action = new LauncherLogProto.Action(); - event.action.type = LauncherLogProto.Action.TOUCH; - event.action.touch = LauncherLogProto.Action.TAP; - - event.srcTarget = new LauncherLogProto.Target(); - event.srcTarget.type = LauncherLogProto.Target.ITEM; - event.srcTarget.itemType = LauncherLogProto.APP_ICON; - // TODO: package hash name should be different per device. - event.srcTarget.packageNameHash = provider.hashCode(); - - event.srcTarget.parent = new LauncherLogProto.Target(); - String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER); - - if (shortcut != null) { - event.srcTarget.parent.containerType = LoggerUtils.getContainerType(shortcut); - event.srcTarget.pageIndex = (int) shortcut.screenId; - event.srcTarget.gridX = shortcut.cellX; - event.srcTarget.gridX = shortcut.cellY; + if (DEBUG_BROADCASTS) { + launcher.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.v(TAG, "got broadcast: " + intent + " for launched intent: " + + intent.getStringExtra(EXTRA_INTENT)); + } + }, + new IntentFilter(ACTION_LAUNCH), + mLaunchBroadcastPermission, + null + ); } - if (subContainer != null) { - event.srcTarget.parent.type = LauncherLogProto.Target.CONTAINER; - if (subContainer.equals(Stats.SUB_CONTAINER_FOLDER)) { - event.srcTarget.parent.containerType = LauncherLogProto.FOLDER; - } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_A_Z)) { - event.srcTarget.parent.containerType = LauncherLogProto.ALLAPPS; - } else if (subContainer.equals(Stats.CONTAINER_HOTSEAT)) { - event.srcTarget.parent.containerType = LauncherLogProto.HOTSEAT; - } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_PREDICTION)) { - event.srcTarget.parent.containerType = LauncherLogProto.PREDICTION; - } + } - if (DEBUG) { - Log.d(TAG, String.format("parent bundle: %s %s %s %s", - bundle.getString(Stats.SOURCE_EXTRA_CONTAINER), - bundle.getString(Stats.SOURCE_EXTRA_CONTAINER_PAGE), - bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER), - bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE))); - } + // APP_ICON SHORTCUT WIDGET + // -------------------------------------------------------------- + // packageNameHash required optional required + // componentNameHash required required + // intentHash required + // -------------------------------------------------------------- + + /** + * Prepare {@link LauncherEvent} and {@link Intent} and then attach the event + * to the intent and then broadcast. + */ + public final void broadcastEvent(LauncherEvent ev, Intent intent) { + intent = new Intent(intent); + intent.setSourceBounds(null); + + final String flat = intent.toUri(0); + Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat); + + broadcastIntent.putExtra(EXTRA_SOURCE, MessageNano.toByteArray(ev)); + String[] packages = ((Context)mLauncher).getResources().getStringArray(R.array.launch_broadcast_targets); + for(String p: packages) { + broadcastIntent.setPackage(p); + mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); } + } + + public final void logLaunch(View v, Intent intent) { + LauncherEvent event = LoggerUtils.initLauncherEvent( + Action.TOUCH, Target.ITEM, 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. + int idx = 0; + LaunchSourceProvider provider = getLaunchProviderRecursive(v); + provider.fillInLaunchSourceData(v, (ItemInfo) v.getTag(), event.srcTarget[idx], event.srcTarget[idx + 1]); + + // TODO: Fill in all the hashes and the predictedRank + + // Fill in the duration of time spent navigating in Launcher and the container. event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis; event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis; processEvent(event); + + broadcastEvent(event, intent); + } + + public void logTap(View v) { + // TODO + } + + public void logLongPress() { + // TODO + } + + public void logDragNDrop() { + // TODO + } + + public void setPredictedApps(List predictedApps) { + mPredictedApps = predictedApps; } /** @@ -73,25 +184,22 @@ public abstract class UserEventLogger { */ public final void resetElapsedContainerMillis() { mElapsedContainerMillis = System.currentTimeMillis(); - if(DEBUG) { - Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis); - } } public final void resetElapsedSessionMillis() { mElapsedSessionMillis = System.currentTimeMillis(); mElapsedContainerMillis = System.currentTimeMillis(); - if(DEBUG) { - Log.d(TAG, "resetElapsedSessionMillis " + mElapsedSessionMillis); - } + } public final void resetActionDurationMillis() { mActionDurationMillis = System.currentTimeMillis(); - if(DEBUG) { - Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis); - } } public abstract void processEvent(LauncherLogProto.LauncherEvent ev); -} \ No newline at end of file + + public int getPredictedRank(ComponentKey key) { + if (mPredictedApps == null) return -1; + return mPredictedApps.indexOf(key); + } +} diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index de8da36f3d..2fde8f3e46 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -1,6 +1,5 @@ package com.android.launcher3.testing; -import android.content.ComponentName; import android.content.Intent; import android.graphics.Color; import android.graphics.Rect; @@ -12,9 +11,8 @@ import android.widget.FrameLayout; import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherCallbacks; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.logging.UserEventLogger; import com.android.launcher3.allapps.AllAppsSearchBarController; +import com.android.launcher3.logging.UserEventLogger; import com.android.launcher3.util.ComponentKey; import java.io.FileDescriptor;