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