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