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::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::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 tray {
    100 
    101 class UpdateNagger : public ui::LayerAnimationObserver {
    102  public:
    103   explicit UpdateNagger(SystemTrayItem* owner)
    104       : owner_(owner) {
    105     RestartTimer();
    106     owner_->system_tray()->GetWidget()->GetNativeView()->layer()->
    107         GetAnimator()->AddObserver(this);
    108   }
    109 
    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 
    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:
    128   void Nag() {
    129     owner_->PopupDetailedView(kShowUpdateNaggerForSeconds, false);
    130   }
    131 
    132   // Overridden from ui::LayerAnimationObserver.
    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 
    143   virtual void OnLayerAnimationAborted(
    144       ui::LayerAnimationSequence* sequence) OVERRIDE {}
    145 
    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 
    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 
    163 TrayUpdate::~TrayUpdate() {
    164   Shell::GetInstance()->system_tray_notifier()->RemoveUpdateObserver(this);
    165 }
    166 
    167 bool TrayUpdate::GetInitialVisibility() {
    168   return Shell::GetInstance()->system_tray_delegate()->SystemShouldUpgrade();
    169 }
    170 
    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 
    177 views::View* TrayUpdate::CreateDetailedView(user::LoginStatus status) {
    178   return CreateDefaultView(status);
    179 }
    180 
    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 
    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