tray_user.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/user/tray_user.h"
6
7#include <algorithm>
8#include <climits>
9#include <vector>
10
11#include "ash/shell.h"
12#include "ash/system/tray/system_tray.h"
13#include "ash/system/tray/system_tray_delegate.h"
14#include "ash/system/tray/system_tray_notifier.h"
15#include "ash/system/tray/tray_constants.h"
16#include "ash/system/tray/tray_item_view.h"
17#include "ash/system/tray/tray_views.h"
18#include "base/i18n/rtl.h"
19#include "base/logging.h"
20#include "base/memory/scoped_vector.h"
21#include "base/string16.h"
22#include "base/string_util.h"
23#include "base/utf_string_conversions.h"
24#include "grit/ash_resources.h"
25#include "grit/ash_strings.h"
26#include "skia/ext/image_operations.h"
27#include "third_party/skia/include/core/SkCanvas.h"
28#include "third_party/skia/include/core/SkPaint.h"
29#include "third_party/skia/include/core/SkPath.h"
30#include "ui/base/l10n/l10n_util.h"
31#include "ui/base/range/range.h"
32#include "ui/base/resource/resource_bundle.h"
33#include "ui/base/text/text_elider.h"
34#include "ui/gfx/canvas.h"
35#include "ui/gfx/font.h"
36#include "ui/gfx/image/image.h"
37#include "ui/gfx/image/image_skia_operations.h"
38#include "ui/gfx/insets.h"
39#include "ui/gfx/rect.h"
40#include "ui/gfx/render_text.h"
41#include "ui/gfx/size.h"
42#include "ui/gfx/skia_util.h"
43#include "ui/views/border.h"
44#include "ui/views/bubble/tray_bubble_view.h"
45#include "ui/views/controls/button/button.h"
46#include "ui/views/controls/button/custom_button.h"
47#include "ui/views/controls/image_view.h"
48#include "ui/views/controls/label.h"
49#include "ui/views/controls/link.h"
50#include "ui/views/controls/link_listener.h"
51#include "ui/views/layout/box_layout.h"
52#include "ui/views/layout/fill_layout.h"
53#include "ui/views/painter.h"
54#include "ui/views/view.h"
55#include "ui/views/widget/widget.h"
56
57namespace {
58
59const int kUserDetailsVerticalPadding = 5;
60const int kUserCardVerticalPadding = 10;
61const int kProfileRoundedCornerRadius = 2;
62const int kUserIconSize = 27;
63
64// The invisible word joiner character, used as a marker to indicate the start
65// and end of the user's display name in the public account user card's text.
66const char16 kDisplayNameMark[] = { 0x2060, 0 };
67
68const int kPublicAccountLogoutButtonBorderImagesNormal[] = {
69    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
70    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
71    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
72    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
73    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
74    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
75    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
76    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
77    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
78};
79
80const int kPublicAccountLogoutButtonBorderImagesHovered[] = {
81    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
82    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
83    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
84    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
85    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND,
86    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
87    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
88    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
89    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
90};
91
92}  // namespace
93
94namespace ash {
95namespace internal {
96
97namespace tray {
98
99// A custom image view with rounded edges.
100class RoundedImageView : public views::View {
101 public:
102  // Constructs a new rounded image view with rounded corners of radius
103  // |corner_radius|.
104  explicit RoundedImageView(int corner_radius);
105  virtual ~RoundedImageView();
106
107  // Set the image that should be displayed. The image contents is copied to the
108  // receiver's image.
109  void SetImage(const gfx::ImageSkia& img, const gfx::Size& size);
110
111 private:
112  // Overridden from views::View.
113  virtual gfx::Size GetPreferredSize() OVERRIDE;
114  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
115
116  gfx::ImageSkia image_;
117  gfx::ImageSkia resized_;
118  gfx::Size image_size_;
119  int corner_radius_;
120
121  DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
122};
123
124class ClickableAvatar : public views::CustomButton {
125 public:
126  explicit ClickableAvatar(views::ButtonListener* listener);
127  virtual ~ClickableAvatar();
128
129 private:
130  DISALLOW_COPY_AND_ASSIGN(ClickableAvatar);
131};
132
133// The user details shown in public account mode. This is essentially a label
134// but with custom painting code as the text is styled with multiple colors and
135// contains a link.
136class PublicAccountUserDetails : public views::View,
137                                 public views::LinkListener {
138 public:
139  PublicAccountUserDetails(SystemTrayItem* owner, int used_width);
140  virtual ~PublicAccountUserDetails();
141
142 private:
143  // Overridden from views::View.
144  virtual void Layout() OVERRIDE;
145  virtual gfx::Size GetPreferredSize() OVERRIDE;
146  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
147
148  // Overridden from views::LinkListener.
149  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
150
151  // Calculate a preferred size that ensures the label text and the following
152  // link do not wrap over more than three lines in total for aesthetic reasons
153  // if possible.
154  void CalculatePreferredSize(SystemTrayItem* owner, int used_width);
155
156  string16 text_;
157  views::Link* learn_more_;
158  gfx::Size preferred_size_;
159  ScopedVector<gfx::RenderText> lines_;
160
161  DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
162};
163
164class UserView : public views::View,
165                 public views::ButtonListener {
166 public:
167  explicit UserView(SystemTrayItem* owner, ash::user::LoginStatus login);
168  virtual ~UserView();
169
170 private:
171  // Overridden from views::View.
172  virtual gfx::Size GetPreferredSize() OVERRIDE;
173  virtual void Layout() OVERRIDE;
174
175  // Overridden from views::ButtonListener.
176  virtual void ButtonPressed(views::Button* sender,
177                             const ui::Event& event) OVERRIDE;
178
179  void AddLogoutButton(ash::user::LoginStatus login);
180  void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login);
181
182  views::View* user_card_;
183  views::View* logout_button_;
184  ClickableAvatar* profile_picture_;
185
186  DISALLOW_COPY_AND_ASSIGN(UserView);
187};
188
189RoundedImageView::RoundedImageView(int corner_radius)
190    : corner_radius_(corner_radius) {}
191
192RoundedImageView::~RoundedImageView() {}
193
194void RoundedImageView::SetImage(const gfx::ImageSkia& img,
195                                const gfx::Size& size) {
196  image_ = img;
197  image_size_ = size;
198
199  // Try to get the best image quality for the avatar.
200  resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
201      skia::ImageOperations::RESIZE_BEST, size);
202  if (GetWidget() && visible()) {
203    PreferredSizeChanged();
204    SchedulePaint();
205  }
206}
207
208gfx::Size RoundedImageView::GetPreferredSize() {
209  return gfx::Size(image_size_.width() + GetInsets().width(),
210                   image_size_.height() + GetInsets().height());
211}
212
213void RoundedImageView::OnPaint(gfx::Canvas* canvas) {
214  View::OnPaint(canvas);
215  gfx::Rect image_bounds(size());
216  image_bounds.ClampToCenteredSize(GetPreferredSize());
217  image_bounds.Inset(GetInsets());
218  const SkScalar kRadius = SkIntToScalar(corner_radius_);
219  SkPath path;
220  path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius);
221  SkPaint paint;
222  paint.setAntiAlias(true);
223  paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
224  canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
225                          path, paint);
226}
227
228ClickableAvatar::ClickableAvatar(views::ButtonListener* listener)
229    : views::CustomButton(listener) {
230  SetLayoutManager(new views::FillLayout());
231  RoundedImageView* user_picture =
232      new RoundedImageView(kProfileRoundedCornerRadius);
233  user_picture->SetImage(
234      ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(),
235      gfx::Size(kUserIconSize, kUserIconSize));
236  AddChildView(user_picture);
237}
238
239ClickableAvatar::~ClickableAvatar() {}
240
241PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
242                                                   int used_width)
243    : learn_more_(NULL) {
244  const int inner_padding =
245      kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
246  const bool rtl = base::i18n::IsRTL();
247  set_border(views::Border::CreateEmptyBorder(
248      kUserDetailsVerticalPadding, rtl ? 0 : inner_padding,
249      kUserDetailsVerticalPadding, rtl ? inner_padding : 0));
250
251  ash::SystemTrayDelegate* delegate =
252      ash::Shell::GetInstance()->system_tray_delegate();
253  // Retrieve the user's display name and wrap it with markers.
254  string16 display_name = delegate->GetUserDisplayName();
255  RemoveChars(display_name, kDisplayNameMark, &display_name);
256  display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
257  // Retrieve the domain managing the device and wrap it with markers.
258  string16 domain = UTF8ToUTF16(delegate->GetEnterpriseDomain());
259  RemoveChars(domain, kDisplayNameMark, &domain);
260  base::i18n::WrapStringWithLTRFormatting(&domain);
261  // Retrieve the label text, inserting the display name and domain.
262  text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
263                                     display_name, domain);
264
265  learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
266  learn_more_->SetUnderline(false);
267  learn_more_->set_listener(this);
268  AddChildView(learn_more_);
269
270  CalculatePreferredSize(owner, used_width);
271}
272
273PublicAccountUserDetails::~PublicAccountUserDetails() {}
274
275void PublicAccountUserDetails::Layout() {
276  lines_.clear();
277  const gfx::Rect contents_area = GetContentsBounds();
278  if (contents_area.IsEmpty())
279     return;
280
281  // Word-wrap the label text.
282  const gfx::Font font;
283  std::vector<string16> lines;
284  ui::ElideRectangleText(text_, font, contents_area.width(),
285                         contents_area.height(), ui::ELIDE_LONG_WORDS, &lines);
286  // Loop through the lines, creating a renderer for each.
287  gfx::Point position = contents_area.origin();
288  ui::Range display_name(ui::Range::InvalidRange());
289  for (std::vector<string16>::const_iterator it = lines.begin();
290       it != lines.end(); ++it) {
291    gfx::RenderText* line = gfx::RenderText::CreateInstance();
292    line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
293    line->SetText(*it);
294    const gfx::Size size(contents_area.width(), line->GetStringSize().height());
295    line->SetDisplayRect(gfx::Rect(position, size));
296    position.set_y(position.y() + size.height());
297
298    // Set the default text color for the line.
299    line->SetColor(kPublicAccountUserCardTextColor);
300
301    // If a range of the line contains the user's display name, apply a custom
302    // text color to it.
303    if (display_name.is_empty())
304      display_name.set_start(it->find(kDisplayNameMark));
305    if (!display_name.is_empty()) {
306      display_name.set_end(
307          it->find(kDisplayNameMark, display_name.start() + 1));
308      ui::Range line_range(0, it->size());
309      line->ApplyColor(kPublicAccountUserCardNameColor,
310                       display_name.Intersect(line_range));
311      // Update the range for the next line.
312      if (display_name.end() >= line_range.end())
313        display_name.set_start(0);
314      else
315        display_name = ui::Range::InvalidRange();
316    }
317
318    lines_.push_back(line);
319  }
320
321  // Position the link after the label text, separated by a space. If it does
322  // not fit onto the last line of the text, wrap the link onto its own line.
323  const gfx::Size last_line_size = lines_.back()->GetStringSize();
324  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
325  const gfx::Size link_size = learn_more_->GetPreferredSize();
326  if (contents_area.width() - last_line_size.width() >=
327      space_width + link_size.width()) {
328    position.set_x(position.x() + last_line_size.width() + space_width);
329    position.set_y(position.y() - last_line_size.height());
330  }
331  position.set_y(position.y() - learn_more_->GetInsets().top());
332  gfx::Rect learn_more_bounds(position, link_size);
333  learn_more_bounds.Intersect(contents_area);
334  if (base::i18n::IsRTL()) {
335    const gfx::Insets insets = GetInsets();
336    learn_more_bounds.Offset(insets.right() - insets.left(), 0);
337  }
338  learn_more_->SetBoundsRect(learn_more_bounds);
339}
340
341gfx::Size PublicAccountUserDetails::GetPreferredSize() {
342  return preferred_size_;
343}
344
345void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
346  for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
347       it != lines_.end(); ++it) {
348    (*it)->Draw(canvas);
349  }
350  views::View::OnPaint(canvas);
351}
352
353void PublicAccountUserDetails::LinkClicked(views::Link* source,
354                                           int event_flags) {
355  DCHECK_EQ(source, learn_more_);
356  ash::Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
357}
358
359void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner,
360                                                      int used_width) {
361  const gfx::Font font;
362  const gfx::Size link_size = learn_more_->GetPreferredSize();
363  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
364  const gfx::Insets insets = GetInsets();
365  views::TrayBubbleView* bubble_view =
366      owner->system_tray()->GetSystemBubble()->bubble_view();
367  int min_width = std::max(
368      link_size.width(),
369      bubble_view->GetPreferredSize().width() - (used_width + insets.width()));
370  int max_width = std::min(
371      font.GetStringWidth(text_) + space_width + link_size.width(),
372      bubble_view->GetMaximumSize().width() - (used_width + insets.width()));
373  // Do a binary search for the minimum width that ensures no more than three
374  // lines are needed. The lower bound is the minimum of the current bubble
375  // width and the width of the link (as no wrapping is permitted inside the
376  // link). The upper bound is the maximum of the largest allowed bubble width
377  // and the sum of the label text and link widths when put on a single line.
378  std::vector<string16> lines;
379  while (min_width < max_width) {
380    lines.clear();
381    const int width = (min_width + max_width) / 2;
382    const bool too_narrow = ui::ElideRectangleText(
383        text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0;
384    int line_count = lines.size();
385    if (!too_narrow && line_count == 3 &&
386            width - font.GetStringWidth(lines.back()) <=
387            space_width + link_size.width()) {
388      ++line_count;
389    }
390    if (too_narrow || line_count > 3)
391      min_width = width + 1;
392    else
393      max_width = width;
394  }
395
396  // Calculate the corresponding height and set the preferred size.
397  lines.clear();
398  ui::ElideRectangleText(
399      text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines);
400  int line_count = lines.size();
401  if (min_width - font.GetStringWidth(lines.back()) <=
402      space_width + link_size.width()) {
403    ++line_count;
404  }
405  const int line_height = font.GetHeight();
406  const int link_extra_height = std::max(
407      link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
408  preferred_size_ = gfx::Size(
409      min_width + insets.width(),
410      line_count * line_height + link_extra_height + insets.height());
411
412  bubble_view->SetWidth(preferred_size_.width() + used_width);
413}
414
415UserView::UserView(SystemTrayItem* owner, ash::user::LoginStatus login)
416    : user_card_(NULL),
417      logout_button_(NULL),
418      profile_picture_(NULL) {
419  CHECK_NE(ash::user::LOGGED_IN_NONE, login);
420  set_background(views::Background::CreateSolidBackground(
421      login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor :
422                                             kBackgroundColor));
423  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
424                                        kTrayPopupPaddingBetweenItems));
425  // The logout button must be added before the user card so that the user card
426  // can correctly calculate the remaining available width.
427  AddLogoutButton(login);
428  AddUserCard(owner, login);
429}
430
431UserView::~UserView() {}
432
433gfx::Size UserView::GetPreferredSize() {
434  gfx::Size size = views::View::GetPreferredSize();
435  if (!user_card_) {
436    // Make sure the default user view item is at least as tall as the other
437    // items.
438    size.set_height(std::max(size.height(),
439                             kTrayPopupItemHeight + GetInsets().height()));
440  }
441  return size;
442}
443
444void UserView::Layout() {
445  gfx::Rect contents_area(GetContentsBounds());
446  if (user_card_ && logout_button_) {
447    // Give the logout button the space it requests.
448    gfx::Rect logout_area = contents_area;
449    logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize());
450    logout_area.set_x(contents_area.right() - logout_area.width());
451    logout_button_->SetBoundsRect(logout_area);
452
453    // Give the remaining space to the user card.
454    gfx::Rect user_card_area = contents_area;
455    int remaining_width = contents_area.width() -
456        (logout_area.width() + kTrayPopupPaddingBetweenItems);
457    user_card_area.set_width(std::max(0, remaining_width));
458    user_card_->SetBoundsRect(user_card_area);
459  } else if (user_card_) {
460    user_card_->SetBoundsRect(contents_area);
461  } else if (logout_button_) {
462    logout_button_->SetBoundsRect(contents_area);
463  }
464}
465
466void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
467  if (sender == logout_button_)
468    ash::Shell::GetInstance()->system_tray_delegate()->SignOut();
469  else if (sender == profile_picture_)
470    ash::Shell::GetInstance()->system_tray_delegate()->ChangeProfilePicture();
471  else
472    NOTREACHED();
473}
474
475void UserView::AddLogoutButton(ash::user::LoginStatus login) {
476  // A user should not be able to modify logged-in state when screen is
477  // locked.
478  if (login == ash::user::LOGGED_IN_LOCKED)
479    return;
480
481  const string16 title = ash::user::GetLocalizedSignOutStringForStatus(login,
482                                                                       true);
483  TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title);
484  logout_button->SetAccessibleName(title);
485  logout_button_ = logout_button;
486  // In public account mode, the logout button border has a custom color.
487  if (login == ash::user::LOGGED_IN_PUBLIC) {
488    TrayPopupLabelButtonBorder* border =
489        static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border());
490    border->SetPainter(false, views::Button::STATE_NORMAL,
491                       views::Painter::CreateImageGridPainter(
492                           kPublicAccountLogoutButtonBorderImagesNormal));
493    border->SetPainter(false, views::Button::STATE_HOVERED,
494                       views::Painter::CreateImageGridPainter(
495                           kPublicAccountLogoutButtonBorderImagesHovered));
496    border->SetPainter(false, views::Button::STATE_PRESSED,
497                       views::Painter::CreateImageGridPainter(
498                           kPublicAccountLogoutButtonBorderImagesHovered));
499  }
500  AddChildView(logout_button_);
501}
502
503void UserView::AddUserCard(SystemTrayItem* owner,
504                           ash::user::LoginStatus login) {
505  if (login == ash::user::LOGGED_IN_GUEST)
506    return;
507
508  set_border(views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal,
509                                              0, kTrayPopupPaddingHorizontal));
510
511  user_card_ = new views::View();
512  user_card_->SetLayoutManager(new views::BoxLayout(
513      views::BoxLayout::kHorizontal, 0, kUserCardVerticalPadding,
514      kTrayPopupPaddingBetweenItems));
515  AddChildViewAt(user_card_, 0);
516
517  if (login == ash::user::LOGGED_IN_RETAIL_MODE) {
518    views::Label* details = new views::Label;
519    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
520    details->SetText(
521        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
522    details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1));
523    details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
524    user_card_->AddChildView(details);
525    return;
526  }
527  profile_picture_ = new ClickableAvatar(this);
528  user_card_->AddChildView(profile_picture_);
529
530  if (login == ash::user::LOGGED_IN_PUBLIC) {
531    user_card_->AddChildView(new PublicAccountUserDetails(
532        owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems));
533    return;
534  }
535
536  ash::SystemTrayDelegate* delegate =
537      ash::Shell::GetInstance()->system_tray_delegate();
538  views::View* details = new views::View;
539  details->SetLayoutManager(new views::BoxLayout(
540      views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
541  views::Label* username = new views::Label(delegate->GetUserDisplayName());
542  username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
543  details->AddChildView(username);
544
545  views::Label* email = new views::Label(UTF8ToUTF16(delegate->GetUserEmail()));
546  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
547  email->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont));
548  email->SetHorizontalAlignment(gfx::ALIGN_LEFT);
549  email->SetEnabled(false);
550  details->AddChildView(email);
551  user_card_->AddChildView(details);
552}
553
554}  // namespace tray
555
556TrayUser::TrayUser(SystemTray* system_tray)
557    : SystemTrayItem(system_tray),
558      user_(NULL),
559      avatar_(NULL),
560      label_(NULL) {
561  Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
562}
563
564TrayUser::~TrayUser() {
565  Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
566}
567
568views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
569  CHECK(avatar_ == NULL);
570  CHECK(label_ == NULL);
571  if (status == user::LOGGED_IN_GUEST) {
572    label_ = new views::Label;
573    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
574    label_->SetText(bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL));
575    SetupLabelForTray(label_);
576  } else {
577    avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius);
578  }
579  UpdateAfterLoginStatusChange(status);
580  return avatar_ ? static_cast<views::View*>(avatar_)
581                 : static_cast<views::View*>(label_);
582}
583
584views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
585  if (status == user::LOGGED_IN_NONE)
586    return NULL;
587
588  CHECK(user_ == NULL);
589  user_ = new tray::UserView(this, status);
590  return user_;
591}
592
593views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
594  return NULL;
595}
596
597void TrayUser::DestroyTrayView() {
598  avatar_ = NULL;
599  label_ = NULL;
600}
601
602void TrayUser::DestroyDefaultView() {
603  user_ = NULL;
604}
605
606void TrayUser::DestroyDetailedView() {
607}
608
609void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
610  switch (status) {
611    case user::LOGGED_IN_LOCKED:
612    case user::LOGGED_IN_USER:
613    case user::LOGGED_IN_OWNER:
614    case user::LOGGED_IN_PUBLIC:
615    case user::LOGGED_IN_LOCALLY_MANAGED:
616      avatar_->SetImage(
617          ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(),
618          gfx::Size(kUserIconSize, kUserIconSize));
619      avatar_->SetVisible(true);
620      break;
621
622    case user::LOGGED_IN_GUEST:
623      label_->SetVisible(true);
624      break;
625
626    case user::LOGGED_IN_RETAIL_MODE:
627    case user::LOGGED_IN_KIOSK_APP:
628    case user::LOGGED_IN_NONE:
629      avatar_->SetVisible(false);
630      break;
631  }
632}
633
634void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
635  if (avatar_) {
636    if (alignment == SHELF_ALIGNMENT_BOTTOM ||
637        alignment == SHELF_ALIGNMENT_TOP) {
638      avatar_->set_border(views::Border::CreateEmptyBorder(
639          0, kTrayImageItemHorizontalPaddingBottomAlignment + 2,
640          0, kTrayImageItemHorizontalPaddingBottomAlignment));
641    } else {
642        SetTrayImageItemBorder(avatar_, alignment);
643    }
644  } else {
645    if (alignment == SHELF_ALIGNMENT_BOTTOM ||
646        alignment == SHELF_ALIGNMENT_TOP) {
647      label_->set_border(views::Border::CreateEmptyBorder(
648          0, kTrayLabelItemHorizontalPaddingBottomAlignment,
649          0, kTrayLabelItemHorizontalPaddingBottomAlignment));
650    } else {
651      label_->set_border(views::Border::CreateEmptyBorder(
652          kTrayLabelItemVerticalPaddingVeriticalAlignment,
653          kTrayLabelItemHorizontalPaddingBottomAlignment,
654          kTrayLabelItemVerticalPaddingVeriticalAlignment,
655          kTrayLabelItemHorizontalPaddingBottomAlignment));
656    }
657  }
658}
659
660void TrayUser::OnUserUpdate() {
661  // Check for null to avoid crbug.com/150944.
662  if (avatar_) {
663    avatar_->SetImage(
664        ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(),
665        gfx::Size(kUserIconSize, kUserIconSize));
666  }
667}
668
669}  // namespace internal
670}  // namespace ash
671