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