Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / app_list_service_impl.cc
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.
4
5 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
6
7 #include <string>
8
9 #include "base/bind.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"
25
26 namespace {
27
28 const int kDiscoverabilityTimeoutMinutes = 60;
29
30 void SendAppListAppLaunch(int count) {
31   UMA_HISTOGRAM_CUSTOM_COUNTS(
32       "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
33   if (count > 0)
34     UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
35 }
36
37 void SendAppListLaunch(int count) {
38   UMA_HISTOGRAM_CUSTOM_COUNTS(
39       "Apps.AppListDailyLaunches", count, 1, 1000, 50);
40   if (count > 0)
41     UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
42 }
43
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();
49
50   base::Time now = base::Time::Now();
51   base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
52       last_ping_pref));
53   int days = (now - last).InDays();
54   if (days > 0) {
55     send_callback(local_state->GetInteger(count_pref));
56     local_state->SetInt64(
57         last_ping_pref,
58         (last + base::TimeDelta::FromDays(days)).ToInternalValue());
59     local_state->SetInteger(count_pref, 0);
60     return true;
61   }
62   return false;
63 }
64
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.
71
72   PrefService* local_state = g_browser_process->local_state();
73   if (!local_state)
74     return;  // In a unit test.
75
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);
80   }
81 }
82
83 class ProfileStoreImpl : public ProfileStore {
84  public:
85   explicit ProfileStoreImpl(ProfileManager* profile_manager)
86       : profile_manager_(profile_manager),
87         weak_factory_(this) {
88   }
89
90   void AddProfileObserver(ProfileInfoCacheObserver* observer) override {
91     profile_manager_->GetProfileInfoCache().AddObserver(observer);
92   }
93
94   void LoadProfileAsync(const base::FilePath& path,
95                         base::Callback<void(Profile*)> callback) override {
96     profile_manager_->CreateProfileAsync(
97         path,
98         base::Bind(&ProfileStoreImpl::OnProfileCreated,
99                    weak_factory_.GetWeakPtr(),
100                    callback),
101         base::string16(),
102         base::string16(),
103         std::string());
104   }
105
106   void OnProfileCreated(base::Callback<void(Profile*)> callback,
107                         Profile* profile,
108                         Profile::CreateStatus status) {
109     switch (status) {
110       case Profile::CREATE_STATUS_CREATED:
111         break;
112       case Profile::CREATE_STATUS_INITIALIZED:
113         callback.Run(profile);
114         break;
115       case Profile::CREATE_STATUS_LOCAL_FAIL:
116       case Profile::CREATE_STATUS_REMOTE_FAIL:
117       case Profile::CREATE_STATUS_CANCELED:
118         break;
119       case Profile::MAX_CREATE_STATUS:
120         NOTREACHED();
121         break;
122     }
123   }
124
125   Profile* GetProfileByPath(const base::FilePath& path) override {
126     return profile_manager_->GetProfileByPath(path);
127   }
128
129   base::FilePath GetUserDataDir() override {
130     return profile_manager_->user_data_dir();
131   }
132
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);
139   }
140
141  private:
142   ProfileManager* profile_manager_;
143   base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
144 };
145
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())
151     return;
152
153   int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
154   if (enable_time_value == 0)
155     return;  // Already recorded or never enabled.
156
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) -
165         base::Time::Now();
166     if (time_remaining > base::TimeDelta()) {
167       base::MessageLoop::current()->PostDelayedTask(
168           FROM_HERE,
169           base::Bind(&RecordAppListDiscoverability,
170                      base::Unretained(local_state),
171                      false),
172           time_remaining);
173       return;
174     }
175   }
176
177   local_state->SetInt64(prefs::kAppListEnableTime, 0);
178
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",
190         time_taken,
191         base::TimeDelta::FromSeconds(1),
192         base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
193         10 /* bucket_count */);
194   }
195   UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
196                             enable_source,
197                             AppListService::ENABLE_NUM_ENABLE_SOURCES);
198 }
199
200 }  // namespace
201
202 void AppListServiceImpl::RecordAppListLaunch() {
203   RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
204                             prefs::kAppListLaunchCount,
205                             &SendAppListLaunch);
206   RecordAppListDiscoverability(local_state_, false);
207 }
208
209 // static
210 void AppListServiceImpl::RecordAppListAppLaunch() {
211   RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
212                             prefs::kAppListAppLaunchCount,
213                             &SendAppListAppLaunch);
214 }
215
216 // static
217 void AppListServiceImpl::SendAppListStats() {
218   if (!g_browser_process || g_browser_process->IsShuttingDown())
219     return;
220
221   SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
222                           prefs::kAppListLaunchCount,
223                           &SendAppListLaunch);
224   SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
225                           prefs::kAppListAppLaunchCount,
226                           &SendAppListAppLaunch);
227 }
228
229 AppListServiceImpl::AppListServiceImpl()
230     : profile_store_(
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);
237 }
238
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);
248 }
249
250 AppListServiceImpl::~AppListServiceImpl() {}
251
252 AppListViewDelegate* AppListServiceImpl::GetViewDelegate(Profile* profile) {
253   if (!view_delegate_)
254     view_delegate_.reset(new AppListViewDelegate(GetControllerDelegate()));
255   view_delegate_->SetProfile(profile);
256   return view_delegate_.get();
257 }
258
259 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
260
261 void AppListServiceImpl::HandleFirstRun() {}
262
263 void AppListServiceImpl::Init(Profile* initial_profile) {}
264
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);
270
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);
276   }
277
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;
281
282   return user_data_dir.AppendASCII(app_list_profile);
283 }
284
285 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
286   local_state_->SetString(
287       prefs::kAppListProfile,
288       profile_path.BaseName().MaybeAsASCII());
289 }
290
291 void AppListServiceImpl::CreateShortcut() {}
292
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)
300     return;
301
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
306   // something valid.
307   local_state_->SetString(prefs::kAppListProfile,
308                           local_state_->GetString(prefs::kProfileLastUsed));
309
310   // If the app list was never shown, there won't be a |view_delegate_| yet.
311   if (!view_delegate_)
312     return;
313
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
320   // the view.
321   DestroyAppList();
322   view_delegate_->SetProfile(NULL);
323 }
324
325 void AppListServiceImpl::Show() {
326   profile_loader_->LoadProfileInvalidatingOtherLoads(
327       GetProfilePath(profile_store_->GetUserDataDir()),
328       base::Bind(&AppListServiceImpl::ShowForProfile,
329                  weak_factory_.GetWeakPtr()));
330 }
331
332 void AppListServiceImpl::ShowForVoiceSearch(Profile* profile) {
333   ShowForProfile(profile);
334   view_delegate_->ToggleSpeechRecognition();
335 }
336
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);
342   } else {
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);
349     }
350     ShowForProfile(profile);
351   }
352   if (extension_id.empty())
353     return;  // Nothing to highlight. Only used in tests.
354
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);
362 }
363
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)) {
370     return;
371   }
372
373   local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
374   CreateShortcut();
375
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(
384         FROM_HERE,
385         base::Bind(&RecordAppListDiscoverability,
386                    base::Unretained(local_state_),
387                    false),
388         base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
389   }
390 }
391
392 void AppListServiceImpl::InvalidatePendingProfileLoads() {
393   profile_loader_->InvalidatePendingProfileLoads();
394 }
395
396 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
397   // Except in rare, once-off cases, this just checks that a pref is "0" and
398   // returns.
399   RecordAppListDiscoverability(local_state_, true);
400
401   if (command_line_.HasSwitch(switches::kResetAppListInstallState))
402     local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
403
404   if (command_line_.HasSwitch(switches::kEnableAppList))
405     EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
406
407   if (!base::MessageLoop::current())
408     return;  // In a unit test.
409
410   // Send app list usage stats after a delay.
411   const int kSendUsageStatsDelay = 5;
412   base::MessageLoop::current()->PostDelayedTask(
413       FROM_HERE,
414       base::Bind(&AppListServiceImpl::SendAppListStats),
415       base::TimeDelta::FromSeconds(kSendUsageStatsDelay));
416 }