Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / ui / app_list / app_list_service_impl.cc
blob885decb69b7ecc40f57e662b4b992d12271bf6df
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"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string16.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/browser_shutdown.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
22 #include "chrome/browser/ui/app_list/profile_loader.h"
23 #include "chrome/browser/ui/app_list/profile_store.h"
24 #include "chrome/common/chrome_constants.h"
25 #include "chrome/common/pref_names.h"
26 #include "ui/app_list/app_list_model.h"
27 #include "ui/app_list/app_list_switches.h"
29 namespace {
31 const int kDiscoverabilityTimeoutMinutes = 60;
33 void SendAppListAppLaunch(int count) {
34 UMA_HISTOGRAM_CUSTOM_COUNTS(
35 "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
36 if (count > 0)
37 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
40 void SendAppListLaunch(int count) {
41 UMA_HISTOGRAM_CUSTOM_COUNTS(
42 "Apps.AppListDailyLaunches", count, 1, 1000, 50);
43 if (count > 0)
44 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
47 bool SendDailyEventFrequency(
48 const char* last_ping_pref,
49 const char* count_pref,
50 void (*send_callback)(int count)) {
51 PrefService* local_state = g_browser_process->local_state();
53 base::Time now = base::Time::Now();
54 base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
55 last_ping_pref));
56 int days = (now - last).InDays();
57 if (days > 0) {
58 send_callback(local_state->GetInteger(count_pref));
59 local_state->SetInt64(
60 last_ping_pref,
61 (last + base::TimeDelta::FromDays(days)).ToInternalValue());
62 local_state->SetInteger(count_pref, 0);
63 return true;
65 return false;
68 void RecordDailyEventFrequency(
69 const char* last_ping_pref,
70 const char* count_pref,
71 void (*send_callback)(int count)) {
72 if (!g_browser_process)
73 return; // In a unit test.
75 PrefService* local_state = g_browser_process->local_state();
76 if (!local_state)
77 return; // In a unit test.
79 int count = local_state->GetInteger(count_pref);
80 local_state->SetInteger(count_pref, count + 1);
81 if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) {
82 local_state->SetInteger(count_pref, 1);
86 class ProfileStoreImpl : public ProfileStore {
87 public:
88 explicit ProfileStoreImpl(ProfileManager* profile_manager)
89 : profile_manager_(profile_manager),
90 weak_factory_(this) {
93 void AddProfileObserver(ProfileInfoCacheObserver* observer) override {
94 profile_manager_->GetProfileInfoCache().AddObserver(observer);
97 void LoadProfileAsync(const base::FilePath& path,
98 base::Callback<void(Profile*)> callback) override {
99 profile_manager_->CreateProfileAsync(
100 path,
101 base::Bind(&ProfileStoreImpl::OnProfileCreated,
102 weak_factory_.GetWeakPtr(),
103 callback),
104 base::string16(),
105 base::string16(),
106 std::string());
109 void OnProfileCreated(base::Callback<void(Profile*)> callback,
110 Profile* profile,
111 Profile::CreateStatus status) {
112 switch (status) {
113 case Profile::CREATE_STATUS_CREATED:
114 break;
115 case Profile::CREATE_STATUS_INITIALIZED:
116 callback.Run(profile);
117 break;
118 case Profile::CREATE_STATUS_LOCAL_FAIL:
119 case Profile::CREATE_STATUS_REMOTE_FAIL:
120 case Profile::CREATE_STATUS_CANCELED:
121 break;
122 case Profile::MAX_CREATE_STATUS:
123 NOTREACHED();
124 break;
128 Profile* GetProfileByPath(const base::FilePath& path) override {
129 DCHECK(!IsProfileLocked(path));
130 return profile_manager_->GetProfileByPath(path);
133 base::FilePath GetUserDataDir() override {
134 return profile_manager_->user_data_dir();
137 std::string GetLastUsedProfileName() override {
138 return profile_manager_->GetLastUsedProfileName();
141 bool IsProfileSupervised(const base::FilePath& profile_path) override {
142 ProfileInfoCache& profile_info =
143 g_browser_process->profile_manager()->GetProfileInfoCache();
144 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path);
145 return profile_index != std::string::npos &&
146 profile_info.ProfileIsSupervisedAtIndex(profile_index);
149 bool IsProfileLocked(const base::FilePath& profile_path) override {
150 ProfileInfoCache& profile_info =
151 g_browser_process->profile_manager()->GetProfileInfoCache();
152 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path);
153 return profile_index != std::string::npos &&
154 profile_info.ProfileIsSigninRequiredAtIndex(profile_index);
157 private:
158 ProfileManager* profile_manager_;
159 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
162 void RecordAppListDiscoverability(PrefService* local_state,
163 bool is_startup_check) {
164 // Since this task may be delayed, ensure it does not interfere with shutdown
165 // when they unluckily coincide.
166 if (browser_shutdown::IsTryingToQuit())
167 return;
169 int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
170 if (enable_time_value == 0)
171 return; // Already recorded or never enabled.
173 base::Time app_list_enable_time =
174 base::Time::FromInternalValue(enable_time_value);
175 if (is_startup_check) {
176 // When checking at startup, only clear and record the "timeout" case,
177 // otherwise wait for a timeout.
178 base::TimeDelta time_remaining =
179 app_list_enable_time +
180 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes) -
181 base::Time::Now();
182 if (time_remaining > base::TimeDelta()) {
183 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
184 FROM_HERE, base::Bind(&RecordAppListDiscoverability,
185 base::Unretained(local_state), false),
186 time_remaining);
187 return;
191 local_state->SetInt64(prefs::kAppListEnableTime, 0);
193 AppListService::AppListEnableSource enable_source =
194 static_cast<AppListService::AppListEnableSource>(
195 local_state->GetInteger(prefs::kAppListEnableMethod));
196 if (enable_source == AppListService::ENABLE_FOR_APP_INSTALL) {
197 base::TimeDelta time_taken = base::Time::Now() - app_list_enable_time;
198 // This means the user "discovered" the app launcher naturally, after it was
199 // enabled on the first app install. Record how long it took to discover.
200 // Note that the last bucket is essentially "not discovered": subtract 1
201 // minute to account for clock inaccuracy.
202 UMA_HISTOGRAM_CUSTOM_TIMES(
203 "Apps.AppListTimeToDiscover",
204 time_taken,
205 base::TimeDelta::FromSeconds(1),
206 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
207 10 /* bucket_count */);
209 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
210 enable_source,
211 AppListService::ENABLE_NUM_ENABLE_SOURCES);
214 // Checks whether a profile name is valid for the app list. Returns false if the
215 // name is empty, or represents a guest profile.
216 bool IsValidProfileName(const std::string& profile_name) {
217 if (profile_name.empty())
218 return false;
220 return
221 profile_name != base::FilePath(chrome::kGuestProfileDir).AsUTF8Unsafe() &&
222 profile_name != base::FilePath(chrome::kSystemProfileDir).AsUTF8Unsafe();
225 } // namespace
227 void AppListServiceImpl::RecordAppListLaunch() {
228 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
229 prefs::kAppListLaunchCount,
230 &SendAppListLaunch);
231 RecordAppListDiscoverability(local_state_, false);
232 RecordAppListLastLaunch();
235 // static
236 void AppListServiceImpl::RecordAppListAppLaunch() {
237 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
238 prefs::kAppListAppLaunchCount,
239 &SendAppListAppLaunch);
242 // static
243 void AppListServiceImpl::RecordAppListLastLaunch() {
244 if (!g_browser_process)
245 return; // In a unit test.
247 PrefService* local_state = g_browser_process->local_state();
248 if (!local_state)
249 return; // In a unit test.
251 local_state->SetInt64(prefs::kAppListLastLaunchTime,
252 base::Time::Now().ToInternalValue());
255 // static
256 void AppListServiceImpl::SendAppListStats() {
257 if (!g_browser_process || g_browser_process->IsShuttingDown())
258 return;
260 SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
261 prefs::kAppListLaunchCount,
262 &SendAppListLaunch);
263 SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
264 prefs::kAppListAppLaunchCount,
265 &SendAppListAppLaunch);
268 AppListServiceImpl::AppListServiceImpl()
269 : profile_store_(
270 new ProfileStoreImpl(g_browser_process->profile_manager())),
271 command_line_(*base::CommandLine::ForCurrentProcess()),
272 local_state_(g_browser_process->local_state()),
273 profile_loader_(new ProfileLoader(profile_store_.get())),
274 weak_factory_(this) {
275 profile_store_->AddProfileObserver(this);
278 AppListServiceImpl::AppListServiceImpl(const base::CommandLine& command_line,
279 PrefService* local_state,
280 scoped_ptr<ProfileStore> profile_store)
281 : profile_store_(profile_store.Pass()),
282 command_line_(command_line),
283 local_state_(local_state),
284 profile_loader_(new ProfileLoader(profile_store_.get())),
285 weak_factory_(this) {
286 profile_store_->AddProfileObserver(this);
289 AppListServiceImpl::~AppListServiceImpl() {}
291 AppListViewDelegate* AppListServiceImpl::GetViewDelegate(Profile* profile) {
292 if (!view_delegate_)
293 view_delegate_.reset(new AppListViewDelegate(GetControllerDelegate()));
294 view_delegate_->SetProfile(profile);
295 return view_delegate_.get();
298 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
300 void AppListServiceImpl::Init(Profile* initial_profile) {}
302 base::FilePath AppListServiceImpl::GetProfilePath(
303 const base::FilePath& user_data_dir) {
304 return user_data_dir.AppendASCII(GetProfileName());
307 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
308 local_state_->SetString(
309 prefs::kAppListProfile,
310 profile_path.BaseName().MaybeAsASCII());
313 void AppListServiceImpl::CreateShortcut() {}
315 std::string AppListServiceImpl::GetProfileName() {
316 std::string app_list_profile =
317 local_state_->GetString(prefs::kAppListProfile);
318 if (IsValidProfileName(app_list_profile))
319 return app_list_profile;
321 // If the user has no profile preference for the app launcher, default to the
322 // last browser profile used.
323 app_list_profile = profile_store_->GetLastUsedProfileName();
324 if (IsValidProfileName(app_list_profile))
325 return app_list_profile;
327 // If the last profile used was invalid (ie, guest profile), use the initial
328 // profile.
329 return chrome::kInitialProfile;
332 void AppListServiceImpl::OnProfileWillBeRemoved(
333 const base::FilePath& profile_path) {
334 // We need to watch for profile removal to keep kAppListProfile updated, for
335 // the case that the deleted profile is being used by the app list.
336 std::string app_list_last_profile = local_state_->GetString(
337 prefs::kAppListProfile);
338 if (profile_path.BaseName().MaybeAsASCII() != app_list_last_profile)
339 return;
341 // Switch the app list over to a valid profile.
342 // Before ProfileInfoCache::DeleteProfileFromCache() calls this function,
343 // ProfileManager::ScheduleProfileForDeletion() will have checked to see if
344 // the deleted profile was also "last used", and updated that setting with
345 // something valid.
346 local_state_->SetString(prefs::kAppListProfile,
347 local_state_->GetString(prefs::kProfileLastUsed));
349 // If the app list was never shown, there won't be a |view_delegate_| yet.
350 if (!view_delegate_)
351 return;
353 // The Chrome AppListViewDelegate now needs its profile cleared, because:
354 // 1. it has many references to the profile and can't be profile-keyed, and
355 // 2. the last used profile might not be loaded yet.
356 // - this loading is sometimes done by the ProfileManager asynchronously,
357 // so the app list can't just switch to that.
358 // Only Mac supports showing the app list with a NULL profile, so tear down
359 // the view.
360 DestroyAppList();
361 view_delegate_->SetProfile(NULL);
364 void AppListServiceImpl::Show() {
365 profile_loader_->LoadProfileInvalidatingOtherLoads(
366 GetProfilePath(profile_store_->GetUserDataDir()),
367 base::Bind(&AppListServiceImpl::ShowForProfile,
368 weak_factory_.GetWeakPtr()));
371 void AppListServiceImpl::ShowForVoiceSearch(
372 Profile* profile,
373 const scoped_refptr<content::SpeechRecognitionSessionPreamble>& preamble) {
374 ShowForProfile(profile);
375 view_delegate_->ToggleSpeechRecognitionForHotword(preamble);
378 void AppListServiceImpl::ShowForAppInstall(Profile* profile,
379 const std::string& extension_id,
380 bool start_discovery_tracking) {
381 if (start_discovery_tracking) {
382 CreateForProfile(profile);
383 } else {
384 // Check if the app launcher has not yet been shown ever. Since this will
385 // show it, if discoverability UMA hasn't yet been recorded, it needs to be
386 // counted as undiscovered.
387 if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) {
388 local_state_->SetInteger(prefs::kAppListEnableMethod,
389 ENABLE_SHOWN_UNDISCOVERED);
391 ShowForProfile(profile);
393 if (extension_id.empty())
394 return; // Nothing to highlight. Only used in tests.
396 // The only way an install can happen is with the profile already loaded. So,
397 // ShowForProfile() can never be asynchronous, and the model is guaranteed to
398 // exist after a show.
399 DCHECK(view_delegate_->GetModel());
400 view_delegate_->GetModel()
401 ->top_level_item_list()
402 ->HighlightItemInstalledFromUI(extension_id);
405 void AppListServiceImpl::EnableAppList(Profile* initial_profile,
406 AppListEnableSource enable_source) {
407 SetProfilePath(initial_profile->GetPath());
408 // Always allow the webstore "enable" button to re-run the install flow.
409 if (enable_source != AppListService::ENABLE_VIA_WEBSTORE_LINK &&
410 local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)) {
411 return;
414 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
415 CreateShortcut();
417 // UMA for launcher discoverability.
418 local_state_->SetInt64(prefs::kAppListEnableTime,
419 base::Time::Now().ToInternalValue());
420 local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source);
421 if (base::ThreadTaskRunnerHandle::IsSet()) {
422 // Ensure a value is recorded if the user "never" shows the app list. Note
423 // there is no message loop in unit tests.
424 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
425 FROM_HERE, base::Bind(&RecordAppListDiscoverability,
426 base::Unretained(local_state_), false),
427 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
431 void AppListServiceImpl::InvalidatePendingProfileLoads() {
432 profile_loader_->InvalidatePendingProfileLoads();
435 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
436 // Except in rare, once-off cases, this just checks that a pref is "0" and
437 // returns.
438 RecordAppListDiscoverability(local_state_, true);
440 if (command_line_.HasSwitch(app_list::switches::kResetAppListInstallState))
441 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
443 if (command_line_.HasSwitch(app_list::switches::kEnableAppList))
444 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
446 if (!base::ThreadTaskRunnerHandle::IsSet())
447 return; // In a unit test.
449 // Send app list usage stats after a delay.
450 const int kSendUsageStatsDelay = 5;
451 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
452 FROM_HERE, base::Bind(&AppListServiceImpl::SendAppListStats),
453 base::TimeDelta::FromSeconds(kSendUsageStatsDelay));