Home | History | Annotate | Download | only in session
      1 // Copyright 2014 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/system/chromeos/session/tray_session_length_limit.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/shell.h"
     10 #include "ash/system/chromeos/label_tray_view.h"
     11 #include "ash/system/system_notifier.h"
     12 #include "ash/system/tray/system_tray.h"
     13 #include "ash/system/tray/system_tray_delegate.h"
     14 #include "ash/system/tray/system_tray_notifier.h"
     15 #include "base/logging.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "grit/ash_resources.h"
     18 #include "grit/ash_strings.h"
     19 #include "ui/base/l10n/l10n_util.h"
     20 #include "ui/base/l10n/time_format.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "ui/message_center/message_center.h"
     23 #include "ui/message_center/notification.h"
     24 #include "ui/views/view.h"
     25 
     26 namespace ash {
     27 namespace {
     28 
     29 // If the remaining session time falls below this threshold, the user should be
     30 // informed that the session is about to expire.
     31 const int kExpiringSoonThresholdInMinutes = 5;
     32 
     33 // Use 500ms interval for updates to notification and tray bubble to reduce the
     34 // likelihood of a user-visible skip in high load situations (as might happen
     35 // with 1000ms).
     36 const int kTimerIntervalInMilliseconds = 500;
     37 
     38 }  // namespace
     39 
     40 // static
     41 const char TraySessionLengthLimit::kNotificationId[] =
     42     "chrome://session/timeout";
     43 
     44 TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray)
     45     : SystemTrayItem(system_tray),
     46       limit_state_(LIMIT_NONE),
     47       last_limit_state_(LIMIT_NONE),
     48       tray_bubble_view_(NULL) {
     49   Shell::GetInstance()->system_tray_notifier()->
     50       AddSessionLengthLimitObserver(this);
     51   Update();
     52 }
     53 
     54 TraySessionLengthLimit::~TraySessionLengthLimit() {
     55   Shell::GetInstance()->system_tray_notifier()->
     56       RemoveSessionLengthLimitObserver(this);
     57 }
     58 
     59 // Add view to tray bubble.
     60 views::View* TraySessionLengthLimit::CreateDefaultView(
     61     user::LoginStatus status) {
     62   CHECK(!tray_bubble_view_);
     63   UpdateState();
     64   if (limit_state_ == LIMIT_NONE)
     65     return NULL;
     66   tray_bubble_view_ = new LabelTrayView(
     67       NULL /* click_listener */,
     68       IDR_AURA_UBER_TRAY_BUBBLE_SESSION_LENGTH_LIMIT);
     69   tray_bubble_view_->SetMessage(ComposeTrayBubbleMessage());
     70   return tray_bubble_view_;
     71 }
     72 
     73 // View has been removed from tray bubble.
     74 void TraySessionLengthLimit::DestroyDefaultView() {
     75   tray_bubble_view_ = NULL;
     76 }
     77 
     78 void TraySessionLengthLimit::OnSessionStartTimeChanged() {
     79   Update();
     80 }
     81 
     82 void TraySessionLengthLimit::OnSessionLengthLimitChanged() {
     83   Update();
     84 }
     85 
     86 void TraySessionLengthLimit::Update() {
     87   UpdateState();
     88   UpdateNotification();
     89   UpdateTrayBubbleView();
     90 }
     91 
     92 void TraySessionLengthLimit::UpdateState() {
     93   SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
     94   if (delegate->GetSessionStartTime(&session_start_time_) &&
     95       delegate->GetSessionLengthLimit(&time_limit_)) {
     96     const base::TimeDelta expiring_soon_threshold(
     97         base::TimeDelta::FromMinutes(kExpiringSoonThresholdInMinutes));
     98     remaining_session_time_ = std::max(
     99         time_limit_ - (base::TimeTicks::Now() - session_start_time_),
    100         base::TimeDelta());
    101     limit_state_ = remaining_session_time_ <= expiring_soon_threshold ?
    102         LIMIT_EXPIRING_SOON : LIMIT_SET;
    103     if (!timer_)
    104       timer_.reset(new base::RepeatingTimer<TraySessionLengthLimit>);
    105     if (!timer_->IsRunning()) {
    106       timer_->Start(FROM_HERE,
    107                     base::TimeDelta::FromMilliseconds(
    108                         kTimerIntervalInMilliseconds),
    109                     this,
    110                     &TraySessionLengthLimit::Update);
    111     }
    112   } else {
    113     remaining_session_time_ = base::TimeDelta();
    114     limit_state_ = LIMIT_NONE;
    115     timer_.reset();
    116   }
    117 }
    118 
    119 void TraySessionLengthLimit::UpdateNotification() {
    120   message_center::MessageCenter* message_center =
    121       message_center::MessageCenter::Get();
    122 
    123   // If state hasn't changed and the notification has already been acknowledged,
    124   // we won't re-create it.
    125   if (limit_state_ == last_limit_state_ &&
    126       !message_center->FindVisibleNotificationById(kNotificationId)) {
    127     return;
    128   }
    129 
    130   // After state change, any possibly existing notification is removed to make
    131   // sure it is re-shown even if it had been acknowledged by the user before
    132   // (and in the rare case of state change towards LIMIT_NONE to make the
    133   // notification disappear).
    134   if (limit_state_ != last_limit_state_ &&
    135       message_center->FindVisibleNotificationById(kNotificationId)) {
    136     message_center::MessageCenter::Get()->RemoveNotification(
    137         kNotificationId, false /* by_user */);
    138   }
    139 
    140   // For LIMIT_NONE, there's nothing more to do.
    141   if (limit_state_ == LIMIT_NONE) {
    142     last_limit_state_ = limit_state_;
    143     return;
    144   }
    145 
    146   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
    147   message_center::RichNotificationData data;
    148   data.should_make_spoken_feedback_for_popup_updates =
    149       (limit_state_ != last_limit_state_);
    150   scoped_ptr<message_center::Notification> notification(
    151       new message_center::Notification(
    152           message_center::NOTIFICATION_TYPE_SIMPLE,
    153           kNotificationId,
    154           base::string16() /* title */,
    155           ComposeNotificationMessage() /* message */,
    156           bundle.GetImageNamed(
    157               IDR_AURA_UBER_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT),
    158           base::string16() /* display_source */,
    159           message_center::NotifierId(
    160               message_center::NotifierId::SYSTEM_COMPONENT,
    161               system_notifier::kNotifierSessionLengthTimeout),
    162           data,
    163           NULL /* delegate */));
    164   notification->SetSystemPriority();
    165   if (message_center->FindVisibleNotificationById(kNotificationId))
    166     message_center->UpdateNotification(kNotificationId, notification.Pass());
    167   else
    168     message_center->AddNotification(notification.Pass());
    169   last_limit_state_ = limit_state_;
    170 }
    171 
    172 void TraySessionLengthLimit::UpdateTrayBubbleView() const {
    173   if (!tray_bubble_view_)
    174     return;
    175   if (limit_state_ == LIMIT_NONE)
    176     tray_bubble_view_->SetMessage(base::string16());
    177   else
    178     tray_bubble_view_->SetMessage(ComposeTrayBubbleMessage());
    179   tray_bubble_view_->Layout();
    180 }
    181 
    182 base::string16 TraySessionLengthLimit::ComposeNotificationMessage() const {
    183   return l10n_util::GetStringFUTF16(
    184       IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT,
    185       ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
    186                                ui::TimeFormat::LENGTH_LONG,
    187                                10,
    188                                remaining_session_time_));
    189 }
    190 
    191 base::string16 TraySessionLengthLimit::ComposeTrayBubbleMessage() const {
    192   return l10n_util::GetStringFUTF16(
    193       IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT,
    194       ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
    195                                ui::TimeFormat::LENGTH_LONG,
    196                                10,
    197                                remaining_session_time_));
    198 }
    199 
    200 }  // namespace ash
    201