2c454029e79d7ef4332407487358a78d00a4289e
[platform/framework/web/crosswalk.git] / src / ash / display / resolution_notification_controller.cc
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
23 using message_center::Notification;
24
25 namespace ash {
26 namespace {
27
28 bool g_use_timer = true;
29
30 class 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
55 ResolutionChangeNotificationDelegate::ResolutionChangeNotificationDelegate(
56     ResolutionNotificationController* controller,
57     bool has_timeout)
58     : controller_(controller),
59       has_timeout_(has_timeout) {
60   DCHECK(controller_);
61 }
62
63 ResolutionChangeNotificationDelegate::~ResolutionChangeNotificationDelegate() {
64 }
65
66 void ResolutionChangeNotificationDelegate::Display() {
67 }
68
69 void ResolutionChangeNotificationDelegate::Error() {
70 }
71
72 void ResolutionChangeNotificationDelegate::Close(bool by_user) {
73   if (by_user)
74     controller_->AcceptResolutionChange(false);
75 }
76
77 void ResolutionChangeNotificationDelegate::Click() {
78   controller_->AcceptResolutionChange(true);
79 }
80
81 bool ResolutionChangeNotificationDelegate::HasClickedListener() {
82   return true;
83 }
84
85 void 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
97 const int ResolutionNotificationController::kTimeoutInSec = 15;
98
99 // static
100 const char ResolutionNotificationController::kNotificationId[] =
101     "chrome://settings/display/resolution";
102
103 struct 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
138 ResolutionNotificationController::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
155 ResolutionNotificationController::ResolutionChangeInfo::
156     ~ResolutionChangeInfo() {
157 }
158
159 ResolutionNotificationController::ResolutionNotificationController() {
160   Shell::GetInstance()->display_controller()->AddObserver(this);
161   Shell::GetScreen()->AddObserver(this);
162 }
163
164 ResolutionNotificationController::~ResolutionNotificationController() {
165   Shell::GetInstance()->display_controller()->RemoveObserver(this);
166   Shell::GetScreen()->RemoveObserver(this);
167 }
168
169 void 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
194 bool ResolutionNotificationController::DoesNotificationTimeout() {
195   return change_info_ && change_info_->timeout_count > 0;
196 }
197
198 void 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
256 void 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
267 void 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
278 void 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
288 void ResolutionNotificationController::OnDisplayBoundsChanged(
289     const gfx::Display& display) {
290 }
291
292 void ResolutionNotificationController::OnDisplayAdded(
293     const gfx::Display& new_display) {
294 }
295
296 void ResolutionNotificationController::OnDisplayRemoved(
297     const gfx::Display& old_display) {
298   if (change_info_ && change_info_->display_id == old_display.id())
299     RevertResolutionChange();
300 }
301
302 void 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
318 void ResolutionNotificationController::SuppressTimerForTest() {
319   g_use_timer = false;
320 }
321
322 }  // namespace ash