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
33 namespace {
34
35 const int kUserLabelToIconPadding = 5;
36
37 } // namespace
38
39 namespace ash {
40
TrayUser(SystemTray * system_tray,MultiProfileIndex index)41 TrayUser::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
~TrayUser()51 TrayUser::~TrayUser() {
52 Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
53 }
54
GetStateForTest() const55 TrayUser::TestState TrayUser::GetStateForTest() const {
56 if (!user_)
57 return HIDDEN;
58 return user_->GetStateForTest();
59 }
60
GetLayoutSizeForTest() const61 gfx::Size TrayUser::GetLayoutSizeForTest() const {
62 if (!layout_view_) {
63 return gfx::Size(0, 0);
64 } else {
65 return layout_view_->size();
66 }
67 }
68
GetUserPanelBoundsInScreenForTest() const69 gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const {
70 DCHECK(user_);
71 return user_->GetBoundsInScreenOfUserButtonForTest();
72 }
73
UpdateAfterLoginStatusChangeForTest(user::LoginStatus status)74 void TrayUser::UpdateAfterLoginStatusChangeForTest(user::LoginStatus status) {
75 UpdateAfterLoginStatusChange(status);
76 }
77
CreateTrayView(user::LoginStatus status)78 views::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
CreateDefaultView(user::LoginStatus status)89 views::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
CreateDetailedView(user::LoginStatus status)111 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
112 return new tray::AccountsDetailedView(this, status);
113 }
114
DestroyTrayView()115 void TrayUser::DestroyTrayView() {
116 layout_view_ = NULL;
117 avatar_ = NULL;
118 label_ = NULL;
119 }
120
DestroyDefaultView()121 void TrayUser::DestroyDefaultView() {
122 user_ = NULL;
123 }
124
DestroyDetailedView()125 void TrayUser::DestroyDetailedView() {
126 }
127
UpdateAfterLoginStatusChange(user::LoginStatus status)128 void 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
UpdateAfterShelfAlignmentChange(ShelfAlignment alignment)192 void 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
OnUserUpdate()238 void TrayUser::OnUserUpdate() {
239 UpdateAvatarImage(Shell::GetInstance()->system_tray_delegate()->
240 GetUserLoginStatus());
241 }
242
OnUserAddedToSession()243 void 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
UpdateAvatarImage(user::LoginStatus status)258 void 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
GetTrayIndex()275 MultiProfileIndex 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
UpdateLayoutOfItem()286 void 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