ProfilePolicyConnectorFactory: Refactoring from Profile to BrowserContext.
[chromium-blink-merge.git] / ash / system / chromeos / tray_display.cc
blob10f9ede1839ad6a1aef7153cb9a152114dee3e99
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 <vector>
9 #include "ash/content/display/screen_orientation_controller_chromeos.h"
10 #include "ash/display/display_controller.h"
11 #include "ash/display/display_manager.h"
12 #include "ash/shell.h"
13 #include "ash/system/system_notifier.h"
14 #include "ash/system/tray/actionable_view.h"
15 #include "ash/system/tray/fixed_sized_image_view.h"
16 #include "ash/system/tray/system_tray.h"
17 #include "ash/system/tray/system_tray_delegate.h"
18 #include "ash/system/tray/tray_constants.h"
19 #include "ash/system/tray/tray_notification_view.h"
20 #include "base/bind.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "grit/ash_resources.h"
24 #include "grit/ash_strings.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/resource/resource_bundle.h"
27 #include "ui/message_center/message_center.h"
28 #include "ui/message_center/notification.h"
29 #include "ui/message_center/notification_delegate.h"
30 #include "ui/views/controls/image_view.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/layout/box_layout.h"
34 using message_center::Notification;
36 namespace ash {
37 namespace {
39 DisplayManager* GetDisplayManager() {
40 return Shell::GetInstance()->display_manager();
43 base::string16 GetDisplayName(int64 display_id) {
44 return base::UTF8ToUTF16(
45 GetDisplayManager()->GetDisplayNameForId(display_id));
48 base::string16 GetDisplaySize(int64 display_id) {
49 DisplayManager* display_manager = GetDisplayManager();
51 const gfx::Display* display = &display_manager->GetDisplayForId(display_id);
53 // We don't show display size for mirrored display. Fallback
54 // to empty string if this happens on release build.
55 bool mirrored_display = display_manager->mirrored_display_id() == display_id;
56 DCHECK(!mirrored_display);
57 if (mirrored_display)
58 return base::string16();
60 DCHECK(display->is_valid());
61 return base::UTF8ToUTF16(display->size().ToString());
64 // Returns 1-line information for the specified display, like
65 // "InternalDisplay: 1280x750"
66 base::string16 GetDisplayInfoLine(int64 display_id) {
67 const DisplayInfo& display_info =
68 GetDisplayManager()->GetDisplayInfo(display_id);
69 if (GetDisplayManager()->mirrored_display_id() == display_id)
70 return GetDisplayName(display_id);
72 base::string16 size_text = GetDisplaySize(display_id);
73 base::string16 display_data;
74 if (display_info.has_overscan()) {
75 display_data = l10n_util::GetStringFUTF16(
76 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
77 size_text,
78 l10n_util::GetStringUTF16(
79 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
80 } else {
81 display_data = size_text;
84 return l10n_util::GetStringFUTF16(
85 IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
86 GetDisplayName(display_id),
87 display_data);
90 base::string16 GetAllDisplayInfo() {
91 DisplayManager* display_manager = GetDisplayManager();
92 std::vector<base::string16> lines;
93 int64 internal_id = gfx::Display::kInvalidDisplayID;
94 // Make sure to show the internal display first.
95 if (display_manager->HasInternalDisplay() &&
96 display_manager->IsInternalDisplayId(
97 display_manager->first_display_id())) {
98 internal_id = display_manager->first_display_id();
99 lines.push_back(GetDisplayInfoLine(internal_id));
102 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
103 int64 id = display_manager->GetDisplayAt(i).id();
104 if (id == internal_id)
105 continue;
106 lines.push_back(GetDisplayInfoLine(id));
109 return JoinString(lines, '\n');
112 void OpenSettings() {
113 // switch is intentionally introduced without default, to cause an error when
114 // a new type of login status is introduced.
115 switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) {
116 case user::LOGGED_IN_NONE:
117 case user::LOGGED_IN_LOCKED:
118 return;
120 case user::LOGGED_IN_USER:
121 case user::LOGGED_IN_OWNER:
122 case user::LOGGED_IN_GUEST:
123 case user::LOGGED_IN_PUBLIC:
124 case user::LOGGED_IN_SUPERVISED:
125 case user::LOGGED_IN_KIOSK_APP:
126 ash::SystemTrayDelegate* delegate =
127 Shell::GetInstance()->system_tray_delegate();
128 if (delegate->ShouldShowSettings())
129 delegate->ShowDisplaySettings();
133 } // namespace
135 const char TrayDisplay::kNotificationId[] = "chrome://settings/display";
137 class DisplayView : public ActionableView {
138 public:
139 explicit DisplayView() {
140 SetLayoutManager(new views::BoxLayout(
141 views::BoxLayout::kHorizontal,
142 kTrayPopupPaddingHorizontal, 0,
143 kTrayPopupPaddingBetweenItems));
145 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
146 image_ = new FixedSizedImageView(0, kTrayPopupItemHeight);
147 image_->SetImage(
148 bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
149 AddChildView(image_);
151 label_ = new views::Label();
152 label_->SetMultiLine(true);
153 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
154 AddChildView(label_);
155 Update();
158 ~DisplayView() override {}
160 void Update() {
161 base::string16 message = GetTrayDisplayMessage(NULL);
162 if (message.empty() && ShouldShowFirstDisplayInfo())
163 message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
164 SetVisible(!message.empty());
165 label_->SetText(message);
166 SetAccessibleName(message);
167 Layout();
170 const views::Label* label() const { return label_; }
172 // Overridden from views::View.
173 bool GetTooltipText(const gfx::Point& p,
174 base::string16* tooltip) const override {
175 base::string16 tray_message = GetTrayDisplayMessage(NULL);
176 base::string16 display_message = GetAllDisplayInfo();
177 if (tray_message.empty() && display_message.empty())
178 return false;
180 *tooltip = tray_message + base::ASCIIToUTF16("\n") + display_message;
181 return true;
184 // Returns the name of the currently connected external display.
185 // This should not be used when the external display is used for
186 // mirroring.
187 static base::string16 GetExternalDisplayName() {
188 DisplayManager* display_manager = GetDisplayManager();
189 DCHECK(!display_manager->IsMirrored());
191 int64 external_id = gfx::Display::kInvalidDisplayID;
192 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
193 int64 id = display_manager->GetDisplayAt(i).id();
194 if (id != gfx::Display::InternalDisplayId()) {
195 external_id = id;
196 break;
200 if (external_id == gfx::Display::kInvalidDisplayID) {
201 return l10n_util::GetStringUTF16(
202 IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
205 // The external display name may have an annotation of "(width x height)" in
206 // case that the display is rotated or its resolution is changed.
207 base::string16 name = GetDisplayName(external_id);
208 const DisplayInfo& display_info =
209 display_manager->GetDisplayInfo(external_id);
210 if (display_info.rotation() != gfx::Display::ROTATE_0 ||
211 display_info.configured_ui_scale() != 1.0f ||
212 !display_info.overscan_insets_in_dip().empty()) {
213 name = l10n_util::GetStringFUTF16(
214 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
215 name, GetDisplaySize(external_id));
216 } else if (display_info.overscan_insets_in_dip().empty() &&
217 display_info.has_overscan()) {
218 name = l10n_util::GetStringFUTF16(
219 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
220 name, l10n_util::GetStringUTF16(
221 IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
224 return name;
227 static base::string16 GetTrayDisplayMessage(
228 base::string16* additional_message_out) {
229 DisplayManager* display_manager = GetDisplayManager();
230 if (display_manager->GetNumDisplays() > 1) {
231 if (GetDisplayManager()->HasInternalDisplay()) {
232 return l10n_util::GetStringFUTF16(
233 IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName());
235 return l10n_util::GetStringUTF16(
236 IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
239 if (display_manager->IsMirrored()) {
240 if (GetDisplayManager()->HasInternalDisplay()) {
241 return l10n_util::GetStringFUTF16(
242 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
243 GetDisplayName(display_manager->mirrored_display_id()));
245 return l10n_util::GetStringUTF16(
246 IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
249 int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id();
250 if (display_manager->HasInternalDisplay() &&
251 !display_manager->IsInternalDisplayId(primary_id)) {
252 if (additional_message_out) {
253 *additional_message_out = l10n_util::GetStringUTF16(
254 IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION);
256 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
259 return base::string16();
262 private:
263 bool ShouldShowFirstDisplayInfo() const {
264 const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(
265 GetDisplayManager()->first_display_id());
266 return display_info.rotation() != gfx::Display::ROTATE_0 ||
267 display_info.configured_ui_scale() != 1.0f ||
268 !display_info.overscan_insets_in_dip().empty() ||
269 display_info.has_overscan();
272 // Overridden from ActionableView.
273 bool PerformAction(const ui::Event& event) override {
274 OpenSettings();
275 return true;
278 void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
279 int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
280 kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
281 label_->SizeToFit(label_max_width);
284 views::ImageView* image_;
285 views::Label* label_;
287 DISALLOW_COPY_AND_ASSIGN(DisplayView);
290 TrayDisplay::TrayDisplay(SystemTray* system_tray)
291 : SystemTrayItem(system_tray),
292 default_(NULL) {
293 Shell::GetInstance()->display_controller()->AddObserver(this);
294 UpdateDisplayInfo(NULL);
297 TrayDisplay::~TrayDisplay() {
298 Shell::GetInstance()->display_controller()->RemoveObserver(this);
301 void TrayDisplay::UpdateDisplayInfo(TrayDisplay::DisplayInfoMap* old_info) {
302 if (old_info)
303 old_info->swap(display_info_);
304 display_info_.clear();
306 DisplayManager* display_manager = GetDisplayManager();
307 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
308 int64 id = display_manager->GetDisplayAt(i).id();
309 display_info_[id] = display_manager->GetDisplayInfo(id);
313 bool TrayDisplay::GetDisplayMessageForNotification(
314 const TrayDisplay::DisplayInfoMap& old_info,
315 base::string16* message_out,
316 base::string16* additional_message_out) {
317 // Display is added or removed. Use the same message as the one in
318 // the system tray.
319 if (display_info_.size() != old_info.size()) {
320 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
321 return true;
324 for (DisplayInfoMap::const_iterator iter = display_info_.begin();
325 iter != display_info_.end(); ++iter) {
326 DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
327 // The display's number is same but different displays. This happens
328 // for the transition between docked mode and mirrored display. Falls back
329 // to GetTrayDisplayMessage().
330 if (old_iter == old_info.end()) {
331 *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
332 return true;
335 if (iter->second.configured_ui_scale() !=
336 old_iter->second.configured_ui_scale()) {
337 *message_out = l10n_util::GetStringFUTF16(
338 IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
339 GetDisplayName(iter->first),
340 GetDisplaySize(iter->first));
341 return true;
343 if (iter->second.rotation() != old_iter->second.rotation()) {
344 int rotation_text_id = 0;
345 switch (iter->second.rotation()) {
346 case gfx::Display::ROTATE_0:
347 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION;
348 break;
349 case gfx::Display::ROTATE_90:
350 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90;
351 break;
352 case gfx::Display::ROTATE_180:
353 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180;
354 break;
355 case gfx::Display::ROTATE_270:
356 rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270;
357 break;
359 *message_out = l10n_util::GetStringFUTF16(
360 IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED,
361 GetDisplayName(iter->first),
362 l10n_util::GetStringUTF16(rotation_text_id));
363 return true;
367 // Found nothing special
368 return false;
371 void TrayDisplay::CreateOrUpdateNotification(
372 const base::string16& message,
373 const base::string16& additional_message) {
374 // Always remove the notification to make sure the notification appears
375 // as a popup in any situation.
376 message_center::MessageCenter::Get()->RemoveNotification(
377 kNotificationId, false /* by_user */);
379 if (message.empty())
380 return;
382 // Don't display notifications for accelerometer triggered screen rotations.
383 // See http://crbug.com/364949
384 if (Shell::GetInstance()
385 ->screen_orientation_controller()
386 ->ignore_display_configuration_updates()) {
387 return;
390 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
391 scoped_ptr<Notification> notification(new Notification(
392 message_center::NOTIFICATION_TYPE_SIMPLE,
393 kNotificationId,
394 message,
395 additional_message,
396 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_DISPLAY),
397 base::string16(), // display_source
398 message_center::NotifierId(
399 message_center::NotifierId::SYSTEM_COMPONENT,
400 system_notifier::kNotifierDisplay),
401 message_center::RichNotificationData(),
402 new message_center::HandleNotificationClickedDelegate(
403 base::Bind(&OpenSettings))));
405 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
408 views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
409 DCHECK(default_ == NULL);
410 default_ = new DisplayView();
411 return default_;
414 void TrayDisplay::DestroyDefaultView() {
415 default_ = NULL;
418 void TrayDisplay::OnDisplayConfigurationChanged() {
419 DisplayInfoMap old_info;
420 UpdateDisplayInfo(&old_info);
422 if (default_)
423 default_->Update();
425 if (!Shell::GetInstance()->system_tray_delegate()->
426 ShouldShowDisplayNotification()) {
427 return;
430 base::string16 message;
431 base::string16 additional_message;
432 if (GetDisplayMessageForNotification(old_info, &message, &additional_message))
433 CreateOrUpdateNotification(message, additional_message);
436 base::string16 TrayDisplay::GetDefaultViewMessage() const {
437 if (!default_ || !default_->visible())
438 return base::string16();
440 return static_cast<DisplayView*>(default_)->label()->text();
443 bool TrayDisplay::GetAccessibleStateForTesting(ui::AXViewState* state) {
444 views::View* view = default_;
445 if (view) {
446 view->GetAccessibleState(state);
447 return true;
449 return false;
452 } // namespace ash