Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / ash / system / chromeos / tray_display.cc
blobb5814c1304f39c7bdc5835b17bd001905e128155
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.
5 #include "ash/system/chromeos/tray_display.h"
7 #include "ash/display/display_controller.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/shell.h"
10 #include "ash/system/system_notifier.h"
11 #include "ash/system/tray/actionable_view.h"
12 #include "ash/system/tray/fixed_sized_image_view.h"
13 #include "ash/system/tray/system_tray.h"
14 #include "ash/system/tray/system_tray_delegate.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "ash/system/tray/tray_notification_view.h"
17 #include "base/bind.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "grit/ash_resources.h"
21 #include "grit/ash_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/notification.h"
26 #include "ui/message_center/notification_delegate.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/box_layout.h"
31 using message_center::Notification;
33 namespace ash {
34 namespace {
36 DisplayManager* GetDisplayManager() {
37 return Shell::GetInstance()->display_manager();
40 base::string16 GetDisplayName(int64 display_id) {
41 return base::UTF8ToUTF16(
42 GetDisplayManager()->GetDisplayNameForId(display_id));
45 base::string16 GetDisplaySize(int64 display_id) {
46 DisplayManager* display_manager = GetDisplayManager();
48 const gfx::Display* display = &display_manager->GetDisplayForId(display_id);
50 // We don't show display size for mirrored display. Fallback
51 // to empty string if this happens on release build.
52 bool mirrored_display = display_manager->mirrored_display_id() == display_id;
53 DCHECK(!mirrored_display);
54 if (mirrored_display)
55 return base::string16();
57 DCHECK(display->is_valid());
58 return base::UTF8ToUTF16(display->size().ToString());
61 // Returns 1-line information for the specified display, like
62 // "InternalDisplay: 1280x750"
63 base::string16 GetDisplayInfoLine(int64 display_id) {
64 const DisplayInfo& display_info =
65 GetDisplayManager()->GetDisplayInfo(display_id);
66 if (GetDisplayManager()->mirrored_display_id() == display_id)
67 return GetDisplayName(display_id);
69 base::string16 size_text = GetDisplaySize(display_id);
70 base::string16 display_data;
71 if (display_info.has_overscan()) {
72 display_data = l10n_util::GetStringFUTF16(
73 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
74 size_text,
75 l10n_util::GetStringUTF16(
76 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
77 } else {
78 display_data = size_text;
81 return l10n_util::GetStringFUTF16(
82 IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
83 GetDisplayName(display_id),
84 display_data);
87 base::string16 GetAllDisplayInfo() {
88 DisplayManager* display_manager = GetDisplayManager();
89 std::vector<base::string16> lines;
90 int64 internal_id = gfx::Display::kInvalidDisplayID;
91 // Make sure to show the internal display first.
92 if (display_manager->HasInternalDisplay() &&
93 display_manager->IsInternalDisplayId(
94 display_manager->first_display_id())) {
95 internal_id = display_manager->first_display_id();
96 lines.push_back(GetDisplayInfoLine(internal_id));
99 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
100 int64 id = display_manager->GetDisplayAt(i).id();
101 if (id == internal_id)
102 continue;
103 lines.push_back(GetDisplayInfoLine(id));
106 return JoinString(lines, '\n');
109 void OpenSettings() {
110 // switch is intentionally introduced without default, to cause an error when
111 // a new type of login status is introduced.
112 switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) {
113 case user::LOGGED_IN_NONE:
114 case user::LOGGED_IN_LOCKED:
115 return;
117 case user::LOGGED_IN_USER:
118 case user::LOGGED_IN_OWNER:
119 case user::LOGGED_IN_GUEST:
120 case user::LOGGED_IN_RETAIL_MODE:
121 case user::LOGGED_IN_PUBLIC:
122 case user::LOGGED_IN_LOCALLY_MANAGED:
123 case user::LOGGED_IN_KIOSK_APP:
124 Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
128 } // namespace
130 const char TrayDisplay::kNotificationId[] = "chrome://settings/display";
132 class DisplayView : public ActionableView {
133 public:
134 explicit DisplayView() {
135 SetLayoutManager(new views::BoxLayout(
136 views::BoxLayout::kHorizontal,
137 kTrayPopupPaddingHorizontal, 0,
138 kTrayPopupPaddingBetweenItems));
140 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
141 image_ = new FixedSizedImageView(0, kTrayPopupItemHeight);
142 image_->SetImage(
143 bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
144 AddChildView(image_);
146 label_ = new views::Label();
147 label_->SetMultiLine(true);
148 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
149 AddChildView(label_);
150 Update();
153 virtual ~DisplayView() {}
155 void Update() {
156 base::string16 message = GetTrayDisplayMessage(NULL);
157 if (message.empty() && ShouldShowFirstDisplayInfo())
158 message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
159 SetVisible(!message.empty());
160 label_->SetText(message);
161 SetAccessibleName(message);
162 Layout();
165 const views::Label* label() const { return label_; }
167 // Overridden from views::View.
168 virtual bool GetTooltipText(const gfx::Point& p,
169 base::string16* tooltip) const OVERRIDE {
170 base::string16 tray_message = GetTrayDisplayMessage(NULL);
171 base::string16 display_message = GetAllDisplayInfo();
172 if (tray_message.empty() && display_message.empty())
173 return false;
175 *tooltip = tray_message + base::ASCIIToUTF16("\n") + display_message;
176 return true;
179 // Returns the name of the currently connected external display.
180 // This should not be used when the external display is used for
181 // mirroring.
182 static base::string16 GetExternalDisplayName() {
183 DisplayManager* display_manager = GetDisplayManager();
184 DCHECK(!display_manager->IsMirrored());
186 int64 external_id = gfx::Display::kInvalidDisplayID;
187 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
188 int64 id = display_manager->GetDisplayAt(i).id();
189 if (id != gfx::Display::InternalDisplayId()) {
190 external_id = id;
191 break;
195 if (external_id == gfx::Display::kInvalidDisplayID) {
196 return l10n_util::GetStringUTF16(
197 IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
200 // The external display name may have an annotation of "(width x height)" in
201 // case that the display is rotated or its resolution is changed.
202 base::string16 name = GetDisplayName(external_id);
203 const DisplayInfo& display_info =
204 display_manager->GetDisplayInfo(external_id);
205 if (display_info.rotation() != gfx::Display::ROTATE_0 ||
206 display_info.configured_ui_scale() != 1.0f ||
207 !display_info.overscan_insets_in_dip().empty()) {
208 name = l10n_util::GetStringFUTF16(
209 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
210 name, GetDisplaySize(external_id));
211 } else if (display_info.overscan_insets_in_dip().empty() &&
212 display_info.has_overscan()) {
213 name = l10n_util::GetStringFUTF16(
214 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
215 name, l10n_util::GetStringUTF16(
216 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
219 return name;
222 static base::string16 GetTrayDisplayMessage(
223 base::string16* additional_message_out) {
224 DisplayManager* display_manager = GetDisplayManager();
225 if (display_manager->GetNumDisplays() > 1) {
226 if (GetDisplayManager()->HasInternalDisplay()) {
227 return l10n_util::GetStringFUTF16(
228 IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName());
230 return l10n_util::GetStringUTF16(
231 IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
234 if (display_manager->IsMirrored()) {
235 if (GetDisplayManager()->HasInternalDisplay()) {
236 return l10n_util::GetStringFUTF16(
237 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
238 GetDisplayName(display_manager->mirrored_display_id()));
240 return l10n_util::GetStringUTF16(
241 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
244 int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id();
245 if (display_manager->HasInternalDisplay() &&
246 !display_manager->IsInternalDisplayId(primary_id)) {
247 if (additional_message_out) {
248 *additional_message_out = l10n_util::GetStringUTF16(
249 IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION);
251 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
254 return base::string16();
257 private:
258 bool ShouldShowFirstDisplayInfo() const {
259 const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(
260 GetDisplayManager()->first_display_id());
261 return display_info.rotation() != gfx::Display::ROTATE_0 ||
262 display_info.configured_ui_scale() != 1.0f ||
263 !display_info.overscan_insets_in_dip().empty() ||
264 display_info.has_overscan();
267 // Overridden from ActionableView.
268 virtual bool PerformAction(const ui::Event& event) OVERRIDE {
269 OpenSettings();
270 return true;
273 virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE {
274 int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
275 kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
276 label_->SizeToFit(label_max_width);
279 views::ImageView* image_;
280 views::Label* label_;
282 DISALLOW_COPY_AND_ASSIGN(DisplayView);
285 TrayDisplay::TrayDisplay(SystemTray* system_tray)
286 : SystemTrayItem(system_tray),
287 default_(NULL) {
288 Shell::GetInstance()->display_controller()->AddObserver(this);
289 UpdateDisplayInfo(NULL);
292 TrayDisplay::~TrayDisplay() {
293 Shell::GetInstance()->display_controller()->RemoveObserver(this);
296 void TrayDisplay::UpdateDisplayInfo(TrayDisplay::DisplayInfoMap* old_info) {
297 if (old_info)
298 old_info->swap(display_info_);
299 display_info_.clear();
301 DisplayManager* display_manager = GetDisplayManager();
302 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
303 int64 id = display_manager->GetDisplayAt(i).id();
304 display_info_[id] = display_manager->GetDisplayInfo(id);
308 bool TrayDisplay::GetDisplayMessageForNotification(
309 const TrayDisplay::DisplayInfoMap& old_info,
310 base::string16* message_out,
311 base::string16* additional_message_out) {
312 // Display is added or removed. Use the same message as the one in
313 // the system tray.
314 if (display_info_.size() != old_info.size()) {
315 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
316 return true;
319 for (DisplayInfoMap::const_iterator iter = display_info_.begin();
320 iter != display_info_.end(); ++iter) {
321 DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
322 // The display's number is same but different displays. This happens
323 // for the transition between docked mode and mirrored display. Falls back
324 // to GetTrayDisplayMessage().
325 if (old_iter == old_info.end()) {
326 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
327 return true;
330 if (iter->second.configured_ui_scale() !=
331 old_iter->second.configured_ui_scale()) {
332 *message_out = l10n_util::GetStringFUTF16(
333 IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
334 GetDisplayName(iter->first),
335 GetDisplaySize(iter->first));
336 return true;
338 if (iter->second.rotation() != old_iter->second.rotation()) {
339 int rotation_text_id = 0;
340 switch (iter->second.rotation()) {
341 case gfx::Display::ROTATE_0:
342 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION;
343 break;
344 case gfx::Display::ROTATE_90:
345 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90;
346 break;
347 case gfx::Display::ROTATE_180:
348 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180;
349 break;
350 case gfx::Display::ROTATE_270:
351 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270;
352 break;
354 *message_out = l10n_util::GetStringFUTF16(
355 IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED,
356 GetDisplayName(iter->first),
357 l10n_util::GetStringUTF16(rotation_text_id));
358 return true;
362 // Found nothing special
363 return false;
366 void TrayDisplay::CreateOrUpdateNotification(
367 const base::string16& message,
368 const base::string16& additional_message) {
369 // Always remove the notification to make sure the notification appears
370 // as a popup in any situation.
371 message_center::MessageCenter::Get()->RemoveNotification(
372 kNotificationId, false /* by_user */);
374 if (message.empty())
375 return;
377 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
378 scoped_ptr<Notification> notification(new Notification(
379 message_center::NOTIFICATION_TYPE_SIMPLE,
380 kNotificationId,
381 message,
382 additional_message,
383 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_DISPLAY),
384 base::string16(), // display_source
385 message_center::NotifierId(
386 message_center::NotifierId::SYSTEM_COMPONENT,
387 system_notifier::kNotifierDisplay),
388 message_center::RichNotificationData(),
389 new message_center::HandleNotificationClickedDelegate(
390 base::Bind(&OpenSettings))));
391 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
394 views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
395 DCHECK(default_ == NULL);
396 default_ = new DisplayView();
397 return default_;
400 void TrayDisplay::DestroyDefaultView() {
401 default_ = NULL;
404 void TrayDisplay::OnDisplayConfigurationChanged() {
405 DisplayInfoMap old_info;
406 UpdateDisplayInfo(&old_info);
408 if (default_)
409 default_->Update();
411 if (!Shell::GetInstance()->system_tray_delegate()->
412 ShouldShowDisplayNotification()) {
413 return;
416 base::string16 message;
417 base::string16 additional_message;
418 if (GetDisplayMessageForNotification(old_info, &message, &additional_message))
419 CreateOrUpdateNotification(message, additional_message);
422 base::string16 TrayDisplay::GetDefaultViewMessage() const {
423 if (!default_ || !default_->visible())
424 return base::string16();
426 return static_cast<DisplayView*>(default_)->label()->text();
429 bool TrayDisplay::GetAccessibleStateForTesting(ui::AXViewState* state) {
430 views::View* view = default_;
431 if (view) {
432 view->GetAccessibleState(state);
433 return true;
435 return false;
438 } // namespace ash