1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/app_list/app_list_metrics.h"
6 
7 #include <algorithm>
8 
9 #include "ash/app_list/model/app_list_model.h"
10 #include "ash/app_list/model/search/search_model.h"
11 #include "ash/app_list/model/search/search_result.h"
12 #include "ash/public/cpp/app_menu_constants.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "ui/compositor/compositor.h"
17 
18 namespace ash {
19 
20 namespace {
21 
22 // These constants affect logging, and  should not be changed without
23 // deprecating the following UMA histograms:
24 //  - Apps.AppListTileClickIndexAndQueryLength
25 //  - Apps.AppListResultClickIndexAndQueryLength
26 constexpr int kMaxLoggedQueryLength = 10;
27 constexpr int kMaxLoggedSuggestionIndex = 6;
28 constexpr int kMaxLoggedHistogramValue =
29     (kMaxLoggedSuggestionIndex + 1) * kMaxLoggedQueryLength +
30     kMaxLoggedSuggestionIndex;
31 
32 }  // namespace
33 
34 // The UMA histogram that logs smoothness of pagination animation.
35 constexpr char kPaginationTransitionAnimationSmoothness[] =
36     "Apps.PaginationTransition.AnimationSmoothness";
37 constexpr char kPaginationTransitionAnimationSmoothnessInTablet[] =
38     "Apps.PaginationTransition.AnimationSmoothness.TabletMode";
39 constexpr char kPaginationTransitionAnimationSmoothnessInClamshell[] =
40     "Apps.PaginationTransition.AnimationSmoothness.ClamshellMode";
41 
42 // The UMA histogram that logs which state search results are opened from.
43 constexpr char kAppListSearchResultOpenSourceHistogram[] =
44     "Apps.AppListSearchResultOpenedSource";
45 
46 // The UMA histogram that logs smoothness of cardified animation.
47 constexpr char kCardifiedStateAnimationSmoothnessEnter[] =
48     "Apps.AppList.CardifiedStateAnimation.AnimationSmoothness."
49     "EnterCardifiedState";
50 constexpr char kCardifiedStateAnimationSmoothnessExit[] =
51     "Apps.AppList.CardifiedStateAnimation.AnimationSmoothness."
52     "ExitCardifiedState";
53 
54 // The UMA hisotogram that logs the action user performs on zero state
55 // search result.
56 constexpr char kAppListZeroStateSearchResultUserActionHistogram[] =
57     "Apps.AppList.ZeroStateSearchResultUserActionType";
58 
59 // The UMA histogram that logs user's decision(remove or cancel) for zero state
60 // search result removal confirmation.
61 constexpr char kAppListZeroStateSearchResultRemovalHistogram[] =
62     "Apps.AppList.ZeroStateSearchResultRemovalDecision";
63 
64 // The UMA histogram that logs the length of the query when user abandons
65 // results of a queried search or recommendations of zero state(zero length
66 // query) in launcher UI.
67 constexpr char kSearchAbandonQueryLengthHistogram[] =
68     "Apps.AppListSearchAbandonQueryLength";
69 
70 // The base UMA histogram that logs app launches within the AppList and shelf.
71 constexpr char kAppListAppLaunched[] = "Apps.AppListAppLaunchedV2";
72 
73 // The UMA histograms that log app launches within the AppList and shelf. The
74 // app launches are divided by histogram for each of the the different AppList
75 // states.
76 constexpr char kAppListAppLaunchedClosed[] = "Apps.AppListAppLaunchedV2.Closed";
77 constexpr char kAppListAppLaunchedPeeking[] =
78     "Apps.AppListAppLaunchedV2.Peeking";
79 constexpr char kAppListAppLaunchedHalf[] = "Apps.AppListAppLaunchedV2.Half";
80 constexpr char kAppListAppLaunchedFullscreenAllApps[] =
81     "Apps.AppListAppLaunchedV2.FullscreenAllApps";
82 constexpr char kAppListAppLaunchedFullscreenSearch[] =
83     "Apps.AppListAppLaunchedV2.FullscreenSearch";
84 constexpr char kAppListAppLaunchedHomecherClosed[] =
85     "Apps.AppListAppLaunchedV2.HomecherClosed";
86 constexpr char kAppListAppLaunchedHomecherAllApps[] =
87     "Apps.AppListAppLaunchedV2.HomecherAllApps";
88 constexpr char kAppListAppLaunchedHomecherSearch[] =
89     "Apps.AppListAppLaunchedV2.HomecherSearch";
90 
91 // The different sources from which a search result is displayed. These values
92 // are written to logs.  New enum values can be added, but existing enums must
93 // never be renumbered or deleted and reused.
94 enum class ApplistSearchResultOpenedSource {
95   kHalfClamshell = 0,
96   kFullscreenClamshell = 1,
97   kFullscreenTablet = 2,
98   kMaxApplistSearchResultOpenedSource = 3,
99 };
100 
AppListRecordPageSwitcherSourceByEventType(ui::EventType type,bool is_tablet_mode)101 void AppListRecordPageSwitcherSourceByEventType(ui::EventType type,
102                                                 bool is_tablet_mode) {
103   AppListPageSwitcherSource source;
104 
105   switch (type) {
106     case ui::ET_MOUSEWHEEL:
107       source = kMouseWheelScroll;
108       break;
109     case ui::ET_SCROLL:
110       source = kMousePadScroll;
111       break;
112     case ui::ET_GESTURE_SCROLL_END:
113       source = kSwipeAppGrid;
114       break;
115     case ui::ET_SCROLL_FLING_START:
116       source = kFlingAppGrid;
117       break;
118     case ui::ET_MOUSE_RELEASED:
119       source = kMouseDrag;
120       break;
121     default:
122       NOTREACHED();
123       return;
124   }
125   RecordPageSwitcherSource(source, is_tablet_mode);
126 }
127 
RecordPageSwitcherSource(AppListPageSwitcherSource source,bool is_tablet_mode)128 void RecordPageSwitcherSource(AppListPageSwitcherSource source,
129                               bool is_tablet_mode) {
130   UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogram, source,
131                             kMaxAppListPageSwitcherSource);
132   if (is_tablet_mode) {
133     UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogramInTablet,
134                               source, kMaxAppListPageSwitcherSource);
135   } else {
136     UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogramInClamshell,
137                               source, kMaxAppListPageSwitcherSource);
138   }
139 }
140 
RecordSearchResultOpenSource(const SearchResult * result,const AppListModel * model,const SearchModel * search_model)141 APP_LIST_EXPORT void RecordSearchResultOpenSource(
142     const SearchResult* result,
143     const AppListModel* model,
144     const SearchModel* search_model) {
145   // Record the search metric if the SearchResult is not a suggested app.
146   if (result->is_recommendation())
147     return;
148 
149   ApplistSearchResultOpenedSource source;
150   AppListViewState state = model->state_fullscreen();
151   if (search_model->tablet_mode()) {
152     source = ApplistSearchResultOpenedSource::kFullscreenTablet;
153   } else {
154     source = state == AppListViewState::kHalf
155                  ? ApplistSearchResultOpenedSource::kHalfClamshell
156                  : ApplistSearchResultOpenedSource::kFullscreenClamshell;
157   }
158   UMA_HISTOGRAM_ENUMERATION(
159       kAppListSearchResultOpenSourceHistogram, source,
160       ApplistSearchResultOpenedSource::kMaxApplistSearchResultOpenedSource);
161 }
162 
RecordSearchLaunchIndexAndQueryLength(SearchResultLaunchLocation launch_location,int query_length,int suggestion_index)163 void RecordSearchLaunchIndexAndQueryLength(
164     SearchResultLaunchLocation launch_location,
165     int query_length,
166     int suggestion_index) {
167   if (suggestion_index < 0) {
168     LOG(ERROR) << "Received invalid suggestion index.";
169     return;
170   }
171 
172   query_length = std::min(query_length, kMaxLoggedQueryLength);
173   suggestion_index = std::min(suggestion_index, kMaxLoggedSuggestionIndex);
174   const int logged_value =
175       (kMaxLoggedSuggestionIndex + 1) * query_length + suggestion_index;
176 
177   if (launch_location == SearchResultLaunchLocation::kResultList) {
178     UMA_HISTOGRAM_EXACT_LINEAR(kAppListResultLaunchIndexAndQueryLength,
179                                logged_value, kMaxLoggedHistogramValue);
180     UMA_HISTOGRAM_BOOLEAN(kAppListResultLaunchIsEmptyQuery, query_length == 0);
181   }
182 }
183 
RecordSearchAbandonWithQueryLengthHistogram(int query_length)184 void RecordSearchAbandonWithQueryLengthHistogram(int query_length) {
185   UMA_HISTOGRAM_EXACT_LINEAR(kSearchAbandonQueryLengthHistogram,
186                              std::min(query_length, kMaxLoggedQueryLength),
187                              kMaxLoggedQueryLength);
188 }
189 
RecordZeroStateSearchResultUserActionHistogram(ZeroStateSearchResultUserActionType action)190 void RecordZeroStateSearchResultUserActionHistogram(
191     ZeroStateSearchResultUserActionType action) {
192   UMA_HISTOGRAM_ENUMERATION(kAppListZeroStateSearchResultUserActionHistogram,
193                             action);
194 }
195 
RecordZeroStateSearchResultRemovalHistogram(ZeroStateSearchResutRemovalConfirmation removal_decision)196 void RecordZeroStateSearchResultRemovalHistogram(
197     ZeroStateSearchResutRemovalConfirmation removal_decision) {
198   UMA_HISTOGRAM_ENUMERATION(kAppListZeroStateSearchResultRemovalHistogram,
199                             removal_decision);
200 }
201 
RecordAppListAppLaunched(AppListLaunchedFrom launched_from,AppListViewState app_list_state,bool is_tablet_mode,bool home_launcher_shown)202 void RecordAppListAppLaunched(AppListLaunchedFrom launched_from,
203                               AppListViewState app_list_state,
204                               bool is_tablet_mode,
205                               bool home_launcher_shown) {
206   UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunched, launched_from);
207   switch (app_list_state) {
208     case AppListViewState::kClosed:
209       UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedClosed, launched_from);
210       break;
211     case AppListViewState::kPeeking:
212       UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedPeeking, launched_from);
213       break;
214     case AppListViewState::kHalf:
215       UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedHalf, launched_from);
216       break;
217     case AppListViewState::kFullscreenAllApps:
218       if (is_tablet_mode) {
219         if (home_launcher_shown) {
220           UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedHomecherAllApps,
221                                     launched_from);
222         } else {
223           UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedHomecherClosed,
224                                     launched_from);
225         }
226       } else {
227         UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedFullscreenAllApps,
228                                   launched_from);
229       }
230       break;
231     case AppListViewState::kFullscreenSearch:
232       if (is_tablet_mode) {
233         if (home_launcher_shown) {
234           UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedHomecherSearch,
235                                     launched_from);
236         } else {
237           // (http://crbug.com/947729) Search box still expanded when opening
238           // launcher in tablet mode
239           UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedHomecherClosed,
240                                     launched_from);
241         }
242       } else {
243         UMA_HISTOGRAM_ENUMERATION(kAppListAppLaunchedFullscreenSearch,
244                                   launched_from);
245       }
246       break;
247   }
248 }
249 
IsCommandIdAnAppLaunch(int command_id_number)250 bool IsCommandIdAnAppLaunch(int command_id_number) {
251   CommandId command_id = static_cast<CommandId>(command_id_number);
252 
253   // Consider all platform app menu options as launches.
254   if (command_id >= CommandId::EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
255       command_id < CommandId::EXTENSIONS_CONTEXT_CUSTOM_LAST) {
256     return true;
257   }
258 
259   // Consider all arc app shortcut options as launches.
260   if (command_id >= CommandId::LAUNCH_APP_SHORTCUT_FIRST &&
261       command_id < CommandId::LAUNCH_APP_SHORTCUT_LAST) {
262     return true;
263   }
264 
265   // All app menu items in a ShelfApplicationMenuModel are not launches.
266   if (command_id >= CommandId::APP_MENU_ITEM_ID_FIRST &&
267       command_id < CommandId::APP_MENU_ITEM_ID_LAST) {
268     return false;
269   }
270 
271   switch (command_id) {
272     // Used by ShelfContextMenu (shelf).
273     case CommandId::MENU_OPEN_NEW:
274     case CommandId::MENU_NEW_WINDOW:
275     case CommandId::MENU_NEW_INCOGNITO_WINDOW:
276     // Used by AppContextMenu and/or ShelfContextMenu.
277     case CommandId::LAUNCH_NEW:
278     case CommandId::SHOW_APP_INFO:
279     case CommandId::OPTIONS:
280     case CommandId::APP_CONTEXT_MENU_NEW_WINDOW:
281     case CommandId::APP_CONTEXT_MENU_NEW_INCOGNITO_WINDOW:
282     case CommandId::SETTINGS:
283     // Used by both AppContextMenu and ShelfContextMenu for app shortcuts.
284     case CommandId::LAUNCH_APP_SHORTCUT_FIRST:
285     case CommandId::LAUNCH_APP_SHORTCUT_LAST:
286       return true;
287 
288     // Used by ShelfContextMenu (shelf).
289     case CommandId::MENU_CLOSE:
290     case CommandId::MENU_PIN:
291     case CommandId::LAUNCH_TYPE_PINNED_TAB:
292     case CommandId::LAUNCH_TYPE_REGULAR_TAB:
293     case CommandId::LAUNCH_TYPE_FULLSCREEN:
294     case CommandId::LAUNCH_TYPE_WINDOW:
295     case CommandId::LAUNCH_TYPE_TABBED_WINDOW:
296     case CommandId::SWAP_WITH_NEXT:
297     case CommandId::SWAP_WITH_PREVIOUS:
298     // Used by AppMenuModelAdapter
299     case CommandId::NOTIFICATION_CONTAINER:
300     // Used by CrostiniShelfContextMenu.
301     case CommandId::CROSTINI_USE_LOW_DENSITY:
302     case CommandId::CROSTINI_USE_HIGH_DENSITY:
303     // Used by AppContextMenu.
304     case CommandId::TOGGLE_PIN:
305     case CommandId::UNINSTALL:
306     case CommandId::REMOVE_FROM_FOLDER:
307     case CommandId::INSTALL:
308     case CommandId::USE_LAUNCH_TYPE_PINNED:
309     case CommandId::USE_LAUNCH_TYPE_REGULAR:
310     case CommandId::USE_LAUNCH_TYPE_FULLSCREEN:
311     case CommandId::USE_LAUNCH_TYPE_WINDOW:
312     case CommandId::USE_LAUNCH_TYPE_TABBED_WINDOW:
313     case CommandId::USE_LAUNCH_TYPE_COMMAND_END:
314     case CommandId::SHUTDOWN_GUEST_OS:
315     case CommandId::EXTENSIONS_CONTEXT_CUSTOM_FIRST:
316     case CommandId::EXTENSIONS_CONTEXT_CUSTOM_LAST:
317     case CommandId::COMMAND_ID_COUNT:
318     // Used by ShelfApplicationMenuModel.
319     case CommandId::APP_MENU_ITEM_ID_FIRST:
320     case CommandId::APP_MENU_ITEM_ID_LAST:
321       return false;
322   }
323   NOTREACHED();
324   return false;
325 }
326 
ReportPaginationSmoothness(bool is_tablet_mode,int smoothness)327 void ReportPaginationSmoothness(bool is_tablet_mode, int smoothness) {
328   UMA_HISTOGRAM_PERCENTAGE(kPaginationTransitionAnimationSmoothness,
329                            smoothness);
330 
331   if (is_tablet_mode) {
332     UMA_HISTOGRAM_PERCENTAGE(kPaginationTransitionAnimationSmoothnessInTablet,
333                              smoothness);
334   } else {
335     UMA_HISTOGRAM_PERCENTAGE(
336         kPaginationTransitionAnimationSmoothnessInClamshell, smoothness);
337   }
338 }
339 
ReportCardifiedSmoothness(bool is_entering_cardified,int smoothness)340 void ReportCardifiedSmoothness(bool is_entering_cardified, int smoothness) {
341   if (is_entering_cardified) {
342     UMA_HISTOGRAM_PERCENTAGE(kCardifiedStateAnimationSmoothnessEnter,
343                              smoothness);
344   } else {
345     UMA_HISTOGRAM_PERCENTAGE(kCardifiedStateAnimationSmoothnessExit,
346                              smoothness);
347   }
348 }
349 
350 }  // namespace ash
351