tray_update.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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.h"
18#include "base/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::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
98namespace ash {
99namespace internal {
100
101namespace tray {
102
103class 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
159TrayUpdate::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
165TrayUpdate::~TrayUpdate() {
166  Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this);
167}
168
169bool TrayUpdate::GetInitialVisibility() {
170  return Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade();
171}
172
173views::View* TrayUpdate::CreateDefaultView(user::LoginStatus status) {
174  if (!Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade())
175    return NULL;
176  return new UpdateView(severity_);
177}
178
179views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) {
180  return CreateDefaultView(status);
181}
182
183void 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
193void 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