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
This commit is contained in:
Hyunyoung Song 2016-04-12 18:32:04 -07:00
parent 30a716157b
commit ddec1c739e
13 changed files with 292 additions and 310 deletions

View File

@ -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;
}
}

View File

@ -68,6 +68,10 @@
filter the activities shown in the launcher. Can be empty. -->
<string name="app_filter_class" translatable="false"></string>
<!-- List of package names that com.android.launcher3.action.LAUNCH
should be targeting. Can be empty. -->
<array name="launch_broadcast_targets" translatable="false"></array>
<!-- Name of an icon provider class. -->
<string name="icon_provider_class" translatable="false"></string>

View File

@ -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;
}
}

View File

@ -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<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
if (apps != null) {
mAppsView.setPredictedApps(apps);
mUserEventLogger.setPredictedApps(apps);
}
}
}

View File

@ -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;

View File

@ -39,8 +39,8 @@ public class LauncherFiles {
// TODO: Delete these files on upgrade
public static final List<String> OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList(
"launches.log",
"stats.log",
"launches.logTap",
"stats.logTap",
"launcher.preferences",
"com.android.launcher3.compat.PackageInstallerCompatV16.queue"));
}

View File

@ -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);
}
}
}

View File

@ -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;
}
/**

View File

@ -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<AlphabeticalAppsList.AdapterItem> 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;
}
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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<ComponentKey> 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<ComponentKey> 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);
}
public int getPredictedRank(ComponentKey key) {
if (mPredictedApps == null) return -1;
return mPredictedApps.indexOf(key);
}
}

View File

@ -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;