Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1105)

Side by Side Diff: ash/system/chromeos/network/vpn_list_view.cc

Issue 980943005: Add ash UI for third-party VPNs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@f_2_407541_434711_remove_combined_name
Patch Set: Added a VPNProvider::Key abstraction. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ash/system/chromeos/network/vpn_list_view.h ('k') | ash/system/tray/hover_highlight_view.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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/vpn_list_view.h"
6
7 #include <utility>
8 #include <vector>
9
10 #include "ash/metrics/user_metrics_recorder.h"
11 #include "ash/shell.h"
12 #include "ash/system/chromeos/network/vpn_delegate.h"
13 #include "ash/system/tray/hover_highlight_view.h"
14 #include "ash/system/tray/system_tray_delegate.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chromeos/network/network_connection_handler.h"
23 #include "chromeos/network/network_handler.h"
24 #include "chromeos/network/network_state.h"
25 #include "chromeos/network/network_type_pattern.h"
26 #include "grit/ash_resources.h"
27 #include "grit/ash_strings.h"
28 #include "third_party/skia/include/core/SkColor.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/chromeos/network/network_icon.h"
32 #include "ui/chromeos/network/network_icon_animation.h"
33 #include "ui/chromeos/network/network_icon_animation_observer.h"
34 #include "ui/chromeos/network/network_list_delegate.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/gfx/geometry/rect.h"
37 #include "ui/gfx/geometry/size.h"
38 #include "ui/gfx/image/image_skia.h"
39 #include "ui/gfx/text_constants.h"
40 #include "ui/views/border.h"
41 #include "ui/views/controls/label.h"
42 #include "ui/views/layout/box_layout.h"
43 #include "ui/views/view.h"
44
45 namespace ash {
46
47 namespace {
48
49 bool IsConnectedOrConnecting(const chromeos::NetworkState* network) {
50 return network->IsConnectedState() || network->IsConnectingState();
51 }
52
53 void IgnoreDisconnectError(const std::string& error_name,
54 scoped_ptr<base::DictionaryValue> error_data) {
55 }
56
57 // The base class of all list entries, a |HoverHighlightView| with no border.
58 class VPNListEntryBase : public HoverHighlightView {
59 public:
60 // When the user clicks the entry, the |parent|'s OnViewClicked() will be
61 // invoked.
62 explicit VPNListEntryBase(VPNListView* parent);
63
64 private:
65 DISALLOW_COPY_AND_ASSIGN(VPNListEntryBase);
66 };
67
68 // A list entry that represents a VPN provider.
69 class VPNListProviderEntry : public VPNListEntryBase {
70 public:
71 VPNListProviderEntry(VPNListView* parent, const std::string& name);
72
73 private:
74 DISALLOW_COPY_AND_ASSIGN(VPNListProviderEntry);
75 };
76
77 // A list entry that represents a network. If the network is currently
78 // connecting, the icon shown by this list entry will be animated.
79 class VPNListNetworkEntry : public VPNListEntryBase,
80 public ui::network_icon::AnimationObserver {
81 public:
82 VPNListNetworkEntry(VPNListView* parent,
83 const chromeos::NetworkState* network);
84 ~VPNListNetworkEntry() override;
85
86 // ui::network_icon::AnimationObserver:
87 void NetworkIconChanged() override;
88
89 private:
90 void UpdateFromNetworkState(const chromeos::NetworkState* network);
91
92 const std::string service_path_;
93
94 DISALLOW_COPY_AND_ASSIGN(VPNListNetworkEntry);
95 };
96
97 // A list entry that represents the disconnect button shown directly underneath
98 // the currently connected or connecting network.
99 class VPNDisconnectButtonEntry : public VPNListEntryBase {
100 public:
101 explicit VPNDisconnectButtonEntry(VPNListView* parent);
102
103 private:
104 DISALLOW_COPY_AND_ASSIGN(VPNDisconnectButtonEntry);
105 };
106
107 // A separator placed after the list of networks for each provider.
108 class ScrollSeparator : public views::View {
109 public:
110 ScrollSeparator();
111
112 // views::View:
113 void OnPaint(gfx::Canvas* canvas) override;
114 gfx::Size GetPreferredSize() const override;
115
116 private:
117 DISALLOW_COPY_AND_ASSIGN(ScrollSeparator);
118 };
119
120 VPNListEntryBase::VPNListEntryBase(VPNListView* parent)
121 : HoverHighlightView(parent) {
122 SetBorder(
123 views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, 0, 0));
124 }
125
126 VPNListProviderEntry::VPNListProviderEntry(VPNListView* parent,
127 const std::string& name)
128 : VPNListEntryBase(parent) {
129 views::Label* const label =
130 AddLabel(base::UTF8ToUTF16(name), gfx::ALIGN_LEFT, false /* highlight */);
131 label->SetBorder(views::Border::CreateEmptyBorder(5, 0, 5, 0));
132 label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
133 ui::ResourceBundle::BoldFont));
134 label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0));
135 }
136
137 VPNListNetworkEntry::VPNListNetworkEntry(VPNListView* parent,
138 const chromeos::NetworkState* network)
139 : VPNListEntryBase(parent), service_path_(network->path()) {
140 UpdateFromNetworkState(network);
141 }
142
143 VPNListNetworkEntry::~VPNListNetworkEntry() {
144 ui::network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
145 }
146
147 void VPNListNetworkEntry::UpdateFromNetworkState(
148 const chromeos::NetworkState* network) {
149 if (network && network->IsConnectingState())
150 ui::network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
151 else
152 ui::network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
153
154 if (!network)
155 return;
stevenjb 2015/03/12 18:29:24 This generally shouldn't happen, at least not for
bartfab (slow) 2015/03/17 10:48:32 Done.
156
157 RemoveAllChildViews(true);
158 AddIconAndLabel(ui::network_icon::GetImageForNetwork(
159 network, ui::network_icon::ICON_TYPE_LIST),
160 ui::network_icon::GetLabelForNetwork(
161 network, ui::network_icon::ICON_TYPE_LIST),
162 IsConnectedOrConnecting(network));
163 Layout();
164 }
165
166 void VPNListNetworkEntry::NetworkIconChanged() {
167 UpdateFromNetworkState(
168 chromeos::NetworkHandler::Get()->network_state_handler()->GetNetworkState(
169 service_path_));
170 }
171
172 VPNDisconnectButtonEntry::VPNDisconnectButtonEntry(VPNListView* parent)
173 : VPNListEntryBase(parent) {
174 AddIconAndLabel(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
175 IDR_AURA_UBER_TRAY_VPN_DISCONNECT),
176 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_VPN_DISCONNECT),
177 false /* highlight */);
178 }
179
180 ScrollSeparator::ScrollSeparator() {
181 }
182
183 void ScrollSeparator::OnPaint(gfx::Canvas* canvas) {
184 canvas->FillRect(gfx::Rect(0, height() / 2, width(), 1), kBorderLightColor);
185 }
186
187 gfx::Size ScrollSeparator::GetPreferredSize() const {
188 return gfx::Size(1, kTrayPopupScrollSeparatorHeight);
189 }
190
191 } // namespace
192
193 VPNListView::VPNListView(ui::NetworkListDelegate* delegate)
194 : delegate_(delegate) {
195 Shell::GetInstance()->system_tray_delegate()->GetVPNDelegate()->AddObserver(
196 this);
197 }
198
199 VPNListView::~VPNListView() {
200 Shell::GetInstance()
201 ->system_tray_delegate()
202 ->GetVPNDelegate()
203 ->RemoveObserver(this);
204 }
205
206 void VPNListView::Update() {
207 // Before updating the list, determine whether the user was hovering over the
208 // disconnect button, one of the VPN provider entries or one of the network
209 // entries.
210 const bool hovered_disconnect_button =
211 disconnect_button_ && disconnect_button_->hover();
212 scoped_ptr<VPNProvider::Key> hovered_provider_key;
213 std::string hovered_network_service_path;
214 if (!hovered_disconnect_button) {
215 for (const std::pair<const views::View* const, VPNProvider::Key>& provider :
216 provider_view_key_map_) {
217 if (static_cast<const HoverHighlightView*>(provider.first)->hover()) {
218 hovered_provider_key.reset(new VPNProvider::Key(provider.second));
219 break;
220 }
221 }
222 if (!hovered_provider_key) {
223 for (const std::pair<const views::View*, std::string>& entry :
224 network_view_service_path_map_) {
225 if (static_cast<const HoverHighlightView*>(entry.first)->hover()) {
226 hovered_network_service_path = entry.second;
227 break;
228 }
229 }
230 }
231 }
232
233 // Clear the list.
234 container_->RemoveAllChildViews(true);
235 connected_or_connecting_service_path_.clear();
236 disconnect_button_ = nullptr;
237 provider_view_key_map_.clear();
238 network_view_service_path_map_.clear();
239
240 // Get the list of available VPN networks, in shill's priority order.
241 chromeos::NetworkStateHandler::NetworkStateList networks;
242 chromeos::NetworkHandler::Get()
243 ->network_state_handler()
244 ->GetVisibleNetworkListByType(chromeos::NetworkTypePattern::VPN(),
245 &networks);
246
247 if (!networks.empty() && IsConnectedOrConnecting(networks.front())) {
248 // If there is a connected or connecting network, show only this network,
249 // followed by a disconnect button.
250 AddConnectedOrConnectingNetwork(networks.front());
251 } else {
252 // If there is no connected or connecting network, show a list of all
253 // configured networks.
254 AddProvidersAndNetworks(networks);
255 }
256
257 // Determine whether one of the new list entries corresponds to the entry that
258 // the user was previously hovering over. If such an entry is found, the list
259 // will be scrolled to ensure the entry is visible.
260 const views::View* scroll_to_show_view = nullptr;
261 if (hovered_disconnect_button) {
262 scroll_to_show_view = disconnect_button_;
263 } else if (hovered_provider_key) {
264 for (const std::pair<const views::View* const, VPNProvider::Key>& provider :
265 provider_view_key_map_) {
266 if (provider.second == *hovered_provider_key) {
267 scroll_to_show_view = provider.first;
268 break;
269 }
270 }
271 } else if (!hovered_network_service_path.empty()) {
272 for (const std::pair<const views::View*, std::string>& entry :
273 network_view_service_path_map_) {
274 if (entry.second == hovered_network_service_path) {
275 scroll_to_show_view = entry.first;
276 break;
277 }
278 }
279 }
280
281 // Layout the updated list.
282 container_->SizeToPreferredSize();
283 delegate_->RelayoutScrollList();
284
285 if (scroll_to_show_view) {
286 // Scroll the list so that |scroll_to_show_view| is in view.
287 container_->ScrollRectToVisible(scroll_to_show_view->bounds());
288 }
289 }
290
291 bool VPNListView::IsNetworkEntry(views::View* view,
292 std::string* service_path) const {
293 const auto& entry = network_view_service_path_map_.find(view);
294 if (entry == network_view_service_path_map_.end())
295 return false;
296 *service_path = entry->second;
297 return true;
298 }
299
300 void VPNListView::OnVPNProvidersChanged() {
301 Update();
302 }
303
304 void VPNListView::OnViewClicked(views::View* sender) {
305 if (sender == disconnect_button_) {
306 // If the user clicked on the network button, disconnect from the currently
307 // connected or connecting network.
308 DCHECK(!connected_or_connecting_service_path_.empty());
309 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
310 UMA_STATUS_AREA_VPN_DISCONNECT_CLICKED);
311 chromeos::NetworkHandler::Get()
312 ->network_connection_handler()
313 ->DisconnectNetwork(connected_or_connecting_service_path_,
314 base::Bind(&base::DoNothing),
315 base::Bind(&IgnoreDisconnectError));
316 return;
317 }
318
319 const auto& provider = provider_view_key_map_.find(sender);
320 if (provider != provider_view_key_map_.end()) {
321 // If the user clicks on a provider entry, request that the "add network"
322 // dialog for this provider be shown.
323 const VPNProvider::Key& key = provider->second;
324 Shell* shell = Shell::GetInstance();
325 shell->metrics()->RecordUserMetricsAction(
326 key.third_party ? UMA_STATUS_AREA_VPN_ADD_THIRD_PARTY_CLICKED
327 : UMA_STATUS_AREA_VPN_ADD_BUILT_IN_CLICKED);
328 shell->system_tray_delegate()->GetVPNDelegate()->ShowAddPage(key);
329 return;
330 }
331
332 // If the user clicked on a network entry, let the |delegate_| trigger a
333 // connection attempt (if the network is currently disconnected) or show a
334 // configuration dialog (if the network is currently connected or connecting).
335 delegate_->OnViewClicked(sender);
336 }
337
338 void VPNListView::AddConnectedOrConnectingNetwork(
339 const chromeos::NetworkState* network) {
340 connected_or_connecting_service_path_ = network->path();
341 views::View* entry(new VPNListNetworkEntry(this, network));
342 container_->AddChildView(entry);
343 network_view_service_path_map_[entry] = network->path();
344 disconnect_button_ = new VPNDisconnectButtonEntry(this);
345 container_->AddChildView(disconnect_button_);
346 }
347
348 void VPNListView::AddProviderAndNetworks(
349 const VPNProvider::Key& key,
350 const std::string& name,
351 const chromeos::NetworkStateHandler::NetworkStateList& networks) {
352 // Add a list entry for the VPN provider.
353 views::View* provider(new VPNListProviderEntry(this, name));
354 container_->AddChildView(provider);
355 provider_view_key_map_[provider] = key;
356 // Add the networks belonging to this provider, in the priority order returned
357 // by shill.
358 for (const chromeos::NetworkState* const& network : networks) {
359 if (key.MatchesNetwork(*network)) {
360 views::View* entry(new VPNListNetworkEntry(this, network));
361 container_->AddChildView(entry);
362 network_view_service_path_map_[entry] = network->path();
363 }
364 }
365 // Add a visual separator.
366 container_->AddChildView(new ScrollSeparator);
367 }
368
369 void VPNListView::AddProvidersAndNetworks(
370 const chromeos::NetworkStateHandler::NetworkStateList& networks) {
371 // Get the list of VPN providers enabled in the primary user's profile.
372 std::vector<VPNProvider> providers = Shell::GetInstance()
373 ->system_tray_delegate()
374 ->GetVPNDelegate()
375 ->GetVPNProviders();
376
377 // Add providers with at least one configured network along with their
378 // networks. Providers are added in the order of their highest priority
379 // network.
380 for (const chromeos::NetworkState* const& network : networks) {
381 for (auto provider = providers.begin(); provider != providers.end();
382 ++provider) {
383 if (!provider->key.MatchesNetwork(*network))
384 continue;
385 AddProviderAndNetworks(provider->key, provider->name, networks);
386 providers.erase(provider);
387 break;
388 }
389 }
390
391 // Add providers without any configured networks, in the order that the
392 // providers were returned by the extensions system.
393 for (const VPNProvider& provider : providers)
394 AddProviderAndNetworks(provider.key, provider.name, networks);
395 }
396
397 } // namespace ash
OLDNEW
« no previous file with comments | « ash/system/chromeos/network/vpn_list_view.h ('k') | ash/system/tray/hover_highlight_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698