tray_network.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/shell.h"
8#include "ash/system/chromeos/network/network_list_detailed_view_base.h"
9#include "ash/system/tray/system_tray.h"
10#include "ash/system/tray/system_tray_delegate.h"
11#include "ash/system/tray/tray_constants.h"
12#include "ash/system/tray/tray_item_more.h"
13#include "ash/system/tray/tray_item_view.h"
14#include "ash/system/tray/tray_notification_view.h"
15#include "grit/ash_resources.h"
16#include "grit/ash_strings.h"
17#include "ui/base/l10n/l10n_util.h"
18#include "ui/base/resource/resource_bundle.h"
19#include "ui/views/controls/link.h"
20#include "ui/views/controls/link_listener.h"
21#include "ui/views/layout/box_layout.h"
22
23namespace {
24
25using ash::internal::TrayNetwork;
26
27int GetMessageIcon(TrayNetwork::MessageType message_type) {
28  switch(message_type) {
29    case TrayNetwork::ERROR_CONNECT_FAILED:
30      return IDR_AURA_UBER_TRAY_NETWORK_FAILED;
31    case TrayNetwork::MESSAGE_DATA_LOW:
32      return IDR_AURA_UBER_TRAY_NETWORK_DATA_LOW;
33    case TrayNetwork::MESSAGE_DATA_NONE:
34      return IDR_AURA_UBER_TRAY_NETWORK_DATA_NONE;
35    case TrayNetwork::MESSAGE_DATA_PROMO:
36      return IDR_AURA_UBER_TRAY_NOTIFICATION_3G;
37  }
38  NOTREACHED();
39  return 0;
40}
41
42}  // namespace
43
44namespace ash {
45namespace internal {
46
47namespace tray {
48
49enum ColorTheme {
50  LIGHT,
51  DARK,
52};
53
54class NetworkMessages {
55 public:
56  struct Message {
57    Message() : delegate(NULL) {}
58    Message(NetworkTrayDelegate* in_delegate,
59            const string16& in_title,
60            const string16& in_message,
61            const std::vector<string16>& in_links) :
62        delegate(in_delegate),
63        title(in_title),
64        message(in_message),
65        links(in_links) {}
66    NetworkTrayDelegate* delegate;
67    string16 title;
68    string16 message;
69    std::vector<string16> links;
70  };
71  typedef std::map<TrayNetwork::MessageType, Message> MessageMap;
72
73  MessageMap& messages() { return messages_; }
74  const MessageMap& messages() const { return messages_; }
75
76 private:
77  MessageMap messages_;
78};
79
80class NetworkTrayView : public TrayItemView {
81 public:
82  NetworkTrayView(ColorTheme size, bool tray_icon)
83      : color_theme_(size), tray_icon_(tray_icon) {
84    SetLayoutManager(
85        new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
86
87    image_view_ = color_theme_ == DARK ?
88        new FixedSizedImageView(0, kTrayPopupItemHeight) :
89        new views::ImageView;
90    AddChildView(image_view_);
91
92    NetworkIconInfo info;
93    Shell::GetInstance()->tray_delegate()->
94        GetMostRelevantNetworkIcon(&info, false);
95    Update(info);
96  }
97
98  virtual ~NetworkTrayView() {}
99
100  void Update(const NetworkIconInfo& info) {
101    image_view_->SetImage(info.image);
102    if (tray_icon_)
103      SetVisible(info.tray_icon_visible);
104    SchedulePaint();
105  }
106
107 private:
108  views::ImageView* image_view_;
109  ColorTheme color_theme_;
110  bool tray_icon_;
111
112  DISALLOW_COPY_AND_ASSIGN(NetworkTrayView);
113};
114
115class NetworkDefaultView : public TrayItemMore {
116 public:
117  NetworkDefaultView(SystemTrayItem* owner, bool show_more)
118      : TrayItemMore(owner, show_more) {
119    Update();
120  }
121
122  virtual ~NetworkDefaultView() {}
123
124  void Update() {
125    NetworkIconInfo info;
126    Shell::GetInstance()->tray_delegate()->
127        GetMostRelevantNetworkIcon(&info, true);
128    SetImage(&info.image);
129    SetLabel(info.description);
130    SetAccessibleName(info.description);
131  }
132
133 private:
134  DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView);
135};
136
137class NetworkListDetailedView : public NetworkListDetailedViewBase {
138 public:
139  NetworkListDetailedView(user::LoginStatus login, int header_string_id)
140      : NetworkListDetailedViewBase(login, header_string_id),
141        airplane_(NULL),
142        button_wifi_(NULL),
143        button_mobile_(NULL),
144        view_mobile_account_(NULL),
145        setup_mobile_account_(NULL),
146        other_wifi_(NULL),
147        turn_on_wifi_(NULL),
148        other_mobile_(NULL) {
149  }
150
151  virtual ~NetworkListDetailedView() {
152  }
153
154  // Overridden from NetworkListDetailedViewBase:
155
156  virtual void AppendHeaderButtons() OVERRIDE {
157    button_wifi_ = new TrayPopupHeaderButton(this,
158        IDR_AURA_UBER_TRAY_WIFI_ENABLED,
159        IDR_AURA_UBER_TRAY_WIFI_DISABLED,
160        IDR_AURA_UBER_TRAY_WIFI_ENABLED_HOVER,
161        IDR_AURA_UBER_TRAY_WIFI_DISABLED_HOVER,
162        IDS_ASH_STATUS_TRAY_WIFI);
163    button_wifi_->SetTooltipText(
164        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_WIFI));
165    button_wifi_->SetToggledTooltipText(
166        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_WIFI));
167    footer()->AddButton(button_wifi_);
168
169    button_mobile_ = new TrayPopupHeaderButton(this,
170        IDR_AURA_UBER_TRAY_CELLULAR_ENABLED,
171        IDR_AURA_UBER_TRAY_CELLULAR_DISABLED,
172        IDR_AURA_UBER_TRAY_CELLULAR_ENABLED_HOVER,
173        IDR_AURA_UBER_TRAY_CELLULAR_DISABLED_HOVER,
174        IDS_ASH_STATUS_TRAY_CELLULAR);
175    button_mobile_->SetTooltipText(
176        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_MOBILE));
177    button_mobile_->SetToggledTooltipText(
178        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_MOBILE));
179    footer()->AddButton(button_mobile_);
180
181    AppendInfoButtonToHeader();
182  }
183
184  virtual void UpdateHeaderButtons() OVERRIDE {
185    SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate();
186    button_wifi_->SetToggled(!delegate->GetWifiEnabled());
187    button_mobile_->SetToggled(!delegate->GetMobileEnabled());
188    button_mobile_->SetVisible(delegate->GetMobileAvailable());
189    UpdateSettingButton();
190  }
191
192  virtual void AppendNetworkEntries() OVERRIDE {
193    CreateScrollableList();
194
195    HoverHighlightView* container = new HoverHighlightView(this);
196    container->set_fixed_height(kTrayPopupItemHeight);
197    container->AddLabel(ui::ResourceBundle::GetSharedInstance().
198        GetLocalizedString(IDS_ASH_STATUS_TRAY_MOBILE_VIEW_ACCOUNT),
199        gfx::Font::NORMAL);
200    AddChildView(container);
201    view_mobile_account_ = container;
202
203    container = new HoverHighlightView(this);
204    container->set_fixed_height(kTrayPopupItemHeight);
205    container->AddLabel(ui::ResourceBundle::GetSharedInstance().
206        GetLocalizedString(IDS_ASH_STATUS_TRAY_SETUP_MOBILE),
207        gfx::Font::NORMAL);
208    AddChildView(container);
209    setup_mobile_account_ = container;
210  }
211
212  virtual void GetAvailableNetworkList(
213      std::vector<NetworkIconInfo>* list) OVERRIDE {
214    Shell::GetInstance()->tray_delegate()->GetAvailableNetworks(list);
215  }
216
217  virtual void RefreshNetworkScrollWithEmptyNetworkList() OVERRIDE {
218    ClearNetworkScrollWithEmptyNetworkList();
219    HoverHighlightView* container = new HoverHighlightView(this);
220    container->set_fixed_height(kTrayPopupItemHeight);
221
222    if (Shell::GetInstance()->tray_delegate()->GetWifiEnabled()) {
223      NetworkIconInfo info;
224      Shell::GetInstance()->tray_delegate()->
225          GetMostRelevantNetworkIcon(&info, true);
226      container->AddIconAndLabel(info.image,
227          info.description,
228          gfx::Font::NORMAL);
229      container->set_border(views::Border::CreateEmptyBorder(0,
230          kTrayPopupPaddingHorizontal, 0, 0));
231
232    } else {
233      container->AddLabel(ui::ResourceBundle::GetSharedInstance().
234          GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED),
235          gfx::Font::NORMAL);
236    }
237
238    scroll_content()->AddChildViewAt(container, 0);
239    scroll_content()->SizeToPreferredSize();
240    static_cast<views::View*>(scroller())->Layout();
241  }
242
243  virtual void UpdateNetworkEntries() OVERRIDE {
244    RefreshNetworkScrollWithUpdatedNetworkData();
245
246    view_mobile_account_->SetVisible(false);
247    setup_mobile_account_->SetVisible(false);
248
249    if (login() == user::LOGGED_IN_NONE)
250      return;
251
252    std::string carrier_id, topup_url, setup_url;
253    if (Shell::GetInstance()->tray_delegate()->
254            GetCellularCarrierInfo(&carrier_id,
255                                   &topup_url,
256                                   &setup_url)) {
257      if (carrier_id != carrier_id_) {
258        carrier_id_ = carrier_id;
259        if (!topup_url.empty())
260          topup_url_ = topup_url;
261      }
262
263      if (!setup_url.empty())
264        setup_url_ = setup_url;
265
266      if (!topup_url_.empty())
267        view_mobile_account_->SetVisible(true);
268      if (!setup_url_.empty())
269        setup_mobile_account_->SetVisible(true);
270    }
271  }
272
273  virtual void AppendCustomButtonsToBottomRow(
274      TrayPopupTextButtonContainer* bottom_row) OVERRIDE {
275    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
276    other_wifi_ = new TrayPopupTextButton(this,
277        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_WIFI));
278    bottom_row->AddTextButton(other_wifi_);
279
280    turn_on_wifi_ = new TrayPopupTextButton(this,
281        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_TURN_ON_WIFI));
282    bottom_row->AddTextButton(turn_on_wifi_);
283
284    other_mobile_ = new TrayPopupTextButton(this,
285        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_MOBILE));
286    bottom_row->AddTextButton(other_mobile_);
287  }
288
289  virtual void UpdateNetworkExtra() OVERRIDE {
290    if (login() == user::LOGGED_IN_LOCKED)
291      return;
292
293    SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate();
294    if (IsNetworkListEmpty() && !delegate->GetWifiEnabled()) {
295      turn_on_wifi_->SetVisible(true);
296      other_wifi_->SetVisible(false);
297    } else {
298      turn_on_wifi_->SetVisible(false);
299      other_wifi_->SetVisible(true);
300      other_wifi_->SetEnabled(delegate->GetWifiEnabled());
301    }
302    other_mobile_->SetVisible(delegate->GetMobileAvailable() &&
303                              delegate->GetMobileScanSupported());
304    if (other_mobile_->visible())
305      other_mobile_->SetEnabled(delegate->GetMobileEnabled());
306
307    turn_on_wifi_->parent()->Layout();
308  }
309
310  virtual void CustomButtonPressed(views::Button* sender,
311      const ui::Event& event) OVERRIDE {
312    ash::SystemTrayDelegate* delegate =
313        ash::Shell::GetInstance()->tray_delegate();
314    if (sender == button_wifi_)
315      delegate->ToggleWifi();
316    else if (sender == button_mobile_)
317      delegate->ToggleMobile();
318    else if (sender == other_mobile_)
319      delegate->ShowOtherCellular();
320    else if (sender == other_wifi_)
321      delegate->ShowOtherWifi();
322    else if (sender == turn_on_wifi_)
323      delegate->ToggleWifi();
324    else
325      NOTREACHED();
326  }
327
328  virtual bool CustomLinkClickedOn(views::View* sender) OVERRIDE {
329    ash::SystemTrayDelegate* delegate =
330        ash::Shell::GetInstance()->tray_delegate();
331    if (sender == view_mobile_account_) {
332      delegate->ShowCellularURL(topup_url_);
333      return true;
334    } else if (sender == setup_mobile_account_) {
335      delegate->ShowCellularURL(setup_url_);
336      return true;
337    } else if (sender == airplane_) {
338      delegate->ToggleAirplaneMode();
339      return true;
340    } else {
341      return false;
342    }
343  }
344
345 private:
346  std::string carrier_id_;
347  std::string topup_url_;
348  std::string setup_url_;
349
350  views::View* airplane_;
351  TrayPopupHeaderButton* button_wifi_;
352  TrayPopupHeaderButton* button_mobile_;
353  views::View* view_mobile_account_;
354  views::View* setup_mobile_account_;
355  TrayPopupTextButton* other_wifi_;
356  TrayPopupTextButton* turn_on_wifi_;
357  TrayPopupTextButton* other_mobile_;
358
359  DISALLOW_COPY_AND_ASSIGN(NetworkListDetailedView);
360};
361
362class NetworkWifiDetailedView : public NetworkDetailedView {
363 public:
364  explicit NetworkWifiDetailedView(bool wifi_enabled) {
365    SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
366                                          kTrayPopupPaddingHorizontal,
367                                          10,
368                                          kTrayPopupPaddingBetweenItems));
369    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
370    views::ImageView* image = new views::ImageView;
371    const int image_id = wifi_enabled ?
372        IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED;
373    image->SetImage(bundle.GetImageNamed(image_id).ToImageSkia());
374    AddChildView(image);
375
376    const int string_id = wifi_enabled ?
377        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED:
378        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
379    views::Label* label =
380        new views::Label(bundle.GetLocalizedString(string_id));
381    label->SetMultiLine(true);
382    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
383    AddChildView(label);
384  }
385
386  virtual ~NetworkWifiDetailedView() {}
387
388  // Overridden from NetworkDetailedView:
389
390  virtual void Init() OVERRIDE {
391  }
392
393  virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE {
394    return NetworkDetailedView::WIFI_VIEW;
395  }
396
397  virtual void Update() OVERRIDE {}
398
399 private:
400  DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView);
401};
402
403class NetworkMessageView : public views::View,
404                           public views::LinkListener {
405 public:
406  NetworkMessageView(TrayNetwork* tray,
407                     TrayNetwork::MessageType message_type,
408                     const NetworkMessages::Message& network_msg)
409      : tray_(tray),
410        message_type_(message_type) {
411    SetLayoutManager(
412        new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
413
414    if (!network_msg.title.empty()) {
415      views::Label* title = new views::Label(network_msg.title);
416      title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
417      title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD));
418      AddChildView(title);
419    }
420
421    if (!network_msg.message.empty()) {
422      views::Label* message = new views::Label(network_msg.message);
423      message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
424      message->SetMultiLine(true);
425      message->SizeToFit(kTrayNotificationContentsWidth);
426      AddChildView(message);
427    }
428
429    if (!network_msg.links.empty()) {
430      for (size_t i = 0; i < network_msg.links.size(); ++i) {
431        views::Link* link = new views::Link(network_msg.links[i]);
432        link->set_id(i);
433        link->set_listener(this);
434        link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
435        link->SetMultiLine(true);
436        link->SizeToFit(kTrayNotificationContentsWidth);
437        AddChildView(link);
438      }
439    }
440  }
441
442  virtual ~NetworkMessageView() {
443  }
444
445  // Overridden from views::LinkListener.
446  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
447    tray_->LinkClicked(message_type_, source->id());
448  }
449
450  TrayNetwork::MessageType message_type() const { return message_type_; }
451
452 private:
453  TrayNetwork* tray_;
454  TrayNetwork::MessageType message_type_;
455
456  DISALLOW_COPY_AND_ASSIGN(NetworkMessageView);
457};
458
459class NetworkNotificationView : public TrayNotificationView {
460 public:
461  explicit NetworkNotificationView(TrayNetwork* tray)
462      : TrayNotificationView(tray, 0) {
463    CreateMessageView();
464    InitView(network_message_view_);
465    SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
466        GetMessageIcon(network_message_view_->message_type())));
467  }
468
469  // Overridden from TrayNotificationView.
470  virtual void OnClose() OVERRIDE {
471    tray_network()->ClearNetworkMessage(network_message_view_->message_type());
472  }
473
474  virtual void OnClickAction() OVERRIDE {
475    if (network_message_view_->message_type() !=
476        TrayNetwork::MESSAGE_DATA_PROMO)
477      tray()->PopupDetailedView(0, true);
478  }
479
480  void Update() {
481    CreateMessageView();
482    UpdateViewAndImage(network_message_view_,
483        *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
484            GetMessageIcon(network_message_view_->message_type())));
485  }
486
487 private:
488  TrayNetwork* tray_network() {
489    return static_cast<TrayNetwork*>(tray());
490  }
491
492  void CreateMessageView() {
493    // Display the first (highest priority) message.
494    CHECK(!tray_network()->messages()->messages().empty());
495    NetworkMessages::MessageMap::const_iterator iter =
496        tray_network()->messages()->messages().begin();
497    network_message_view_ =
498        new NetworkMessageView(tray_network(), iter->first, iter->second);
499  }
500
501  tray::NetworkMessageView* network_message_view_;
502
503  DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView);
504};
505
506}  // namespace tray
507
508TrayNetwork::TrayNetwork()
509    : tray_(NULL),
510      default_(NULL),
511      detailed_(NULL),
512      notification_(NULL),
513      messages_(new tray::NetworkMessages()),
514      request_wifi_view_(false) {
515}
516
517TrayNetwork::~TrayNetwork() {
518}
519
520views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) {
521  CHECK(tray_ == NULL);
522  tray_ = new tray::NetworkTrayView(tray::LIGHT, true /*tray_icon*/);
523  return tray_;
524}
525
526views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) {
527  CHECK(default_ == NULL);
528  default_ =
529      new tray::NetworkDefaultView(this, status != user::LOGGED_IN_LOCKED);
530  return default_;
531}
532
533views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) {
534  CHECK(detailed_ == NULL);
535  // Clear any notifications when showing the detailed view.
536  messages_->messages().clear();
537  HideNotificationView();
538  if (request_wifi_view_) {
539    SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate();
540    // The Wi-Fi state is not toggled yet at this point.
541    detailed_ = new tray::NetworkWifiDetailedView(!delegate->GetWifiEnabled());
542    request_wifi_view_ = false;
543  } else {
544    detailed_ = new tray::NetworkListDetailedView(
545        status, IDS_ASH_STATUS_TRAY_NETWORK);
546    detailed_->Init();
547  }
548  return detailed_;
549}
550
551views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) {
552  CHECK(notification_ == NULL);
553  if (messages_->messages().empty())
554    return NULL;  // Message has already been cleared.
555  notification_ = new tray::NetworkNotificationView(this);
556  return notification_;
557}
558
559void TrayNetwork::DestroyTrayView() {
560  tray_ = NULL;
561}
562
563void TrayNetwork::DestroyDefaultView() {
564  default_ = NULL;
565}
566
567void TrayNetwork::DestroyDetailedView() {
568  detailed_ = NULL;
569}
570
571void TrayNetwork::DestroyNotificationView() {
572  notification_ = NULL;
573}
574
575void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) {
576}
577
578void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
579  SetTrayImageItemBorder(tray_, alignment);
580}
581
582void TrayNetwork::OnNetworkRefresh(const NetworkIconInfo& info) {
583  if (tray_)
584    tray_->Update(info);
585  if (default_)
586    default_->Update();
587  if (detailed_)
588    detailed_->Update();
589}
590
591void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate,
592                                   MessageType message_type,
593                                   const string16& title,
594                                   const string16& message,
595                                   const std::vector<string16>& links) {
596  messages_->messages()[message_type] =
597      tray::NetworkMessages::Message(delegate, title, message, links);
598  if (notification_)
599    notification_->Update();
600  else
601    ShowNotificationView();
602}
603
604void TrayNetwork::ClearNetworkMessage(MessageType message_type) {
605  messages_->messages().erase(message_type);
606  if (messages_->messages().empty()) {
607    HideNotificationView();
608    return;
609  }
610  if (notification_)
611    notification_->Update();
612  else
613    ShowNotificationView();
614}
615
616void TrayNetwork::OnWillToggleWifi() {
617  if (!detailed_ ||
618      detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) {
619    request_wifi_view_ = true;
620    PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
621  }
622}
623
624void TrayNetwork::LinkClicked(MessageType message_type, int link_id) {
625  tray::NetworkMessages::MessageMap::const_iterator iter =
626      messages()->messages().find(message_type);
627  if (iter != messages()->messages().end() && iter->second.delegate)
628    iter->second.delegate->NotificationLinkClicked(link_id);
629}
630
631}  // namespace internal
632}  // namespace ash
633