app_list_service_impl.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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 "base/metrics/histogram.h"
8#include "base/prefs/pref_service.h"
9#include "base/time.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/lifetime/application_lifetime.h"
12#include "chrome/browser/profiles/profile_manager.h"
13#include "chrome/common/chrome_constants.h"
14#include "chrome/common/chrome_notification_types.h"
15#include "chrome/common/pref_names.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/public/browser/notification_details.h"
18#include "content/public/browser/notification_source.h"
19
20namespace {
21
22void SendAppListAppLaunch(int count) {
23  UMA_HISTOGRAM_CUSTOM_COUNTS(
24      "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
25  if (count > 0)
26    UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
27}
28
29void SendAppListLaunch(int count) {
30  UMA_HISTOGRAM_CUSTOM_COUNTS(
31      "Apps.AppListDailyLaunches", count, 1, 1000, 50);
32  if (count > 0)
33    UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
34}
35
36bool SendDailyEventFrequency(
37    const char* last_ping_pref,
38    const char* count_pref,
39    void (*send_callback)(int count)) {
40  PrefService* local_state = g_browser_process->local_state();
41
42  base::Time now = base::Time::Now();
43  base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
44      last_ping_pref));
45  int days = (now - last).InDays();
46  if (days > 0) {
47    send_callback(local_state->GetInteger(count_pref));
48    local_state->SetInt64(
49        last_ping_pref,
50        (last + base::TimeDelta::FromDays(days)).ToInternalValue());
51    local_state->SetInteger(count_pref, 0);
52    return true;
53  }
54  return false;
55}
56
57void RecordDailyEventFrequency(
58    const char* last_ping_pref,
59    const char* count_pref,
60    void (*send_callback)(int count)) {
61  PrefService* local_state = g_browser_process->local_state();
62
63  int count = local_state->GetInteger(count_pref);
64  local_state->SetInteger(count_pref, count + 1);
65  if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) {
66    local_state->SetInteger(count_pref, 1);
67  }
68}
69
70}  // namespace
71
72// static
73void AppListServiceImpl::RecordAppListLaunch() {
74  RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
75                            prefs::kAppListLaunchCount,
76                            &SendAppListLaunch);
77}
78
79// static
80void AppListServiceImpl::RecordAppListAppLaunch() {
81  RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
82                            prefs::kAppListAppLaunchCount,
83                            &SendAppListAppLaunch);
84}
85
86// static
87void AppListServiceImpl::SendAppListStats() {
88  if (!g_browser_process || g_browser_process->IsShuttingDown())
89    return;
90
91  SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
92                          prefs::kAppListLaunchCount,
93                          &SendAppListLaunch);
94  SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
95                          prefs::kAppListAppLaunchCount,
96                          &SendAppListAppLaunch);
97}
98
99AppListServiceImpl::AppListServiceImpl()
100    : profile_(NULL),
101      profile_load_sequence_id_(0),
102      pending_profile_loads_(0),
103      weak_factory_(this) {
104  ProfileManager* profile_manager = g_browser_process->profile_manager();
105  profile_manager->GetProfileInfoCache().AddObserver(this);
106}
107
108AppListServiceImpl::~AppListServiceImpl() {}
109
110void AppListServiceImpl::Init(Profile* initial_profile) {}
111
112base::FilePath AppListServiceImpl::GetAppListProfilePath(
113    const base::FilePath& user_data_dir) {
114  PrefService* local_state = g_browser_process->local_state();
115  DCHECK(local_state);
116
117  std::string app_list_profile;
118  if (local_state->HasPrefPath(prefs::kAppListProfile))
119    app_list_profile = local_state->GetString(prefs::kAppListProfile);
120
121  // If the user has no profile preference for the app launcher, default to the
122  // last browser profile used.
123  if (app_list_profile.empty() &&
124      local_state->HasPrefPath(prefs::kProfileLastUsed))
125    app_list_profile = local_state->GetString(prefs::kProfileLastUsed);
126
127  std::string profile_path = app_list_profile.empty() ?
128      chrome::kInitialProfile :
129      app_list_profile;
130
131  return user_data_dir.AppendASCII(profile_path);
132}
133
134AppListControllerDelegate* AppListServiceImpl::CreateControllerDelegate() {
135  return NULL;
136}
137
138bool AppListServiceImpl::HasCurrentView() const { return false; }
139
140void AppListServiceImpl::DoWarmupForProfile(Profile* initial_profile) {}
141
142void AppListServiceImpl::OnSigninStatusChanged() {}
143
144void AppListServiceImpl::OnProfileAdded(const base::FilePath& profilePath) {}
145
146void AppListServiceImpl::OnProfileWasRemoved(
147    const base::FilePath& profile_path, const string16& profile_name) {}
148
149void AppListServiceImpl::OnProfileNameChanged(
150    const base::FilePath& profile_path, const string16& profile_name) {}
151
152void AppListServiceImpl::OnProfileAvatarChanged(
153    const base::FilePath& profile_path) {}
154
155// We need to watch for profile removal to keep kAppListProfile updated.
156void AppListServiceImpl::OnProfileWillBeRemoved(
157    const base::FilePath& profile_path) {
158  // If the profile the app list uses just got deleted, reset it to the last
159  // used profile.
160  PrefService* local_state = g_browser_process->local_state();
161  std::string app_list_last_profile = local_state->GetString(
162      prefs::kAppListProfile);
163  if (profile_path.BaseName().MaybeAsASCII() == app_list_last_profile) {
164    local_state->SetString(prefs::kAppListProfile,
165        local_state->GetString(prefs::kProfileLastUsed));
166  }
167}
168
169void AppListServiceImpl::Observe(
170    int type,
171    const content::NotificationSource& source,
172    const content::NotificationDetails& details) {
173  OnSigninStatusChanged();
174}
175
176void AppListServiceImpl::SetAppListProfile(
177    const base::FilePath& profile_file_path) {
178  ProfileManager* profile_manager = g_browser_process->profile_manager();
179  Profile* profile = profile_manager->GetProfileByPath(profile_file_path);
180
181  if (!profile) {
182    LoadProfileAsync(profile_file_path);
183    return;
184  }
185
186  ShowAppList(profile);
187}
188
189void AppListServiceImpl::ShowForSavedProfile() {
190  SetAppListProfile(GetAppListProfilePath(
191      g_browser_process->profile_manager()->user_data_dir()));
192}
193
194Profile* AppListServiceImpl::GetCurrentAppListProfile() {
195  return profile();
196}
197
198void AppListServiceImpl::SetProfile(Profile* new_profile) {
199  registrar_.RemoveAll();
200  profile_ = new_profile;
201  if (!profile_)
202    return;
203
204  registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
205                 content::Source<Profile>(profile_));
206  registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
207                 content::Source<Profile>(profile_));
208}
209
210void AppListServiceImpl::InvalidatePendingProfileLoads() {
211  profile_load_sequence_id_++;
212}
213
214void AppListServiceImpl::LoadProfileAsync(
215    const base::FilePath& profile_file_path) {
216  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
217
218  InvalidatePendingProfileLoads();
219  IncrementPendingProfileLoads();
220
221  ProfileManager* profile_manager = g_browser_process->profile_manager();
222  profile_manager->CreateProfileAsync(
223      profile_file_path,
224      base::Bind(&AppListServiceImpl::OnProfileLoaded,
225                 weak_factory_.GetWeakPtr(), profile_load_sequence_id_),
226      string16(), string16(), false);
227}
228
229void AppListServiceImpl::OnProfileLoaded(int profile_load_sequence_id,
230                                         Profile* profile,
231                                         Profile::CreateStatus status) {
232  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
233  switch (status) {
234    case Profile::CREATE_STATUS_CREATED:
235      break;
236    case Profile::CREATE_STATUS_INITIALIZED:
237      // Only show if there has been no other profile shown since this load
238      // started.
239      if (profile_load_sequence_id == profile_load_sequence_id_)
240        ShowAppList(profile);
241      DecrementPendingProfileLoads();
242      break;
243    case Profile::CREATE_STATUS_FAIL:
244      DecrementPendingProfileLoads();
245      break;
246  }
247}
248
249void AppListServiceImpl::ScheduleWarmup() {
250  // Post a task to create the app list. This is posted to not impact startup
251  // time.
252  const int kInitWindowDelay = 5;
253  base::MessageLoop::current()->PostDelayedTask(
254      FROM_HERE,
255      base::Bind(&AppListServiceImpl::LoadProfileForInit,
256                 weak_factory_.GetWeakPtr()),
257      base::TimeDelta::FromSeconds(kInitWindowDelay));
258
259  // Send app list usage stats after a delay.
260  const int kSendUsageStatsDelay = 5;
261  MessageLoop::current()->PostDelayedTask(
262      FROM_HERE,
263      base::Bind(&AppListServiceImpl::SendAppListStats),
264      base::TimeDelta::FromSeconds(kSendUsageStatsDelay));
265}
266
267bool AppListServiceImpl::IsInitViewNeeded() const {
268  if (!g_browser_process || g_browser_process->IsShuttingDown())
269    return false;
270
271  // We only need to initialize the view if there's no view already created and
272  // there's no profile loading to be shown.
273  return !HasCurrentView() && profile_load_sequence_id_ == 0;
274}
275
276void AppListServiceImpl::LoadProfileForInit() {
277  if (!IsInitViewNeeded())
278    return;
279
280  ProfileManager* profile_manager = g_browser_process->profile_manager();
281  base::FilePath profile_file_path(
282      GetAppListProfilePath(profile_manager->user_data_dir()));
283  Profile* profile = profile_manager->GetProfileByPath(profile_file_path);
284
285  if (!profile) {
286    profile_manager->CreateProfileAsync(
287        profile_file_path,
288        base::Bind(&AppListServiceImpl::OnProfileLoadedForInit,
289                   weak_factory_.GetWeakPtr(), profile_load_sequence_id_),
290        string16(), string16(), false);
291    return;
292  }
293
294  DoWarmupForProfile(profile);
295}
296
297void AppListServiceImpl::OnProfileLoadedForInit(int profile_load_sequence_id,
298                                                Profile* profile,
299                                                Profile::CreateStatus status) {
300  if (!IsInitViewNeeded())
301    return;
302
303  if (status != Profile::CREATE_STATUS_INITIALIZED)
304    return;
305
306  DoWarmupForProfile(profile);
307}
308
309void AppListServiceImpl::IncrementPendingProfileLoads() {
310  pending_profile_loads_++;
311  if (pending_profile_loads_ == 1)
312    chrome::StartKeepAlive();
313}
314
315void AppListServiceImpl::DecrementPendingProfileLoads() {
316  pending_profile_loads_--;
317  if (pending_profile_loads_ == 0)
318    chrome::EndKeepAlive();
319}
320
321void AppListServiceImpl::SaveProfilePathToLocalState(
322    const base::FilePath& profile_file_path) {
323  g_browser_process->local_state()->SetString(
324      prefs::kAppListProfile,
325      profile_file_path.BaseName().MaybeAsASCII());
326}
327