blob: 5b67baee94365f717af56acce210a83778f69ea2 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/tray_update.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/fixed_sized_image_view.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_constants.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.h"
#include "ui/aura/window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
namespace {
// How many seconds should we wait before showing the nag reminder?
const int kUpdateNaggingTimeSeconds = 24 * 60 * 60;
// How long should the nag reminder be displayed?
const int kShowUpdateNaggerForSeconds = 15;
int DecideResource(ash::UpdateInfo::UpdateSeverity severity, bool dark) {
switch (severity) {
case ash::UpdateInfo::UPDATE_NORMAL:
return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK:
IDR_AURA_UBER_TRAY_UPDATE;
case ash::UpdateInfo::UPDATE_LOW_GREEN:
return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_GREEN :
IDR_AURA_UBER_TRAY_UPDATE_GREEN;
case ash::UpdateInfo::UPDATE_HIGH_ORANGE:
return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_ORANGE :
IDR_AURA_UBER_TRAY_UPDATE_ORANGE;
case ash::UpdateInfo::UPDATE_SEVERE_RED:
return dark ? IDR_AURA_UBER_TRAY_UPDATE_DARK_RED :
IDR_AURA_UBER_TRAY_UPDATE_RED;
}
NOTREACHED() << "Unknown update severity level.";
return 0;
}
class UpdateView : public ash::ActionableView {
public:
explicit UpdateView(const ash::UpdateInfo& info) {
SetLayoutManager(new
views::BoxLayout(views::BoxLayout::kHorizontal,
ash::kTrayPopupPaddingHorizontal, 0,
ash::kTrayPopupPaddingBetweenItems));
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
views::ImageView* image =
new ash::FixedSizedImageView(0, ash::kTrayPopupItemHeight);
image->SetImage(bundle.GetImageNamed(DecideResource(info.severity, true))
.ToImageSkia());
AddChildView(image);
base::string16 label =
info.factory_reset_required
? bundle.GetLocalizedString(
IDS_ASH_STATUS_TRAY_RESTART_AND_POWERWASH_TO_UPDATE)
: bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_UPDATE);
AddChildView(new views::Label(label));
SetAccessibleName(label);
}
~UpdateView() override {}
private:
// Overridden from ActionableView.
bool PerformAction(const ui::Event& event) override {
ash::Shell::GetInstance()->
system_tray_delegate()->RequestRestartForUpdate();
return true;
}
DISALLOW_COPY_AND_ASSIGN(UpdateView);
};
}
namespace ash {
namespace tray {
class UpdateNagger : public ui::LayerAnimationObserver {
public:
explicit UpdateNagger(SystemTrayItem* owner)
: owner_(owner) {
RestartTimer();
owner_->system_tray()->GetWidget()->GetNativeView()->layer()->
GetAnimator()->AddObserver(this);
}
~UpdateNagger() override {
StatusAreaWidget* status_area =
Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
if (status_area) {
status_area->system_tray()->GetWidget()->GetNativeView()->layer()->
GetAnimator()->RemoveObserver(this);
}
}
void RestartTimer() {
timer_.Stop();
timer_.Start(FROM_HERE,
base::TimeDelta::FromSeconds(kUpdateNaggingTimeSeconds),
this,
&UpdateNagger::Nag);
}
private:
void Nag() {
owner_->PopupDetailedView(kShowUpdateNaggerForSeconds, false);
}
// Overridden from ui::LayerAnimationObserver.
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
// TODO(oshima): Find out if the updator will be shown on non
// primary display.
if (Shell::GetPrimaryRootWindowController()->shelf()->IsVisible())
timer_.Stop();
else if (!timer_.IsRunning())
RestartTimer();
}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {}
SystemTrayItem* owner_;
base::OneShotTimer timer_;
DISALLOW_COPY_AND_ASSIGN(UpdateNagger);
};
} // namespace tray
TrayUpdate::TrayUpdate(SystemTray* system_tray)
: TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_UPDATE) {
Shell::GetInstance()->system_tray_notifier()->AddUpdateObserver(this);
}
TrayUpdate::~TrayUpdate() {
Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this);
}
bool TrayUpdate::GetInitialVisibility() {
UpdateInfo info;
Shell::GetInstance()->system_tray_delegate()->GetSystemUpdateInfo(&info);
return info.update_required;
}
views::View* TrayUpdate::CreateDefaultView(user::LoginStatus status) {
UpdateInfo info;
Shell::GetInstance()->system_tray_delegate()->GetSystemUpdateInfo(&info);
return info.update_required ? new UpdateView(info) : nullptr;
}
views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) {
return CreateDefaultView(status);
}
void TrayUpdate::DestroyDetailedView() {
if (nagger_) {
// The nagger was being displayed. Now that the detailed view is being
// closed, that means either the user clicks on it to restart, or the user
// didn't click on it to restart. In either case, start the timer to show
// the nag reminder again after the specified time.
nagger_->RestartTimer();
}
}
void TrayUpdate::OnUpdateRecommended(const UpdateInfo& info) {
SetImageFromResourceId(DecideResource(info.severity, false));
tray_view()->SetVisible(true);
if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() &&
!nagger_.get()) {
// The shelf is not visible, and there is no nagger scheduled.
nagger_.reset(new tray::UpdateNagger(this));
}
}
} // namespace ash