Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ash / system / session_length_limit / tray_session_length_limit.cc
blob4982658afa3c47d7dd3dafa9cbfff77426a1cf0f
1 // Copyright (c) 2012 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.
5 #include "ash/system/session_length_limit/tray_session_length_limit.h"
7 #include <algorithm>
9 #include "ash/shelf/shelf_types.h"
10 #include "ash/shell.h"
11 #include "ash/system/tray/system_tray.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "ash/system/tray/system_tray_notifier.h"
14 #include "ash/system/tray/tray_constants.h"
15 #include "ash/system/tray/tray_notification_view.h"
16 #include "ash/system/tray/tray_views.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/string16.h"
20 #include "base/string_number_conversions.h"
21 #include "base/utf_string_conversions.h"
22 #include "grit/ash_resources.h"
23 #include "grit/ash_strings.h"
24 #include "third_party/skia/include/core/SkColor.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/font.h"
27 #include "ui/gfx/text_constants.h"
28 #include "ui/views/border.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/layout/box_layout.h"
31 #include "ui/views/layout/grid_layout.h"
32 #include "ui/views/view.h"
34 namespace ash {
35 namespace internal {
37 namespace {
39 // If the remaining session time falls below this threshold, the user should be
40 // informed that the session is about to expire.
41 const int kExpiringSoonThresholdInSeconds = 5 * 60; // 5 minutes.
43 // Color in which the remaining session time is normally shown.
44 const SkColor kRemainingTimeColor = SK_ColorWHITE;
45 // Color in which the remaining session time is shown when it is expiring soon.
46 const SkColor kRemainingTimeExpiringSoonColor = SK_ColorRED;
48 views::Label* CreateAndSetupLabel() {
49 views::Label* label = new views::Label;
50 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
51 SetupLabelForTray(label);
52 gfx::Font font = label->font();
53 label->SetFont(font.DeriveFont(0, font.GetStyle() & ~gfx::Font::BOLD));
54 return label;
57 string16 IntToTwoDigitString(int value) {
58 DCHECK_GE(value, 0);
59 DCHECK_LE(value, 99);
60 if (value < 10)
61 return ASCIIToUTF16("0") + base::IntToString16(value);
62 return base::IntToString16(value);
65 string16 FormatRemainingSessionTimeNotification(
66 const base::TimeDelta& remaining_session_time) {
67 return l10n_util::GetStringFUTF16(
68 IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME_NOTIFICATION,
69 Shell::GetInstance()->system_tray_delegate()->
70 FormatTimeDuration(remaining_session_time));
73 } // namespace
75 namespace tray {
77 class RemainingSessionTimeNotificationView : public TrayNotificationView {
78 public:
79 explicit RemainingSessionTimeNotificationView(TraySessionLengthLimit* owner);
80 virtual ~RemainingSessionTimeNotificationView();
82 // TrayNotificationView:
83 virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
85 void Update();
87 private:
88 views::Label* label_;
90 DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeNotificationView);
93 class RemainingSessionTimeTrayView : public views::View {
94 public:
95 RemainingSessionTimeTrayView(const TraySessionLengthLimit* owner,
96 ShelfAlignment shelf_alignment);
97 virtual ~RemainingSessionTimeTrayView();
99 void UpdateClockLayout(ShelfAlignment shelf_alignment);
100 void Update();
102 private:
103 void SetBorder(ShelfAlignment shelf_alignment);
105 const TraySessionLengthLimit* owner_;
107 views::Label* horizontal_layout_label_;
108 views::Label* vertical_layout_label_hours_left_;
109 views::Label* vertical_layout_label_hours_right_;
110 views::Label* vertical_layout_label_minutes_left_;
111 views::Label* vertical_layout_label_minutes_right_;
112 views::Label* vertical_layout_label_seconds_left_;
113 views::Label* vertical_layout_label_seconds_right_;
115 DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeTrayView);
118 RemainingSessionTimeNotificationView::RemainingSessionTimeNotificationView(
119 TraySessionLengthLimit* owner)
120 : TrayNotificationView(owner,
121 IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER) {
122 label_ = new views::Label;
123 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
124 label_->SetMultiLine(true);
125 Update();
126 InitView(label_);
129 RemainingSessionTimeNotificationView::~RemainingSessionTimeNotificationView() {
132 void RemainingSessionTimeNotificationView::ChildPreferredSizeChanged(
133 views::View* child) {
134 PreferredSizeChanged();
137 void RemainingSessionTimeNotificationView::Update() {
138 label_->SetText(FormatRemainingSessionTimeNotification(
139 reinterpret_cast<TraySessionLengthLimit*>(owner())->
140 GetRemainingSessionTime()));
143 RemainingSessionTimeTrayView::RemainingSessionTimeTrayView(
144 const TraySessionLengthLimit* owner,
145 ShelfAlignment shelf_alignment)
146 : owner_(owner),
147 horizontal_layout_label_(NULL),
148 vertical_layout_label_hours_left_(NULL),
149 vertical_layout_label_hours_right_(NULL),
150 vertical_layout_label_minutes_left_(NULL),
151 vertical_layout_label_minutes_right_(NULL),
152 vertical_layout_label_seconds_left_(NULL),
153 vertical_layout_label_seconds_right_(NULL) {
154 UpdateClockLayout(shelf_alignment);
157 RemainingSessionTimeTrayView::~RemainingSessionTimeTrayView() {
160 void RemainingSessionTimeTrayView::UpdateClockLayout(
161 ShelfAlignment shelf_alignment) {
162 SetBorder(shelf_alignment);
163 const bool horizontal_layout = (shelf_alignment == SHELF_ALIGNMENT_BOTTOM ||
164 shelf_alignment == SHELF_ALIGNMENT_TOP);
165 if (horizontal_layout && !horizontal_layout_label_) {
166 // Remove labels used for vertical layout.
167 RemoveAllChildViews(true);
168 vertical_layout_label_hours_left_ = NULL;
169 vertical_layout_label_hours_right_ = NULL;
170 vertical_layout_label_minutes_left_ = NULL;
171 vertical_layout_label_minutes_right_ = NULL;
172 vertical_layout_label_seconds_left_ = NULL;
173 vertical_layout_label_seconds_right_ = NULL;
175 // Create label used for horizontal layout.
176 horizontal_layout_label_ = CreateAndSetupLabel();
178 // Construct layout.
179 SetLayoutManager(
180 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
181 AddChildView(horizontal_layout_label_);
183 } else if (!horizontal_layout && horizontal_layout_label_) {
184 // Remove label used for horizontal layout.
185 RemoveAllChildViews(true);
186 horizontal_layout_label_ = NULL;
188 // Create labels used for vertical layout.
189 vertical_layout_label_hours_left_ = CreateAndSetupLabel();
190 vertical_layout_label_hours_right_ = CreateAndSetupLabel();
191 vertical_layout_label_minutes_left_ = CreateAndSetupLabel();
192 vertical_layout_label_minutes_right_ = CreateAndSetupLabel();
193 vertical_layout_label_seconds_left_ = CreateAndSetupLabel();
194 vertical_layout_label_seconds_right_ = CreateAndSetupLabel();
196 // Construct layout.
197 views::GridLayout* layout = new views::GridLayout(this);
198 SetLayoutManager(layout);
199 views::ColumnSet* columns = layout->AddColumnSet(0);
200 columns->AddPaddingColumn(0, 6);
201 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
202 0, views::GridLayout::USE_PREF, 0, 0);
203 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
204 0, views::GridLayout::USE_PREF, 0, 0);
205 layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment);
206 layout->StartRow(0, 0);
207 layout->AddView(vertical_layout_label_hours_left_);
208 layout->AddView(vertical_layout_label_hours_right_);
209 layout->StartRow(0, 0);
210 layout->AddView(vertical_layout_label_minutes_left_);
211 layout->AddView(vertical_layout_label_minutes_right_);
212 layout->StartRow(0, 0);
213 layout->AddView(vertical_layout_label_seconds_left_);
214 layout->AddView(vertical_layout_label_seconds_right_);
215 layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment);
217 Update();
220 void RemainingSessionTimeTrayView::Update() {
221 const TraySessionLengthLimit::LimitState limit_state =
222 owner_->GetLimitState();
224 if (limit_state == TraySessionLengthLimit::LIMIT_NONE) {
225 SetVisible(false);
226 return;
229 int seconds = owner_->GetRemainingSessionTime().InSeconds();
230 // If the remaining session time is 100 hours or more, show 99:59:59 instead.
231 seconds = std::min(seconds, 100 * 60 * 60 - 1); // 100 hours - 1 second.
232 int minutes = seconds / 60;
233 seconds %= 60;
234 const int hours = minutes / 60;
235 minutes %= 60;
237 const string16 hours_str = IntToTwoDigitString(hours);
238 const string16 minutes_str = IntToTwoDigitString(minutes);
239 const string16 seconds_str = IntToTwoDigitString(seconds);
240 const SkColor color =
241 limit_state == TraySessionLengthLimit::LIMIT_EXPIRING_SOON ?
242 kRemainingTimeExpiringSoonColor : kRemainingTimeColor;
244 if (horizontal_layout_label_) {
245 horizontal_layout_label_->SetText(l10n_util::GetStringFUTF16(
246 IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME,
247 hours_str, minutes_str, seconds_str));
248 horizontal_layout_label_->SetEnabledColor(color);
249 } else if (vertical_layout_label_hours_left_) {
250 vertical_layout_label_hours_left_->SetText(hours_str.substr(0, 1));
251 vertical_layout_label_hours_right_->SetText(hours_str.substr(1, 1));
252 vertical_layout_label_minutes_left_->SetText(minutes_str.substr(0, 1));
253 vertical_layout_label_minutes_right_->SetText(minutes_str.substr(1, 1));
254 vertical_layout_label_seconds_left_->SetText(seconds_str.substr(0, 1));
255 vertical_layout_label_seconds_right_->SetText(seconds_str.substr(1, 1));
256 vertical_layout_label_hours_left_->SetEnabledColor(color);
257 vertical_layout_label_hours_right_->SetEnabledColor(color);
258 vertical_layout_label_minutes_left_->SetEnabledColor(color);
259 vertical_layout_label_minutes_right_->SetEnabledColor(color);
260 vertical_layout_label_seconds_left_->SetEnabledColor(color);
261 vertical_layout_label_seconds_right_->SetEnabledColor(color);
264 Layout();
265 SetVisible(true);
268 void RemainingSessionTimeTrayView::SetBorder(ShelfAlignment shelf_alignment) {
269 if (shelf_alignment == SHELF_ALIGNMENT_BOTTOM ||
270 shelf_alignment == SHELF_ALIGNMENT_TOP) {
271 set_border(views::Border::CreateEmptyBorder(
272 0, kTrayLabelItemHorizontalPaddingBottomAlignment,
273 0, kTrayLabelItemHorizontalPaddingBottomAlignment));
274 } else {
275 set_border(NULL);
279 } // namespace tray
281 TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray)
282 : SystemTrayItem(system_tray),
283 tray_view_(NULL),
284 notification_view_(NULL),
285 limit_state_(LIMIT_NONE) {
286 Shell::GetInstance()->system_tray_notifier()->
287 AddSessionLengthLimitObserver(this);
288 Update();
291 TraySessionLengthLimit::~TraySessionLengthLimit() {
292 Shell::GetInstance()->system_tray_notifier()->
293 RemoveSessionLengthLimitObserver(this);
296 views::View* TraySessionLengthLimit::CreateTrayView(user::LoginStatus status) {
297 CHECK(tray_view_ == NULL);
298 tray_view_ = new tray::RemainingSessionTimeTrayView(
299 this, system_tray()->shelf_alignment());
300 return tray_view_;
303 views::View* TraySessionLengthLimit::CreateNotificationView(
304 user::LoginStatus status) {
305 CHECK(notification_view_ == NULL);
306 notification_view_ = new tray::RemainingSessionTimeNotificationView(this);
307 return notification_view_;
310 void TraySessionLengthLimit::DestroyTrayView() {
311 tray_view_ = NULL;
314 void TraySessionLengthLimit::DestroyNotificationView() {
315 notification_view_ = NULL;
318 void TraySessionLengthLimit::UpdateAfterShelfAlignmentChange(
319 ShelfAlignment alignment) {
320 if (tray_view_)
321 tray_view_->UpdateClockLayout(alignment);
324 void TraySessionLengthLimit::OnSessionStartTimeChanged() {
325 Update();
328 void TraySessionLengthLimit::OnSessionLengthLimitChanged() {
329 Update();
332 TraySessionLengthLimit::LimitState
333 TraySessionLengthLimit::GetLimitState() const {
334 return limit_state_;
337 base::TimeDelta TraySessionLengthLimit::GetRemainingSessionTime() const {
338 return remaining_session_time_;
341 void TraySessionLengthLimit::ShowAndSpeakNotification() {
342 ShowNotificationView();
343 Shell::GetInstance()->system_tray_delegate()->MaybeSpeak(UTF16ToUTF8(
344 FormatRemainingSessionTimeNotification(remaining_session_time_)));
347 void TraySessionLengthLimit::Update() {
348 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
349 const LimitState previous_limit_state = limit_state_;
350 if (!delegate->GetSessionStartTime(&session_start_time_) ||
351 !delegate->GetSessionLengthLimit(&limit_)) {
352 remaining_session_time_ = base::TimeDelta();
353 limit_state_ = LIMIT_NONE;
354 timer_.reset();
355 } else {
356 remaining_session_time_ = std::max(
357 limit_ - (base::TimeTicks::Now() - session_start_time_),
358 base::TimeDelta());
359 limit_state_ = remaining_session_time_.InSeconds() <=
360 kExpiringSoonThresholdInSeconds ? LIMIT_EXPIRING_SOON : LIMIT_SET;
361 if (!timer_)
362 timer_.reset(new base::RepeatingTimer<TraySessionLengthLimit>);
363 if (!timer_->IsRunning()) {
364 // Start a timer that will update the remaining session time every second.
365 timer_->Start(FROM_HERE,
366 base::TimeDelta::FromSeconds(1),
367 this,
368 &TraySessionLengthLimit::Update);
372 if (notification_view_)
373 notification_view_->Update();
375 switch (limit_state_) {
376 case LIMIT_NONE:
377 HideNotificationView();
378 break;
379 case LIMIT_SET:
380 if (previous_limit_state == LIMIT_NONE)
381 ShowAndSpeakNotification();
382 break;
383 case LIMIT_EXPIRING_SOON:
384 if (previous_limit_state == LIMIT_NONE ||
385 previous_limit_state == LIMIT_SET) {
386 ShowAndSpeakNotification();
388 break;
391 // Update the tray view last so that it can check whether the notification
392 // view is currently visible or not.
393 if (tray_view_)
394 tray_view_->Update();
397 } // namespace internal
398 } // namespace ash