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"
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"
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
));
57 string16
IntToTwoDigitString(int value
) {
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
));
77 class RemainingSessionTimeNotificationView
: public TrayNotificationView
{
79 explicit RemainingSessionTimeNotificationView(TraySessionLengthLimit
* owner
);
80 virtual ~RemainingSessionTimeNotificationView();
82 // TrayNotificationView:
83 virtual void ChildPreferredSizeChanged(views::View
* child
) OVERRIDE
;
90 DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeNotificationView
);
93 class RemainingSessionTimeTrayView
: public views::View
{
95 RemainingSessionTimeTrayView(const TraySessionLengthLimit
* owner
,
96 ShelfAlignment shelf_alignment
);
97 virtual ~RemainingSessionTimeTrayView();
99 void UpdateClockLayout(ShelfAlignment shelf_alignment
);
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);
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
)
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();
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();
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
);
220 void RemainingSessionTimeTrayView::Update() {
221 const TraySessionLengthLimit::LimitState limit_state
=
222 owner_
->GetLimitState();
224 if (limit_state
== TraySessionLengthLimit::LIMIT_NONE
) {
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;
234 const int hours
= 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
);
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
));
281 TraySessionLengthLimit::TraySessionLengthLimit(SystemTray
* system_tray
)
282 : SystemTrayItem(system_tray
),
284 notification_view_(NULL
),
285 limit_state_(LIMIT_NONE
) {
286 Shell::GetInstance()->system_tray_notifier()->
287 AddSessionLengthLimitObserver(this);
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());
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() {
314 void TraySessionLengthLimit::DestroyNotificationView() {
315 notification_view_
= NULL
;
318 void TraySessionLengthLimit::UpdateAfterShelfAlignmentChange(
319 ShelfAlignment alignment
) {
321 tray_view_
->UpdateClockLayout(alignment
);
324 void TraySessionLengthLimit::OnSessionStartTimeChanged() {
328 void TraySessionLengthLimit::OnSessionLengthLimitChanged() {
332 TraySessionLengthLimit::LimitState
333 TraySessionLengthLimit::GetLimitState() const {
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
;
356 remaining_session_time_
= std::max(
357 limit_
- (base::TimeTicks::Now() - session_start_time_
),
359 limit_state_
= remaining_session_time_
.InSeconds() <=
360 kExpiringSoonThresholdInSeconds
? LIMIT_EXPIRING_SOON
: LIMIT_SET
;
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),
368 &TraySessionLengthLimit::Update
);
372 if (notification_view_
)
373 notification_view_
->Update();
375 switch (limit_state_
) {
377 HideNotificationView();
380 if (previous_limit_state
== LIMIT_NONE
)
381 ShowAndSpeakNotification();
383 case LIMIT_EXPIRING_SOON
:
384 if (previous_limit_state
== LIMIT_NONE
||
385 previous_limit_state
== LIMIT_SET
) {
386 ShowAndSpeakNotification();
391 // Update the tray view last so that it can check whether the notification
392 // view is currently visible or not.
394 tray_view_
->Update();
397 } // namespace internal