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