// 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;
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
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>
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;
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;
}
@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;
}
}
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
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
int appWidgetId;
}
- private Stats mStats;
private UserEventLogger mUserEventLogger;
public FocusIndicatorView mFocusHandler;
mDragController = new DragController(this);
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
- mStats = new Stats(this);
initLogger();
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
}
}
- 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
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));
}
};
}
}
boolean success = startActivitySafely(v, intent, tag);
- mStats.recordLaunch(v, intent, shortcut);
+ mUserEventLogger.logLaunch(v, intent);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
if (apps != null) {
mAppsView.setPredictedApps(apps);
+ mUserEventLogger.setPredictedApps(apps);
}
}
}
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;
// 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"));
}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
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;
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;
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;
}
@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;
}
/**
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;
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;
}
@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;
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;
}
}
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;
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;
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;
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";
/**
}
@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 {
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;
*/
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) {
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) {
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";
}
}
}
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;
+ }
}
+/*
+ * 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 com.google.protobuf.nano.MessageNano;
-import java.util.Locale;
+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 UserEventLogger(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(TAG, "got broadcast: " + intent + " for launched intent: "
+ + intent.getStringExtra(EXTRA_INTENT));
+ }
+ },
+ new IntentFilter(ACTION_LAUNCH),
+ mLaunchBroadcastPermission,
+ null
+ );
+ }
+ }
- public final void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) {
- if (FeatureFlags.LAUNCHER3_LEGACY_LOGGING) return;
+ // APP_ICON SHORTCUT WIDGET
+ // --------------------------------------------------------------
+ // packageNameHash required optional required
+ // componentNameHash required required
+ // intentHash required
+ // --------------------------------------------------------------
- LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
- event.action = new LauncherLogProto.Action();
- event.action.type = LauncherLogProto.Action.TOUCH;
- event.action.touch = LauncherLogProto.Action.TAP;
+ /**
+ * 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);
+ }
+ }
- 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();
+ public final void logLaunch(View v, Intent intent) {
+ LauncherEvent event = LoggerUtils.initLauncherEvent(
+ Action.TOUCH, Target.ITEM, Target.CONTAINER);
+ event.action.touch = Action.TAP;
- event.srcTarget.parent = new LauncherLogProto.Target();
- String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER);
+ // 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]);
- 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 (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;
- }
+ // TODO: Fill in all the hashes and the predictedRank
- 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)));
- }
- }
+ // 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;
}
/**
*/
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);
+ }
+}
package com.android.launcher3.testing;
-import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Rect;
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;