Commit a889e94c authored by tnagel@chromium.org's avatar tnagel@chromium.org

Move session length timer into tray bubble.

BUG=287784

Review URL: https://codereview.chromium.org/262973004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269813 0039d316-1c4b-4281-b951-d872f2087c98
parent ae018707
...@@ -512,11 +512,11 @@ Press Shift + Alt to switch. ...@@ -512,11 +512,11 @@ Press Shift + Alt to switch.
<message name="IDS_ASH_STATUS_TRAY_SHUTDOWN" desc="The accessible text for the shutdown button."> <message name="IDS_ASH_STATUS_TRAY_SHUTDOWN" desc="The accessible text for the shutdown button.">
Shut down Shut down
</message> </message>
<message name="IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME" desc="The time remaining until the end of the session. Shown if the session length is limited."> <message name="IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT" desc="Tray bubble item shown to inform the user that the session length is limited.">
<ph name="hours">$1<ex>01</ex></ph>:<ph name="minutes">$2<ex>35</ex></ph>:<ph name="seconds">$3<ex>12</ex></ph> Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>.
</message> </message>
<message name="IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME_NOTIFICATION" desc="Notification shown to inform the user that the session length is limited."> <message name="IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT" desc="Notification shown to inform the user that the session length is limited.">
This session will end in <ph name="session_time_remaining">$1<ex>15 minutes</ex></ph>. You will be automatically signed out. Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. You will be signed out.
</message> </message>
<message name="IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL" desc="Label shown instead of email for supervised users"> <message name="IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL" desc="Label shown instead of email for supervised users">
......
...@@ -143,7 +143,6 @@ ...@@ -143,7 +143,6 @@
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE_DARK" file="cros/status/status_power_small_all_dark_fluctuating.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE_DARK" file="cros/status/status_power_small_all_dark_fluctuating.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE" file="cros/status/status_screenshare.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE" file="cros/status/status_screenshare.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE_DARK" file="cros/status/status_screenshare_dark.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE_DARK" file="cros/status/status_screenshare_dark.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER" file="cros/status/status_session_length_limit_timer.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SETTINGS" file="cros/status/status_settings.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SETTINGS" file="cros/status/status_settings.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN" file="cros/status/status_shutdown.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN" file="cros/status/status_shutdown.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN_HOVER" file="cros/status/status_shutdown_hover.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN_HOVER" file="cros/status/status_shutdown_hover.png" />
...@@ -155,6 +154,7 @@ ...@@ -155,6 +154,7 @@
<if expr="chromeos"> <if expr="chromeos">
<structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER" file="cros/notification/notification_low_power_charger.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER" file="cros/notification/notification_low_power_charger.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_DISPLAY" file="cros/notification/display_notification_icon.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_DISPLAY" file="cros/notification/display_notification_icon.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_BUBBLE_SESSION_LENGTH_LIMIT" file="cros/status/status_session_length_timer.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ENTERPRISE" file="cros/status/status_managed.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ENTERPRISE" file="cros/status/status_managed.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_MANAGED_USER" file="cros/status/status_managed_mode_user.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_MANAGED_USER" file="cros/status/status_managed_mode_user.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_1X" file="cros/network/statusbar_network_1x.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_1X" file="cros/network/statusbar_network_1x.png" />
...@@ -188,6 +188,7 @@ ...@@ -188,6 +188,7 @@
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_VPN" file="cros/network/statusbar_vpn_dark.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_VPN" file="cros/network/statusbar_vpn_dark.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE" file="cros/network/statusbar_network_vpn_badge.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE" file="cros/network/statusbar_network_vpn_badge.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_WIRED" file="cros/network/statusbar_wired.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_WIRED" file="cros/network/statusbar_wired.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT" file="cros/notification/notification_session_length_timer.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING" file="cros/status/status_recording.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING" file="cros/status/status_recording.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING_RED" file="cros/status/status_recording_red.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING_RED" file="cros/status/status_recording_red.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_VIRTUAL_KEYBOARD" file="cros/status/status_virtual_keyboard.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_VIRTUAL_KEYBOARD" file="cros/status/status_virtual_keyboard.png" />
......
...@@ -6,287 +6,46 @@ ...@@ -6,287 +6,46 @@
#include <algorithm> #include <algorithm>
#include "ash/shelf/shelf_types.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/system/chromeos/label_tray_view.h"
#include "ash/system/system_notifier.h" #include "ash/system/system_notifier.h"
#include "ash/system/tray/system_tray.h" #include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_delegate.h" #include "ash/system/tray/system_tray_delegate.h"
#include "ash/system/tray/system_tray_notifier.h" #include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_utils.h"
#include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "grit/ash_resources.h" #include "grit/ash_resources.h"
#include "grit/ash_strings.h" #include "grit/ash_strings.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h" #include "ui/base/l10n/time_format.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/font_list.h"
#include "ui/message_center/message_center.h" #include "ui/message_center/message_center.h"
#include "ui/message_center/notification.h" #include "ui/message_center/notification.h"
#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/view.h" #include "ui/views/view.h"
using message_center::Notification;
namespace ash { namespace ash {
namespace { namespace {
// If the remaining session time falls below this threshold, the user should be // If the remaining session time falls below this threshold, the user should be
// informed that the session is about to expire. // informed that the session is about to expire.
const int kExpiringSoonThresholdInSeconds = 5 * 60; // 5 minutes. const int kExpiringSoonThresholdInMinutes = 5;
// Color in which the remaining session time is normally shown.
const SkColor kRemainingTimeColor = SK_ColorWHITE;
// Color in which the remaining session time is shown when it is expiring soon.
const SkColor kRemainingTimeExpiringSoonColor = SK_ColorRED;
views::Label* CreateAndSetupLabel() { // Use 500ms interval for updates to notification and tray bubble to reduce the
views::Label* label = new views::Label; // likelihood of a user-visible skip in high load situations (as might happen
label->SetHorizontalAlignment(gfx::ALIGN_LEFT); // with 1000ms).
SetupLabelForTray(label); const int kTimerIntervalInMilliseconds = 500;
label->SetFontList(label->font_list().DeriveWithStyle(
label->font_list().GetFontStyle() & ~gfx::Font::BOLD));
return label;
}
base::string16 IntToTwoDigitString(int value) {
DCHECK_GE(value, 0);
DCHECK_LE(value, 99);
if (value < 10)
return base::ASCIIToUTF16("0") + base::IntToString16(value);
return base::IntToString16(value);
}
base::string16 FormatRemainingSessionTimeNotification(
const base::TimeDelta& remaining_session_time) {
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME_NOTIFICATION,
ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
ui::TimeFormat::LENGTH_LONG,
10,
remaining_session_time));
}
// Creates, or updates the notification for session length timeout with
// |remaining_time|. |state_changed| is true when its internal state has been
// changed from another.
void CreateOrUpdateNotification(const std::string& notification_id,
const base::TimeDelta& remaining_time,
bool state_changed) {
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
// Do not create a new notification if no state has changed. It may happen
// when the notification is already closed by the user, see crbug.com/285941.
if (!state_changed && !message_center->HasNotification(notification_id))
return;
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
message_center::RichNotificationData data;
// Makes the spoken feedback only when the state has been changed.
data.should_make_spoken_feedback_for_popup_updates = state_changed;
scoped_ptr<Notification> notification(new Notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
notification_id,
FormatRemainingSessionTimeNotification(remaining_time),
base::string16() /* message */,
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER),
base::string16() /* display_source */,
message_center::NotifierId(
message_center::NotifierId::SYSTEM_COMPONENT,
system_notifier::kNotifierSessionLengthTimeout),
data,
NULL /* delegate */));
notification->SetSystemPriority();
message_center::MessageCenter::Get()->AddNotification(notification.Pass());
}
} // namespace } // namespace
namespace tray {
class RemainingSessionTimeTrayView : public views::View {
public:
RemainingSessionTimeTrayView(const TraySessionLengthLimit* owner,
ShelfAlignment shelf_alignment);
virtual ~RemainingSessionTimeTrayView();
void UpdateClockLayout(ShelfAlignment shelf_alignment);
void Update();
private:
void SetBorderFromAlignment(ShelfAlignment shelf_alignment);
const TraySessionLengthLimit* owner_;
views::Label* horizontal_layout_label_;
views::Label* vertical_layout_label_hours_left_;
views::Label* vertical_layout_label_hours_right_;
views::Label* vertical_layout_label_minutes_left_;
views::Label* vertical_layout_label_minutes_right_;
views::Label* vertical_layout_label_seconds_left_;
views::Label* vertical_layout_label_seconds_right_;
DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeTrayView);
};
RemainingSessionTimeTrayView::RemainingSessionTimeTrayView(
const TraySessionLengthLimit* owner,
ShelfAlignment shelf_alignment)
: owner_(owner),
horizontal_layout_label_(NULL),
vertical_layout_label_hours_left_(NULL),
vertical_layout_label_hours_right_(NULL),
vertical_layout_label_minutes_left_(NULL),
vertical_layout_label_minutes_right_(NULL),
vertical_layout_label_seconds_left_(NULL),
vertical_layout_label_seconds_right_(NULL) {
UpdateClockLayout(shelf_alignment);
}
RemainingSessionTimeTrayView::~RemainingSessionTimeTrayView() {
}
void RemainingSessionTimeTrayView::UpdateClockLayout(
ShelfAlignment shelf_alignment) {
SetBorderFromAlignment(shelf_alignment);
const bool horizontal_layout = (shelf_alignment == SHELF_ALIGNMENT_BOTTOM ||
shelf_alignment == SHELF_ALIGNMENT_TOP);
if (horizontal_layout && !horizontal_layout_label_) {
// Remove labels used for vertical layout.
RemoveAllChildViews(true);
vertical_layout_label_hours_left_ = NULL;
vertical_layout_label_hours_right_ = NULL;
vertical_layout_label_minutes_left_ = NULL;
vertical_layout_label_minutes_right_ = NULL;
vertical_layout_label_seconds_left_ = NULL;
vertical_layout_label_seconds_right_ = NULL;
// Create label used for horizontal layout.
horizontal_layout_label_ = CreateAndSetupLabel();
// Construct layout.
SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
AddChildView(horizontal_layout_label_);
} else if (!horizontal_layout && horizontal_layout_label_) {
// Remove label used for horizontal layout.
RemoveAllChildViews(true);
horizontal_layout_label_ = NULL;
// Create labels used for vertical layout.
vertical_layout_label_hours_left_ = CreateAndSetupLabel();
vertical_layout_label_hours_right_ = CreateAndSetupLabel();
vertical_layout_label_minutes_left_ = CreateAndSetupLabel();
vertical_layout_label_minutes_right_ = CreateAndSetupLabel();
vertical_layout_label_seconds_left_ = CreateAndSetupLabel();
vertical_layout_label_seconds_right_ = CreateAndSetupLabel();
// Construct layout.
views::GridLayout* layout = new views::GridLayout(this);
SetLayoutManager(layout);
views::ColumnSet* columns = layout->AddColumnSet(0);
columns->AddPaddingColumn(0, 6);
columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
0, views::GridLayout::USE_PREF, 0, 0);
columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
0, views::GridLayout::USE_PREF, 0, 0);
layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVerticalAlignment);
layout->StartRow(0, 0);
layout->AddView(vertical_layout_label_hours_left_);
layout->AddView(vertical_layout_label_hours_right_);
layout->StartRow(0, 0);
layout->AddView(vertical_layout_label_minutes_left_);
layout->AddView(vertical_layout_label_minutes_right_);
layout->StartRow(0, 0);
layout->AddView(vertical_layout_label_seconds_left_);
layout->AddView(vertical_layout_label_seconds_right_);
layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVerticalAlignment);
}
Update();
}
void RemainingSessionTimeTrayView::Update() {
const TraySessionLengthLimit::LimitState limit_state =
owner_->GetLimitState();
if (limit_state == TraySessionLengthLimit::LIMIT_NONE) {
SetVisible(false);
return;
}
int seconds = owner_->GetRemainingSessionTime().InSeconds();
// If the remaining session time is 100 hours or more, show 99:59:59 instead.
seconds = std::min(seconds, 100 * 60 * 60 - 1); // 100 hours - 1 second.
int minutes = seconds / 60;
seconds %= 60;
const int hours = minutes / 60;
minutes %= 60;
const base::string16 hours_str = IntToTwoDigitString(hours);
const base::string16 minutes_str = IntToTwoDigitString(minutes);
const base::string16 seconds_str = IntToTwoDigitString(seconds);
const SkColor color =
limit_state == TraySessionLengthLimit::LIMIT_EXPIRING_SOON ?
kRemainingTimeExpiringSoonColor : kRemainingTimeColor;
if (horizontal_layout_label_) {
horizontal_layout_label_->SetText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_REMAINING_SESSION_TIME,
hours_str, minutes_str, seconds_str));
horizontal_layout_label_->SetEnabledColor(color);
} else if (vertical_layout_label_hours_left_) {
vertical_layout_label_hours_left_->SetText(hours_str.substr(0, 1));
vertical_layout_label_hours_right_->SetText(hours_str.substr(1, 1));
vertical_layout_label_minutes_left_->SetText(minutes_str.substr(0, 1));
vertical_layout_label_minutes_right_->SetText(minutes_str.substr(1, 1));
vertical_layout_label_seconds_left_->SetText(seconds_str.substr(0, 1));
vertical_layout_label_seconds_right_->SetText(seconds_str.substr(1, 1));
vertical_layout_label_hours_left_->SetEnabledColor(color);
vertical_layout_label_hours_right_->SetEnabledColor(color);
vertical_layout_label_minutes_left_->SetEnabledColor(color);
vertical_layout_label_minutes_right_->SetEnabledColor(color);
vertical_layout_label_seconds_left_->SetEnabledColor(color);
vertical_layout_label_seconds_right_->SetEnabledColor(color);
}
Layout();
SetVisible(true);
}
void RemainingSessionTimeTrayView::SetBorderFromAlignment(
ShelfAlignment shelf_alignment) {
if (shelf_alignment == SHELF_ALIGNMENT_BOTTOM ||
shelf_alignment == SHELF_ALIGNMENT_TOP) {
SetBorder(views::Border::CreateEmptyBorder(
0,
kTrayLabelItemHorizontalPaddingBottomAlignment,
0,
kTrayLabelItemHorizontalPaddingBottomAlignment));
} else {
SetBorder(views::Border::NullBorder());
}
}
} // namespace tray
// static // static
const char TraySessionLengthLimit::kNotificationId[] = const char TraySessionLengthLimit::kNotificationId[] =
"chrome://session/timeout"; "chrome://session/timeout";
TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray) TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray)
: SystemTrayItem(system_tray), : SystemTrayItem(system_tray),
tray_view_(NULL), limit_state_(LIMIT_NONE),
limit_state_(LIMIT_NONE) { last_limit_state_(LIMIT_NONE),
tray_bubble_view_(NULL) {
Shell::GetInstance()->system_tray_notifier()-> Shell::GetInstance()->system_tray_notifier()->
AddSessionLengthLimitObserver(this); AddSessionLengthLimitObserver(this);
Update(); Update();
...@@ -297,21 +56,23 @@ TraySessionLengthLimit::~TraySessionLengthLimit() { ...@@ -297,21 +56,23 @@ TraySessionLengthLimit::~TraySessionLengthLimit() {
RemoveSessionLengthLimitObserver(this); RemoveSessionLengthLimitObserver(this);
} }
views::View* TraySessionLengthLimit::CreateTrayView(user::LoginStatus status) { // Add view to tray bubble.
CHECK(tray_view_ == NULL); views::View* TraySessionLengthLimit::CreateDefaultView(
tray_view_ = new tray::RemainingSessionTimeTrayView( user::LoginStatus status) {
this, system_tray()->shelf_alignment()); CHECK(!tray_bubble_view_);
return tray_view_; UpdateState();
if (limit_state_ == LIMIT_NONE)
return NULL;
tray_bubble_view_ = new LabelTrayView(
NULL /* click_listener */,
IDR_AURA_UBER_TRAY_BUBBLE_SESSION_LENGTH_LIMIT);
tray_bubble_view_->SetMessage(ComposeTrayBubbleMessage());
return tray_bubble_view_;
} }
void TraySessionLengthLimit::DestroyTrayView() { // View has been removed from tray bubble.
tray_view_ = NULL; void TraySessionLengthLimit::DestroyDefaultView() {
} tray_bubble_view_ = NULL;
void TraySessionLengthLimit::UpdateAfterShelfAlignmentChange(
ShelfAlignment alignment) {
if (tray_view_)
tray_view_->UpdateClockLayout(alignment);
} }
void TraySessionLengthLimit::OnSessionStartTimeChanged() { void TraySessionLengthLimit::OnSessionStartTimeChanged() {
...@@ -322,68 +83,118 @@ void TraySessionLengthLimit::OnSessionLengthLimitChanged() { ...@@ -322,68 +83,118 @@ void TraySessionLengthLimit::OnSessionLengthLimitChanged() {
Update(); Update();
} }
TraySessionLengthLimit::LimitState void TraySessionLengthLimit::Update() {
TraySessionLengthLimit::GetLimitState() const { UpdateState();
return limit_state_; UpdateNotification();
} UpdateTrayBubbleView();
base::TimeDelta TraySessionLengthLimit::GetRemainingSessionTime() const {
return remaining_session_time_;
} }
void TraySessionLengthLimit::Update() { void TraySessionLengthLimit::UpdateState() {
SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
const LimitState previous_limit_state = limit_state_; if (delegate->GetSessionStartTime(&session_start_time_) &&
if (!delegate->GetSessionStartTime(&session_start_time_) || delegate->GetSessionLengthLimit(&time_limit_)) {
!delegate->GetSessionLengthLimit(&limit_)) { const base::TimeDelta expiring_soon_threshold(
remaining_session_time_ = base::TimeDelta(); base::TimeDelta::FromMinutes(kExpiringSoonThresholdInMinutes));
limit_state_ = LIMIT_NONE;
timer_.reset();
} else {
remaining_session_time_ = std::max( remaining_session_time_ = std::max(
limit_ - (base::TimeTicks::Now() - session_start_time_), time_limit_ - (base::TimeTicks::Now() - session_start_time_),
base::TimeDelta()); base::TimeDelta());
limit_state_ = remaining_session_time_.InSeconds() <= limit_state_ = remaining_session_time_ <= expiring_soon_threshold ?
kExpiringSoonThresholdInSeconds ? LIMIT_EXPIRING_SOON : LIMIT_SET; LIMIT_EXPIRING_SOON : LIMIT_SET;
if (!timer_) if (!timer_)
timer_.reset(new base::RepeatingTimer<TraySessionLengthLimit>); timer_.reset(new base::RepeatingTimer<TraySessionLengthLimit>);
if (!timer_->IsRunning()) { if (!timer_->IsRunning()) {
// Start a timer that will update the remaining session time every second.
timer_->Start(FROM_HERE, timer_->Start(FROM_HERE,
base::TimeDelta::FromSeconds(1), base::TimeDelta::FromMilliseconds(
kTimerIntervalInMilliseconds),
this, this,
&TraySessionLengthLimit::Update); &TraySessionLengthLimit::Update);
} }
} else {
remaining_session_time_ = base::TimeDelta();
limit_state_ = LIMIT_NONE;
timer_.reset();
} }
}
switch (limit_state_) { void TraySessionLengthLimit::UpdateNotification() {
case LIMIT_NONE: message_center::MessageCenter* message_center =
message_center::MessageCenter::Get()->RemoveNotification( message_center::MessageCenter::Get();
kNotificationId, false /* by_user */);
break; // If state hasn't changed and the notification has already been acknowledged,
case LIMIT_SET: // we won't re-create it.
CreateOrUpdateNotification( if (limit_state_ == last_limit_state_ &&
kNotificationId, !message_center->HasNotification(kNotificationId)) {
remaining_session_time_, return;
previous_limit_state == LIMIT_NONE); }
break;
case LIMIT_EXPIRING_SOON: // After state change, any possibly existing notification is removed to make
CreateOrUpdateNotification( // sure it is re-shown even if it had been acknowledged by the user before
kNotificationId, // (and in the rare case of state change towards LIMIT_NONE to make the
remaining_session_time_, // notification disappear).
previous_limit_state == LIMIT_NONE || if (limit_state_ != last_limit_state_ &&
previous_limit_state == LIMIT_SET); message_center->HasNotification(kNotificationId)) {
break; message_center::MessageCenter::Get()->RemoveNotification(
kNotificationId, false /* by_user */);
}
// For LIMIT_NONE, there's nothing more to do.
if (limit_state_ == LIMIT_NONE) {
last_limit_state_ = limit_state_;
return;
} }
// Update the tray view last so that it can check whether the notification ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
// view is currently visible or not. message_center::RichNotificationData data;
if (tray_view_) data.should_make_spoken_feedback_for_popup_updates =
tray_view_->Update(); (limit_state_ != last_limit_state_);
scoped_ptr<message_center::Notification> notification(
new message_center::Notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
kNotificationId,
base::string16() /* title */,
ComposeNotificationMessage() /* message */,
bundle.GetImageNamed(
IDR_AURA_UBER_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT),
base::string16() /* display_source */,
message_center::NotifierId(
message_center::NotifierId::SYSTEM_COMPONENT,
system_notifier::kNotifierSessionLengthTimeout),
data,
NULL /* delegate */));
notification->SetSystemPriority();
if (message_center->HasNotification(kNotificationId))
message_center->UpdateNotification(kNotificationId, notification.Pass());
else
message_center->AddNotification(notification.Pass());
last_limit_state_ = limit_state_;
}
void TraySessionLengthLimit::UpdateTrayBubbleView() const {
if (!tray_bubble_view_)
return;
if (limit_state_ == LIMIT_NONE)
tray_bubble_view_->SetMessage(base::string16());
else
tray_bubble_view_->SetMessage(ComposeTrayBubbleMessage());
tray_bubble_view_->Layout();
}
base::string16 TraySessionLengthLimit::ComposeNotificationMessage() const {
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT,
ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
ui::TimeFormat::LENGTH_LONG,
10,
remaining_session_time_));
} }
bool TraySessionLengthLimit::IsTrayViewVisibleForTest() { base::string16 TraySessionLengthLimit::ComposeTrayBubbleMessage() const {
return tray_view_ && tray_view_->visible(); return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT,
ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
ui::TimeFormat::LENGTH_LONG,
10,
remaining_session_time_));
} }
} // namespace ash } // namespace ash
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
...@@ -18,14 +19,12 @@ namespace test { ...@@ -18,14 +19,12 @@ namespace test {
class TraySessionLengthLimitTest; class TraySessionLengthLimitTest;
} }
namespace tray { class LabelTrayView;
class RemainingSessionTimeTrayView;
}
// Adds a countdown timer to the system tray if the session length is limited. // Adds a countdown timer to the system tray if the session length is limited.
class ASH_EXPORT TraySessionLengthLimit : public SystemTrayItem, class ASH_EXPORT TraySessionLengthLimit : public SystemTrayItem,
public SessionLengthLimitObserver { public SessionLengthLimitObserver {
public: public:
enum LimitState { enum LimitState {
LIMIT_NONE, LIMIT_NONE,
LIMIT_SET, LIMIT_SET,
...@@ -36,33 +35,40 @@ class ASH_EXPORT TraySessionLengthLimit : public SystemTrayItem, ...@@ -36,33 +35,40 @@ class ASH_EXPORT TraySessionLengthLimit : public SystemTrayItem,
virtual ~TraySessionLengthLimit(); virtual ~TraySessionLengthLimit();
// SystemTrayItem: // SystemTrayItem:
virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE; virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE;
virtual void DestroyTrayView() OVERRIDE; virtual void DestroyDefaultView() OVERRIDE;
virtual void UpdateAfterShelfAlignmentChange(
ShelfAlignment alignment) OVERRIDE;
// SessionLengthLimitObserver: // SessionLengthLimitObserver:
virtual void OnSessionStartTimeChanged() OVERRIDE; virtual void OnSessionStartTimeChanged() OVERRIDE;
virtual void OnSessionLengthLimitChanged() OVERRIDE; virtual void OnSessionLengthLimitChanged() OVERRIDE;
LimitState GetLimitState() const;
base::TimeDelta GetRemainingSessionTime() const;
private: private:
friend class test::TraySessionLengthLimitTest; friend class test::TraySessionLengthLimitTest;
static const char kNotificationId[]; static const char kNotificationId[];
// Update state, notification and tray bubble view. Called by the
// RepeatingTimer in regular intervals and also by OnSession*Changed().
void Update(); void Update();
bool IsTrayViewVisibleForTest(); // Recalculate |limit_state_| and |remaining_session_time_|.
void UpdateState();
void UpdateNotification();
void UpdateTrayBubbleView() const;
tray::RemainingSessionTimeTrayView* tray_view_; // These require that the state has been updated before.
base::string16 ComposeNotificationMessage() const;
base::string16 ComposeTrayBubbleMessage() const;
LimitState limit_state_;
base::TimeTicks session_start_time_; base::TimeTicks session_start_time_;
base::TimeDelta limit_; base::TimeDelta time_limit_;
base::TimeDelta remaining_session_time_; base::TimeDelta remaining_session_time_;
LimitState limit_state_; // Current state.
LimitState last_limit_state_; // State of last notification update.
LabelTrayView* tray_bubble_view_;
scoped_ptr<base::RepeatingTimer<TraySessionLengthLimit> > timer_; scoped_ptr<base::RepeatingTimer<TraySessionLengthLimit> > timer_;
DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimit); DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimit);
......
...@@ -59,17 +59,13 @@ class TraySessionLengthLimitTest : public AshTestBase { ...@@ -59,17 +59,13 @@ class TraySessionLengthLimitTest : public AshTestBase {
void RemoveNotification() { void RemoveNotification() {
message_center::MessageCenter::Get()->RemoveNotification( message_center::MessageCenter::Get()->RemoveNotification(
TraySessionLengthLimit::kNotificationId, true /* by_user */); TraySessionLengthLimit::kNotificationId, false /* by_user */);
} }
TraySessionLengthLimit* tray_session_length_limit() { TraySessionLengthLimit* tray_session_length_limit() {
return tray_session_length_limit_; return tray_session_length_limit_;
} }
bool IsTrayViewVisible() {
return tray_session_length_limit_->IsTrayViewVisibleForTest();
}
private: private:
// Weak reference, owned by the SystemTray. // Weak reference, owned by the SystemTray.
TraySessionLengthLimit* tray_session_length_limit_; TraySessionLengthLimit* tray_session_length_limit_;
...@@ -77,41 +73,6 @@ class TraySessionLengthLimitTest : public AshTestBase { ...@@ -77,41 +73,6 @@ class TraySessionLengthLimitTest : public AshTestBase {
DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimitTest); DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimitTest);
}; };
TEST_F(TraySessionLengthLimitTest, TrayView) {
// No session limit.
EXPECT_FALSE(IsTrayViewVisible());
// Limit is 15 min.
UpdateSessionLengthLimitInMin(15);
EXPECT_EQ(TraySessionLengthLimit::LIMIT_SET,
tray_session_length_limit()->GetLimitState());
EXPECT_TRUE(IsTrayViewVisible());
// Limit is 3 min.
UpdateSessionLengthLimitInMin(3);
EXPECT_EQ(TraySessionLengthLimit::LIMIT_EXPIRING_SOON,
tray_session_length_limit()->GetLimitState());
EXPECT_TRUE(IsTrayViewVisible());
// Nothing left.
UpdateSessionLengthLimitInMin(0);
EXPECT_EQ(TraySessionLengthLimit::LIMIT_EXPIRING_SOON,
tray_session_length_limit()->GetLimitState());
EXPECT_TRUE(IsTrayViewVisible());
// Checks the behavior in case the limit goes negative.
UpdateSessionLengthLimitInMin(-5);
EXPECT_EQ(TraySessionLengthLimit::LIMIT_EXPIRING_SOON,
tray_session_length_limit()->GetLimitState());
EXPECT_TRUE(IsTrayViewVisible());
// Clears the session length limit, the TrayView should get invisible.
ClearSessionLengthLimit();
ASSERT_EQ(TraySessionLengthLimit::LIMIT_NONE,
tray_session_length_limit()->GetLimitState());
EXPECT_FALSE(IsTrayViewVisible());
}
TEST_F(TraySessionLengthLimitTest, Notification) { TEST_F(TraySessionLengthLimitTest, Notification) {
// No notifications when no session limit. // No notifications when no session limit.
EXPECT_FALSE(GetNotification()); EXPECT_FALSE(GetNotification());
...@@ -121,7 +82,7 @@ TEST_F(TraySessionLengthLimitTest, Notification) { ...@@ -121,7 +82,7 @@ TEST_F(TraySessionLengthLimitTest, Notification) {
message_center::Notification* notification = GetNotification(); message_center::Notification* notification = GetNotification();
EXPECT_TRUE(notification); EXPECT_TRUE(notification);
EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
base::string16 first_content = notification->title(); base::string16 first_content = notification->message();
// Should read the content. // Should read the content.
EXPECT_TRUE(notification->rich_notification_data(). EXPECT_TRUE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates); should_make_spoken_feedback_for_popup_updates);
...@@ -132,7 +93,7 @@ TEST_F(TraySessionLengthLimitTest, Notification) { ...@@ -132,7 +93,7 @@ TEST_F(TraySessionLengthLimitTest, Notification) {
EXPECT_TRUE(notification); EXPECT_TRUE(notification);
EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
// The content should be updated. // The content should be updated.
EXPECT_NE(first_content, notification->title()); EXPECT_NE(first_content, notification->message());
// Should NOT read, because just update the remaining time. // Should NOT read, because just update the remaining time.
EXPECT_FALSE(notification->rich_notification_data(). EXPECT_FALSE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates); should_make_spoken_feedback_for_popup_updates);
...@@ -146,15 +107,14 @@ TEST_F(TraySessionLengthLimitTest, Notification) { ...@@ -146,15 +107,14 @@ TEST_F(TraySessionLengthLimitTest, Notification) {
EXPECT_TRUE(notification->rich_notification_data(). EXPECT_TRUE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates); should_make_spoken_feedback_for_popup_updates);
// Session length limit is updated to longer. This should not read the // Session length limit is updated to longer: 15 min.
// notification content again.
UpdateSessionLengthLimitInMin(15); UpdateSessionLengthLimitInMin(15);
notification = GetNotification(); notification = GetNotification();
EXPECT_TRUE(notification); EXPECT_TRUE(notification);
EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
// Should not read again because the state has changed to longer. // Should read again because an increase of the remaining time is noteworthy.
EXPECT_FALSE(notification->rich_notification_data(). EXPECT_TRUE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates); should_make_spoken_feedback_for_popup_updates);
// Clears the limit: the notification should be gone. // Clears the limit: the notification should be gone.
ClearSessionLengthLimit(); ClearSessionLengthLimit();
...@@ -162,32 +122,37 @@ TEST_F(TraySessionLengthLimitTest, Notification) { ...@@ -162,32 +122,37 @@ TEST_F(TraySessionLengthLimitTest, Notification) {
} }
TEST_F(TraySessionLengthLimitTest, RemoveNotification) { TEST_F(TraySessionLengthLimitTest, RemoveNotification) {
message_center::Notification* notification;
// Limit is 15 min. // Limit is 15 min.
UpdateSessionLengthLimitInMin(15); UpdateSessionLengthLimitInMin(15);
EXPECT_TRUE(GetNotification()); EXPECT_TRUE(GetNotification());
// Limit is 14 min.
UpdateSessionLengthLimitInMin(14);
EXPECT_TRUE(GetNotification());
// Removes the notification. // Removes the notification.
RemoveNotification(); RemoveNotification();
EXPECT_FALSE(GetNotification()); EXPECT_FALSE(GetNotification());
// Limit is 13 min. The notification should not re-appear. // Limit is 10 min. The notification should not re-appear.
UpdateSessionLengthLimitInMin(13); UpdateSessionLengthLimitInMin(10);
EXPECT_FALSE(GetNotification()); EXPECT_FALSE(GetNotification());
// Limit is 3 min. The notification should re-appear because of state change. // Limit is 3 min. The notification should re-appear and should be re-read
// because of state change.
UpdateSessionLengthLimitInMin(3); UpdateSessionLengthLimitInMin(3);
EXPECT_TRUE(GetNotification()); notification = GetNotification();
EXPECT_TRUE(notification);
EXPECT_TRUE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates);
RemoveNotification(); RemoveNotification();
// Session length limit is updated to longer state. This should not re-appear // Session length limit is updated to longer state. Notification should
// the notification. // re-appear and be re-read.
UpdateSessionLengthLimitInMin(15); UpdateSessionLengthLimitInMin(15);
EXPECT_FALSE(GetNotification()); notification = GetNotification();
EXPECT_TRUE(notification);
EXPECT_TRUE(notification->rich_notification_data().
should_make_spoken_feedback_for_popup_updates);
} }
} // namespace test } // namespace test
......
...@@ -153,9 +153,6 @@ void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) { ...@@ -153,9 +153,6 @@ void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
} }
void SystemTray::CreateItems(SystemTrayDelegate* delegate) { void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
#if defined(OS_CHROMEOS)
AddTrayItem(new TraySessionLengthLimit(this));
#endif
#if !defined(OS_WIN) #if !defined(OS_WIN)
// Create user items for each possible user. // Create user items for each possible user.
ash::Shell* shell = ash::Shell::GetInstance(); ash::Shell* shell = ash::Shell::GetInstance();
...@@ -175,6 +172,7 @@ void SystemTray::CreateItems(SystemTrayDelegate* delegate) { ...@@ -175,6 +172,7 @@ void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
tray_date_ = new TrayDate(this); tray_date_ = new TrayDate(this);
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
AddTrayItem(new TraySessionLengthLimit(this));
AddTrayItem(new TrayEnterprise(this)); AddTrayItem(new TrayEnterprise(this));
AddTrayItem(new TrayLocallyManagedUser(this)); AddTrayItem(new TrayLocallyManagedUser(this));
AddTrayItem(new TrayIME(this)); AddTrayItem(new TrayIME(this));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment