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. 4 5#include "ash/system/tray_update.h" 6 7#include "ash/root_window_controller.h" 8#include "ash/shelf/shelf_layout_manager.h" 9#include "ash/shelf/shelf_widget.h" 10#include "ash/shell.h" 11#include "ash/system/status_area_widget.h" 12#include "ash/system/tray/fixed_sized_image_view.h" 13#include "ash/system/tray/system_tray.h" 14#include "ash/system/tray/system_tray_delegate.h" 15#include "ash/system/tray/system_tray_notifier.h" 16#include "ash/system/tray/tray_constants.h" 17#include "base/time/time.h" 18#include "base/timer/timer.h" 19#include "grit/ash_resources.h" 20#include "grit/ash_strings.h" 21#include "ui/aura/window.h" 22#include "ui/base/resource/resource_bundle.h" 23#include "ui/compositor/layer.h" 24#include "ui/compositor/layer_animation_observer.h" 25#include "ui/compositor/layer_animation_sequence.h" 26#include "ui/gfx/image/image.h" 27#include "ui/views/controls/image_view.h" 28#include "ui/views/controls/label.h" 29#include "ui/views/layout/box_layout.h" 30#include "ui/views/widget/widget.h" 31 32namespace { 33 34// How many seconds should we wait before showing the nag reminder? 35const int kUpdateNaggingTimeSeconds = 24 * 60 * 60; 36 37// How long should the nag reminder be displayed? 38const int kShowUpdateNaggerForSeconds = 15; 39 40int DecideResource(ash::UpdateObserver::UpdateSeverity severity, bool dark) { 41 switch (severity) { 42 case ash::UpdateObserver::UPDATE_NORMAL: 43 return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK: 44 IDR_AURA_UBER_TRAY_UPDATE; 45 46 case ash::UpdateObserver::UPDATE_LOW_GREEN: 47 return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_GREEN : 48 IDR_AURA_UBER_TRAY_UPDATE_GREEN; 49 50 case ash::UpdateObserver::UPDATE_HIGH_ORANGE: 51 return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_ORANGE : 52 IDR_AURA_UBER_TRAY_UPDATE_ORANGE; 53 54 case ash::UpdateObserver::UPDATE_SEVERE_RED: 55 return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_RED : 56 IDR_AURA_UBER_TRAY_UPDATE_RED; 57 } 58 59 NOTREACHED() << "Unknown update severity level."; 60 return 0; 61} 62 63class UpdateView : public ash::ActionableView { 64 public: 65 explicit UpdateView(ash::UpdateObserver::UpdateSeverity severity) { 66 SetLayoutManager(new 67 views::BoxLayout(views::BoxLayout::kHorizontal, 68 ash::kTrayPopupPaddingHorizontal, 0, 69 ash::kTrayPopupPaddingBetweenItems)); 70 71 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 72 views::ImageView* image = 73 new ash::FixedSizedImageView(0, ash::kTrayPopupItemHeight); 74 image->SetImage(bundle.GetImageNamed(DecideResource(severity, true)). 75 ToImageSkia()); 76 77 AddChildView(image); 78 AddChildView(new views::Label( 79 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_UPDATE))); 80 SetAccessibleName(bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_UPDATE)); 81 } 82 83 virtual ~UpdateView() {} 84 85 private: 86 // Overridden from ActionableView. 87 virtual bool PerformAction(const ui::Event& event) OVERRIDE { 88 ash::Shell::GetInstance()-> 89 system_tray_delegate()->RequestRestartForUpdate(); 90 return true; 91 } 92 93 DISALLOW_COPY_AND_ASSIGN(UpdateView); 94}; 95 96} 97 98namespace ash { 99namespace tray { 100 101class UpdateNagger : public ui::LayerAnimationObserver { 102 public: 103 explicit UpdateNagger(SystemTrayItem* owner) 104 : owner_(owner) { 105 RestartTimer(); 106 owner_->system_tray()->GetWidget()->GetNativeView()->layer()-> 107 GetAnimator()->AddObserver(this); 108 } 109 110 virtual ~UpdateNagger() { 111 StatusAreaWidget* status_area = 112 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget(); 113 if (status_area) { 114 status_area->system_tray()->GetWidget()->GetNativeView()->layer()-> 115 GetAnimator()->RemoveObserver(this); 116 } 117 } 118 119 void RestartTimer() { 120 timer_.Stop(); 121 timer_.Start(FROM_HERE, 122 base::TimeDelta::FromSeconds(kUpdateNaggingTimeSeconds), 123 this, 124 &UpdateNagger::Nag); 125 } 126 127 private: 128 void Nag() { 129 owner_->PopupDetailedView(kShowUpdateNaggerForSeconds, false); 130 } 131 132 // Overridden from ui::LayerAnimationObserver. 133 virtual void OnLayerAnimationEnded( 134 ui::LayerAnimationSequence* sequence) OVERRIDE { 135 // TODO(oshima): Find out if the updator will be shown on non 136 // primary display. 137 if (Shell::GetPrimaryRootWindowController()->shelf()->IsVisible()) 138 timer_.Stop(); 139 else if (!timer_.IsRunning()) 140 RestartTimer(); 141 } 142 143 virtual void OnLayerAnimationAborted( 144 ui::LayerAnimationSequence* sequence) OVERRIDE {} 145 146 virtual void OnLayerAnimationScheduled( 147 ui::LayerAnimationSequence* sequence) OVERRIDE {} 148 149 SystemTrayItem* owner_; 150 base::OneShotTimer<UpdateNagger> timer_; 151 152 DISALLOW_COPY_AND_ASSIGN(UpdateNagger); 153}; 154 155} // namespace tray 156 157TrayUpdate::TrayUpdate(SystemTray* system_tray) 158 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_UPDATE), 159 severity_(UpdateObserver::UPDATE_NORMAL) { 160 Shell::GetInstance()->system_tray_notifier()->AddUpdateObserver(this); 161} 162 163TrayUpdate::~TrayUpdate() { 164 Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this); 165} 166 167bool TrayUpdate::GetInitialVisibility() { 168 return Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade(); 169} 170 171views::View* TrayUpdate::CreateDefaultView(user::LoginStatus status) { 172 if (!Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade()) 173 return NULL; 174 return new UpdateView(severity_); 175} 176 177views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) { 178 return CreateDefaultView(status); 179} 180 181void TrayUpdate::DestroyDetailedView() { 182 if (nagger_) { 183 // The nagger was being displayed. Now that the detailed view is being 184 // closed, that means either the user clicks on it to restart, or the user 185 // didn't click on it to restart. In either case, start the timer to show 186 // the nag reminder again after the specified time. 187 nagger_->RestartTimer(); 188 } 189} 190 191void TrayUpdate::OnUpdateRecommended(UpdateObserver::UpdateSeverity severity) { 192 severity_ = severity; 193 SetImageFromResourceId(DecideResource(severity_, false)); 194 tray_view()->SetVisible(true); 195 if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() && 196 !nagger_.get()) { 197 // The shelf is not visible, and there is no nagger scheduled. 198 nagger_.reset(new tray::UpdateNagger(this)); 199 } 200} 201 202} // namespace ash 203