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 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 51 TrayUser::~TrayUser() { 52 Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this); 53 } 54 55 TrayUser::TestState TrayUser::GetStateForTest() const { 56 if (!user_) 57 return HIDDEN; 58 return user_->GetStateForTest(); 59 } 60 61 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 69 gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const { 70 DCHECK(user_); 71 return user_->GetBoundsInScreenOfUserButtonForTest(); 72 } 73 74 void TrayUser::UpdateAfterLoginStatusChangeForTest(user::LoginStatus status) { 75 UpdateAfterLoginStatusChange(status); 76 } 77 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 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 111 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { 112 return new tray::AccountsDetailedView(this, status); 113 } 114 115 void TrayUser::DestroyTrayView() { 116 layout_view_ = NULL; 117 avatar_ = NULL; 118 label_ = NULL; 119 } 120 121 void TrayUser::DestroyDefaultView() { 122 user_ = NULL; 123 } 124 125 void TrayUser::DestroyDetailedView() { 126 } 127 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 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 238 void TrayUser::OnUserUpdate() { 239 UpdateAvatarImage(Shell::GetInstance()->system_tray_delegate()-> 240 GetUserLoginStatus()); 241 } 242 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 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 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 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