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

Side by Side Diff: ash/system/chromeos/tray_display.cc

Issue 2395523002: chromeos: Refactor system tray "show settings" commands to use mojo (Closed)
Patch Set: Created 4 years, 2 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
OLDNEW
(Empty)
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/tray_display.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "ash/common/metrics/user_metrics_action.h"
12 #include "ash/common/system/chromeos/devicetype_utils.h"
13 #include "ash/common/system/system_notifier.h"
14 #include "ash/common/system/tray/actionable_view.h"
15 #include "ash/common/system/tray/fixed_sized_image_view.h"
16 #include "ash/common/system/tray/system_tray.h"
17 #include "ash/common/system/tray/system_tray_delegate.h"
18 #include "ash/common/system/tray/tray_constants.h"
19 #include "ash/common/system/tray/tray_notification_view.h"
20 #include "ash/common/wm_shell.h"
21 #include "ash/display/display_manager.h"
22 #include "ash/display/screen_orientation_controller_chromeos.h"
23 #include "ash/shell.h"
24 #include "base/bind.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "grit/ash_resources.h"
28 #include "grit/ash_strings.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/display/display.h"
32 #include "ui/message_center/message_center.h"
33 #include "ui/message_center/notification.h"
34 #include "ui/message_center/notification_delegate.h"
35 #include "ui/views/controls/image_view.h"
36 #include "ui/views/controls/label.h"
37 #include "ui/views/layout/box_layout.h"
38
39 using message_center::Notification;
40
41 namespace ash {
42 namespace {
43
44 DisplayManager* GetDisplayManager() {
45 return Shell::GetInstance()->display_manager();
46 }
47
48 base::string16 GetDisplayName(int64_t display_id) {
49 return base::UTF8ToUTF16(
50 GetDisplayManager()->GetDisplayNameForId(display_id));
51 }
52
53 base::string16 GetDisplaySize(int64_t display_id) {
54 DisplayManager* display_manager = GetDisplayManager();
55
56 const display::Display* display =
57 &display_manager->GetDisplayForId(display_id);
58
59 // We don't show display size for mirrored display. Fallback
60 // to empty string if this happens on release build.
61 bool mirroring = display_manager->mirroring_display_id() == display_id;
62 DCHECK(!mirroring);
63 if (mirroring)
64 return base::string16();
65
66 DCHECK(display->is_valid());
67 return base::UTF8ToUTF16(display->size().ToString());
68 }
69
70 // Returns 1-line information for the specified display, like
71 // "InternalDisplay: 1280x750"
72 base::string16 GetDisplayInfoLine(int64_t display_id) {
73 const display::ManagedDisplayInfo& display_info =
74 GetDisplayManager()->GetDisplayInfo(display_id);
75 if (GetDisplayManager()->mirroring_display_id() == display_id)
76 return GetDisplayName(display_id);
77
78 base::string16 size_text = GetDisplaySize(display_id);
79 base::string16 display_data;
80 if (display_info.has_overscan()) {
81 display_data = l10n_util::GetStringFUTF16(
82 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION, size_text,
83 l10n_util::GetStringUTF16(
84 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
85 } else {
86 display_data = size_text;
87 }
88
89 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
90 GetDisplayName(display_id), display_data);
91 }
92
93 base::string16 GetAllDisplayInfo() {
94 DisplayManager* display_manager = GetDisplayManager();
95 std::vector<base::string16> lines;
96 int64_t internal_id = display::Display::kInvalidDisplayID;
97 // Make sure to show the internal display first.
98 if (!display_manager->IsInUnifiedMode() &&
99 display::Display::IsInternalDisplayId(
100 display_manager->first_display_id())) {
101 internal_id = display_manager->first_display_id();
102 lines.push_back(GetDisplayInfoLine(internal_id));
103 }
104
105 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
106 int64_t id = display_manager->GetDisplayAt(i).id();
107 if (id == internal_id)
108 continue;
109 lines.push_back(GetDisplayInfoLine(id));
110 }
111
112 return base::JoinString(lines, base::ASCIIToUTF16("\n"));
113 }
114
115 // Attempts to open the display settings, returns true if successful.
116 bool OpenSettings() {
117 // switch is intentionally introduced without default, to cause an error when
118 // a new type of login status is introduced.
119 switch (WmShell::Get()->system_tray_delegate()->GetUserLoginStatus()) {
120 case LoginStatus::NOT_LOGGED_IN:
121 case LoginStatus::LOCKED:
122 return false;
123
124 case LoginStatus::USER:
125 case LoginStatus::OWNER:
126 case LoginStatus::GUEST:
127 case LoginStatus::PUBLIC:
128 case LoginStatus::SUPERVISED:
129 case LoginStatus::KIOSK_APP:
130 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate();
131 if (delegate->ShouldShowSettings()) {
132 delegate->ShowDisplaySettings();
133 return true;
134 }
135 }
136
137 return false;
138 }
139
140 // Callback to handle a user selecting the notification view.
141 void OpenSettingsFromNotification() {
142 WmShell::Get()->RecordUserMetricsAction(
143 UMA_STATUS_AREA_DISPLAY_NOTIFICATION_SELECTED);
144 if (OpenSettings()) {
145 WmShell::Get()->RecordUserMetricsAction(
146 UMA_STATUS_AREA_DISPLAY_NOTIFICATION_SHOW_SETTINGS);
147 }
148 }
149
150 } // namespace
151
152 const char TrayDisplay::kNotificationId[] = "chrome://settings/display";
153
154 class DisplayView : public ActionableView {
155 public:
156 explicit DisplayView() {
157 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
158 kTrayPopupPaddingHorizontal, 0,
159 kTrayPopupPaddingBetweenItems));
160
161 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
162 image_ =
163 new FixedSizedImageView(0, GetTrayConstant(TRAY_POPUP_ITEM_HEIGHT));
164 image_->SetImage(
165 bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
166 AddChildView(image_);
167
168 label_ = new views::Label();
169 label_->SetMultiLine(true);
170 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
171 AddChildView(label_);
172 Update();
173 }
174
175 ~DisplayView() override {}
176
177 void Update() {
178 base::string16 message = GetTrayDisplayMessage(NULL);
179 if (message.empty() && ShouldShowFirstDisplayInfo())
180 message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
181 SetVisible(!message.empty());
182 label_->SetText(message);
183 SetAccessibleName(message);
184 Layout();
185 }
186
187 const views::Label* label() const { return label_; }
188
189 // Overridden from views::View.
190 bool GetTooltipText(const gfx::Point& p,
191 base::string16* tooltip) const override {
192 base::string16 tray_message = GetTrayDisplayMessage(NULL);
193 base::string16 display_message = GetAllDisplayInfo();
194 if (tray_message.empty() && display_message.empty())
195 return false;
196
197 *tooltip = tray_message + base::ASCIIToUTF16("\n") + display_message;
198 return true;
199 }
200
201 // Returns the name of the currently connected external display.
202 // This should not be used when the external display is used for
203 // mirroring.
204 static base::string16 GetExternalDisplayName() {
205 DisplayManager* display_manager = GetDisplayManager();
206 DCHECK(!display_manager->IsInMirrorMode());
207
208 int64_t external_id = display::Display::kInvalidDisplayID;
209 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
210 int64_t id = display_manager->GetDisplayAt(i).id();
211 if (!display::Display::IsInternalDisplayId(id)) {
212 external_id = id;
213 break;
214 }
215 }
216
217 if (external_id == display::Display::kInvalidDisplayID) {
218 return l10n_util::GetStringUTF16(
219 IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
220 }
221
222 // The external display name may have an annotation of "(width x height)" in
223 // case that the display is rotated or its resolution is changed.
224 base::string16 name = GetDisplayName(external_id);
225 const display::ManagedDisplayInfo& display_info =
226 display_manager->GetDisplayInfo(external_id);
227 if (display_info.GetActiveRotation() != display::Display::ROTATE_0 ||
228 display_info.configured_ui_scale() != 1.0f ||
229 !display_info.overscan_insets_in_dip().IsEmpty()) {
230 name =
231 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
232 name, GetDisplaySize(external_id));
233 } else if (display_info.overscan_insets_in_dip().IsEmpty() &&
234 display_info.has_overscan()) {
235 name = l10n_util::GetStringFUTF16(
236 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME, name,
237 l10n_util::GetStringUTF16(
238 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
239 }
240
241 return name;
242 }
243
244 static base::string16 GetTrayDisplayMessage(
245 base::string16* additional_message_out) {
246 DisplayManager* display_manager = GetDisplayManager();
247 if (display_manager->GetNumDisplays() > 1) {
248 if (display::Display::HasInternalDisplay()) {
249 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED,
250 GetExternalDisplayName());
251 }
252 return l10n_util::GetStringUTF16(
253 IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
254 }
255
256 if (display_manager->IsInMirrorMode()) {
257 if (display::Display::HasInternalDisplay()) {
258 return l10n_util::GetStringFUTF16(
259 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
260 GetDisplayName(display_manager->mirroring_display_id()));
261 }
262 return l10n_util::GetStringUTF16(
263 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
264 }
265
266 if (display_manager->IsInUnifiedMode())
267 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED);
268
269 int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
270 if (display::Display::HasInternalDisplay() &&
271 !(display::Display::IsInternalDisplayId(primary_id))) {
272 if (additional_message_out) {
273 *additional_message_out = ash::SubstituteChromeOSDeviceType(
274 IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION);
275 }
276 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
277 }
278
279 return base::string16();
280 }
281
282 private:
283 bool ShouldShowFirstDisplayInfo() const {
284 const int64_t first_display_id = GetDisplayManager()->first_display_id();
285
286 const display::ManagedDisplayInfo& display_info =
287 GetDisplayManager()->GetDisplayInfo(first_display_id);
288 return (display_info.GetActiveRotation() != display::Display::ROTATE_0 &&
289 (display_info.active_rotation_source() !=
290 display::Display::ROTATION_SOURCE_ACCELEROMETER ||
291 !display::Display::IsInternalDisplayId(first_display_id))) ||
292 display_info.configured_ui_scale() != 1.0f ||
293 !display_info.overscan_insets_in_dip().IsEmpty() ||
294 display_info.has_overscan();
295 }
296
297 // Overridden from ActionableView.
298 bool PerformAction(const ui::Event& event) override {
299 WmShell::Get()->RecordUserMetricsAction(
300 UMA_STATUS_AREA_DISPLAY_DEFAULT_SELECTED);
301 if (OpenSettings()) {
302 WmShell::Get()->RecordUserMetricsAction(
303 UMA_STATUS_AREA_DISPLAY_DEFAULT_SHOW_SETTINGS);
304 }
305 return true;
306 }
307
308 void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
309 int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
310 kTrayPopupPaddingBetweenItems -
311 image_->GetPreferredSize().width();
312 label_->SizeToFit(label_max_width);
313 }
314
315 views::ImageView* image_;
316 views::Label* label_;
317
318 DISALLOW_COPY_AND_ASSIGN(DisplayView);
319 };
320
321 TrayDisplay::TrayDisplay(SystemTray* system_tray)
322 : SystemTrayItem(system_tray, UMA_DISPLAY), default_(nullptr) {
323 WmShell::Get()->AddDisplayObserver(this);
324 UpdateDisplayInfo(NULL);
325 }
326
327 TrayDisplay::~TrayDisplay() {
328 WmShell::Get()->RemoveDisplayObserver(this);
329 }
330
331 void TrayDisplay::UpdateDisplayInfo(TrayDisplay::DisplayInfoMap* old_info) {
332 if (old_info)
333 old_info->swap(display_info_);
334 display_info_.clear();
335
336 DisplayManager* display_manager = GetDisplayManager();
337 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
338 int64_t id = display_manager->GetDisplayAt(i).id();
339 display_info_[id] = display_manager->GetDisplayInfo(id);
340 }
341 }
342
343 bool TrayDisplay::GetDisplayMessageForNotification(
344 const TrayDisplay::DisplayInfoMap& old_info,
345 base::string16* message_out,
346 base::string16* additional_message_out) {
347 // Display is added or removed. Use the same message as the one in
348 // the system tray.
349 if (display_info_.size() != old_info.size()) {
350 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
351 return true;
352 }
353
354 for (DisplayInfoMap::const_iterator iter = display_info_.begin();
355 iter != display_info_.end(); ++iter) {
356 DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
357 // The display's number is same but different displays. This happens
358 // for the transition between docked mode and mirrored display. Falls back
359 // to GetTrayDisplayMessage().
360 if (old_iter == old_info.end()) {
361 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
362 return true;
363 }
364
365 if (iter->second.configured_ui_scale() !=
366 old_iter->second.configured_ui_scale()) {
367 *additional_message_out = l10n_util::GetStringFUTF16(
368 IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
369 GetDisplayName(iter->first), GetDisplaySize(iter->first));
370 return true;
371 }
372 if (iter->second.GetActiveRotation() !=
373 old_iter->second.GetActiveRotation()) {
374 int rotation_text_id = 0;
375 switch (iter->second.GetActiveRotation()) {
376 case display::Display::ROTATE_0:
377 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION;
378 break;
379 case display::Display::ROTATE_90:
380 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90;
381 break;
382 case display::Display::ROTATE_180:
383 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180;
384 break;
385 case display::Display::ROTATE_270:
386 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270;
387 break;
388 }
389 *additional_message_out = l10n_util::GetStringFUTF16(
390 IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetDisplayName(iter->first),
391 l10n_util::GetStringUTF16(rotation_text_id));
392 return true;
393 }
394 }
395
396 // Found nothing special
397 return false;
398 }
399
400 void TrayDisplay::CreateOrUpdateNotification(
401 const base::string16& message,
402 const base::string16& additional_message) {
403 // Always remove the notification to make sure the notification appears
404 // as a popup in any situation.
405 message_center::MessageCenter::Get()->RemoveNotification(kNotificationId,
406 false /* by_user */);
407
408 if (message.empty() && additional_message.empty())
409 return;
410
411 // Don't display notifications for accelerometer triggered screen rotations.
412 // See http://crbug.com/364949
413 if (Shell::GetInstance()
414 ->screen_orientation_controller()
415 ->ignore_display_configuration_updates()) {
416 return;
417 }
418
419 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
420 std::unique_ptr<Notification> notification(new Notification(
421 message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, message,
422 additional_message, bundle.GetImageNamed(IDR_AURA_NOTIFICATION_DISPLAY),
423 base::string16(), // display_source
424 GURL(),
425 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
426 system_notifier::kNotifierDisplay),
427 message_center::RichNotificationData(),
428 new message_center::HandleNotificationClickedDelegate(
429 base::Bind(&OpenSettingsFromNotification))));
430
431 WmShell::Get()->RecordUserMetricsAction(
432 UMA_STATUS_AREA_DISPLAY_NOTIFICATION_CREATED);
433 message_center::MessageCenter::Get()->AddNotification(
434 std::move(notification));
435 }
436
437 views::View* TrayDisplay::CreateDefaultView(LoginStatus status) {
438 DCHECK(default_ == NULL);
439 default_ = new DisplayView();
440 return default_;
441 }
442
443 void TrayDisplay::DestroyDefaultView() {
444 default_ = NULL;
445 }
446
447 void TrayDisplay::OnDisplayConfigurationChanged() {
448 DisplayInfoMap old_info;
449 UpdateDisplayInfo(&old_info);
450
451 if (default_)
452 default_->Update();
453
454 if (!WmShell::Get()
455 ->system_tray_delegate()
456 ->ShouldShowDisplayNotification()) {
457 return;
458 }
459
460 base::string16 message;
461 base::string16 additional_message;
462 if (GetDisplayMessageForNotification(old_info, &message, &additional_message))
463 CreateOrUpdateNotification(message, additional_message);
464 }
465
466 base::string16 TrayDisplay::GetDefaultViewMessage() const {
467 if (!default_ || !default_->visible())
468 return base::string16();
469
470 return static_cast<DisplayView*>(default_)->label()->text();
471 }
472
473 bool TrayDisplay::GetAccessibleStateForTesting(ui::AXViewState* state) {
474 views::View* view = default_;
475 if (view) {
476 view->GetAccessibleState(state);
477 return true;
478 }
479 return false;
480 }
481
482 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698