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/cast/tray_cast.h" 6 7 #include <map> 8 #include <string> 9 #include <utility> 10 #include <vector> 11 12 #include "ash/metrics/user_metrics_recorder.h" 13 #include "ash/public/cpp/ash_view_ids.h" 14 #include "ash/resources/vector_icons/vector_icons.h" 15 #include "ash/shell.h" 16 #include "ash/strings/grit/ash_strings.h" 17 #include "ash/system/tray/hover_highlight_view.h" 18 #include "ash/system/tray/tray_constants.h" 19 #include "ash/system/tray/tray_detailed_view.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "build/branding_buildflags.h" 22 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/base/resource/resource_bundle.h" 24 #include "ui/gfx/image/image.h" 25 #include "ui/gfx/paint_vector_icon.h" 26 #include "ui/gfx/vector_icon_types.h" 27 #include "ui/views/controls/button/button.h" 28 #include "ui/views/controls/scroll_view.h" 29 30 namespace ash { 31 32 namespace { 33 34 // Returns the correct vector icon for |icon_type|. Some types may be different 35 // for branded builds. SinkIconTypeToIcon(SinkIconType icon_type)36const gfx::VectorIcon& SinkIconTypeToIcon(SinkIconType icon_type) { 37 switch (icon_type) { 38 #if BUILDFLAG(GOOGLE_CHROME_BRANDING) 39 case SinkIconType::kCast: 40 return kSystemMenuCastDeviceIcon; 41 case SinkIconType::kEducation: 42 return kSystemMenuCastEducationIcon; 43 case SinkIconType::kHangout: 44 return kSystemMenuCastHangoutIcon; 45 case SinkIconType::kMeeting: 46 return kSystemMenuCastMeetingIcon; 47 #else 48 case SinkIconType::kCast: 49 case SinkIconType::kEducation: 50 return kSystemMenuCastGenericIcon; 51 case SinkIconType::kHangout: 52 case SinkIconType::kMeeting: 53 return kSystemMenuCastMessageIcon; 54 #endif 55 case SinkIconType::kGeneric: 56 return kSystemMenuCastGenericIcon; 57 case SinkIconType::kCastAudioGroup: 58 return kSystemMenuCastAudioGroupIcon; 59 case SinkIconType::kCastAudio: 60 return kSystemMenuCastAudioIcon; 61 case SinkIconType::kWiredDisplay: 62 return kSystemMenuCastGenericIcon; 63 } 64 65 NOTREACHED(); 66 return kSystemMenuCastGenericIcon; 67 } 68 69 } // namespace 70 71 namespace tray { 72 CastDetailedView(DetailedViewDelegate * delegate)73CastDetailedView::CastDetailedView(DetailedViewDelegate* delegate) 74 : TrayDetailedView(delegate) { 75 CreateItems(); 76 OnDevicesUpdated(CastConfigController::Get()->GetSinksAndRoutes()); 77 CastConfigController::Get()->AddObserver(this); 78 } 79 ~CastDetailedView()80CastDetailedView::~CastDetailedView() { 81 CastConfigController::Get()->RemoveObserver(this); 82 } 83 CreateItems()84void CastDetailedView::CreateItems() { 85 CreateScrollableList(); 86 CreateTitleRow(IDS_ASH_STATUS_TRAY_CAST); 87 } 88 OnDevicesUpdated(const std::vector<SinkAndRoute> & sinks_routes)89void CastDetailedView::OnDevicesUpdated( 90 const std::vector<SinkAndRoute>& sinks_routes) { 91 // Add/update existing. 92 for (const auto& device : sinks_routes) 93 sinks_and_routes_.insert(std::make_pair(device.sink.id, device)); 94 95 // Remove non-existent sinks. Removing an element invalidates all existing 96 // iterators. 97 auto iter = sinks_and_routes_.begin(); 98 while (iter != sinks_and_routes_.end()) { 99 bool has_receiver = false; 100 for (auto& receiver : sinks_routes) { 101 if (iter->first == receiver.sink.id) 102 has_receiver = true; 103 } 104 105 if (has_receiver) 106 ++iter; 107 else 108 iter = sinks_and_routes_.erase(iter); 109 } 110 111 // Update UI. 112 UpdateReceiverListFromCachedData(); 113 Layout(); 114 } 115 GetClassName() const116const char* CastDetailedView::GetClassName() const { 117 return "CastDetailedView"; 118 } 119 UpdateReceiverListFromCachedData()120void CastDetailedView::UpdateReceiverListFromCachedData() { 121 // Remove all of the existing views. 122 view_to_sink_map_.clear(); 123 scroll_content()->RemoveAllChildViews(true); 124 125 // Add a view for each receiver. 126 for (auto& it : sinks_and_routes_) { 127 const CastSink& sink = it.second.sink; 128 views::View* container = AddScrollListItem( 129 SinkIconTypeToIcon(sink.sink_icon_type), base::UTF8ToUTF16(sink.name)); 130 view_to_sink_map_[container] = sink.id; 131 } 132 133 scroll_content()->SizeToPreferredSize(); 134 scroller()->Layout(); 135 } 136 HandleViewClicked(views::View * view)137void CastDetailedView::HandleViewClicked(views::View* view) { 138 // Find the receiver we are going to cast to. 139 auto it = view_to_sink_map_.find(view); 140 if (it != view_to_sink_map_.end()) { 141 CastConfigController::Get()->CastToSink(it->second); 142 Shell::Get()->metrics()->RecordUserMetricsAction( 143 UMA_STATUS_AREA_DETAILED_CAST_VIEW_LAUNCH_CAST); 144 } 145 } 146 147 } // namespace tray 148 } // namespace ash 149