resolution_notification_controller.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "ash/display/resolution_notification_controller.h" 6 7#include "ash/display/display_controller.h" 8#include "ash/display/display_manager.h" 9#include "ash/shell.h" 10#include "ash/system/system_notifier.h" 11#include "base/strings/utf_string_conversions.h" 12#include "grit/ash_resources.h" 13#include "grit/ash_strings.h" 14#include "ui/base/l10n/l10n_util.h" 15#include "ui/base/l10n/time_format.h" 16#include "ui/base/resource/resource_bundle.h" 17#include "ui/gfx/display.h" 18#include "ui/gfx/screen.h" 19#include "ui/message_center/message_center.h" 20#include "ui/message_center/notification.h" 21#include "ui/message_center/notification_delegate.h" 22 23using message_center::Notification; 24 25namespace ash { 26namespace internal { 27namespace { 28 29bool g_use_timer = true; 30 31class ResolutionChangeNotificationDelegate 32 : public message_center::NotificationDelegate { 33 public: 34 ResolutionChangeNotificationDelegate( 35 ResolutionNotificationController* controller, 36 bool has_timeout); 37 38 protected: 39 virtual ~ResolutionChangeNotificationDelegate(); 40 41 private: 42 // message_center::NotificationDelegate overrides: 43 virtual void Display() OVERRIDE; 44 virtual void Error() OVERRIDE; 45 virtual void Close(bool by_user) OVERRIDE; 46 virtual void Click() OVERRIDE; 47 virtual bool HasClickedListener() OVERRIDE; 48 virtual void ButtonClick(int button_index) OVERRIDE; 49 50 ResolutionNotificationController* controller_; 51 bool has_timeout_; 52 53 DISALLOW_COPY_AND_ASSIGN(ResolutionChangeNotificationDelegate); 54}; 55 56ResolutionChangeNotificationDelegate::ResolutionChangeNotificationDelegate( 57 ResolutionNotificationController* controller, 58 bool has_timeout) 59 : controller_(controller), 60 has_timeout_(has_timeout) { 61 DCHECK(controller_); 62} 63 64ResolutionChangeNotificationDelegate::~ResolutionChangeNotificationDelegate() { 65} 66 67void ResolutionChangeNotificationDelegate::Display() { 68} 69 70void ResolutionChangeNotificationDelegate::Error() { 71} 72 73void ResolutionChangeNotificationDelegate::Close(bool by_user) { 74 if (by_user) 75 controller_->AcceptResolutionChange(false); 76} 77 78void ResolutionChangeNotificationDelegate::Click() { 79 controller_->AcceptResolutionChange(true); 80} 81 82bool ResolutionChangeNotificationDelegate::HasClickedListener() { 83 return true; 84} 85 86void ResolutionChangeNotificationDelegate::ButtonClick(int button_index) { 87 // If there's the timeout, the first button is "Accept". Otherwise the 88 // button click should be "Revert". 89 if (has_timeout_ && button_index == 0) 90 controller_->AcceptResolutionChange(true); 91 else 92 controller_->RevertResolutionChange(); 93} 94 95} // namespace 96 97// static 98const int ResolutionNotificationController::kTimeoutInSec = 15; 99 100// static 101const char ResolutionNotificationController::kNotificationId[] = 102 "chrome://settings/display/resolution"; 103 104struct ResolutionNotificationController::ResolutionChangeInfo { 105 ResolutionChangeInfo(int64 display_id, 106 const gfx::Size& old_resolution, 107 const gfx::Size& new_resolution, 108 const base::Closure& accept_callback); 109 ~ResolutionChangeInfo(); 110 111 // The id of the display where the resolution change happens. 112 int64 display_id; 113 114 // The resolution before the change. 115 gfx::Size old_resolution; 116 117 // The new resolution after the change. 118 gfx::Size new_resolution; 119 120 // The callback when accept is chosen. 121 base::Closure accept_callback; 122 123 // The remaining timeout in seconds. 0 if the change does not time out. 124 uint8 timeout_count; 125 126 // The timer to invoke OnTimerTick() every second. This cannot be 127 // OneShotTimer since the message contains text "automatically closed in xx 128 // seconds..." which has to be updated every second. 129 base::RepeatingTimer<ResolutionNotificationController> timer; 130 131 private: 132 DISALLOW_COPY_AND_ASSIGN(ResolutionChangeInfo); 133}; 134 135ResolutionNotificationController::ResolutionChangeInfo::ResolutionChangeInfo( 136 int64 display_id, 137 const gfx::Size& old_resolution, 138 const gfx::Size& new_resolution, 139 const base::Closure& accept_callback) 140 : display_id(display_id), 141 old_resolution(old_resolution), 142 new_resolution(new_resolution), 143 accept_callback(accept_callback), 144 timeout_count(0) { 145 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); 146 if (!display_manager->HasInternalDisplay() && 147 display_manager->num_connected_displays() == 1u) { 148 timeout_count = kTimeoutInSec; 149 } 150} 151 152ResolutionNotificationController::ResolutionChangeInfo:: 153 ~ResolutionChangeInfo() { 154} 155 156ResolutionNotificationController::ResolutionNotificationController() { 157 Shell::GetInstance()->display_controller()->AddObserver(this); 158 Shell::GetScreen()->AddObserver(this); 159} 160 161ResolutionNotificationController::~ResolutionNotificationController() { 162 Shell::GetInstance()->display_controller()->RemoveObserver(this); 163 Shell::GetScreen()->RemoveObserver(this); 164} 165 166void ResolutionNotificationController::SetDisplayResolutionAndNotify( 167 int64 display_id, 168 const gfx::Size& old_resolution, 169 const gfx::Size& new_resolution, 170 const base::Closure& accept_callback) { 171 // If multiple resolution changes are invoked for the same display, 172 // the original resolution for the first resolution change has to be used 173 // instead of the specified |old_resolution|. 174 gfx::Size original_resolution; 175 if (change_info_ && change_info_->display_id == display_id) { 176 DCHECK(change_info_->new_resolution == old_resolution); 177 original_resolution = change_info_->old_resolution; 178 } 179 180 change_info_.reset(new ResolutionChangeInfo( 181 display_id, old_resolution, new_resolution, accept_callback)); 182 if (!original_resolution.IsEmpty()) 183 change_info_->old_resolution = original_resolution; 184 185 // SetDisplayResolution() causes OnConfigurationChanged() and the notification 186 // will be shown at that point. 187 Shell::GetInstance()->display_manager()->SetDisplayResolution( 188 display_id, new_resolution); 189} 190 191bool ResolutionNotificationController::DoesNotificationTimeout() { 192 return change_info_ && change_info_->timeout_count > 0; 193} 194 195void ResolutionNotificationController::CreateOrUpdateNotification( 196 bool enable_spoken_feedback) { 197 message_center::MessageCenter* message_center = 198 message_center::MessageCenter::Get(); 199 if (!change_info_) { 200 message_center->RemoveNotification(kNotificationId, false /* by_user */); 201 return; 202 } 203 204 base::string16 timeout_message; 205 message_center::RichNotificationData data; 206 if (change_info_->timeout_count > 0) { 207 data.buttons.push_back(message_center::ButtonInfo( 208 l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_RESOLUTION_CHANGE_ACCEPT))); 209 timeout_message = l10n_util::GetStringFUTF16( 210 IDS_ASH_DISPLAY_RESOLUTION_TIMEOUT, 211 ui::TimeFormat::TimeDurationLong( 212 base::TimeDelta::FromSeconds(change_info_->timeout_count))); 213 } 214 data.buttons.push_back(message_center::ButtonInfo( 215 l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_RESOLUTION_CHANGE_REVERT))); 216 217 data.should_make_spoken_feedback_for_popup_updates = enable_spoken_feedback; 218 219 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 220 scoped_ptr<Notification> notification(new Notification( 221 message_center::NOTIFICATION_TYPE_SIMPLE, 222 kNotificationId, 223 l10n_util::GetStringFUTF16( 224 IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED, 225 UTF8ToUTF16(Shell::GetInstance()->display_manager()-> 226 GetDisplayNameForId(change_info_->display_id)), 227 UTF8ToUTF16(change_info_->new_resolution.ToString())), 228 timeout_message, 229 bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), 230 base::string16() /* display_source */, 231 message_center::NotifierId( 232 system_notifier::NOTIFIER_DISPLAY_RESOLUTION_CHANGE), 233 data, 234 new ResolutionChangeNotificationDelegate( 235 this, change_info_->timeout_count > 0))); 236 notification->SetSystemPriority(); 237 message_center->AddNotification(notification.Pass()); 238} 239 240void ResolutionNotificationController::OnTimerTick() { 241 if (!change_info_) 242 return; 243 244 --change_info_->timeout_count; 245 if (change_info_->timeout_count == 0) 246 RevertResolutionChange(); 247 else 248 CreateOrUpdateNotification(false); 249} 250 251void ResolutionNotificationController::AcceptResolutionChange( 252 bool close_notification) { 253 if (close_notification) { 254 message_center::MessageCenter::Get()->RemoveNotification( 255 kNotificationId, false /* by_user */); 256 } 257 base::Closure callback = change_info_->accept_callback; 258 change_info_.reset(); 259 callback.Run(); 260} 261 262void ResolutionNotificationController::RevertResolutionChange() { 263 message_center::MessageCenter::Get()->RemoveNotification( 264 kNotificationId, false /* by_user */); 265 int64 display_id = change_info_->display_id; 266 gfx::Size old_resolution = change_info_->old_resolution; 267 change_info_.reset(); 268 Shell::GetInstance()->display_manager()->SetDisplayResolution( 269 display_id, old_resolution); 270} 271 272void ResolutionNotificationController::OnDisplayBoundsChanged( 273 const gfx::Display& display) { 274} 275 276void ResolutionNotificationController::OnDisplayAdded( 277 const gfx::Display& new_display) { 278} 279 280void ResolutionNotificationController::OnDisplayRemoved( 281 const gfx::Display& old_display) { 282 if (change_info_ && change_info_->display_id == old_display.id()) 283 RevertResolutionChange(); 284} 285 286void ResolutionNotificationController::OnDisplayConfigurationChanged() { 287 if (!change_info_) 288 return; 289 290 CreateOrUpdateNotification(true); 291 if (g_use_timer && change_info_->timeout_count > 0) { 292 change_info_->timer.Start(FROM_HERE, 293 base::TimeDelta::FromSeconds(1), 294 this, 295 &ResolutionNotificationController::OnTimerTick); 296 } 297} 298 299void ResolutionNotificationController::SuppressTimerForTest() { 300 g_use_timer = false; 301} 302 303} // namespace internal 304} // namespace ash 305