Home | History | Annotate | Download | only in system
      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