tray_network.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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
37using chromeos::NetworkHandler;
38using chromeos::NetworkState;
39using chromeos::NetworkStateHandler;
40
41namespace ash {
42namespace internal {
43
44namespace {
45
46int 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
67namespace tray {
68
69class 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
98class 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
184class 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
223class 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  void Update() {
268    bool wifi_enabled = NetworkHandler::Get()->network_state_handler()->
269        IsTechnologyEnabled(flimflam::kTypeWifi);
270    const int image_id = wifi_enabled ?
271        IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED;
272    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
273    image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia());
274
275    const int string_id = wifi_enabled ?
276        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED :
277        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
278    label_view_->SetText(bundle.GetLocalizedString(string_id));
279  }
280
281  views::ImageView* image_view_;
282  views::Label* label_view_;
283
284  DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView);
285};
286
287class NetworkMessageView : public views::View,
288                           public views::LinkListener {
289 public:
290  NetworkMessageView(TrayNetwork* tray_network,
291                     NetworkObserver::MessageType message_type,
292                     const NetworkMessages::Message& network_msg)
293      : tray_network_(tray_network),
294        message_type_(message_type),
295        network_type_(network_msg.network_type_) {
296    SetLayoutManager(
297        new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
298
299    if (!network_msg.title.empty()) {
300      views::Label* title = new views::Label(network_msg.title);
301      title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
302      title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD));
303      AddChildView(title);
304    }
305
306    if (!network_msg.message.empty()) {
307      views::Label* message = new views::Label(network_msg.message);
308      message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
309      message->SetMultiLine(true);
310      message->SizeToFit(kTrayNotificationContentsWidth);
311      AddChildView(message);
312    }
313
314    if (!network_msg.links.empty()) {
315      for (size_t i = 0; i < network_msg.links.size(); ++i) {
316        views::Link* link = new views::Link(network_msg.links[i]);
317        link->set_id(i);
318        link->set_listener(this);
319        link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
320        link->SetMultiLine(true);
321        link->SizeToFit(kTrayNotificationContentsWidth);
322        AddChildView(link);
323      }
324    }
325  }
326
327  virtual ~NetworkMessageView() {
328  }
329
330  // Overridden from views::LinkListener.
331  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
332    tray_network_->LinkClicked(message_type_, source->id());
333  }
334
335  NetworkObserver::MessageType message_type() const { return message_type_; }
336  NetworkObserver::NetworkType network_type() const { return network_type_; }
337
338 private:
339  TrayNetwork* tray_network_;
340  NetworkObserver::MessageType message_type_;
341  NetworkObserver::NetworkType network_type_;
342
343  DISALLOW_COPY_AND_ASSIGN(NetworkMessageView);
344};
345
346class NetworkNotificationView : public TrayNotificationView {
347 public:
348  explicit NetworkNotificationView(TrayNetwork* tray_network)
349      : TrayNotificationView(tray_network, 0),
350        tray_network_(tray_network) {
351    CreateMessageView();
352    InitView(network_message_view_);
353    SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
354        GetMessageIcon(network_message_view_->message_type(),
355            network_message_view_->network_type())));
356  }
357
358  // Overridden from TrayNotificationView.
359  virtual void OnClose() OVERRIDE {
360    tray_network_->ClearNetworkMessage(network_message_view_->message_type());
361  }
362
363  virtual void OnClickAction() OVERRIDE {
364    if (network_message_view_->message_type() !=
365        TrayNetwork::MESSAGE_DATA_PROMO)
366      tray_network_->PopupDetailedView(0, true);
367  }
368
369  void Update() {
370    CreateMessageView();
371    UpdateViewAndImage(network_message_view_,
372        *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
373            GetMessageIcon(network_message_view_->message_type(),
374                network_message_view_->network_type())));
375  }
376
377 private:
378  void CreateMessageView() {
379    // Display the first (highest priority) message.
380    CHECK(!tray_network_->messages()->messages().empty());
381    NetworkMessages::MessageMap::const_iterator iter =
382        tray_network_->messages()->messages().begin();
383    network_message_view_ =
384        new NetworkMessageView(tray_network_, iter->first, iter->second);
385  }
386
387  TrayNetwork* tray_network_;
388  tray::NetworkMessageView* network_message_view_;
389
390  DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView);
391};
392
393}  // namespace tray
394
395TrayNetwork::TrayNetwork(SystemTray* system_tray)
396    : SystemTrayItem(system_tray),
397      tray_(NULL),
398      default_(NULL),
399      detailed_(NULL),
400      notification_(NULL),
401      messages_(new tray::NetworkMessages()),
402      request_wifi_view_(false) {
403  network_state_observer_.reset(new TrayNetworkStateObserver(this));
404  Shell::GetInstance()->system_tray_notifier()->AddNetworkObserver(this);
405}
406
407TrayNetwork::~TrayNetwork() {
408  Shell::GetInstance()->system_tray_notifier()->RemoveNetworkObserver(this);
409}
410
411views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) {
412  CHECK(tray_ == NULL);
413  if (!chromeos::NetworkHandler::IsInitialized())
414    return NULL;
415  tray_ = new tray::NetworkTrayView(this);
416  return tray_;
417}
418
419views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) {
420  CHECK(default_ == NULL);
421  if (!chromeos::NetworkHandler::IsInitialized())
422    return NULL;
423  CHECK(tray_ != NULL);
424  default_ = new tray::NetworkDefaultView(
425      this, status != user::LOGGED_IN_LOCKED);
426  return default_;
427}
428
429views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) {
430  CHECK(detailed_ == NULL);
431  if (!chromeos::NetworkHandler::IsInitialized())
432    return NULL;
433  // Clear any notifications when showing the detailed view.
434  messages_->messages().clear();
435  HideNotificationView();
436  if (request_wifi_view_) {
437    detailed_ = new tray::NetworkWifiDetailedView(this);
438    request_wifi_view_ = false;
439  } else {
440    detailed_ = new tray::NetworkStateListDetailedView(
441        this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status);
442    detailed_->Init();
443  }
444  return detailed_;
445}
446
447views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) {
448  CHECK(notification_ == NULL);
449  if (messages_->messages().empty())
450    return NULL;  // Message has already been cleared.
451  notification_ = new tray::NetworkNotificationView(this);
452  return notification_;
453}
454
455void TrayNetwork::DestroyTrayView() {
456  tray_ = NULL;
457}
458
459void TrayNetwork::DestroyDefaultView() {
460  default_ = NULL;
461}
462
463void TrayNetwork::DestroyDetailedView() {
464  detailed_ = NULL;
465}
466
467void TrayNetwork::DestroyNotificationView() {
468  notification_ = NULL;
469}
470
471void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) {
472}
473
474void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
475  if (tray_)
476    SetTrayImageItemBorder(tray_, alignment);
477}
478
479void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate,
480                                    MessageType message_type,
481                                    NetworkType network_type,
482                                    const base::string16& title,
483                                    const base::string16& message,
484                                    const std::vector<base::string16>& links) {
485  messages_->messages()[message_type] = tray::NetworkMessages::Message(
486      delegate, network_type, title, message, links);
487  if (!Shell::GetInstance()->system_tray_delegate()->IsOobeCompleted())
488    return;
489  if (notification_)
490    notification_->Update();
491  else
492    ShowNotificationView();
493}
494
495void TrayNetwork::ClearNetworkMessage(MessageType message_type) {
496  messages_->messages().erase(message_type);
497  if (messages_->messages().empty()) {
498    HideNotificationView();
499    return;
500  }
501  if (notification_)
502    notification_->Update();
503  else
504    ShowNotificationView();
505}
506
507void TrayNetwork::RequestToggleWifi() {
508  // This will always be triggered by a user action (e.g. keyboard shortcut)
509  if (!detailed_ ||
510      detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) {
511    request_wifi_view_ = true;
512    PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
513  }
514  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
515  bool enabled = handler->IsTechnologyEnabled(flimflam::kTypeWifi);
516  handler->SetTechnologyEnabled(
517      flimflam::kTypeWifi, !enabled,
518      chromeos::network_handler::ErrorCallback());
519}
520
521void TrayNetwork::NetworkStateChanged(bool list_changed) {
522  if (tray_)
523    tray_->UpdateNetworkStateHandlerIcon();
524  if (default_)
525    default_->Update();
526  if (detailed_) {
527    if (list_changed)
528      detailed_->NetworkListChanged();
529    else
530      detailed_->ManagerChanged();
531  }
532}
533
534void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) {
535  if (detailed_)
536    detailed_->NetworkServiceChanged(network);
537}
538
539void TrayNetwork::LinkClicked(MessageType message_type, int link_id) {
540  tray::NetworkMessages::MessageMap::const_iterator iter =
541      messages()->messages().find(message_type);
542  if (iter != messages()->messages().end() && iter->second.delegate)
543    iter->second.delegate->NotificationLinkClicked(message_type, link_id);
544}
545
546}  // namespace internal
547}  // namespace ash
548