1 // Copyright 2013 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.
5 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string16.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/browser_shutdown.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
19 #include "chrome/browser/ui/app_list/profile_loader.h"
20 #include "chrome/browser/ui/app_list/profile_store.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "ui/app_list/app_list_model.h"
28 const int kDiscoverabilityTimeoutMinutes = 60;
30 void SendAppListAppLaunch(int count) {
31 UMA_HISTOGRAM_CUSTOM_COUNTS(
32 "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
34 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
37 void SendAppListLaunch(int count) {
38 UMA_HISTOGRAM_CUSTOM_COUNTS(
39 "Apps.AppListDailyLaunches", count, 1, 1000, 50);
41 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
44 bool SendDailyEventFrequency(
45 const char* last_ping_pref,
46 const char* count_pref,
47 void (*send_callback)(int count)) {
48 PrefService* local_state = g_browser_process->local_state();
50 base::Time now = base::Time::Now();
51 base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
53 int days = (now - last).InDays();
55 send_callback(local_state->GetInteger(count_pref));
56 local_state->SetInt64(
58 (last + base::TimeDelta::FromDays(days)).ToInternalValue());
59 local_state->SetInteger(count_pref, 0);
65 void RecordDailyEventFrequency(
66 const char* last_ping_pref,
67 const char* count_pref,
68 void (*send_callback)(int count)) {
69 if (!g_browser_process)
70 return; // In a unit test.
72 PrefService* local_state = g_browser_process->local_state();
74 return; // In a unit test.
76 int count = local_state->GetInteger(count_pref);
77 local_state->SetInteger(count_pref, count + 1);
78 if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) {
79 local_state->SetInteger(count_pref, 1);
83 class ProfileStoreImpl : public ProfileStore {
85 explicit ProfileStoreImpl(ProfileManager* profile_manager)
86 : profile_manager_(profile_manager),
90 void AddProfileObserver(ProfileInfoCacheObserver* observer) override {
91 profile_manager_->GetProfileInfoCache().AddObserver(observer);
94 void LoadProfileAsync(const base::FilePath& path,
95 base::Callback<void(Profile*)> callback) override {
96 profile_manager_->CreateProfileAsync(
98 base::Bind(&ProfileStoreImpl::OnProfileCreated,
99 weak_factory_.GetWeakPtr(),
106 void OnProfileCreated(base::Callback<void(Profile*)> callback,
108 Profile::CreateStatus status) {
110 case Profile::CREATE_STATUS_CREATED:
112 case Profile::CREATE_STATUS_INITIALIZED:
113 callback.Run(profile);
115 case Profile::CREATE_STATUS_LOCAL_FAIL:
116 case Profile::CREATE_STATUS_REMOTE_FAIL:
117 case Profile::CREATE_STATUS_CANCELED:
119 case Profile::MAX_CREATE_STATUS:
125 Profile* GetProfileByPath(const base::FilePath& path) override {
126 return profile_manager_->GetProfileByPath(path);
129 base::FilePath GetUserDataDir() override {
130 return profile_manager_->user_data_dir();
133 bool IsProfileSupervised(const base::FilePath& profile_path) override {
134 ProfileInfoCache& profile_info =
135 g_browser_process->profile_manager()->GetProfileInfoCache();
136 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path);
137 return profile_index != std::string::npos &&
138 profile_info.ProfileIsSupervisedAtIndex(profile_index);
142 ProfileManager* profile_manager_;
143 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
146 void RecordAppListDiscoverability(PrefService* local_state,
147 bool is_startup_check) {
148 // Since this task may be delayed, ensure it does not interfere with shutdown
149 // when they unluckily coincide.
150 if (browser_shutdown::IsTryingToQuit())
153 int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
154 if (enable_time_value == 0)
155 return; // Already recorded or never enabled.
157 base::Time app_list_enable_time =
158 base::Time::FromInternalValue(enable_time_value);
159 if (is_startup_check) {
160 // When checking at startup, only clear and record the "timeout" case,
161 // otherwise wait for a timeout.
162 base::TimeDelta time_remaining =
163 app_list_enable_time +
164 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes) -
166 if (time_remaining > base::TimeDelta()) {
167 base::MessageLoop::current()->PostDelayedTask(
169 base::Bind(&RecordAppListDiscoverability,
170 base::Unretained(local_state),
177 local_state->SetInt64(prefs::kAppListEnableTime, 0);
179 AppListService::AppListEnableSource enable_source =
180 static_cast<AppListService::AppListEnableSource>(
181 local_state->GetInteger(prefs::kAppListEnableMethod));
182 if (enable_source == AppListService::ENABLE_FOR_APP_INSTALL) {
183 base::TimeDelta time_taken = base::Time::Now() - app_list_enable_time;
184 // This means the user "discovered" the app launcher naturally, after it was
185 // enabled on the first app install. Record how long it took to discover.
186 // Note that the last bucket is essentially "not discovered": subtract 1
187 // minute to account for clock inaccuracy.
188 UMA_HISTOGRAM_CUSTOM_TIMES(
189 "Apps.AppListTimeToDiscover",
191 base::TimeDelta::FromSeconds(1),
192 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
193 10 /* bucket_count */);
195 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
197 AppListService::ENABLE_NUM_ENABLE_SOURCES);
202 void AppListServiceImpl::RecordAppListLaunch() {
203 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
204 prefs::kAppListLaunchCount,
206 RecordAppListDiscoverability(local_state_, false);
210 void AppListServiceImpl::RecordAppListAppLaunch() {
211 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
212 prefs::kAppListAppLaunchCount,
213 &SendAppListAppLaunch);
217 void AppListServiceImpl::SendAppListStats() {
218 if (!g_browser_process || g_browser_process->IsShuttingDown())
221 SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
222 prefs::kAppListLaunchCount,
224 SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
225 prefs::kAppListAppLaunchCount,
226 &SendAppListAppLaunch);
229 AppListServiceImpl::AppListServiceImpl()
231 new ProfileStoreImpl(g_browser_process->profile_manager())),
232 command_line_(*CommandLine::ForCurrentProcess()),
233 local_state_(g_browser_process->local_state()),
234 profile_loader_(new ProfileLoader(profile_store_.get())),
235 weak_factory_(this) {
236 profile_store_->AddProfileObserver(this);
239 AppListServiceImpl::AppListServiceImpl(const CommandLine& command_line,
240 PrefService* local_state,
241 scoped_ptr<ProfileStore> profile_store)
242 : profile_store_(profile_store.Pass()),
243 command_line_(command_line),
244 local_state_(local_state),
245 profile_loader_(new ProfileLoader(profile_store_.get())),
246 weak_factory_(this) {
247 profile_store_->AddProfileObserver(this);
250 AppListServiceImpl::~AppListServiceImpl() {}
252 AppListViewDelegate* AppListServiceImpl::GetViewDelegate(Profile* profile) {
254 view_delegate_.reset(new AppListViewDelegate(GetControllerDelegate()));
255 view_delegate_->SetProfile(profile);
256 return view_delegate_.get();
259 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
261 void AppListServiceImpl::HandleFirstRun() {}
263 void AppListServiceImpl::Init(Profile* initial_profile) {}
265 base::FilePath AppListServiceImpl::GetProfilePath(
266 const base::FilePath& user_data_dir) {
267 std::string app_list_profile;
268 if (local_state_->HasPrefPath(prefs::kAppListProfile))
269 app_list_profile = local_state_->GetString(prefs::kAppListProfile);
271 // If the user has no profile preference for the app launcher, default to the
272 // last browser profile used.
273 if (app_list_profile.empty() &&
274 local_state_->HasPrefPath(prefs::kProfileLastUsed)) {
275 app_list_profile = local_state_->GetString(prefs::kProfileLastUsed);
278 // If there is no last used profile recorded, use the initial profile.
279 if (app_list_profile.empty())
280 app_list_profile = chrome::kInitialProfile;
282 return user_data_dir.AppendASCII(app_list_profile);
285 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
286 local_state_->SetString(
287 prefs::kAppListProfile,
288 profile_path.BaseName().MaybeAsASCII());
291 void AppListServiceImpl::CreateShortcut() {}
293 void AppListServiceImpl::OnProfileWillBeRemoved(
294 const base::FilePath& profile_path) {
295 // We need to watch for profile removal to keep kAppListProfile updated, for
296 // the case that the deleted profile is being used by the app list.
297 std::string app_list_last_profile = local_state_->GetString(
298 prefs::kAppListProfile);
299 if (profile_path.BaseName().MaybeAsASCII() != app_list_last_profile)
302 // Switch the app list over to a valid profile.
303 // Before ProfileInfoCache::DeleteProfileFromCache() calls this function,
304 // ProfileManager::ScheduleProfileForDeletion() will have checked to see if
305 // the deleted profile was also "last used", and updated that setting with
307 local_state_->SetString(prefs::kAppListProfile,
308 local_state_->GetString(prefs::kProfileLastUsed));
310 // If the app list was never shown, there won't be a |view_delegate_| yet.
314 // The Chrome AppListViewDelegate now needs its profile cleared, because:
315 // 1. it has many references to the profile and can't be profile-keyed, and
316 // 2. the last used profile might not be loaded yet.
317 // - this loading is sometimes done by the ProfileManager asynchronously,
318 // so the app list can't just switch to that.
319 // Only Mac supports showing the app list with a NULL profile, so tear down
322 view_delegate_->SetProfile(NULL);
325 void AppListServiceImpl::Show() {
326 profile_loader_->LoadProfileInvalidatingOtherLoads(
327 GetProfilePath(profile_store_->GetUserDataDir()),
328 base::Bind(&AppListServiceImpl::ShowForProfile,
329 weak_factory_.GetWeakPtr()));
332 void AppListServiceImpl::ShowForVoiceSearch(Profile* profile) {
333 ShowForProfile(profile);
334 view_delegate_->ToggleSpeechRecognition();
337 void AppListServiceImpl::ShowForAppInstall(Profile* profile,
338 const std::string& extension_id,
339 bool start_discovery_tracking) {
340 if (start_discovery_tracking) {
341 CreateForProfile(profile);
343 // Check if the app launcher has not yet been shown ever. Since this will
344 // show it, if discoverability UMA hasn't yet been recorded, it needs to be
345 // counted as undiscovered.
346 if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) {
347 local_state_->SetInteger(prefs::kAppListEnableMethod,
348 ENABLE_SHOWN_UNDISCOVERED);
350 ShowForProfile(profile);
352 if (extension_id.empty())
353 return; // Nothing to highlight. Only used in tests.
355 // The only way an install can happen is with the profile already loaded. So,
356 // ShowForProfile() can never be asynchronous, and the model is guaranteed to
357 // exist after a show.
358 DCHECK(view_delegate_->GetModel());
359 view_delegate_->GetModel()
360 ->top_level_item_list()
361 ->HighlightItemInstalledFromUI(extension_id);
364 void AppListServiceImpl::EnableAppList(Profile* initial_profile,
365 AppListEnableSource enable_source) {
366 SetProfilePath(initial_profile->GetPath());
367 // Always allow the webstore "enable" button to re-run the install flow.
368 if (enable_source != AppListService::ENABLE_VIA_WEBSTORE_LINK &&
369 local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)) {
373 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
376 // UMA for launcher discoverability.
377 local_state_->SetInt64(prefs::kAppListEnableTime,
378 base::Time::Now().ToInternalValue());
379 local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source);
380 if (base::MessageLoop::current()) {
381 // Ensure a value is recorded if the user "never" shows the app list. Note
382 // there is no message loop in unit tests.
383 base::MessageLoop::current()->PostDelayedTask(
385 base::Bind(&RecordAppListDiscoverability,
386 base::Unretained(local_state_),
388 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
392 void AppListServiceImpl::InvalidatePendingProfileLoads() {
393 profile_loader_->InvalidatePendingProfileLoads();
396 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
397 // Except in rare, once-off cases, this just checks that a pref is "0" and
399 RecordAppListDiscoverability(local_state_, true);
401 if (command_line_.HasSwitch(switches::kResetAppListInstallState))
402 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
404 if (command_line_.HasSwitch(switches::kEnableAppList))
405 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
407 if (!base::MessageLoop::current())
408 return; // In a unit test.
410 // Send app list usage stats after a delay.
411 const int kSendUsageStatsDelay = 5;
412 base::MessageLoop::current()->PostDelayedTask(
414 base::Bind(&AppListServiceImpl::SendAppListStats),
415 base::TimeDelta::FromSeconds(kSendUsageStatsDelay));