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.
5 #include "ash/system/tray_update.h"
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"
34 // How many seconds should we wait before showing the nag reminder?
35 const int kUpdateNaggingTimeSeconds
= 24 * 60 * 60;
37 // How long should the nag reminder be displayed?
38 const int kShowUpdateNaggerForSeconds
= 15;
40 int DecideResource(ash::UpdateInfo::UpdateSeverity severity
, bool dark
) {
42 case ash::UpdateInfo::UPDATE_NORMAL
:
43 return dark
? IDR_AURA_UBER_TRAY_UPDATE_DARK
:
44 IDR_AURA_UBER_TRAY_UPDATE
;
46 case ash::UpdateInfo::UPDATE_LOW_GREEN
:
47 return dark
? IDR_AURA_UBER_TRAY_UPDATE_DARK_GREEN
:
48 IDR_AURA_UBER_TRAY_UPDATE_GREEN
;
50 case ash::UpdateInfo::UPDATE_HIGH_ORANGE
:
51 return dark
? IDR_AURA_UBER_TRAY_UPDATE_DARK_ORANGE
:
52 IDR_AURA_UBER_TRAY_UPDATE_ORANGE
;
54 case ash::UpdateInfo::UPDATE_SEVERE_RED
:
55 return dark
? IDR_AURA_UBER_TRAY_UPDATE_DARK_RED
:
56 IDR_AURA_UBER_TRAY_UPDATE_RED
;
59 NOTREACHED() << "Unknown update severity level.";
63 class UpdateView
: public ash::ActionableView
{
65 explicit UpdateView(const ash::UpdateInfo
& info
) {
67 views::BoxLayout(views::BoxLayout::kHorizontal
,
68 ash::kTrayPopupPaddingHorizontal
, 0,
69 ash::kTrayPopupPaddingBetweenItems
));
71 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
72 views::ImageView
* image
=
73 new ash::FixedSizedImageView(0, ash::kTrayPopupItemHeight
);
74 image
->SetImage(bundle
.GetImageNamed(DecideResource(info
.severity
, true))
79 base::string16 label
=
80 info
.factory_reset_required
81 ? bundle
.GetLocalizedString(
82 IDS_ASH_STATUS_TRAY_RESTART_AND_POWERWASH_TO_UPDATE
)
83 : bundle
.GetLocalizedString(IDS_ASH_STATUS_TRAY_UPDATE
);
84 AddChildView(new views::Label(label
));
85 SetAccessibleName(label
);
88 ~UpdateView() override
{}
91 // Overridden from ActionableView.
92 bool PerformAction(const ui::Event
& event
) override
{
93 ash::Shell::GetInstance()->
94 system_tray_delegate()->RequestRestartForUpdate();
98 DISALLOW_COPY_AND_ASSIGN(UpdateView
);
106 class UpdateNagger
: public ui::LayerAnimationObserver
{
108 explicit UpdateNagger(SystemTrayItem
* owner
)
111 owner_
->system_tray()->GetWidget()->GetNativeView()->layer()->
112 GetAnimator()->AddObserver(this);
115 ~UpdateNagger() override
{
116 StatusAreaWidget
* status_area
=
117 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
119 status_area
->system_tray()->GetWidget()->GetNativeView()->layer()->
120 GetAnimator()->RemoveObserver(this);
124 void RestartTimer() {
126 timer_
.Start(FROM_HERE
,
127 base::TimeDelta::FromSeconds(kUpdateNaggingTimeSeconds
),
134 owner_
->PopupDetailedView(kShowUpdateNaggerForSeconds
, false);
137 // Overridden from ui::LayerAnimationObserver.
138 void OnLayerAnimationEnded(ui::LayerAnimationSequence
* sequence
) override
{
139 // TODO(oshima): Find out if the updator will be shown on non
141 if (Shell::GetPrimaryRootWindowController()->shelf()->IsVisible())
143 else if (!timer_
.IsRunning())
147 void OnLayerAnimationAborted(ui::LayerAnimationSequence
* sequence
) override
{}
149 void OnLayerAnimationScheduled(
150 ui::LayerAnimationSequence
* sequence
) override
{}
152 SystemTrayItem
* owner_
;
153 base::OneShotTimer
<UpdateNagger
> timer_
;
155 DISALLOW_COPY_AND_ASSIGN(UpdateNagger
);
160 TrayUpdate::TrayUpdate(SystemTray
* system_tray
)
161 : TrayImageItem(system_tray
, IDR_AURA_UBER_TRAY_UPDATE
) {
162 Shell::GetInstance()->system_tray_notifier()->AddUpdateObserver(this);
165 TrayUpdate::~TrayUpdate() {
166 Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this);
169 bool TrayUpdate::GetInitialVisibility() {
171 Shell::GetInstance()->system_tray_delegate()->GetSystemUpdateInfo(&info
);
172 return info
.update_required
;
175 views::View
* TrayUpdate::CreateDefaultView(user::LoginStatus status
) {
177 Shell::GetInstance()->system_tray_delegate()->GetSystemUpdateInfo(&info
);
178 return info
.update_required
? new UpdateView(info
) : nullptr;
181 views::View
* TrayUpdate::CreateDetailedView(user::LoginStatus status
) {
182 return CreateDefaultView(status
);
185 void TrayUpdate::DestroyDetailedView() {
187 // The nagger was being displayed. Now that the detailed view is being
188 // closed, that means either the user clicks on it to restart, or the user
189 // didn't click on it to restart. In either case, start the timer to show
190 // the nag reminder again after the specified time.
191 nagger_
->RestartTimer();
195 void TrayUpdate::OnUpdateRecommended(const UpdateInfo
& info
) {
196 SetImageFromResourceId(DecideResource(info
.severity
, false));
197 tray_view()->SetVisible(true);
198 if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() &&
200 // The shelf is not visible, and there is no nagger scheduled.
201 nagger_
.reset(new tray::UpdateNagger(this));