tray_user.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 "ash/ash_switches.h"
8#include "ash/root_window_controller.h"
9#include "ash/session/session_state_delegate.h"
10#include "ash/session/user_info.h"
11#include "ash/shelf/shelf_layout_manager.h"
12#include "ash/shell_delegate.h"
13#include "ash/system/tray/system_tray.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_utils.h"
18#include "ash/system/user/accounts_detailed_view.h"
19#include "ash/system/user/rounded_image_view.h"
20#include "ash/system/user/user_view.h"
21#include "base/logging.h"
22#include "base/strings/string16.h"
23#include "grit/ash_strings.h"
24#include "ui/aura/window.h"
25#include "ui/base/l10n/l10n_util.h"
26#include "ui/gfx/image/image.h"
27#include "ui/views/border.h"
28#include "ui/views/controls/label.h"
29#include "ui/views/layout/box_layout.h"
30#include "ui/views/view.h"
31#include "ui/views/widget/widget.h"
32
33namespace {
34
35const int kUserLabelToIconPadding = 5;
36
37}  // namespace
38
39namespace ash {
40
41TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index)
42    : SystemTrayItem(system_tray),
43      multiprofile_index_(index),
44      user_(NULL),
45      layout_view_(NULL),
46      avatar_(NULL),
47      label_(NULL) {
48  Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
49}
50
51TrayUser::~TrayUser() {
52  Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
53}
54
55TrayUser::TestState TrayUser::GetStateForTest() const {
56  if (!user_)
57    return HIDDEN;
58  return user_->GetStateForTest();
59}
60
61gfx::Size TrayUser::GetLayoutSizeForTest() const {
62  if (!layout_view_) {
63    return gfx::Size(0, 0);
64  } else {
65    return layout_view_->size();
66  }
67}
68
69gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const {
70  DCHECK(user_);
71  return user_->GetBoundsInScreenOfUserButtonForTest();
72}
73
74void TrayUser::UpdateAfterLoginStatusChangeForTest(user::LoginStatus status) {
75  UpdateAfterLoginStatusChange(status);
76}
77
78views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
79  CHECK(layout_view_ == NULL);
80
81  layout_view_ = new views::View;
82  layout_view_->SetLayoutManager(
83      new views::BoxLayout(views::BoxLayout::kHorizontal,
84                           0, 0, kUserLabelToIconPadding));
85  UpdateAfterLoginStatusChange(status);
86  return layout_view_;
87}
88
89views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
90  if (status == user::LOGGED_IN_NONE)
91    return NULL;
92  const SessionStateDelegate* session_state_delegate =
93      Shell::GetInstance()->session_state_delegate();
94
95  // If the screen is locked show only the currently active user.
96  if (multiprofile_index_ && session_state_delegate->IsUserSessionBlocked())
97    return NULL;
98
99  CHECK(user_ == NULL);
100
101  int logged_in_users = session_state_delegate->NumberOfLoggedInUsers();
102
103  // Do not show more UserView's then there are logged in users.
104  if (multiprofile_index_ >= logged_in_users)
105    return NULL;
106
107  user_ = new tray::UserView(this, status, multiprofile_index_, false);
108  return user_;
109}
110
111views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
112  return new tray::AccountsDetailedView(this, status);
113}
114
115void TrayUser::DestroyTrayView() {
116  layout_view_ = NULL;
117  avatar_ = NULL;
118  label_ = NULL;
119}
120
121void TrayUser::DestroyDefaultView() {
122  user_ = NULL;
123}
124
125void TrayUser::DestroyDetailedView() {
126}
127
128void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
129  // Only the active user is represented in the tray.
130  if (!layout_view_)
131    return;
132  if (GetTrayIndex() > 0)
133    return;
134  bool need_label = false;
135  bool need_avatar = false;
136  switch (status) {
137    case user::LOGGED_IN_LOCKED:
138    case user::LOGGED_IN_USER:
139    case user::LOGGED_IN_OWNER:
140    case user::LOGGED_IN_PUBLIC:
141      need_avatar = true;
142      break;
143    case user::LOGGED_IN_LOCALLY_MANAGED:
144      need_avatar = true;
145      need_label = true;
146      break;
147    case user::LOGGED_IN_GUEST:
148      need_label = true;
149      break;
150    case user::LOGGED_IN_RETAIL_MODE:
151    case user::LOGGED_IN_KIOSK_APP:
152    case user::LOGGED_IN_NONE:
153      break;
154  }
155
156  if ((need_avatar != (avatar_ != NULL)) ||
157      (need_label != (label_ != NULL))) {
158    layout_view_->RemoveAllChildViews(true);
159    if (need_label) {
160      label_ = new views::Label;
161      SetupLabelForTray(label_);
162      layout_view_->AddChildView(label_);
163    } else {
164      label_ = NULL;
165    }
166    if (need_avatar) {
167      avatar_ = new tray::RoundedImageView(kTrayAvatarCornerRadius, true);
168      layout_view_->AddChildView(avatar_);
169    } else {
170      avatar_ = NULL;
171    }
172  }
173
174  if (status == user::LOGGED_IN_LOCALLY_MANAGED) {
175    label_->SetText(
176        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL));
177  } else if (status == user::LOGGED_IN_GUEST) {
178    label_->SetText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_GUEST_LABEL));
179  }
180
181  if (avatar_) {
182    avatar_->SetCornerRadii(
183        0, kTrayAvatarCornerRadius, kTrayAvatarCornerRadius, 0);
184    avatar_->SetBorder(views::Border::NullBorder());
185  }
186  UpdateAvatarImage(status);
187
188  // Update layout after setting label_ and avatar_ with new login status.
189  UpdateLayoutOfItem();
190}
191
192void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
193  // Inactive users won't have a layout.
194  if (!layout_view_)
195    return;
196  if (alignment == SHELF_ALIGNMENT_BOTTOM ||
197      alignment == SHELF_ALIGNMENT_TOP) {
198    if (avatar_) {
199      avatar_->SetBorder(views::Border::NullBorder());
200      avatar_->SetCornerRadii(
201          0, kTrayAvatarCornerRadius, kTrayAvatarCornerRadius, 0);
202    }
203    if (label_) {
204      // If label_ hasn't figured out its size yet, do that first.
205      if (label_->GetContentsBounds().height() == 0)
206        label_->SizeToPreferredSize();
207      int height = label_->GetContentsBounds().height();
208      int vertical_pad = (kTrayItemSize - height) / 2;
209      int remainder = height % 2;
210      label_->SetBorder(views::Border::CreateEmptyBorder(
211          vertical_pad + remainder,
212          kTrayLabelItemHorizontalPaddingBottomAlignment,
213          vertical_pad,
214          kTrayLabelItemHorizontalPaddingBottomAlignment));
215    }
216    layout_view_->SetLayoutManager(
217        new views::BoxLayout(views::BoxLayout::kHorizontal,
218                             0, 0, kUserLabelToIconPadding));
219  } else {
220    if (avatar_) {
221      avatar_->SetBorder(views::Border::NullBorder());
222      avatar_->SetCornerRadii(
223          0, 0, kTrayAvatarCornerRadius, kTrayAvatarCornerRadius);
224    }
225    if (label_) {
226      label_->SetBorder(views::Border::CreateEmptyBorder(
227          kTrayLabelItemVerticalPaddingVerticalAlignment,
228          kTrayLabelItemHorizontalPaddingBottomAlignment,
229          kTrayLabelItemVerticalPaddingVerticalAlignment,
230          kTrayLabelItemHorizontalPaddingBottomAlignment));
231    }
232    layout_view_->SetLayoutManager(
233        new views::BoxLayout(views::BoxLayout::kVertical,
234                             0, 0, kUserLabelToIconPadding));
235  }
236}
237
238void TrayUser::OnUserUpdate() {
239  UpdateAvatarImage(Shell::GetInstance()->system_tray_delegate()->
240      GetUserLoginStatus());
241}
242
243void TrayUser::OnUserAddedToSession() {
244  SessionStateDelegate* session_state_delegate =
245      Shell::GetInstance()->session_state_delegate();
246  // Only create views for user items which are logged in.
247  if (GetTrayIndex() >= session_state_delegate->NumberOfLoggedInUsers())
248    return;
249
250  // Enforce a layout change that newly added items become visible.
251  UpdateLayoutOfItem();
252
253  // Update the user item.
254  UpdateAvatarImage(
255      Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus());
256}
257
258void TrayUser::UpdateAvatarImage(user::LoginStatus status) {
259  SessionStateDelegate* session_state_delegate =
260      Shell::GetInstance()->session_state_delegate();
261  if (!avatar_ ||
262      GetTrayIndex() >= session_state_delegate->NumberOfLoggedInUsers())
263    return;
264
265  content::BrowserContext* context = session_state_delegate->
266      GetBrowserContextByIndex(GetTrayIndex());
267  avatar_->SetImage(session_state_delegate->GetUserInfo(context)->GetImage(),
268                    gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
269
270  // Unit tests might come here with no images for some users.
271  if (avatar_->size().IsEmpty())
272    avatar_->SetSize(gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
273}
274
275MultiProfileIndex TrayUser::GetTrayIndex() {
276  Shell* shell = Shell::GetInstance();
277  // If multi profile is not enabled we can use the normal index.
278  if (!shell->delegate()->IsMultiProfilesEnabled())
279    return multiprofile_index_;
280  // In case of multi profile we need to mirror the indices since the system
281  // tray items are in the reverse order then the menu items.
282  return shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() -
283             1 - multiprofile_index_;
284}
285
286void TrayUser::UpdateLayoutOfItem() {
287  RootWindowController* controller = GetRootWindowController(
288      system_tray()->GetWidget()->GetNativeWindow()->GetRootWindow());
289  if (controller && controller->shelf()) {
290    UpdateAfterShelfAlignmentChange(
291        controller->GetShelfLayoutManager()->GetAlignment());
292  }
293}
294
295}  // namespace ash
296