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