1// Copyright 2014 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 "ui/chromeos/network/network_list.h"
6
7#include "chromeos/dbus/dbus_thread_manager.h"
8#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
9#include "chromeos/dbus/power_manager_client.h"
10#include "chromeos/network/network_state.h"
11#include "chromeos/network/network_state_handler.h"
12#include "chromeos/network/network_state_handler_observer.h"
13#include "grit/ui_chromeos_strings.h"
14#include "ui/base/resource/resource_bundle.h"
15#include "ui/chromeos/network/network_icon.h"
16#include "ui/chromeos/network/network_icon_animation.h"
17#include "ui/chromeos/network/network_info.h"
18#include "ui/chromeos/network/network_list_delegate.h"
19#include "ui/gfx/font.h"
20#include "ui/views/controls/label.h"
21#include "ui/views/view.h"
22
23using chromeos::NetworkHandler;
24using chromeos::NetworkStateHandler;
25using chromeos::NetworkTypePattern;
26
27namespace ui {
28
29// NetworkListView:
30
31NetworkListView::NetworkListView(NetworkListDelegate* delegate)
32    : delegate_(delegate),
33      content_(NULL),
34      scanning_view_(NULL),
35      no_wifi_networks_view_(NULL),
36      no_cellular_networks_view_(NULL) {
37  CHECK(delegate);
38}
39
40NetworkListView::~NetworkListView() {
41  network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
42}
43
44void NetworkListView::UpdateNetworkList() {
45  CHECK(content_);
46  NetworkStateHandler::NetworkStateList network_list;
47  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
48  handler->GetVisibleNetworkList(&network_list);
49  UpdateNetworks(network_list);
50  UpdateNetworkListInternal();
51}
52
53bool NetworkListView::IsViewInList(views::View* view,
54                                   std::string* service_path) const {
55  std::map<views::View*, std::string>::const_iterator found =
56      network_map_.find(view);
57  if (found == network_map_.end())
58    return false;
59  *service_path = found->second;
60  return true;
61}
62
63void NetworkListView::UpdateNetworks(
64    const NetworkStateHandler::NetworkStateList& networks) {
65  network_list_.clear();
66  const NetworkTypePattern pattern = delegate_->GetNetworkTypePattern();
67  for (NetworkStateHandler::NetworkStateList::const_iterator iter =
68           networks.begin();
69       iter != networks.end();
70       ++iter) {
71    const chromeos::NetworkState* network = *iter;
72    if (!pattern.MatchesType(network->type()))
73      continue;
74    NetworkInfo* info = new NetworkInfo(network->path());
75    network_list_.push_back(info);
76  }
77}
78
79void NetworkListView::UpdateNetworkListInternal() {
80  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
81
82  // First, update state for all networks
83  bool animating = false;
84  for (size_t i = 0; i < network_list_.size(); ++i) {
85    NetworkInfo* info = network_list_[i];
86    const chromeos::NetworkState* network =
87        handler->GetNetworkState(info->service_path);
88    if (!network)
89      continue;
90    info->image =
91        network_icon::GetImageForNetwork(network, network_icon::ICON_TYPE_LIST);
92    info->label =
93        network_icon::GetLabelForNetwork(network, network_icon::ICON_TYPE_LIST);
94    info->highlight =
95        network->IsConnectedState() || network->IsConnectingState();
96    info->disable =
97        network->activation_state() == shill::kActivationStateActivating;
98    if (!animating && network->IsConnectingState())
99      animating = true;
100  }
101
102  if (animating)
103    network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
104  else
105    network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
106
107  // Get the updated list entries
108  network_map_.clear();
109  std::set<std::string> new_service_paths;
110  bool needs_relayout = UpdateNetworkListEntries(&new_service_paths);
111
112  // Remove old children
113  std::set<std::string> remove_service_paths;
114  for (ServicePathMap::const_iterator it = service_path_map_.begin();
115       it != service_path_map_.end();
116       ++it) {
117    if (new_service_paths.find(it->first) == new_service_paths.end()) {
118      remove_service_paths.insert(it->first);
119      network_map_.erase(it->second);
120      content_->RemoveChildView(it->second);
121      needs_relayout = true;
122    }
123  }
124
125  for (std::set<std::string>::const_iterator remove_it =
126           remove_service_paths.begin();
127       remove_it != remove_service_paths.end();
128       ++remove_it) {
129    service_path_map_.erase(*remove_it);
130  }
131
132  if (needs_relayout) {
133    views::View* selected_view = NULL;
134    for (ServicePathMap::const_iterator iter = service_path_map_.begin();
135         iter != service_path_map_.end();
136         ++iter) {
137      if (delegate_->IsViewHovered(iter->second)) {
138        selected_view = iter->second;
139        break;
140      }
141    }
142    content_->SizeToPreferredSize();
143    delegate_->RelayoutScrollList();
144    if (selected_view)
145      content_->ScrollRectToVisible(selected_view->bounds());
146  }
147}
148
149bool NetworkListView::UpdateNetworkListEntries(
150    std::set<std::string>* new_service_paths) {
151  bool needs_relayout = false;
152  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
153
154  // Insert child views
155  int index = 0;
156
157  // Highlighted networks
158  for (size_t i = 0; i < network_list_.size(); ++i) {
159    const NetworkInfo* info = network_list_[i];
160    if (info->highlight) {
161      if (UpdateNetworkChild(index++, info))
162        needs_relayout = true;
163      new_service_paths->insert(info->service_path);
164    }
165  }
166
167  const NetworkTypePattern pattern = delegate_->GetNetworkTypePattern();
168  if (pattern.MatchesPattern(NetworkTypePattern::Cellular())) {
169    // Cellular initializing
170    int message_id = network_icon::GetCellularUninitializedMsg();
171    if (!message_id &&
172        handler->IsTechnologyEnabled(NetworkTypePattern::Mobile()) &&
173        !handler->FirstNetworkByType(NetworkTypePattern::Mobile())) {
174      message_id = IDS_ASH_STATUS_TRAY_NO_CELLULAR_NETWORKS;
175    }
176    if (UpdateInfoLabel(message_id, index, &no_cellular_networks_view_))
177      needs_relayout = true;
178    if (message_id)
179      ++index;
180  }
181
182  if (pattern.MatchesPattern(NetworkTypePattern::WiFi())) {
183    // "Wifi Enabled / Disabled"
184    int message_id = 0;
185    if (network_list_.empty()) {
186      message_id = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi())
187                       ? IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED
188                       : IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
189    }
190    if (UpdateInfoLabel(message_id, index, &no_wifi_networks_view_))
191      needs_relayout = true;
192    if (message_id)
193      ++index;
194
195    // "Wifi Scanning"
196    message_id = 0;
197    if (handler->GetScanningByType(NetworkTypePattern::WiFi()))
198      message_id = IDS_ASH_STATUS_TRAY_WIFI_SCANNING_MESSAGE;
199    if (UpdateInfoLabel(message_id, index, &scanning_view_))
200      needs_relayout = true;
201    if (message_id)
202      ++index;
203  }
204
205  // Un-highlighted networks
206  for (size_t i = 0; i < network_list_.size(); ++i) {
207    const NetworkInfo* info = network_list_[i];
208    if (!info->highlight) {
209      if (UpdateNetworkChild(index++, info))
210        needs_relayout = true;
211      new_service_paths->insert(info->service_path);
212    }
213  }
214
215  // No networks or other messages (fallback)
216  if (index == 0) {
217    int message_id = 0;
218    if (pattern.Equals(NetworkTypePattern::VPN()))
219      message_id = IDS_ASH_STATUS_TRAY_NETWORK_NO_VPN;
220    else
221      message_id = IDS_ASH_STATUS_TRAY_NO_NETWORKS;
222    if (UpdateInfoLabel(message_id, index, &scanning_view_))
223      needs_relayout = true;
224  }
225
226  return needs_relayout;
227}
228
229bool NetworkListView::UpdateNetworkChild(int index, const NetworkInfo* info) {
230  bool needs_relayout = false;
231  views::View* container = NULL;
232  ServicePathMap::const_iterator found =
233      service_path_map_.find(info->service_path);
234  if (found == service_path_map_.end()) {
235    container = delegate_->CreateViewForNetwork(*info);
236    content_->AddChildViewAt(container, index);
237    needs_relayout = true;
238  } else {
239    container = found->second;
240    container->RemoveAllChildViews(true);
241    delegate_->UpdateViewForNetwork(container, *info);
242    container->Layout();
243    container->SchedulePaint();
244    needs_relayout = PlaceViewAtIndex(container, index);
245  }
246  if (info->disable)
247    container->SetEnabled(false);
248  network_map_[container] = info->service_path;
249  service_path_map_[info->service_path] = container;
250  return needs_relayout;
251}
252
253bool NetworkListView::PlaceViewAtIndex(views::View* view, int index) {
254  if (content_->child_at(index) == view)
255    return false;
256  content_->ReorderChildView(view, index);
257  return true;
258}
259
260bool NetworkListView::UpdateInfoLabel(int message_id,
261                                      int index,
262                                      views::Label** label) {
263  CHECK(label);
264  bool needs_relayout = false;
265  if (message_id) {
266    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
267    base::string16 text = rb.GetLocalizedString(message_id);
268    if (!*label) {
269      *label = delegate_->CreateInfoLabel();
270      (*label)->SetText(text);
271      content_->AddChildViewAt(*label, index);
272      needs_relayout = true;
273    } else {
274      (*label)->SetText(text);
275      needs_relayout = PlaceViewAtIndex(*label, index);
276    }
277  } else if (*label) {
278    content_->RemoveChildView(*label);
279    delete *label;
280    *label = NULL;
281    needs_relayout = true;
282  }
283  return needs_relayout;
284}
285
286void NetworkListView::NetworkIconChanged() {
287  UpdateNetworkList();
288}
289
290}  // namespace ui
291