recommendation_restorer.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/chromeos/policy/recommendation_restorer.h"
6
7#include "ash/shell.h"
8#include "ash/wm/user_activity_detector.h"
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/location.h"
12#include "base/logging.h"
13#include "base/prefs/pref_service.h"
14#include "base/time/time.h"
15#include "base/values.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/chromeos/profiles/profile_helper.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/pref_names.h"
20#include "content/public/browser/notification_details.h"
21#include "content/public/browser/notification_service.h"
22#include "content/public/browser/notification_source.h"
23
24namespace policy {
25
26namespace {
27// The amount of idle time after which recommended values are restored.
28const int kRestoreDelayInMs = 60 * 1000;  // 1 minute.
29}  // namespace
30
31RecommendationRestorer::RecommendationRestorer(Profile* profile)
32    : logged_in_(false) {
33  if (!chromeos::ProfileHelper::IsSigninProfile(profile))
34    return;
35
36  pref_change_registrar_.Init(profile->GetPrefs());
37  pref_change_registrar_.Add(prefs::kLargeCursorEnabled,
38                             base::Bind(&RecommendationRestorer::Restore,
39                                        base::Unretained(this), true));
40  pref_change_registrar_.Add(prefs::kSpokenFeedbackEnabled,
41                             base::Bind(&RecommendationRestorer::Restore,
42                                        base::Unretained(this), true));
43  pref_change_registrar_.Add(prefs::kHighContrastEnabled,
44                             base::Bind(&RecommendationRestorer::Restore,
45                                        base::Unretained(this), true));
46  pref_change_registrar_.Add(prefs::kScreenMagnifierEnabled,
47                             base::Bind(&RecommendationRestorer::Restore,
48                                        base::Unretained(this), true));
49  pref_change_registrar_.Add(prefs::kScreenMagnifierType,
50                             base::Bind(&RecommendationRestorer::Restore,
51                                        base::Unretained(this), true));
52
53  notification_registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
54                              content::NotificationService::AllSources());
55
56  RestoreAll();
57}
58
59RecommendationRestorer::~RecommendationRestorer() {
60}
61
62void RecommendationRestorer::Shutdown() {
63  StopTimer();
64  pref_change_registrar_.RemoveAll();
65  notification_registrar_.RemoveAll();
66}
67
68void RecommendationRestorer::Observe(
69    int type,
70    const content::NotificationSource& source,
71    const content::NotificationDetails& details) {
72  if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) {
73    logged_in_ = true;
74    notification_registrar_.RemoveAll();
75    StopTimer();
76    RestoreAll();
77  } else {
78    NOTREACHED();
79  }
80}
81
82void RecommendationRestorer::OnUserActivity(const ui::Event* event) {
83  if (restore_timer_.IsRunning())
84    restore_timer_.Reset();
85}
86
87void RecommendationRestorer::Restore(bool allow_delay,
88                                     const std::string& pref_name) {
89  const PrefService::Preference* pref =
90      pref_change_registrar_.prefs()->FindPreference(pref_name.c_str());
91  if (!pref) {
92    NOTREACHED();
93    return;
94  }
95
96  if (!pref->GetRecommendedValue() || !pref->HasUserSetting())
97    return;
98
99  if (logged_in_) {
100    allow_delay = false;
101  } else if (allow_delay && ash::Shell::HasInstance()) {
102    // Skip the delay if there has been no user input since the browser started.
103    const ash::UserActivityDetector* user_activity_detector =
104        ash::Shell::GetInstance()->user_activity_detector();
105    allow_delay = !user_activity_detector->last_activity_time().is_null();
106  }
107
108  if (allow_delay)
109    StartTimer();
110  else
111    pref_change_registrar_.prefs()->ClearPref(pref->name().c_str());
112}
113
114void RecommendationRestorer::RestoreAll() {
115  Restore(false, prefs::kLargeCursorEnabled);
116  Restore(false, prefs::kSpokenFeedbackEnabled);
117  Restore(false, prefs::kHighContrastEnabled);
118  Restore(false, prefs::kScreenMagnifierEnabled);
119  Restore(false, prefs::kScreenMagnifierType);
120}
121
122void RecommendationRestorer::StartTimer() {
123  // Listen for user activity so that the timer can be reset while the user is
124  // active, causing it to fire only when the user remains idle for
125  // |kRestoreDelayInMs|.
126  if (ash::Shell::HasInstance()) {
127    ash::UserActivityDetector* user_activity_detector =
128        ash::Shell::GetInstance()->user_activity_detector();
129    if (!user_activity_detector->HasObserver(this))
130      user_activity_detector->AddObserver(this);
131  }
132
133  // There should be a separate timer for each pref. However, in the common
134  // case of the user changing settings, a single timer is sufficient. This is
135  // because a change initiated by the user implies user activity, so that even
136  // if there was a separate timer per pref, they would all be reset at that
137  // point, causing them to fire at exactly the same time. In the much rarer
138  // case of a recommended value changing, a single timer is a close
139  // approximation of the behavior that would be obtained by resetting the timer
140  // for the affected pref only.
141  restore_timer_.Start(FROM_HERE,
142                       base::TimeDelta::FromMilliseconds(kRestoreDelayInMs),
143                       base::Bind(&RecommendationRestorer::RestoreAll,
144                                  base::Unretained(this)));
145}
146
147void RecommendationRestorer::StopTimer() {
148  restore_timer_.Stop();
149  if (ash::Shell::HasInstance())
150    ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
151}
152
153}  // namespace policy
154