tray_network.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/metrics/user_metrics_recorder.h"
9#include "ash/shell.h"
10#include "ash/system/chromeos/network/network_icon_animation.h"
11#include "ash/system/chromeos/network/network_state_list_detailed_view.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_utils.h"
20#include "base/command_line.h"
21#include "base/strings/utf_string_conversions.h"
22#include "chromeos/network/network_state.h"
23#include "chromeos/network/network_state_handler.h"
24#include "grit/ash_resources.h"
25#include "grit/ash_strings.h"
26#include "third_party/cros_system_api/dbus/service_constants.h"
27#include "ui/accessibility/ax_view_state.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/resource/resource_bundle.h"
30#include "ui/views/controls/image_view.h"
31#include "ui/views/controls/link.h"
32#include "ui/views/controls/link_listener.h"
33#include "ui/views/layout/box_layout.h"
34#include "ui/views/widget/widget.h"
35
36using chromeos::NetworkHandler;
37using chromeos::NetworkState;
38using chromeos::NetworkStateHandler;
39using chromeos::NetworkTypePattern;
40
41namespace ash {
42namespace tray {
43
44class NetworkTrayView : public TrayItemView,
45                        public network_icon::AnimationObserver {
46 public:
47  explicit NetworkTrayView(TrayNetwork* network_tray)
48      : TrayItemView(network_tray),
49        network_tray_(network_tray) {
50    SetLayoutManager(
51        new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
52
53    image_view_ = new views::ImageView;
54    AddChildView(image_view_);
55
56    UpdateNetworkStateHandlerIcon();
57  }
58
59  virtual ~NetworkTrayView() {
60    network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
61  }
62
63  virtual const char* GetClassName() const OVERRIDE {
64    return "NetworkTrayView";
65  }
66
67  void UpdateNetworkStateHandlerIcon() {
68    NetworkStateHandler* handler =
69        NetworkHandler::Get()->network_state_handler();
70    gfx::ImageSkia image;
71    base::string16 name;
72    bool animating = false;
73    network_icon::GetDefaultNetworkImageAndLabel(
74        network_icon::ICON_TYPE_TRAY, &image, &name, &animating);
75    bool show_in_tray = !image.isNull();
76    UpdateIcon(show_in_tray, image);
77    if (animating)
78      network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
79    else
80      network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
81    // Update accessibility.
82    const NetworkState* connected_network =
83        handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
84    if (connected_network) {
85      UpdateConnectionStatus(
86          base::UTF8ToUTF16(connected_network->name()), true);
87    } else {
88      UpdateConnectionStatus(base::string16(), false);
89    }
90  }
91
92  void UpdateAlignment(ShelfAlignment alignment) {
93    SetLayoutManager(new views::BoxLayout(
94        alignment == SHELF_ALIGNMENT_BOTTOM ?
95            views::BoxLayout::kHorizontal : views::BoxLayout::kVertical,
96            0, 0, 0));
97    Layout();
98  }
99
100  // views::View override.
101  virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
102    state->name = connection_status_string_;
103    state->role = ui::AX_ROLE_BUTTON;
104  }
105
106  // network_icon::AnimationObserver
107  virtual void NetworkIconChanged() OVERRIDE {
108    UpdateNetworkStateHandlerIcon();
109  }
110
111 private:
112  // Updates connection status and notifies accessibility event when necessary.
113  void UpdateConnectionStatus(const base::string16& network_name,
114                              bool connected) {
115    base::string16 new_connection_status_string;
116    if (connected) {
117      new_connection_status_string = l10n_util::GetStringFUTF16(
118          IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, network_name);
119    }
120    if (new_connection_status_string != connection_status_string_) {
121      connection_status_string_ = new_connection_status_string;
122      if(!connection_status_string_.empty())
123        NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
124    }
125  }
126
127  void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image) {
128    image_view_->SetImage(image);
129    SetVisible(tray_icon_visible);
130    SchedulePaint();
131  }
132
133  TrayNetwork* network_tray_;
134  views::ImageView* image_view_;
135  base::string16 connection_status_string_;
136
137  DISALLOW_COPY_AND_ASSIGN(NetworkTrayView);
138};
139
140class NetworkDefaultView : public TrayItemMore,
141                           public network_icon::AnimationObserver {
142 public:
143  NetworkDefaultView(TrayNetwork* network_tray, bool show_more)
144      : TrayItemMore(network_tray, show_more),
145        network_tray_(network_tray) {
146    Update();
147  }
148
149  virtual ~NetworkDefaultView() {
150    network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
151  }
152
153  void Update() {
154    gfx::ImageSkia image;
155    base::string16 label;
156    bool animating = false;
157    network_icon::GetDefaultNetworkImageAndLabel(
158        network_icon::ICON_TYPE_DEFAULT_VIEW, &image, &label, &animating);
159    if (animating)
160      network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
161    else
162      network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
163    SetImage(&image);
164    SetLabel(label);
165    SetAccessibleName(label);
166  }
167
168  // network_icon::AnimationObserver
169  virtual void NetworkIconChanged() OVERRIDE {
170    Update();
171  }
172
173 private:
174  TrayNetwork* network_tray_;
175
176  DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView);
177};
178
179class NetworkWifiDetailedView : public NetworkDetailedView {
180 public:
181  explicit NetworkWifiDetailedView(SystemTrayItem* owner)
182      : NetworkDetailedView(owner) {
183    SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
184                                          kTrayPopupPaddingHorizontal,
185                                          10,
186                                          kTrayPopupPaddingBetweenItems));
187    image_view_ = new views::ImageView;
188    AddChildView(image_view_);
189
190    label_view_ = new views::Label();
191    label_view_->SetMultiLine(true);
192    label_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
193    AddChildView(label_view_);
194
195    Update();
196  }
197
198  virtual ~NetworkWifiDetailedView() {
199  }
200
201  // Overridden from NetworkDetailedView:
202
203  virtual void Init() OVERRIDE {
204  }
205
206  virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE {
207    return NetworkDetailedView::WIFI_VIEW;
208  }
209
210  virtual void ManagerChanged() OVERRIDE {
211    Update();
212  }
213
214  virtual void NetworkListChanged() OVERRIDE {
215    Update();
216  }
217
218  virtual void NetworkServiceChanged(
219      const chromeos::NetworkState* network) OVERRIDE {
220  }
221
222 private:
223  virtual void Layout() OVERRIDE {
224    // Center both views vertically.
225    views::View::Layout();
226    image_view_->SetY(
227        (height() - image_view_->GetPreferredSize().height()) / 2);
228    label_view_->SetY(
229        (height() - label_view_->GetPreferredSize().height()) / 2);
230  }
231
232  void Update() {
233    bool wifi_enabled =
234        NetworkHandler::Get()->network_state_handler()->IsTechnologyEnabled(
235            NetworkTypePattern::WiFi());
236    const int image_id = wifi_enabled ?
237        IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED;
238    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
239    image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia());
240
241    const int string_id = wifi_enabled ?
242        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED :
243        IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
244    label_view_->SetText(bundle.GetLocalizedString(string_id));
245    label_view_->SizeToFit(kTrayPopupMinWidth -
246        kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems -
247        kTrayPopupDetailsIconWidth);
248  }
249
250  views::ImageView* image_view_;
251  views::Label* label_view_;
252
253  DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView);
254};
255
256}  // namespace tray
257
258TrayNetwork::TrayNetwork(SystemTray* system_tray)
259    : SystemTrayItem(system_tray),
260      tray_(NULL),
261      default_(NULL),
262      detailed_(NULL),
263      request_wifi_view_(false) {
264  network_state_observer_.reset(new TrayNetworkStateObserver(this));
265  SystemTrayNotifier* notifier = Shell::GetInstance()->system_tray_notifier();
266  notifier->AddNetworkObserver(this);
267  notifier->AddNetworkPortalDetectorObserver(this);
268}
269
270TrayNetwork::~TrayNetwork() {
271  SystemTrayNotifier* notifier = Shell::GetInstance()->system_tray_notifier();
272  notifier->RemoveNetworkObserver(this);
273  notifier->RemoveNetworkPortalDetectorObserver(this);
274}
275
276views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) {
277  CHECK(tray_ == NULL);
278  if (!chromeos::NetworkHandler::IsInitialized())
279    return NULL;
280  tray_ = new tray::NetworkTrayView(this);
281  return tray_;
282}
283
284views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) {
285  CHECK(default_ == NULL);
286  if (!chromeos::NetworkHandler::IsInitialized())
287    return NULL;
288  CHECK(tray_ != NULL);
289  default_ = new tray::NetworkDefaultView(
290      this, status != user::LOGGED_IN_LOCKED);
291  return default_;
292}
293
294views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) {
295  CHECK(detailed_ == NULL);
296  Shell::GetInstance()->metrics()->RecordUserMetricsAction(
297    ash::UMA_STATUS_AREA_DETAILED_NETWORK_VIEW);
298  if (!chromeos::NetworkHandler::IsInitialized())
299    return NULL;
300  if (request_wifi_view_) {
301    detailed_ = new tray::NetworkWifiDetailedView(this);
302    request_wifi_view_ = false;
303  } else {
304    detailed_ = new tray::NetworkStateListDetailedView(
305        this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status);
306    detailed_->Init();
307  }
308  return detailed_;
309}
310
311void TrayNetwork::DestroyTrayView() {
312  tray_ = NULL;
313}
314
315void TrayNetwork::DestroyDefaultView() {
316  default_ = NULL;
317}
318
319void TrayNetwork::DestroyDetailedView() {
320  detailed_ = NULL;
321}
322
323void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) {
324}
325
326void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
327  if (tray_) {
328    SetTrayImageItemBorder(tray_, alignment);
329    tray_->UpdateAlignment(alignment);
330  }
331}
332
333void TrayNetwork::RequestToggleWifi() {
334  // This will always be triggered by a user action (e.g. keyboard shortcut)
335  if (!detailed_ ||
336      detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) {
337    request_wifi_view_ = true;
338    PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
339  }
340  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
341  bool enabled = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi());
342  Shell::GetInstance()->metrics()->RecordUserMetricsAction(
343      enabled ?
344      ash::UMA_STATUS_AREA_DISABLE_WIFI :
345      ash::UMA_STATUS_AREA_ENABLE_WIFI);
346  handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(),
347                                !enabled,
348                                chromeos::network_handler::ErrorCallback());
349}
350
351void TrayNetwork::OnCaptivePortalDetected(
352    const std::string& /* service_path */) {
353  NetworkStateChanged(false);
354}
355
356void TrayNetwork::NetworkStateChanged(bool list_changed) {
357  if (tray_)
358    tray_->UpdateNetworkStateHandlerIcon();
359  if (default_)
360    default_->Update();
361  if (detailed_) {
362    if (list_changed)
363      detailed_->NetworkListChanged();
364    else
365      detailed_->ManagerChanged();
366  }
367}
368
369void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) {
370  if (detailed_)
371    detailed_->NetworkServiceChanged(network);
372}
373
374}  // namespace ash
375