tray_session_length_limit.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 26namespace ash { 27namespace { 28 29// If the remaining session time falls below this threshold, the user should be 30// informed that the session is about to expire. 31const 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). 36const int kTimerIntervalInMilliseconds = 500; 37 38} // namespace 39 40// static 41const char TraySessionLengthLimit::kNotificationId[] = 42 "chrome://session/timeout"; 43 44TraySessionLengthLimit::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 54TraySessionLengthLimit::~TraySessionLengthLimit() { 55 Shell::GetInstance()->system_tray_notifier()-> 56 RemoveSessionLengthLimitObserver(this); 57} 58 59// Add view to tray bubble. 60views::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. 74void TraySessionLengthLimit::DestroyDefaultView() { 75 tray_bubble_view_ = NULL; 76} 77 78void TraySessionLengthLimit::OnSessionStartTimeChanged() { 79 Update(); 80} 81 82void TraySessionLengthLimit::OnSessionLengthLimitChanged() { 83 Update(); 84} 85 86void TraySessionLengthLimit::Update() { 87 UpdateState(); 88 UpdateNotification(); 89 UpdateTrayBubbleView(); 90} 91 92void 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 119void 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->HasNotification(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->HasNotification(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->HasNotification(kNotificationId)) 166 message_center->UpdateNotification(kNotificationId, notification.Pass()); 167 else 168 message_center->AddNotification(notification.Pass()); 169 last_limit_state_ = limit_state_; 170} 171 172void 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 182base::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 191base::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