• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.logging;
18 
19 import android.content.ComponentName;
20 import android.content.Intent;
21 import android.os.SystemClock;
22 import android.util.Log;
23 import android.view.View;
24 import android.view.ViewParent;
25 
26 import com.android.launcher3.DropTarget;
27 import com.android.launcher3.ItemInfo;
28 import com.android.launcher3.Utilities;
29 import com.android.launcher3.config.ProviderConfig;
30 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
31 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
32 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
33 import com.android.launcher3.util.ComponentKey;
34 
35 import java.util.List;
36 import java.util.Locale;
37 
38 /**
39  * Manages the creation of {@link LauncherEvent}.
40  * To debug this class, execute following command before sideloading a new apk.
41  *
42  * $ adb shell setprop log.tag.UserEvent VERBOSE
43  */
44 public class UserEventDispatcher {
45 
46     private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
47 
48     private final boolean mIsVerbose;
49 
50     /**
51      * TODO: change the name of this interface to LogContainerProvider
52      * and the method name to fillInLogContainerData. Not changed to minimize CL diff
53      * in this branch.
54      *
55      * Implemented by containers to provide a launch source for a given child.
56      */
57     public interface LaunchSourceProvider {
58 
59         /**
60          * Copies data from the source to the destination proto.
61          *
62          * @param v            source of the data
63          * @param info         source of the data
64          * @param target       dest of the data
65          * @param targetParent dest of the data
66          */
fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent)67         void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent);
68     }
69 
70     /**
71      * Recursively finds the parent of the given child which implements IconLogInfoProvider
72      */
getLaunchProviderRecursive(View v)73     public static LaunchSourceProvider getLaunchProviderRecursive(View v) {
74         ViewParent parent = null;
75 
76         if (v != null) {
77             parent = v.getParent();
78         } else {
79             return null;
80         }
81 
82         // Optimization to only check up to 5 parents.
83         int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
84         while (parent != null && count-- > 0) {
85             if (parent instanceof LaunchSourceProvider) {
86                 return (LaunchSourceProvider) parent;
87             } else {
88                 parent = parent.getParent();
89             }
90         }
91         return null;
92     }
93 
94     private String TAG = "UserEvent";
95 
96     private long mElapsedContainerMillis;
97     private long mElapsedSessionMillis;
98     private long mActionDurationMillis;
99 
100     // Used for filling in predictedRank on {@link Target}s.
101     private List<ComponentKey> mPredictedApps;
102 
UserEventDispatcher()103     public UserEventDispatcher() {
104         if (ProviderConfig.IS_DOGFOOD_BUILD) {
105             mIsVerbose = Utilities.isPropertyEnabled(TAG);
106         } else {
107             mIsVerbose = false;
108         }
109     }
110 
111     //                      APP_ICON    SHORTCUT    WIDGET
112     // --------------------------------------------------------------
113     // packageNameHash      required    optional    required
114     // componentNameHash    required                required
115     // intentHash                       required
116     // --------------------------------------------------------------
117 
createLauncherEvent(View v, Intent intent)118     protected LauncherEvent createLauncherEvent(View v, Intent intent) {
119         LauncherEvent event = LoggerUtils.initLauncherEvent(
120                 Action.TOUCH, v, Target.CONTAINER);
121         event.action.touch = Action.TAP;
122 
123         // Fill in grid(x,y), pageIndex of the child and container type of the parent
124         // TODO: make this percolate up the view hierarchy if needed.
125         int idx = 0;
126         LaunchSourceProvider provider = getLaunchProviderRecursive(v);
127         if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
128             return null;
129         }
130         ItemInfo itemInfo = (ItemInfo) v.getTag();
131         provider.fillInLaunchSourceData(v, itemInfo, event.srcTarget[idx], event.srcTarget[idx + 1]);
132 
133         event.srcTarget[idx].intentHash = intent.hashCode();
134         ComponentName cn = intent.getComponent();
135         if (cn != null) {
136             event.srcTarget[idx].packageNameHash = cn.getPackageName().hashCode();
137             event.srcTarget[idx].componentHash = cn.hashCode();
138             if (mPredictedApps != null) {
139                 event.srcTarget[idx].predictedRank = mPredictedApps.indexOf(
140                         new ComponentKey(cn, itemInfo.user));
141             }
142         }
143         return event;
144     }
145 
logAppLaunch(View v, Intent intent)146     public void logAppLaunch(View v, Intent intent) {
147         LauncherEvent ev = createLauncherEvent(v, intent);
148         if (ev == null) {
149             return;
150         }
151         dispatchUserEvent(ev, intent);
152     }
153 
logActionOnItem(int action, int itemType)154     public void logActionOnItem(int action, int itemType) {
155         LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.ITEM);
156         event.action.touch = action;
157         event.srcTarget[0].itemType = itemType;
158         dispatchUserEvent(event, null);
159     }
160 
logActionOnControl(int action, int controlType)161     public void logActionOnControl(int action, int controlType) {
162         LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTROL);
163         event.action.touch = action;
164         event.srcTarget[0].controlType = controlType;
165         dispatchUserEvent(event, null);
166     }
167 
logActionOnContainer(int action, int dir, int containerType)168     public void logActionOnContainer(int action, int dir, int containerType) {
169         LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTAINER);
170         event.action.touch = action;
171         event.action.dir = dir;
172         event.srcTarget[0].containerType = containerType;
173         dispatchUserEvent(event, null);
174     }
175 
logDeepShortcutsOpen(View icon)176     public void logDeepShortcutsOpen(View icon) {
177         LauncherEvent event = LoggerUtils.initLauncherEvent(
178                 Action.TOUCH, icon, Target.CONTAINER);
179         LaunchSourceProvider provider = getLaunchProviderRecursive(icon);
180         if (icon == null && !(icon.getTag() instanceof ItemInfo)) {
181             return;
182         }
183         ItemInfo info = (ItemInfo) icon.getTag();
184         provider.fillInLaunchSourceData(icon, info, event.srcTarget[0], event.srcTarget[1]);
185         event.action.touch = Action.LONGPRESS;
186         dispatchUserEvent(event, null);
187     }
188 
setPredictedApps(List<ComponentKey> predictedApps)189     public void setPredictedApps(List<ComponentKey> predictedApps) {
190         mPredictedApps = predictedApps;
191     }
192 
logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView)193     public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
194         LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH,
195                 dragObj.dragView,
196                 dragObj.originalDragInfo,
197                 Target.CONTAINER,
198                 dropTargetAsView);
199         event.action.touch = Action.DRAGDROP;
200 
201         dragObj.dragSource.fillInLaunchSourceData(null, dragObj.originalDragInfo,
202                 event.srcTarget[0], event.srcTarget[1]);
203 
204         if (dropTargetAsView instanceof LaunchSourceProvider) {
205             ((LaunchSourceProvider) dropTargetAsView).fillInLaunchSourceData(null,
206                     dragObj.dragInfo, event.destTarget[0], event.destTarget[1]);
207 
208         }
209         event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
210         dispatchUserEvent(event, null);
211     }
212 
213     /**
214      * Currently logs following containers: workspace, allapps, widget tray.
215      */
resetElapsedContainerMillis()216     public final void resetElapsedContainerMillis() {
217         mElapsedContainerMillis = SystemClock.uptimeMillis();
218     }
219 
resetElapsedSessionMillis()220     public final void resetElapsedSessionMillis() {
221         mElapsedSessionMillis = SystemClock.uptimeMillis();
222         mElapsedContainerMillis = SystemClock.uptimeMillis();
223     }
224 
resetActionDurationMillis()225     public final void resetActionDurationMillis() {
226         mActionDurationMillis = SystemClock.uptimeMillis();
227     }
228 
dispatchUserEvent(LauncherEvent ev, Intent intent)229     public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
230         ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
231         ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
232 
233         if (!mIsVerbose) {
234             return;
235         }
236         Log.d(TAG, String.format(Locale.US,
237                 "\naction:%s\n Source child:%s\tparent:%s",
238                 LoggerUtils.getActionStr(ev.action),
239                 LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
240                 LoggerUtils.getTargetStr(ev.srcTarget != null && ev.srcTarget.length > 1 ?
241                         ev.srcTarget[1] : null)));
242         if (ev.destTarget != null && ev.destTarget.length > 0) {
243             Log.d(TAG, String.format(Locale.US,
244                     " Destination child:%s\tparent:%s",
245                     LoggerUtils.getTargetStr(ev.destTarget != null ? ev.destTarget[0] : null),
246                     LoggerUtils.getTargetStr(ev.destTarget != null && ev.destTarget.length > 1 ?
247                             ev.destTarget[1] : null)));
248         }
249         Log.d(TAG, String.format(Locale.US,
250                 " Elapsed container %d ms session %d ms action %d ms",
251                 ev.elapsedContainerMillis,
252                 ev.elapsedSessionMillis,
253                 ev.actionDurationMillis));
254     }
255 }
256