• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DecideResource(ash::UpdateObserver::UpdateSeverity severity,bool dark)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::ActionableView {
64  public:
UpdateView(ash::UpdateObserver::UpdateSeverity severity)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 
~UpdateView()83   virtual ~UpdateView() {}
84 
85  private:
86   // Overridden from ActionableView.
PerformAction(const ui::Event & event)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 tray {
100 
101 class UpdateNagger : public ui::LayerAnimationObserver {
102  public:
UpdateNagger(SystemTrayItem * owner)103   explicit UpdateNagger(SystemTrayItem* owner)
104       : owner_(owner) {
105     RestartTimer();
106     owner_->system_tray()->GetWidget()->GetNativeView()->layer()->
107         GetAnimator()->AddObserver(this);
108   }
109 
~UpdateNagger()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 
RestartTimer()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:
Nag()128   void Nag() {
129     owner_->PopupDetailedView(kShowUpdateNaggerForSeconds, false);
130   }
131 
132   // Overridden from ui::LayerAnimationObserver.
OnLayerAnimationEnded(ui::LayerAnimationSequence * sequence)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 
OnLayerAnimationAborted(ui::LayerAnimationSequence * sequence)143   virtual void OnLayerAnimationAborted(
144       ui::LayerAnimationSequence* sequence) OVERRIDE {}
145 
OnLayerAnimationScheduled(ui::LayerAnimationSequence * sequence)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 
TrayUpdate(SystemTray * system_tray)157 TrayUpdate::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 
~TrayUpdate()163 TrayUpdate::~TrayUpdate() {
164   Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this);
165 }
166 
GetInitialVisibility()167 bool TrayUpdate::GetInitialVisibility() {
168   return Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade();
169 }
170 
CreateDefaultView(user::LoginStatus status)171 views::View* TrayUpdate::CreateDefaultView(user::LoginStatus status) {
172   if (!Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade())
173     return NULL;
174   return new UpdateView(severity_);
175 }
176 
CreateDetailedView(user::LoginStatus status)177 views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) {
178   return CreateDefaultView(status);
179 }
180 
DestroyDetailedView()181 void 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 
OnUpdateRecommended(UpdateObserver::UpdateSeverity severity)191 void 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