• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_info.h"
9 #include "ash/display/display_manager.h"
10 #include "ash/shell.h"
11 #include "ash/system/system_notifier.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "grit/ash_resources.h"
14 #include "grit/ash_strings.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/l10n/time_format.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/gfx/display.h"
19 #include "ui/gfx/screen.h"
20 #include "ui/message_center/message_center.h"
21 #include "ui/message_center/notification.h"
22 #include "ui/message_center/notification_delegate.h"
23 
24 using message_center::Notification;
25 
26 namespace ash {
27 namespace {
28 
29 bool g_use_timer = true;
30 
31 class 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 
ResolutionChangeNotificationDelegate(ResolutionNotificationController * controller,bool has_timeout)56 ResolutionChangeNotificationDelegate::ResolutionChangeNotificationDelegate(
57     ResolutionNotificationController* controller,
58     bool has_timeout)
59     : controller_(controller),
60       has_timeout_(has_timeout) {
61   DCHECK(controller_);
62 }
63 
~ResolutionChangeNotificationDelegate()64 ResolutionChangeNotificationDelegate::~ResolutionChangeNotificationDelegate() {
65 }
66 
Display()67 void ResolutionChangeNotificationDelegate::Display() {
68 }
69 
Error()70 void ResolutionChangeNotificationDelegate::Error() {
71 }
72 
Close(bool by_user)73 void ResolutionChangeNotificationDelegate::Close(bool by_user) {
74   if (by_user)
75     controller_->AcceptResolutionChange(false);
76 }
77 
Click()78 void ResolutionChangeNotificationDelegate::Click() {
79   controller_->AcceptResolutionChange(true);
80 }
81 
HasClickedListener()82 bool ResolutionChangeNotificationDelegate::HasClickedListener() {
83   return true;
84 }
85 
ButtonClick(int button_index)86 void 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
98 const int ResolutionNotificationController::kTimeoutInSec = 15;
99 
100 // static
101 const char ResolutionNotificationController::kNotificationId[] =
102     "chrome://settings/display/resolution";
103 
104 struct ResolutionNotificationController::ResolutionChangeInfo {
105   ResolutionChangeInfo(int64 display_id,
106                        const DisplayMode& old_resolution,
107                        const DisplayMode& 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   DisplayMode old_resolution;
116 
117   // The requested resolution. Note that this may be different from
118   // |current_resolution| which is the actual resolution set.
119   DisplayMode new_resolution;
120 
121   // The actual resolution after the change.
122   DisplayMode current_resolution;
123 
124   // The callback when accept is chosen.
125   base::Closure accept_callback;
126 
127   // The remaining timeout in seconds. 0 if the change does not time out.
128   uint8 timeout_count;
129 
130   // The timer to invoke OnTimerTick() every second. This cannot be
131   // OneShotTimer since the message contains text "automatically closed in xx
132   // seconds..." which has to be updated every second.
133   base::RepeatingTimer<ResolutionNotificationController> timer;
134 
135  private:
136   DISALLOW_COPY_AND_ASSIGN(ResolutionChangeInfo);
137 };
138 
ResolutionChangeInfo(int64 display_id,const DisplayMode & old_resolution,const DisplayMode & new_resolution,const base::Closure & accept_callback)139 ResolutionNotificationController::ResolutionChangeInfo::ResolutionChangeInfo(
140     int64 display_id,
141     const DisplayMode& old_resolution,
142     const DisplayMode& new_resolution,
143     const base::Closure& accept_callback)
144     : display_id(display_id),
145       old_resolution(old_resolution),
146       new_resolution(new_resolution),
147       accept_callback(accept_callback),
148       timeout_count(0) {
149   DisplayManager* display_manager = Shell::GetInstance()->display_manager();
150   if (!display_manager->HasInternalDisplay() &&
151       display_manager->num_connected_displays() == 1u) {
152     timeout_count = kTimeoutInSec;
153   }
154 }
155 
156 ResolutionNotificationController::ResolutionChangeInfo::
~ResolutionChangeInfo()157     ~ResolutionChangeInfo() {
158 }
159 
ResolutionNotificationController()160 ResolutionNotificationController::ResolutionNotificationController() {
161   Shell::GetInstance()->display_controller()->AddObserver(this);
162   Shell::GetScreen()->AddObserver(this);
163 }
164 
~ResolutionNotificationController()165 ResolutionNotificationController::~ResolutionNotificationController() {
166   Shell::GetInstance()->display_controller()->RemoveObserver(this);
167   Shell::GetScreen()->RemoveObserver(this);
168 }
169 
PrepareNotification(int64 display_id,const DisplayMode & old_resolution,const DisplayMode & new_resolution,const base::Closure & accept_callback)170 void ResolutionNotificationController::PrepareNotification(
171     int64 display_id,
172     const DisplayMode& old_resolution,
173     const DisplayMode& new_resolution,
174     const base::Closure& accept_callback) {
175   // If multiple resolution changes are invoked for the same display,
176   // the original resolution for the first resolution change has to be used
177   // instead of the specified |old_resolution|.
178   DisplayMode original_resolution;
179   if (change_info_ && change_info_->display_id == display_id) {
180     DCHECK(change_info_->new_resolution.size == old_resolution.size);
181     original_resolution = change_info_->old_resolution;
182   }
183 
184   change_info_.reset(new ResolutionChangeInfo(
185       display_id, old_resolution, new_resolution, accept_callback));
186   if (!original_resolution.size.IsEmpty())
187     change_info_->old_resolution = original_resolution;
188 }
189 
DoesNotificationTimeout()190 bool ResolutionNotificationController::DoesNotificationTimeout() {
191   return change_info_ && change_info_->timeout_count > 0;
192 }
193 
CreateOrUpdateNotification(bool enable_spoken_feedback)194 void ResolutionNotificationController::CreateOrUpdateNotification(
195     bool enable_spoken_feedback) {
196   message_center::MessageCenter* message_center =
197       message_center::MessageCenter::Get();
198   if (!change_info_) {
199     message_center->RemoveNotification(kNotificationId, false /* by_user */);
200     return;
201   }
202 
203   base::string16 timeout_message;
204   message_center::RichNotificationData data;
205   if (change_info_->timeout_count > 0) {
206     data.buttons.push_back(message_center::ButtonInfo(
207         l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_RESOLUTION_CHANGE_ACCEPT)));
208     timeout_message = l10n_util::GetStringFUTF16(
209         IDS_ASH_DISPLAY_RESOLUTION_TIMEOUT,
210         ui::TimeFormat::Simple(
211             ui::TimeFormat::FORMAT_DURATION, ui::TimeFormat::LENGTH_LONG,
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   const base::string16 display_name = base::UTF8ToUTF16(
220       Shell::GetInstance()->display_manager()->GetDisplayNameForId(
221           change_info_->display_id));
222   const base::string16 message =
223       (change_info_->new_resolution.size ==
224        change_info_->current_resolution.size) ?
225       l10n_util::GetStringFUTF16(
226           IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
227           display_name,
228           base::UTF8ToUTF16(change_info_->new_resolution.size.ToString())) :
229       l10n_util::GetStringFUTF16(
230           IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED,
231           display_name,
232           base::UTF8ToUTF16(change_info_->new_resolution.size.ToString()),
233           base::UTF8ToUTF16(change_info_->current_resolution.size.ToString()));
234 
235   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
236   scoped_ptr<Notification> notification(new Notification(
237       message_center::NOTIFICATION_TYPE_SIMPLE,
238       kNotificationId,
239       message,
240       timeout_message,
241       bundle.GetImageNamed(IDR_AURA_NOTIFICATION_DISPLAY),
242       base::string16() /* display_source */,
243       message_center::NotifierId(
244           message_center::NotifierId::SYSTEM_COMPONENT,
245           system_notifier::kNotifierDisplayResolutionChange),
246       data,
247       new ResolutionChangeNotificationDelegate(
248           this, change_info_->timeout_count > 0)));
249   notification->SetSystemPriority();
250   message_center->AddNotification(notification.Pass());
251 }
252 
OnTimerTick()253 void ResolutionNotificationController::OnTimerTick() {
254   if (!change_info_)
255     return;
256 
257   --change_info_->timeout_count;
258   if (change_info_->timeout_count == 0)
259     RevertResolutionChange();
260   else
261     CreateOrUpdateNotification(false);
262 }
263 
AcceptResolutionChange(bool close_notification)264 void ResolutionNotificationController::AcceptResolutionChange(
265     bool close_notification) {
266   if (close_notification) {
267     message_center::MessageCenter::Get()->RemoveNotification(
268         kNotificationId, false /* by_user */);
269   }
270   base::Closure callback = change_info_->accept_callback;
271   change_info_.reset();
272   callback.Run();
273 }
274 
RevertResolutionChange()275 void ResolutionNotificationController::RevertResolutionChange() {
276   message_center::MessageCenter::Get()->RemoveNotification(
277       kNotificationId, false /* by_user */);
278   int64 display_id = change_info_->display_id;
279   DisplayMode old_resolution = change_info_->old_resolution;
280   change_info_.reset();
281   Shell::GetInstance()->display_manager()->SetDisplayMode(
282       display_id, old_resolution);
283 }
284 
OnDisplayAdded(const gfx::Display & new_display)285 void ResolutionNotificationController::OnDisplayAdded(
286     const gfx::Display& new_display) {
287 }
288 
OnDisplayRemoved(const gfx::Display & old_display)289 void ResolutionNotificationController::OnDisplayRemoved(
290     const gfx::Display& old_display) {
291   if (change_info_ && change_info_->display_id == old_display.id())
292     RevertResolutionChange();
293 }
294 
OnDisplayMetricsChanged(const gfx::Display &,uint32_t)295 void ResolutionNotificationController::OnDisplayMetricsChanged(
296     const gfx::Display&, uint32_t) {
297 }
298 
OnDisplayConfigurationChanged()299 void ResolutionNotificationController::OnDisplayConfigurationChanged() {
300   if (!change_info_)
301     return;
302 
303   change_info_->current_resolution = Shell::GetInstance()->display_manager()->
304       GetActiveModeForDisplayId(change_info_->display_id);
305   CreateOrUpdateNotification(true);
306   if (g_use_timer && change_info_->timeout_count > 0) {
307     change_info_->timer.Start(FROM_HERE,
308                               base::TimeDelta::FromSeconds(1),
309                               this,
310                               &ResolutionNotificationController::OnTimerTick);
311   }
312 }
313 
SuppressTimerForTest()314 void ResolutionNotificationController::SuppressTimerForTest() {
315   g_use_timer = false;
316 }
317 
318 }  // namespace ash
319