blob: d6084004414ba8c634f08e7d8ed1a92b272dab57 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/chromeos/session/tray_session_length_limit.h"
#include <algorithm>
#include "ash/shell.h"
#include "ash/system/chromeos/label_tray_view.h"
#include "ash/system/system_notifier.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification.h"
#include "ui/views/view.h"
namespace ash {
namespace {
// If the remaining session time falls below this threshold, the user should be
// informed that the session is about to expire.
const int kExpiringSoonThresholdInMinutes = 5;
// Use 500ms interval for updates to notification and tray bubble to reduce the
// likelihood of a user-visible skip in high load situations (as might happen
// with 1000ms).
const int kTimerIntervalInMilliseconds = 500;
} // namespace
// static
const char TraySessionLengthLimit::kNotificationId[] =
"chrome://session/timeout";
TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray)
: SystemTrayItem(system_tray),
limit_state_(LIMIT_NONE),
last_limit_state_(LIMIT_NONE),
tray_bubble_view_(NULL) {
Shell::GetInstance()->system_tray_notifier()->
AddSessionLengthLimitObserver(this);
Update();
}
TraySessionLengthLimit::~TraySessionLengthLimit() {
Shell::GetInstance()->system_tray_notifier()->
RemoveSessionLengthLimitObserver(this);
}
// Add view to tray bubble.
views::View* TraySessionLengthLimit::CreateDefaultView(
user::LoginStatus status) {
CHECK(!tray_bubble_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_;
}
// View has been removed from tray bubble.
void TraySessionLengthLimit::DestroyDefaultView() {
tray_bubble_view_ = NULL;
}
void TraySessionLengthLimit::OnSessionStartTimeChanged() {
Update();
}
void TraySessionLengthLimit::OnSessionLengthLimitChanged() {
Update();
}
void TraySessionLengthLimit::Update() {
UpdateState();
UpdateNotification();
UpdateTrayBubbleView();
}
void TraySessionLengthLimit::UpdateState() {
SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
if (delegate->GetSessionStartTime(&session_start_time_) &&
delegate->GetSessionLengthLimit(&time_limit_)) {
const base::TimeDelta expiring_soon_threshold(
base::TimeDelta::FromMinutes(kExpiringSoonThresholdInMinutes));
remaining_session_time_ = std::max(
time_limit_ - (base::TimeTicks::Now() - session_start_time_),
base::TimeDelta());
limit_state_ = remaining_session_time_ <= expiring_soon_threshold ?
LIMIT_EXPIRING_SOON : LIMIT_SET;
if (!timer_)
timer_.reset(new base::RepeatingTimer<TraySessionLengthLimit>);
if (!timer_->IsRunning()) {
timer_->Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(
kTimerIntervalInMilliseconds),
this,
&TraySessionLengthLimit::Update);
}
} else {
remaining_session_time_ = base::TimeDelta();
limit_state_ = LIMIT_NONE;
timer_.reset();
}
}
void TraySessionLengthLimit::UpdateNotification() {
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
// If state hasn't changed and the notification has already been acknowledged,
// we won't re-create it.
if (limit_state_ == last_limit_state_ &&
!message_center->FindVisibleNotificationById(kNotificationId)) {
return;
}
// After state change, any possibly existing notification is removed to make
// sure it is re-shown even if it had been acknowledged by the user before
// (and in the rare case of state change towards LIMIT_NONE to make the
// notification disappear).
if (limit_state_ != last_limit_state_ &&
message_center->FindVisibleNotificationById(kNotificationId)) {
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;
}
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
message_center::RichNotificationData data;
data.should_make_spoken_feedback_for_popup_updates =
(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->FindVisibleNotificationById(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_));
}
base::string16 TraySessionLengthLimit::ComposeTrayBubbleMessage() const {
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