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