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 32 namespace { 33 34 // How many seconds should we wait before showing the nag reminder? 35 const int kUpdateNaggingTimeSeconds = 24 * 60 * 60; 36 37 // How long should the nag reminder be displayed? 38 const int kShowUpdateNaggerForSeconds = 15; 39 40 int 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 63 class UpdateView : public ash::internal::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::internal::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 98 namespace ash { 99 namespace internal { 100 101 namespace tray { 102 103 class UpdateNagger : public ui::LayerAnimationObserver { 104 public: 105 explicit UpdateNagger(SystemTrayItem* owner) 106 : owner_(owner) { 107 RestartTimer(); 108 owner_->system_tray()->GetWidget()->GetNativeView()->layer()-> 109 GetAnimator()->AddObserver(this); 110 } 111 112 virtual ~UpdateNagger() { 113 internal::StatusAreaWidget* status_area = 114 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget(); 115 if (status_area) { 116 status_area->system_tray()->GetWidget()->GetNativeView()->layer()-> 117 GetAnimator()->RemoveObserver(this); 118 } 119 } 120 121 void RestartTimer() { 122 timer_.Stop(); 123 timer_.Start(FROM_HERE, 124 base::TimeDelta::FromSeconds(kUpdateNaggingTimeSeconds), 125 this, 126 &UpdateNagger::Nag); 127 } 128 129 private: 130 void Nag() { 131 owner_->PopupDetailedView(kShowUpdateNaggerForSeconds, false); 132 } 133 134 // Overridden from ui::LayerAnimationObserver. 135 virtual void OnLayerAnimationEnded( 136 ui::LayerAnimationSequence* sequence) OVERRIDE { 137 // TODO(oshima): Find out if the updator will be shown on non 138 // primary display. 139 if (Shell::GetPrimaryRootWindowController()->shelf()->IsVisible()) 140 timer_.Stop(); 141 else if (!timer_.IsRunning()) 142 RestartTimer(); 143 } 144 145 virtual void OnLayerAnimationAborted( 146 ui::LayerAnimationSequence* sequence) OVERRIDE {} 147 148 virtual void OnLayerAnimationScheduled( 149 ui::LayerAnimationSequence* sequence) OVERRIDE {} 150 151 SystemTrayItem* owner_; 152 base::OneShotTimer<UpdateNagger> timer_; 153 154 DISALLOW_COPY_AND_ASSIGN(UpdateNagger); 155 }; 156 157 } // namespace tray 158 159 TrayUpdate::TrayUpdate(SystemTray* system_tray) 160 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_UPDATE), 161 severity_(UpdateObserver::UPDATE_NORMAL) { 162 Shell::GetInstance()->system_tray_notifier()->AddUpdateObserver(this); 163 } 164 165 TrayUpdate::~TrayUpdate() { 166 Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this); 167 } 168 169 bool TrayUpdate::GetInitialVisibility() { 170 return Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade(); 171 } 172 173 views::View* TrayUpdate::CreateDefaultView(user::LoginStatus status) { 174 if (!Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade()) 175 return NULL; 176 return new UpdateView(severity_); 177 } 178 179 views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) { 180 return CreateDefaultView(status); 181 } 182 183 void TrayUpdate::DestroyDetailedView() { 184 if (nagger_) { 185 // The nagger was being displayed. Now that the detailed view is being 186 // closed, that means either the user clicks on it to restart, or the user 187 // didn't click on it to restart. In either case, start the timer to show 188 // the nag reminder again after the specified time. 189 nagger_->RestartTimer(); 190 } 191 } 192 193 void TrayUpdate::OnUpdateRecommended(UpdateObserver::UpdateSeverity severity) { 194 severity_ = severity; 195 SetImageFromResourceId(DecideResource(severity_, false)); 196 tray_view()->SetVisible(true); 197 if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() && 198 !nagger_.get()) { 199 // The shelf is not visible, and there is no nagger scheduled. 200 nagger_.reset(new tray::UpdateNagger(this)); 201 } 202 } 203 204 } // namespace internal 205 } // namespace ash 206