Home | History | Annotate | Download | only in network
      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/chromeos/network/tray_network.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/shell.h"
      9 #include "ash/system/chromeos/network/network_icon_animation.h"
     10 #include "ash/system/chromeos/network/network_state_list_detailed_view.h"
     11 #include "ash/system/chromeos/network/network_tray_delegate.h"
     12 #include "ash/system/chromeos/network/tray_network_state_observer.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 "ash/system/tray/tray_item_more.h"
     18 #include "ash/system/tray/tray_item_view.h"
     19 #include "ash/system/tray/tray_notification_view.h"
     20 #include "ash/system/tray/tray_utils.h"
     21 #include "base/command_line.h"
     22 #include "base/strings/utf_string_conversions.h"
     23 #include "chromeos/network/network_state.h"
     24 #include "chromeos/network/network_state_handler.h"
     25 #include "grit/ash_resources.h"
     26 #include "grit/ash_strings.h"
     27 #include "third_party/cros_system_api/dbus/service_constants.h"
     28 #include "ui/base/accessibility/accessible_view_state.h"
     29 #include "ui/base/l10n/l10n_util.h"
     30 #include "ui/base/resource/resource_bundle.h"
     31 #include "ui/views/controls/image_view.h"
     32 #include "ui/views/controls/link.h"
     33 #include "ui/views/controls/link_listener.h"
     34 #include "ui/views/layout/box_layout.h"
     35 #include "ui/views/widget/widget.h"
     36 
     37 using chromeos::NetworkHandler;
     38 using chromeos::NetworkState;
     39 using chromeos::NetworkStateHandler;
     40 
     41 namespace ash {
     42 namespace internal {
     43 
     44 namespace {
     45 
     46 int GetMessageIcon(NetworkObserver::MessageType message_type,
     47                    NetworkObserver::NetworkType network_type) {
     48   switch(message_type) {
     49     case NetworkObserver::ERROR_CONNECT_FAILED:
     50       if (NetworkObserver::NETWORK_CELLULAR == network_type)
     51         return IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED;
     52       else
     53         return IDR_AURA_UBER_TRAY_NETWORK_FAILED;
     54     case NetworkObserver::ERROR_OUT_OF_CREDITS:
     55     case NetworkObserver::MESSAGE_DATA_PROMO:
     56       if (network_type == TrayNetwork::NETWORK_CELLULAR_LTE)
     57         return IDR_AURA_UBER_TRAY_NOTIFICATION_LTE;
     58       else
     59         return IDR_AURA_UBER_TRAY_NOTIFICATION_3G;
     60   }
     61   NOTREACHED();
     62   return 0;
     63 }
     64 
     65 }  // namespace
     66 
     67 namespace tray {
     68 
     69 class NetworkMessages {
     70  public:
     71   struct Message {
     72     Message() : delegate(NULL) {}
     73     Message(NetworkTrayDelegate* in_delegate,
     74             NetworkObserver::NetworkType network_type,
     75             const base::string16& in_title,
     76             const base::string16& in_message,
     77             const std::vector<base::string16>& in_links) :
     78         delegate(in_delegate),
     79         network_type_(network_type),
     80         title(in_title),
     81         message(in_message),
     82         links(in_links) {}
     83     NetworkTrayDelegate* delegate;
     84     NetworkObserver::NetworkType network_type_;
     85     base::string16 title;
     86     base::string16 message;
     87     std::vector<base::string16> links;
     88   };
     89   typedef std::map<NetworkObserver::MessageType, Message> MessageMap;
     90 
     91   MessageMap& messages() { return messages_; }
     92   const MessageMap& messages() const { return messages_; }
     93 
     94  private:
     95   MessageMap messages_;
     96 };
     97 
     98 class NetworkTrayView : public TrayItemView,
     99                         public network_icon::AnimationObserver {
    100  public:
    101   explicit NetworkTrayView(TrayNetwork* network_tray)
    102       : TrayItemView(network_tray),
    103         network_tray_(network_tray) {
    104     SetLayoutManager(
    105         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
    106 
    107     image_view_ = new views::ImageView;
    108     AddChildView(image_view_);
    109 
    110     UpdateNetworkStateHandlerIcon();
    111   }
    112 
    113   virtual ~NetworkTrayView() {
    114     network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
    115   }
    116 
    117   virtual const char* GetClassName() const OVERRIDE {
    118     return "NetworkTrayView";
    119   }
    120 
    121   void UpdateNetworkStateHandlerIcon() {
    122     NetworkStateHandler* handler =
    123         NetworkHandler::Get()->network_state_handler();
    124     gfx::ImageSkia image;
    125     base::string16 name;
    126     bool animating = false;
    127     network_icon::GetDefaultNetworkImageAndLabel(
    128         network_icon::ICON_TYPE_TRAY, &image, &name, &animating);
    129     bool show_in_tray = !image.isNull();
    130     UpdateIcon(show_in_tray, image);
    131     if (animating)
    132       network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
    133     else
    134       network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
    135     // Update accessibility.
    136     const NetworkState* connected_network = handler->ConnectedNetworkByType(
    137         NetworkStateHandler::kMatchTypeNonVirtual);
    138     if (connected_network)
    139       UpdateConnectionStatus(UTF8ToUTF16(connected_network->name()), true);
    140     else
    141       UpdateConnectionStatus(base::string16(), false);
    142   }
    143 
    144   // views::View override.
    145   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
    146     state->name = connection_status_string_;
    147     state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
    148   }
    149 
    150   // network_icon::AnimationObserver
    151   virtual void NetworkIconChanged() OVERRIDE {
    152     UpdateNetworkStateHandlerIcon();
    153   }
    154 
    155  private:
    156   // Updates connection status and notifies accessibility event when necessary.
    157   void UpdateConnectionStatus(const base::string16& network_name,
    158                               bool connected) {
    159     base::string16 new_connection_status_string;
    160     if (connected) {
    161       new_connection_status_string = l10n_util::GetStringFUTF16(
    162           IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, network_name);
    163     }
    164     if (new_connection_status_string != connection_status_string_) {
    165       connection_status_string_ = new_connection_status_string;
    166       if(!connection_status_string_.empty())
    167         NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
    168     }
    169   }
    170 
    171   void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image) {
    172     image_view_->SetImage(image);
    173     SetVisible(tray_icon_visible);
    174     SchedulePaint();
    175   }
    176 
    177   TrayNetwork* network_tray_;
    178   views::ImageView* image_view_;
    179   base::string16 connection_status_string_;
    180 
    181   DISALLOW_COPY_AND_ASSIGN(NetworkTrayView);
    182 };
    183 
    184 class NetworkDefaultView : public TrayItemMore,
    185                            public network_icon::AnimationObserver {
    186  public:
    187   NetworkDefaultView(TrayNetwork* network_tray, bool show_more)
    188       : TrayItemMore(network_tray, show_more),
    189         network_tray_(network_tray) {
    190     Update();
    191   }
    192 
    193   virtual ~NetworkDefaultView() {
    194     network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
    195   }
    196 
    197   void Update() {
    198     gfx::ImageSkia image;
    199     base::string16 label;
    200     bool animating = false;
    201     network_icon::GetDefaultNetworkImageAndLabel(
    202         network_icon::ICON_TYPE_DEFAULT_VIEW, &image, &label, &animating);
    203     if (animating)
    204       network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
    205     else
    206       network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
    207     SetImage(&image);
    208     SetLabel(label);
    209     SetAccessibleName(label);
    210   }
    211 
    212   // network_icon::AnimationObserver
    213   virtual void NetworkIconChanged() OVERRIDE {
    214     Update();
    215   }
    216 
    217  private:
    218   TrayNetwork* network_tray_;
    219 
    220   DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView);
    221 };
    222 
    223 class NetworkWifiDetailedView : public NetworkDetailedView {
    224  public:
    225   explicit NetworkWifiDetailedView(SystemTrayItem* owner)
    226       : NetworkDetailedView(owner) {
    227     SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
    228                                           kTrayPopupPaddingHorizontal,
    229                                           10,
    230                                           kTrayPopupPaddingBetweenItems));
    231     image_view_ = new views::ImageView;
    232     AddChildView(image_view_);
    233 
    234     label_view_ = new views::Label();
    235     label_view_->SetMultiLine(true);
    236     label_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    237     AddChildView(label_view_);
    238 
    239     Update();
    240   }
    241 
    242   virtual ~NetworkWifiDetailedView() {
    243   }
    244 
    245   // Overridden from NetworkDetailedView:
    246 
    247   virtual void Init() OVERRIDE {
    248   }
    249 
    250   virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE {
    251     return NetworkDetailedView::WIFI_VIEW;
    252   }
    253 
    254   virtual void ManagerChanged() OVERRIDE {
    255     Update();
    256   }
    257 
    258   virtual void NetworkListChanged() OVERRIDE {
    259     Update();
    260   }
    261 
    262   virtual void NetworkServiceChanged(
    263       const chromeos::NetworkState* network) OVERRIDE {
    264   }
    265 
    266  private:
    267   virtual void Layout() OVERRIDE {
    268     // Center both views vertically.
    269     views::View::Layout();
    270     image_view_->SetY(
    271         (height() - image_view_->GetPreferredSize().height()) / 2);
    272     label_view_->SetY(
    273         (height() - label_view_->GetPreferredSize().height()) / 2);
    274   }
    275 
    276   void Update() {
    277     bool wifi_enabled = NetworkHandler::Get()->network_state_handler()->
    278         IsTechnologyEnabled(flimflam::kTypeWifi);
    279     const int image_id = wifi_enabled ?
    280         IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED;
    281     ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
    282     image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia());
    283 
    284     const int string_id = wifi_enabled ?
    285         IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED :
    286         IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
    287     label_view_->SetText(bundle.GetLocalizedString(string_id));
    288     label_view_->SizeToFit(kTrayPopupMinWidth -
    289         kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems -
    290         kTrayPopupDetailsIconWidth);
    291   }
    292 
    293   views::ImageView* image_view_;
    294   views::Label* label_view_;
    295 
    296   DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView);
    297 };
    298 
    299 class NetworkMessageView : public views::View,
    300                            public views::LinkListener {
    301  public:
    302   NetworkMessageView(TrayNetwork* tray_network,
    303                      NetworkObserver::MessageType message_type,
    304                      const NetworkMessages::Message& network_msg)
    305       : tray_network_(tray_network),
    306         message_type_(message_type),
    307         network_type_(network_msg.network_type_) {
    308     SetLayoutManager(
    309         new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
    310 
    311     if (!network_msg.title.empty()) {
    312       views::Label* title = new views::Label(network_msg.title);
    313       title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    314       title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD));
    315       AddChildView(title);
    316     }
    317 
    318     if (!network_msg.message.empty()) {
    319       views::Label* message = new views::Label(network_msg.message);
    320       message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    321       message->SetMultiLine(true);
    322       message->SizeToFit(kTrayNotificationContentsWidth);
    323       AddChildView(message);
    324     }
    325 
    326     if (!network_msg.links.empty()) {
    327       for (size_t i = 0; i < network_msg.links.size(); ++i) {
    328         views::Link* link = new views::Link(network_msg.links[i]);
    329         link->set_id(i);
    330         link->set_listener(this);
    331         link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    332         link->SetMultiLine(true);
    333         link->SizeToFit(kTrayNotificationContentsWidth);
    334         AddChildView(link);
    335       }
    336     }
    337   }
    338 
    339   virtual ~NetworkMessageView() {
    340   }
    341 
    342   // Overridden from views::LinkListener.
    343   virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
    344     tray_network_->LinkClicked(message_type_, source->id());
    345   }
    346 
    347   NetworkObserver::MessageType message_type() const { return message_type_; }
    348   NetworkObserver::NetworkType network_type() const { return network_type_; }
    349 
    350  private:
    351   TrayNetwork* tray_network_;
    352   NetworkObserver::MessageType message_type_;
    353   NetworkObserver::NetworkType network_type_;
    354 
    355   DISALLOW_COPY_AND_ASSIGN(NetworkMessageView);
    356 };
    357 
    358 class NetworkNotificationView : public TrayNotificationView {
    359  public:
    360   explicit NetworkNotificationView(TrayNetwork* tray_network)
    361       : TrayNotificationView(tray_network, 0),
    362         tray_network_(tray_network) {
    363     CreateMessageView();
    364     InitView(network_message_view_);
    365     SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
    366         GetMessageIcon(network_message_view_->message_type(),
    367             network_message_view_->network_type())));
    368   }
    369 
    370   // Overridden from TrayNotificationView.
    371   virtual void OnClose() OVERRIDE {
    372     tray_network_->ClearNetworkMessage(network_message_view_->message_type());
    373   }
    374 
    375   virtual void OnClickAction() OVERRIDE {
    376     if (network_message_view_->message_type() !=
    377         TrayNetwork::MESSAGE_DATA_PROMO)
    378       tray_network_->PopupDetailedView(0, true);
    379   }
    380 
    381   void Update() {
    382     CreateMessageView();
    383     UpdateViewAndImage(network_message_view_,
    384         *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
    385             GetMessageIcon(network_message_view_->message_type(),
    386                 network_message_view_->network_type())));
    387   }
    388 
    389  private:
    390   void CreateMessageView() {
    391     // Display the first (highest priority) message.
    392     CHECK(!tray_network_->messages()->messages().empty());
    393     NetworkMessages::MessageMap::const_iterator iter =
    394         tray_network_->messages()->messages().begin();
    395     network_message_view_ =
    396         new NetworkMessageView(tray_network_, iter->first, iter->second);
    397   }
    398 
    399   TrayNetwork* tray_network_;
    400   tray::NetworkMessageView* network_message_view_;
    401 
    402   DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView);
    403 };
    404 
    405 }  // namespace tray
    406 
    407 TrayNetwork::TrayNetwork(SystemTray* system_tray)
    408     : SystemTrayItem(system_tray),
    409       tray_(NULL),
    410       default_(NULL),
    411       detailed_(NULL),
    412       notification_(NULL),
    413       messages_(new tray::NetworkMessages()),
    414       request_wifi_view_(false) {
    415   network_state_observer_.reset(new TrayNetworkStateObserver(this));
    416   Shell::GetInstance()->system_tray_notifier()->AddNetworkObserver(this);
    417 }
    418 
    419 TrayNetwork::~TrayNetwork() {
    420   Shell::GetInstance()->system_tray_notifier()->RemoveNetworkObserver(this);
    421 }
    422 
    423 views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) {
    424   CHECK(tray_ == NULL);
    425   if (!chromeos::NetworkHandler::IsInitialized())
    426     return NULL;
    427   tray_ = new tray::NetworkTrayView(this);
    428   return tray_;
    429 }
    430 
    431 views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) {
    432   CHECK(default_ == NULL);
    433   if (!chromeos::NetworkHandler::IsInitialized())
    434     return NULL;
    435   CHECK(tray_ != NULL);
    436   default_ = new tray::NetworkDefaultView(
    437       this, status != user::LOGGED_IN_LOCKED);
    438   return default_;
    439 }
    440 
    441 views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) {
    442   CHECK(detailed_ == NULL);
    443   if (!chromeos::NetworkHandler::IsInitialized())
    444     return NULL;
    445   // Clear any notifications when showing the detailed view.
    446   messages_->messages().clear();
    447   HideNotificationView();
    448   if (request_wifi_view_) {
    449     detailed_ = new tray::NetworkWifiDetailedView(this);
    450     request_wifi_view_ = false;
    451   } else {
    452     detailed_ = new tray::NetworkStateListDetailedView(
    453         this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status);
    454     detailed_->Init();
    455   }
    456   return detailed_;
    457 }
    458 
    459 views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) {
    460   CHECK(notification_ == NULL);
    461   if (messages_->messages().empty())
    462     return NULL;  // Message has already been cleared.
    463   notification_ = new tray::NetworkNotificationView(this);
    464   return notification_;
    465 }
    466 
    467 void TrayNetwork::DestroyTrayView() {
    468   tray_ = NULL;
    469 }
    470 
    471 void TrayNetwork::DestroyDefaultView() {
    472   default_ = NULL;
    473 }
    474 
    475 void TrayNetwork::DestroyDetailedView() {
    476   detailed_ = NULL;
    477 }
    478 
    479 void TrayNetwork::DestroyNotificationView() {
    480   notification_ = NULL;
    481 }
    482 
    483 void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) {
    484 }
    485 
    486 void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
    487   if (tray_)
    488     SetTrayImageItemBorder(tray_, alignment);
    489 }
    490 
    491 void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate,
    492                                     MessageType message_type,
    493                                     NetworkType network_type,
    494                                     const base::string16& title,
    495                                     const base::string16& message,
    496                                     const std::vector<base::string16>& links) {
    497   messages_->messages()[message_type] = tray::NetworkMessages::Message(
    498       delegate, network_type, title, message, links);
    499   if (!Shell::GetInstance()->system_tray_delegate()->IsOobeCompleted())
    500     return;
    501   if (notification_)
    502     notification_->Update();
    503   else
    504     ShowNotificationView();
    505 }
    506 
    507 void TrayNetwork::ClearNetworkMessage(MessageType message_type) {
    508   messages_->messages().erase(message_type);
    509   if (messages_->messages().empty()) {
    510     HideNotificationView();
    511     return;
    512   }
    513   if (notification_)
    514     notification_->Update();
    515   else
    516     ShowNotificationView();
    517 }
    518 
    519 void TrayNetwork::RequestToggleWifi() {
    520   // This will always be triggered by a user action (e.g. keyboard shortcut)
    521   if (!detailed_ ||
    522       detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) {
    523     request_wifi_view_ = true;
    524     PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
    525   }
    526   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
    527   bool enabled = handler->IsTechnologyEnabled(flimflam::kTypeWifi);
    528   handler->SetTechnologyEnabled(
    529       flimflam::kTypeWifi, !enabled,
    530       chromeos::network_handler::ErrorCallback());
    531 }
    532 
    533 void TrayNetwork::NetworkStateChanged(bool list_changed) {
    534   if (tray_)
    535     tray_->UpdateNetworkStateHandlerIcon();
    536   if (default_)
    537     default_->Update();
    538   if (detailed_) {
    539     if (list_changed)
    540       detailed_->NetworkListChanged();
    541     else
    542       detailed_->ManagerChanged();
    543   }
    544 }
    545 
    546 void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) {
    547   if (detailed_)
    548     detailed_->NetworkServiceChanged(network);
    549 }
    550 
    551 void TrayNetwork::LinkClicked(MessageType message_type, int link_id) {
    552   tray::NetworkMessages::MessageMap::const_iterator iter =
    553       messages()->messages().find(message_type);
    554   if (iter != messages()->messages().end() && iter->second.delegate)
    555     iter->second.delegate->NotificationLinkClicked(message_type, link_id);
    556 }
    557 
    558 }  // namespace internal
    559 }  // namespace ash
    560