profile_chooser_view.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright 2014 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 "chrome/browser/ui/views/profiles/profile_chooser_view.h" 6 7#include "base/prefs/pref_service.h" 8#include "base/strings/utf_string_conversions.h" 9#include "chrome/browser/browser_process.h" 10#include "chrome/browser/lifetime/application_lifetime.h" 11#include "chrome/browser/profiles/profile_avatar_icon_util.h" 12#include "chrome/browser/profiles/profile_info_cache.h" 13#include "chrome/browser/profiles/profile_manager.h" 14#include "chrome/browser/profiles/profile_metrics.h" 15#include "chrome/browser/profiles/profile_window.h" 16#include "chrome/browser/profiles/profiles_state.h" 17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 18#include "chrome/browser/signin/signin_header_helper.h" 19#include "chrome/browser/signin/signin_manager_factory.h" 20#include "chrome/browser/signin/signin_promo.h" 21#include "chrome/browser/ui/browser.h" 22#include "chrome/browser/ui/browser_commands.h" 23#include "chrome/browser/ui/browser_dialogs.h" 24#include "chrome/browser/ui/chrome_pages.h" 25#include "chrome/browser/ui/singleton_tabs.h" 26#include "chrome/browser/ui/views/profiles/user_manager_view.h" 27#include "chrome/common/pref_names.h" 28#include "chrome/common/url_constants.h" 29#include "components/signin/core/browser/mutable_profile_oauth2_token_service.h" 30#include "components/signin/core/browser/profile_oauth2_token_service.h" 31#include "components/signin/core/browser/signin_error_controller.h" 32#include "components/signin/core/browser/signin_manager.h" 33#include "components/signin/core/common/profile_management_switches.h" 34#include "grit/chromium_strings.h" 35#include "grit/generated_resources.h" 36#include "grit/theme_resources.h" 37#include "third_party/skia/include/core/SkColor.h" 38#include "ui/base/l10n/l10n_util.h" 39#include "ui/base/resource/resource_bundle.h" 40#include "ui/gfx/canvas.h" 41#include "ui/gfx/image/image.h" 42#include "ui/gfx/image/image_skia.h" 43#include "ui/gfx/path.h" 44#include "ui/gfx/skia_util.h" 45#include "ui/gfx/text_elider.h" 46#include "ui/native_theme/native_theme.h" 47#include "ui/views/controls/button/blue_button.h" 48#include "ui/views/controls/button/image_button.h" 49#include "ui/views/controls/button/label_button.h" 50#include "ui/views/controls/button/menu_button.h" 51#include "ui/views/controls/label.h" 52#include "ui/views/controls/link.h" 53#include "ui/views/controls/separator.h" 54#include "ui/views/controls/styled_label.h" 55#include "ui/views/controls/textfield/textfield.h" 56#include "ui/views/controls/webview/webview.h" 57#include "ui/views/layout/grid_layout.h" 58#include "ui/views/layout/layout_constants.h" 59#include "ui/views/widget/widget.h" 60 61namespace { 62 63// Helpers -------------------------------------------------------------------- 64 65const int kFixedMenuWidth = 250; 66const int kButtonHeight = 29; 67const int kProfileAvatarTutorialShowMax = 1; 68const int kFixedGaiaViewHeight = 400; 69const int kFixedGaiaViewWidth = 360; 70const int kFixedAccountRemovalViewWidth = 280; 71const int kFixedEndPreviewViewWidth = 280; 72const int kLargeImageSide = 88; 73 74// Creates a GridLayout with a single column. This ensures that all the child 75// views added get auto-expanded to fill the full width of the bubble. 76views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) { 77 views::GridLayout* layout = new views::GridLayout(view); 78 view->SetLayoutManager(layout); 79 80 views::ColumnSet* columns = layout->AddColumnSet(0); 81 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 82 views::GridLayout::FIXED, width, width); 83 return layout; 84} 85 86views::Link* CreateLink(const base::string16& link_text, 87 views::LinkListener* listener) { 88 views::Link* link_button = new views::Link(link_text); 89 link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT); 90 link_button->SetUnderline(false); 91 link_button->set_listener(listener); 92 return link_button; 93} 94 95gfx::ImageSkia CreateSquarePlaceholderImage(int size) { 96 SkBitmap bitmap; 97 bitmap.allocPixels(SkImageInfo::MakeA8(size, size)); 98 bitmap.eraseARGB(0, 0, 0, 0); 99 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); 100} 101 102bool HasAuthError(Profile* profile) { 103 const SigninErrorController* error = 104 profiles::GetSigninErrorController(profile); 105 return error && error->HasError(); 106} 107 108std::string GetAuthErrorAccountId(Profile* profile) { 109 const SigninErrorController* error = 110 profiles::GetSigninErrorController(profile); 111 if (!error) 112 return std::string(); 113 114 return error->error_account_id(); 115} 116 117std::string GetAuthErrorUsername(Profile* profile) { 118 const SigninErrorController* error = 119 profiles::GetSigninErrorController(profile); 120 if (!error) 121 return std::string(); 122 123 return error->error_username(); 124} 125 126// BackgroundColorHoverButton ------------------------------------------------- 127 128// A custom button that allows for setting a background color when hovered over. 129class BackgroundColorHoverButton : public views::LabelButton { 130 public: 131 BackgroundColorHoverButton(views::ButtonListener* listener, 132 const base::string16& text, 133 const gfx::ImageSkia& normal_icon, 134 const gfx::ImageSkia& hover_icon); 135 virtual ~BackgroundColorHoverButton(); 136 137 private: 138 // views::LabelButton: 139 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 140 141 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton); 142}; 143 144BackgroundColorHoverButton::BackgroundColorHoverButton( 145 views::ButtonListener* listener, 146 const base::string16& text, 147 const gfx::ImageSkia& normal_icon, 148 const gfx::ImageSkia& hover_icon) 149 : views::LabelButton(listener, text) { 150 SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew, 151 0, views::kButtonHEdgeMarginNew)); 152 set_min_size(gfx::Size(0, kButtonHeight)); 153 SetImage(STATE_NORMAL, normal_icon); 154 SetImage(STATE_HOVERED, hover_icon); 155 SetImage(STATE_PRESSED, hover_icon); 156} 157 158BackgroundColorHoverButton::~BackgroundColorHoverButton() {} 159 160void BackgroundColorHoverButton::OnPaint(gfx::Canvas* canvas) { 161 if ((state() == STATE_PRESSED) || (state() == STATE_HOVERED) || HasFocus()) { 162 canvas->DrawColor(GetNativeTheme()->GetSystemColor( 163 ui::NativeTheme::kColorId_ButtonHoverBackgroundColor)); 164 } 165 LabelButton::OnPaint(canvas); 166} 167 168// SizedContainer ------------------------------------------------- 169 170// A simple container view that takes an explicit preferred size. 171class SizedContainer : public views::View { 172 public: 173 explicit SizedContainer(const gfx::Size& preferred_size) 174 : preferred_size_(preferred_size) {} 175 176 virtual gfx::Size GetPreferredSize() const OVERRIDE { 177 return preferred_size_; 178 } 179 180 private: 181 gfx::Size preferred_size_; 182}; 183 184} // namespace 185 186 187// EditableProfilePhoto ------------------------------------------------- 188 189// A custom Image control that shows a "change" button when moused over. 190class EditableProfilePhoto : public views::ImageView { 191 public: 192 EditableProfilePhoto(views::ButtonListener* listener, 193 const gfx::Image& icon, 194 bool is_editing_allowed, 195 const gfx::Rect& bounds) 196 : views::ImageView(), 197 change_photo_button_(NULL) { 198 gfx::Image image = profiles::GetSizedAvatarIcon( 199 icon, true, kLargeImageSide, kLargeImageSide); 200 SetImage(image.ToImageSkia()); 201 SetBoundsRect(bounds); 202 203 // Calculate the circular mask that will be used to display the photo. 204 circular_mask_.addCircle(SkIntToScalar(bounds.width() / 2), 205 SkIntToScalar(bounds.height() / 2), 206 SkIntToScalar(bounds.width() / 2)); 207 208 if (!is_editing_allowed) 209 return; 210 211 set_notify_enter_exit_on_child(true); 212 213 // Button overlay that appears when hovering over the image. 214 change_photo_button_ = new views::LabelButton(listener, base::string16()); 215 change_photo_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 216 change_photo_button_->SetBorder(views::Border::NullBorder()); 217 218 const SkColor kBackgroundColor = SkColorSetARGB(65, 255, 255, 255); 219 change_photo_button_->set_background( 220 views::Background::CreateSolidBackground(kBackgroundColor)); 221 change_photo_button_->SetImage(views::LabelButton::STATE_NORMAL, 222 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 223 IDR_ICON_PROFILES_EDIT_CAMERA)); 224 225 change_photo_button_->SetSize(bounds.size()); 226 change_photo_button_->SetVisible(false); 227 AddChildView(change_photo_button_); 228 } 229 230 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 231 // Display the profile picture as a circle. 232 canvas->ClipPath(circular_mask_, true); 233 views::ImageView::OnPaint(canvas); 234 } 235 236 virtual void PaintChildren(gfx::Canvas* canvas, 237 const views::CullSet& cull_set) OVERRIDE { 238 // Display any children (the "change photo" overlay) as a circle. 239 canvas->ClipPath(circular_mask_, true); 240 View::PaintChildren(canvas, cull_set); 241 } 242 243 views::LabelButton* change_photo_button() { return change_photo_button_; } 244 245 private: 246 // views::View: 247 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 248 if (change_photo_button_) 249 change_photo_button_->SetVisible(true); 250 } 251 252 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 253 if (change_photo_button_) 254 change_photo_button_->SetVisible(false); 255 } 256 257 gfx::Path circular_mask_; 258 259 // Button that is shown when hovering over the image view. Can be NULL if 260 // the photo isn't allowed to be edited (e.g. for guest profiles). 261 views::LabelButton* change_photo_button_; 262 263 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto); 264}; 265 266 267// EditableProfileName ------------------------------------------------- 268 269// A custom text control that turns into a textfield for editing when clicked. 270class EditableProfileName : public views::LabelButton, 271 public views::ButtonListener { 272 public: 273 EditableProfileName(views::TextfieldController* controller, 274 const base::string16& text, 275 bool is_editing_allowed) 276 : views::LabelButton(this, text), 277 profile_name_textfield_(NULL) { 278 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 279 const gfx::FontList& medium_font_list = 280 rb->GetFontList(ui::ResourceBundle::MediumFont); 281 SetFontList(medium_font_list); 282 SetHorizontalAlignment(gfx::ALIGN_CENTER); 283 284 if (!is_editing_allowed) { 285 SetBorder(views::Border::CreateEmptyBorder(2, 0, 2, 0)); 286 return; 287 } 288 289 // Show an "edit" pencil icon when hovering over. In the default state, 290 // we need to create an empty placeholder of the correct size, so that 291 // the text doesn't jump around when the hovered icon appears. 292 gfx::ImageSkia hover_image = 293 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER); 294 SetImage(STATE_NORMAL, CreateSquarePlaceholderImage(hover_image.width())); 295 SetImage(STATE_HOVERED, hover_image); 296 SetImage(STATE_PRESSED, 297 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED)); 298 // To center the text, we need to offest it by the width of the icon we 299 // are adding. We need to also add a small top/bottom padding to account 300 // for the textfield's border. 301 SetBorder(views::Border::CreateEmptyBorder(2, hover_image.width(), 2, 0)); 302 303 // Textfield that overlaps the button. 304 profile_name_textfield_ = new views::Textfield(); 305 profile_name_textfield_->set_controller(controller); 306 profile_name_textfield_->SetFontList(medium_font_list); 307 profile_name_textfield_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 308 309 profile_name_textfield_->SetVisible(false); 310 AddChildView(profile_name_textfield_); 311 } 312 313 views::Textfield* profile_name_textfield() { 314 return profile_name_textfield_; 315 } 316 317 // Hide the editable textfield to show the profile name button instead. 318 void ShowReadOnlyView() { 319 if (profile_name_textfield_) 320 profile_name_textfield_->SetVisible(false); 321 } 322 323 private: 324 // views::ButtonListener: 325 virtual void ButtonPressed(views::Button* sender, 326 const ui::Event& event) OVERRIDE { 327 if (profile_name_textfield_) { 328 profile_name_textfield_->SetVisible(true); 329 profile_name_textfield_->SetText(GetText()); 330 profile_name_textfield_->SelectAll(false); 331 profile_name_textfield_->RequestFocus(); 332 } 333 } 334 335 // views::LabelButton: 336 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE { 337 // Override CustomButton's implementation, which presses the button when 338 // you press space and clicks it when you release space, as the space can be 339 // part of the new profile name typed in the textfield. 340 return false; 341 } 342 343 virtual void Layout() OVERRIDE { 344 if (profile_name_textfield_) 345 profile_name_textfield_->SetBounds(0, 0, width(), height()); 346 // This layout trick keeps the text left-aligned and the icon right-aligned. 347 SetHorizontalAlignment(gfx::ALIGN_RIGHT); 348 views::LabelButton::Layout(); 349 label()->SetHorizontalAlignment(gfx::ALIGN_CENTER); 350 } 351 352 // Textfield that is shown when editing the profile name. Can be NULL if 353 // the profile name isn't allowed to be edited (e.g. for guest profiles). 354 views::Textfield* profile_name_textfield_; 355 356 DISALLOW_COPY_AND_ASSIGN(EditableProfileName); 357}; 358 359// A title card with one back button right aligned and one label center aligned. 360class TitleCard : public views::View { 361 public: 362 TitleCard(int message_id, views::ButtonListener* listener, 363 views::ImageButton** back_button) { 364 back_button_ = new views::ImageButton(listener); 365 back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 366 views::ImageButton::ALIGN_MIDDLE); 367 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 368 back_button_->SetImage(views::ImageButton::STATE_NORMAL, 369 rb->GetImageSkiaNamed(IDR_BACK)); 370 back_button_->SetImage(views::ImageButton::STATE_HOVERED, 371 rb->GetImageSkiaNamed(IDR_BACK_H)); 372 back_button_->SetImage(views::ImageButton::STATE_PRESSED, 373 rb->GetImageSkiaNamed(IDR_BACK_P)); 374 back_button_->SetImage(views::ImageButton::STATE_DISABLED, 375 rb->GetImageSkiaNamed(IDR_BACK_D)); 376 *back_button = back_button_; 377 378 title_label_ = new views::Label(l10n_util::GetStringUTF16(message_id)); 379 title_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 380 const gfx::FontList& medium_font_list = 381 rb->GetFontList(ui::ResourceBundle::MediumFont); 382 title_label_->SetFontList(medium_font_list); 383 384 AddChildView(back_button_); 385 AddChildView(title_label_); 386 } 387 388 // Creates a new view that has the |title_card| with padding at the top, an 389 // edge-to-edge separator below, and the specified |view| at the bottom. 390 static views::View* AddPaddedTitleCard(views::View* view, 391 TitleCard* title_card, 392 int width) { 393 views::View* titled_view = new views::View(); 394 views::GridLayout* layout = new views::GridLayout(titled_view); 395 titled_view->SetLayoutManager(layout); 396 397 // Column set 0 is a single column layout with horizontal padding at left 398 // and right, and column set 1 is a single column layout with no padding. 399 views::ColumnSet* columns = layout->AddColumnSet(0); 400 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew); 401 int available_width = width - 2 * views::kButtonHEdgeMarginNew; 402 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 403 views::GridLayout::FIXED, available_width, available_width); 404 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew); 405 layout->AddColumnSet(1)->AddColumn(views::GridLayout::FILL, 406 views::GridLayout::FILL, 0,views::GridLayout::FIXED, width, width); 407 408 layout->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew); 409 layout->AddView(title_card); 410 layout->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing); 411 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 412 413 layout->StartRow(1, 1); 414 layout->AddView(view); 415 416 return titled_view; 417 } 418 419 private: 420 virtual void Layout() OVERRIDE{ 421 back_button_->SetBounds( 422 0, 0, back_button_->GetPreferredSize().width(), height()); 423 title_label_->SetBoundsRect(GetContentsBounds()); 424 } 425 426 virtual gfx::Size GetPreferredSize() const OVERRIDE{ 427 int height = std::max(title_label_->GetPreferredSize().height(), 428 back_button_->GetPreferredSize().height()); 429 return gfx::Size(width(), height); 430 } 431 432 views::ImageButton* back_button_; 433 views::Label* title_label_; 434 435 DISALLOW_COPY_AND_ASSIGN(TitleCard); 436}; 437 438// ProfileChooserView --------------------------------------------------------- 439 440// static 441ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL; 442bool ProfileChooserView::close_on_deactivate_for_testing_ = true; 443 444// static 445void ProfileChooserView::ShowBubble( 446 profiles::BubbleViewMode view_mode, 447 const signin::ManageAccountsParams& manage_accounts_params, 448 views::View* anchor_view, 449 views::BubbleBorder::Arrow arrow, 450 views::BubbleBorder::BubbleAlignment border_alignment, 451 Browser* browser) { 452 if (IsShowing()) 453 return; 454 455 profile_bubble_ = new ProfileChooserView(anchor_view, arrow, browser, 456 view_mode, manage_accounts_params.service_type); 457 views::BubbleDelegateView::CreateBubble(profile_bubble_); 458 profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_); 459 profile_bubble_->SetAlignment(border_alignment); 460 profile_bubble_->GetWidget()->Show(); 461 profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); 462} 463 464// static 465bool ProfileChooserView::IsShowing() { 466 return profile_bubble_ != NULL; 467} 468 469// static 470void ProfileChooserView::Hide() { 471 if (IsShowing()) 472 profile_bubble_->GetWidget()->Close(); 473} 474 475ProfileChooserView::ProfileChooserView(views::View* anchor_view, 476 views::BubbleBorder::Arrow arrow, 477 Browser* browser, 478 profiles::BubbleViewMode view_mode, 479 signin::GAIAServiceType service_type) 480 : BubbleDelegateView(anchor_view, arrow), 481 browser_(browser), 482 view_mode_(view_mode), 483 tutorial_mode_(profiles::TUTORIAL_MODE_NONE), 484 gaia_service_type_(service_type) { 485 // Reset the default margins inherited from the BubbleDelegateView. 486 set_margins(gfx::Insets()); 487 set_background(views::Background::CreateSolidBackground( 488 GetNativeTheme()->GetSystemColor( 489 ui::NativeTheme::kColorId_DialogBackground))); 490 ResetView(); 491 492 avatar_menu_.reset(new AvatarMenu( 493 &g_browser_process->profile_manager()->GetProfileInfoCache(), 494 this, 495 browser_)); 496 avatar_menu_->RebuildMenu(); 497 498 ProfileOAuth2TokenService* oauth2_token_service = 499 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); 500 if (oauth2_token_service) 501 oauth2_token_service->AddObserver(this); 502} 503 504ProfileChooserView::~ProfileChooserView() { 505 ProfileOAuth2TokenService* oauth2_token_service = 506 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); 507 if (oauth2_token_service) 508 oauth2_token_service->RemoveObserver(this); 509} 510 511void ProfileChooserView::ResetView() { 512 question_mark_button_ = NULL; 513 manage_accounts_link_ = NULL; 514 signin_current_profile_link_ = NULL; 515 users_button_ = NULL; 516 lock_button_ = NULL; 517 add_account_link_ = NULL; 518 current_profile_photo_ = NULL; 519 current_profile_name_ = NULL; 520 tutorial_ok_button_ = NULL; 521 tutorial_learn_more_link_ = NULL; 522 tutorial_enable_new_profile_management_button_ = NULL; 523 tutorial_end_preview_link_ = NULL; 524 tutorial_send_feedback_button_ = NULL; 525 end_preview_and_relaunch_button_ = NULL; 526 end_preview_cancel_button_ = NULL; 527 remove_account_button_ = NULL; 528 account_removal_cancel_button_ = NULL; 529 gaia_signin_cancel_button_ = NULL; 530 open_other_profile_indexes_map_.clear(); 531 delete_account_button_map_.clear(); 532 reauth_account_button_map_.clear(); 533 tutorial_mode_ = profiles::TUTORIAL_MODE_NONE; 534} 535 536void ProfileChooserView::Init() { 537 // If view mode is PROFILE_CHOOSER but there is an auth error, force 538 // ACCOUNT_MANAGEMENT mode. 539 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER && 540 HasAuthError(browser_->profile())) { 541 view_mode_ = profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT; 542 } 543 544 ShowView(view_mode_, avatar_menu_.get()); 545} 546 547void ProfileChooserView::OnAvatarMenuChanged( 548 AvatarMenu* avatar_menu) { 549 // Refresh the view with the new menu. We can't just update the local copy 550 // as this may have been triggered by a sign out action, in which case 551 // the view is being destroyed. 552 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu); 553} 554 555void ProfileChooserView::OnRefreshTokenAvailable( 556 const std::string& account_id) { 557 // Refresh the account management view when a new account is added to the 558 // profile. 559 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT || 560 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN || 561 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || 562 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) { 563 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 564 } 565} 566 567void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) { 568 // Refresh the account management view when an account is removed from the 569 // profile. 570 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) 571 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 572} 573 574void ProfileChooserView::ShowView(profiles::BubbleViewMode view_to_display, 575 AvatarMenu* avatar_menu) { 576 // The account management view should only be displayed if the active profile 577 // is signed in. 578 if (view_to_display == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) { 579 const AvatarMenu::Item& active_item = avatar_menu->GetItemAt( 580 avatar_menu->GetActiveProfileIndex()); 581 DCHECK(active_item.signed_in); 582 } 583 584 if (browser_->profile()->IsSupervised() && 585 (view_to_display == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || 586 view_to_display == profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL)) { 587 LOG(WARNING) << "Supervised user attempted to add/remove account"; 588 return; 589 } 590 591 // Records the last tutorial mode. 592 profiles::TutorialMode last_tutorial_mode = tutorial_mode_; 593 ResetView(); 594 RemoveAllChildViews(true); 595 view_mode_ = view_to_display; 596 597 views::GridLayout* layout; 598 views::View* sub_view; 599 switch (view_mode_) { 600 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: 601 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: 602 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: 603 layout = CreateSingleColumnLayout(this, kFixedGaiaViewWidth); 604 sub_view = CreateGaiaSigninView(); 605 break; 606 case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL: 607 layout = CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth); 608 sub_view = CreateAccountRemovalView(); 609 break; 610 case profiles::BUBBLE_VIEW_MODE_END_PREVIEW: 611 layout = CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth); 612 sub_view = CreateEndPreviewView(); 613 break; 614 default: 615 layout = CreateSingleColumnLayout(this, kFixedMenuWidth); 616 sub_view = CreateProfileChooserView(avatar_menu, last_tutorial_mode); 617 } 618 layout->StartRow(1, 0); 619 layout->AddView(sub_view); 620 Layout(); 621 if (GetBubbleFrameView()) 622 SizeToContents(); 623} 624 625void ProfileChooserView::WindowClosing() { 626 DCHECK_EQ(profile_bubble_, this); 627 profile_bubble_ = NULL; 628} 629 630void ProfileChooserView::ButtonPressed(views::Button* sender, 631 const ui::Event& event) { 632 // Disable button after clicking so that it doesn't get clicked twice and 633 // start a second action... which can crash Chrome. But don't disable if it 634 // has no parent (like in tests) because that will also crash. 635 if (sender->parent()) 636 sender->SetEnabled(false); 637 638 if (sender == users_button_) { 639 profiles::ShowUserManagerMaybeWithTutorial(browser_->profile()); 640 // If this is a guest session, also close all the guest browser windows. 641 if (browser_->profile()->IsGuestSession()) 642 profiles::CloseGuestProfileWindows(); 643 } else if (sender == lock_button_) { 644 profiles::LockProfile(browser_->profile()); 645 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK); 646 } else if (sender == tutorial_ok_button_) { 647 // If the user manually dismissed the tutorial, never show it again by 648 // setting the number of times shown to the maximum plus 1, so that later we 649 // could distinguish between the dismiss case and the case when the tutorial 650 // is indeed shown for the maximum number of times. 651 browser_->profile()->GetPrefs()->SetInteger( 652 prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1); 653 654 ProfileMetrics::LogProfileUpgradeEnrollment( 655 ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD); 656 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 657 } else if (sender == tutorial_enable_new_profile_management_button_) { 658 ProfileMetrics::LogProfileUpgradeEnrollment( 659 ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT); 660 profiles::EnableNewProfileManagementPreview(browser_->profile()); 661 } else if (sender == remove_account_button_) { 662 RemoveAccount(); 663 } else if (sender == account_removal_cancel_button_) { 664 account_id_to_remove_.clear(); 665 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 666 } else if (sender == gaia_signin_cancel_button_) { 667 std::string primary_account = 668 SigninManagerFactory::GetForProfile(browser_->profile())-> 669 GetAuthenticatedUsername(); 670 ShowView(primary_account.empty() ? 671 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER : 672 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, 673 avatar_menu_.get()); 674 } else if (sender == question_mark_button_) { 675 tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK; 676 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 677 } else if (sender == tutorial_send_feedback_button_) { 678 ProfileMetrics::LogProfileUpgradeEnrollment( 679 ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK); 680 chrome::OpenFeedbackDialog(browser_); 681 } else if (sender == end_preview_and_relaunch_button_) { 682 ProfileMetrics::LogProfileUpgradeEnrollment( 683 ProfileMetrics::PROFILE_ENROLLMENT_DISABLE_NEW_PROFILE_MGMT); 684 profiles::DisableNewProfileManagementPreview(browser_->profile()); 685 } else if (sender == end_preview_cancel_button_) { 686 tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK; 687 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 688 } else if (current_profile_photo_ && 689 sender == current_profile_photo_->change_photo_button()) { 690 avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex()); 691 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE); 692 } else if (sender == signin_current_profile_link_) { 693 // Only show the inline signin if the new UI flag is flipped. Otherwise, 694 // use the tab signin page. 695 if (switches::IsNewProfileManagement()) 696 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, avatar_menu_.get()); 697 else 698 chrome::ShowBrowserSignin(browser_, signin::SOURCE_MENU); 699 } else { 700 // Either one of the "other profiles", or one of the profile accounts 701 // buttons was pressed. 702 ButtonIndexes::const_iterator profile_match = 703 open_other_profile_indexes_map_.find(sender); 704 if (profile_match != open_other_profile_indexes_map_.end()) { 705 avatar_menu_->SwitchToProfile( 706 profile_match->second, 707 ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW, 708 ProfileMetrics::SWITCH_PROFILE_ICON); 709 } else { 710 // This was a profile accounts button. 711 AccountButtonIndexes::const_iterator account_match = 712 delete_account_button_map_.find(sender); 713 if (account_match != delete_account_button_map_.end()) { 714 account_id_to_remove_ = account_match->second; 715 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL, 716 avatar_menu_.get()); 717 } else { 718 account_match = reauth_account_button_map_.find(sender); 719 DCHECK(account_match != reauth_account_button_map_.end()); 720 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH, avatar_menu_.get()); 721 } 722 } 723 } 724} 725 726void ProfileChooserView::RemoveAccount() { 727 DCHECK(!account_id_to_remove_.empty()); 728 MutableProfileOAuth2TokenService* oauth2_token_service = 729 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile( 730 browser_->profile()); 731 if (oauth2_token_service) { 732 oauth2_token_service->RevokeCredentials(account_id_to_remove_); 733 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_REMOVE_ACCT); 734 } 735 account_id_to_remove_.clear(); 736 737 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 738} 739 740void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) { 741 if (sender == manage_accounts_link_) { 742 // This link can either mean show/hide the account management view, 743 // depending on which view it is displayed. ShowView() will DCHECK if 744 // the account management view is displayed for non signed-in users. 745 ShowView( 746 view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ? 747 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER : 748 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, 749 avatar_menu_.get()); 750 } else if (sender == add_account_link_) { 751 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT, avatar_menu_.get()); 752 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT); 753 } else if (sender == tutorial_learn_more_link_) { 754 ProfileMetrics::LogProfileUpgradeEnrollment( 755 ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE); 756 // TODO(guohui): update |learn_more_url| once it is decided. 757 const GURL lear_more_url("https://support.google.com/chrome/?hl=en#to"); 758 chrome::NavigateParams params( 759 browser_->profile(), 760 lear_more_url, 761 content::PAGE_TRANSITION_LINK); 762 params.disposition = NEW_FOREGROUND_TAB; 763 chrome::Navigate(¶ms); 764 } else { 765 DCHECK(sender == tutorial_end_preview_link_); 766 ShowView(profiles::BUBBLE_VIEW_MODE_END_PREVIEW, avatar_menu_.get()); 767 } 768} 769 770void ProfileChooserView::StyledLabelLinkClicked( 771 const gfx::Range& range, int event_flags) { 772 chrome::ShowSettings(browser_); 773} 774 775bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender, 776 const ui::KeyEvent& key_event) { 777 views::Textfield* name_textfield = 778 current_profile_name_->profile_name_textfield(); 779 DCHECK(sender == name_textfield); 780 781 if (key_event.key_code() == ui::VKEY_RETURN || 782 key_event.key_code() == ui::VKEY_TAB) { 783 // Pressing Tab/Enter commits the new profile name, unless it's empty. 784 base::string16 new_profile_name = name_textfield->text(); 785 if (new_profile_name.empty()) 786 return true; 787 788 const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt( 789 avatar_menu_->GetActiveProfileIndex()); 790 Profile* profile = g_browser_process->profile_manager()->GetProfile( 791 active_item.profile_path); 792 DCHECK(profile); 793 794 if (profile->IsSupervised()) 795 return true; 796 797 profiles::UpdateProfileName(profile, new_profile_name); 798 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME); 799 current_profile_name_->ShowReadOnlyView(); 800 return true; 801 } 802 return false; 803} 804 805void ProfileChooserView::PostActionPerformed( 806 ProfileMetrics::ProfileDesktopMenu action_performed) { 807 ProfileMetrics::LogProfileDesktopMenu(action_performed, gaia_service_type_); 808 gaia_service_type_ = signin::GAIA_SERVICE_TYPE_NONE; 809} 810 811views::View* ProfileChooserView::CreateProfileChooserView( 812 AvatarMenu* avatar_menu, 813 profiles::TutorialMode last_tutorial_mode) { 814 // TODO(guohui, noms): the view should be customized based on whether new 815 // profile management preview is enabled or not. 816 817 views::View* view = new views::View(); 818 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 819 // Separate items into active and alternatives. 820 Indexes other_profiles; 821 views::View* tutorial_view = NULL; 822 views::View* current_profile_view = NULL; 823 views::View* current_profile_accounts = NULL; 824 views::View* option_buttons_view = NULL; 825 bool is_new_profile_management = switches::IsNewProfileManagement(); 826 for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) { 827 const AvatarMenu::Item& item = avatar_menu->GetItemAt(i); 828 if (item.active) { 829 option_buttons_view = CreateOptionsView(item.signed_in); 830 current_profile_view = CreateCurrentProfileView(item, false); 831 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { 832 if (is_new_profile_management) { 833 tutorial_view = 834 last_tutorial_mode == profiles::TUTORIAL_MODE_SEND_FEEDBACK ? 835 CreateSendPreviewFeedbackView() : 836 CreatePreviewEnabledTutorialView( 837 item, last_tutorial_mode == profiles::TUTORIAL_MODE_WELCOME); 838 } else { 839 tutorial_view = CreateNewProfileManagementPreviewView(); 840 } 841 } else { 842 current_profile_accounts = CreateCurrentProfileAccountsView(item); 843 } 844 } else { 845 other_profiles.push_back(i); 846 } 847 } 848 849 if (tutorial_view) { 850 // Be sure not to track the tutorial display on View refresh, and only count 851 // the preview-promo view, shown when New Profile Management is off. 852 if (tutorial_mode_ != last_tutorial_mode && !is_new_profile_management) { 853 ProfileMetrics::LogProfileUpgradeEnrollment( 854 ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO); 855 } 856 layout->StartRow(1, 0); 857 layout->AddView(tutorial_view); 858 } 859 860 if (!current_profile_view) { 861 // Guest windows don't have an active profile. 862 current_profile_view = CreateGuestProfileView(); 863 option_buttons_view = CreateOptionsView(false); 864 } 865 866 layout->StartRow(1, 0); 867 layout->AddView(current_profile_view); 868 869 if (view_mode_ != profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { 870 DCHECK(current_profile_accounts); 871 layout->StartRow(0, 0); 872 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 873 layout->StartRow(1, 0); 874 layout->AddView(current_profile_accounts); 875 } 876 877 if (browser_->profile()->IsSupervised()) { 878 layout->StartRow(0, 0); 879 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 880 layout->StartRow(1, 0); 881 layout->AddView(CreateSupervisedUserDisclaimerView()); 882 } 883 884 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { 885 layout->StartRow(1, 0); 886 if (switches::IsFastUserSwitching()) 887 layout->AddView(CreateOtherProfilesView(other_profiles)); 888 } 889 890 layout->StartRow(0, 0); 891 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 892 893 // Option buttons. Only available with the new profile management flag. 894 if (option_buttons_view) { 895 layout->StartRow(0, 0); 896 layout->AddView(option_buttons_view); 897 } 898 899 return view; 900} 901 902views::View* ProfileChooserView::CreatePreviewEnabledTutorialView( 903 const AvatarMenu::Item& current_avatar_item, 904 bool tutorial_shown) { 905 if (!switches::IsNewProfileManagementPreviewEnabled()) 906 return NULL; 907 908 Profile* profile = browser_->profile(); 909 const int show_count = profile->GetPrefs()->GetInteger( 910 prefs::kProfileAvatarTutorialShown); 911 // Do not show the tutorial if user has dismissed it. 912 if (show_count > kProfileAvatarTutorialShowMax) 913 return NULL; 914 915 if (!tutorial_shown) { 916 if (show_count == kProfileAvatarTutorialShowMax) 917 return NULL; 918 profile->GetPrefs()->SetInteger( 919 prefs::kProfileAvatarTutorialShown, show_count + 1); 920 } 921 922 return CreateTutorialView( 923 profiles::TUTORIAL_MODE_WELCOME, 924 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE), 925 l10n_util::GetStringUTF16( 926 IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT), 927 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE), 928 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON), 929 &tutorial_learn_more_link_, 930 &tutorial_ok_button_); 931} 932 933views::View* ProfileChooserView::CreateSendPreviewFeedbackView() { 934 return CreateTutorialView( 935 profiles::TUTORIAL_MODE_SEND_FEEDBACK, 936 l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE), 937 l10n_util::GetStringUTF16( 938 IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT), 939 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW), 940 l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON), 941 &tutorial_end_preview_link_, 942 &tutorial_send_feedback_button_); 943} 944 945views::View* ProfileChooserView::CreateTutorialView( 946 profiles::TutorialMode tutorial_mode, 947 const base::string16& title_text, 948 const base::string16& content_text, 949 const base::string16& link_text, 950 const base::string16& button_text, 951 views::Link** link, 952 views::LabelButton** button) { 953 tutorial_mode_ = tutorial_mode; 954 955 views::View* view = new views::View(); 956 view->set_background(views::Background::CreateSolidBackground( 957 profiles::kAvatarTutorialBackgroundColor)); 958 views::GridLayout* layout = CreateSingleColumnLayout(view, 959 kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew); 960 layout->SetInsets(views::kButtonVEdgeMarginNew, 961 views::kButtonHEdgeMarginNew, 962 views::kButtonVEdgeMarginNew, 963 views::kButtonHEdgeMarginNew); 964 965 // Adds title. 966 views::Label* title_label = new views::Label(title_text); 967 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 968 title_label->SetAutoColorReadabilityEnabled(false); 969 title_label->SetEnabledColor(SK_ColorWHITE); 970 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 971 ui::ResourceBundle::MediumFont)); 972 layout->StartRow(1, 0); 973 layout->AddView(title_label); 974 975 // Adds body content. 976 views::Label* content_label = new views::Label(content_text); 977 content_label->SetMultiLine(true); 978 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 979 content_label->SetAutoColorReadabilityEnabled(false); 980 content_label->SetEnabledColor(profiles::kAvatarTutorialContentTextColor); 981 layout->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing); 982 layout->AddView(content_label); 983 984 // Adds links and buttons. 985 views::View* button_row = new views::View(); 986 views::GridLayout* button_layout = new views::GridLayout(button_row); 987 views::ColumnSet* button_columns = button_layout->AddColumnSet(0); 988 button_columns->AddColumn(views::GridLayout::LEADING, 989 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 990 button_columns->AddPaddingColumn( 991 1, views::kUnrelatedControlHorizontalSpacing); 992 button_columns->AddColumn(views::GridLayout::TRAILING, 993 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 994 button_row->SetLayoutManager(button_layout); 995 996 *link = CreateLink(link_text, this); 997 (*link)->SetHorizontalAlignment(gfx::ALIGN_LEFT); 998 (*link)->SetAutoColorReadabilityEnabled(false); 999 (*link)->SetEnabledColor(SK_ColorWHITE); 1000 button_layout->StartRow(1, 0); 1001 button_layout->AddView(*link); 1002 1003 *button = new views::LabelButton(this, button_text); 1004 (*button)->SetHorizontalAlignment(gfx::ALIGN_CENTER); 1005 (*button)->SetStyle(views::Button::STYLE_BUTTON); 1006 button_layout->AddView(*button); 1007 1008 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1009 layout->AddView(button_row); 1010 1011 // Adds a padded caret image at the bottom. 1012 views::View* padded_caret_view = new views::View(); 1013 views::GridLayout* padded_caret_layout = 1014 new views::GridLayout(padded_caret_view); 1015 views::ColumnSet* padded_columns = padded_caret_layout->AddColumnSet(0); 1016 padded_columns->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); 1017 padded_columns->AddColumn(views::GridLayout::LEADING, 1018 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 1019 padded_caret_view->SetLayoutManager(padded_caret_layout); 1020 1021 views::ImageView* caret_image_view = new views::ImageView(); 1022 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1023 caret_image_view->SetImage( 1024 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET)); 1025 1026 padded_caret_layout->StartRow(1, 0); 1027 padded_caret_layout->AddView(caret_image_view); 1028 1029 views::View* view_with_caret = new views::View(); 1030 views::GridLayout* layout_with_caret = 1031 CreateSingleColumnLayout(view_with_caret, kFixedMenuWidth); 1032 layout_with_caret->StartRow(1, 0); 1033 layout_with_caret->AddView(view); 1034 layout_with_caret->StartRow(1, 0); 1035 layout_with_caret->AddView(padded_caret_view); 1036 return view_with_caret; 1037} 1038 1039views::View* ProfileChooserView::CreateCurrentProfileView( 1040 const AvatarMenu::Item& avatar_item, 1041 bool is_guest) { 1042 views::View* view = new views::View(); 1043 int column_width = kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew; 1044 views::GridLayout* layout = CreateSingleColumnLayout(view, column_width); 1045 layout->SetInsets(views::kButtonVEdgeMarginNew, 1046 views::kButtonHEdgeMarginNew, 1047 views::kUnrelatedControlVerticalSpacing, 1048 views::kButtonHEdgeMarginNew); 1049 1050 // Profile icon, centered. 1051 int x_offset = (column_width - kLargeImageSide) / 2; 1052 current_profile_photo_ = new EditableProfilePhoto( 1053 this, avatar_item.icon, !is_guest, 1054 gfx::Rect(x_offset, 0, kLargeImageSide, kLargeImageSide)); 1055 SizedContainer* profile_icon_container = 1056 new SizedContainer(gfx::Size(column_width, kLargeImageSide)); 1057 profile_icon_container->AddChildView(current_profile_photo_); 1058 1059 if (switches::IsNewProfileManagementPreviewEnabled()) { 1060 question_mark_button_ = new views::ImageButton(this); 1061 question_mark_button_->SetImageAlignment( 1062 views::ImageButton::ALIGN_LEFT, views::ImageButton::ALIGN_MIDDLE); 1063 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1064 question_mark_button_->SetImage(views::ImageButton::STATE_NORMAL, 1065 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE)); 1066 question_mark_button_->SetImage(views::ImageButton::STATE_HOVERED, 1067 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER)); 1068 question_mark_button_->SetImage(views::ImageButton::STATE_PRESSED, 1069 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT)); 1070 gfx::Size preferred_size = question_mark_button_->GetPreferredSize(); 1071 question_mark_button_->SetBounds( 1072 0, 0, preferred_size.width(), preferred_size.height()); 1073 profile_icon_container->AddChildView(question_mark_button_); 1074 } 1075 1076 if (browser_->profile()->IsSupervised()) { 1077 views::ImageView* supervised_icon = new views::ImageView(); 1078 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1079 supervised_icon->SetImage( 1080 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_SUPERVISED)); 1081 gfx::Size preferred_size = supervised_icon->GetPreferredSize(); 1082 gfx::Rect parent_bounds = current_profile_photo_->bounds(); 1083 supervised_icon->SetBounds( 1084 parent_bounds.right() - preferred_size.width(), 1085 parent_bounds.bottom() - preferred_size.height(), 1086 preferred_size.width(), 1087 preferred_size.height()); 1088 profile_icon_container->AddChildView(supervised_icon); 1089 } 1090 1091 layout->StartRow(1, 0); 1092 layout->AddView(profile_icon_container); 1093 1094 // Profile name, centered. 1095 bool editing_allowed = !is_guest && !browser_->profile()->IsSupervised(); 1096 current_profile_name_ = new EditableProfileName( 1097 this, 1098 profiles::GetAvatarNameForProfile(browser_->profile()->GetPath()), 1099 editing_allowed); 1100 layout->StartRow(1, 0); 1101 layout->AddView(current_profile_name_); 1102 1103 if (is_guest) 1104 return view; 1105 1106 // The available links depend on the type of profile that is active. 1107 if (avatar_item.signed_in) { 1108 layout->StartRow(1, 0); 1109 if (switches::IsNewProfileManagement()) { 1110 base::string16 link_title = l10n_util::GetStringUTF16( 1111 view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER ? 1112 IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON : 1113 IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON); 1114 manage_accounts_link_ = CreateLink(link_title, this); 1115 manage_accounts_link_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 1116 layout->AddView(manage_accounts_link_); 1117 } else { 1118 views::Label* email_label = new views::Label(avatar_item.sync_state); 1119 email_label->SetHorizontalAlignment(gfx::ALIGN_CENTER); 1120 layout->AddView(email_label); 1121 } 1122 } else { 1123 SigninManagerBase* signin_manager = 1124 SigninManagerFactory::GetForProfile( 1125 browser_->profile()->GetOriginalProfile()); 1126 if (signin_manager->IsSigninAllowed()) { 1127 signin_current_profile_link_ = new views::BlueButton( 1128 this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL, 1129 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); 1130 layout->StartRow(1, 0); 1131 layout->AddView(signin_current_profile_link_); 1132 } 1133 } 1134 1135 return view; 1136} 1137 1138views::View* ProfileChooserView::CreateGuestProfileView() { 1139 gfx::Image guest_icon = 1140 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 1141 profiles::GetPlaceholderAvatarIconResourceID()); 1142 AvatarMenu::Item guest_avatar_item(0, 0, guest_icon); 1143 guest_avatar_item.active = true; 1144 guest_avatar_item.name = l10n_util::GetStringUTF16( 1145 IDS_PROFILES_GUEST_PROFILE_NAME); 1146 guest_avatar_item.signed_in = false; 1147 1148 return CreateCurrentProfileView(guest_avatar_item, true); 1149} 1150 1151views::View* ProfileChooserView::CreateOtherProfilesView( 1152 const Indexes& avatars_to_show) { 1153 views::View* view = new views::View(); 1154 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1155 1156 int num_avatars_to_show = avatars_to_show.size(); 1157 for (int i = 0; i < num_avatars_to_show; ++i) { 1158 const size_t index = avatars_to_show[i]; 1159 const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index); 1160 const int kSmallImageSide = 32; 1161 1162 gfx::Image image = profiles::GetSizedAvatarIcon( 1163 item.icon, true, kSmallImageSide, kSmallImageSide); 1164 1165 views::LabelButton* button = new BackgroundColorHoverButton( 1166 this, 1167 item.name, 1168 *image.ToImageSkia(), 1169 *image.ToImageSkia()); 1170 button->set_min_size(gfx::Size( 1171 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1172 1173 open_other_profile_indexes_map_[button] = index; 1174 1175 layout->StartRow(1, 0); 1176 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 1177 layout->StartRow(1, 0); 1178 layout->AddView(button); 1179 } 1180 1181 return view; 1182} 1183 1184views::View* ProfileChooserView::CreateOptionsView(bool enable_lock) { 1185 if (!switches::IsNewProfileManagement()) 1186 return NULL; 1187 1188 views::View* view = new views::View(); 1189 views::GridLayout* layout; 1190 1191 // Only signed-in users have the ability to lock. 1192 if (enable_lock) { 1193 layout = new views::GridLayout(view); 1194 views::ColumnSet* columns = layout->AddColumnSet(0); 1195 int width_of_lock_button = 1196 2 * views::kUnrelatedControlLargeHorizontalSpacing + 12; 1197 int width_of_users_button = kFixedMenuWidth - width_of_lock_button; 1198 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 1199 views::GridLayout::FIXED, width_of_users_button, 1200 width_of_users_button); 1201 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 1202 views::GridLayout::FIXED, width_of_lock_button, 1203 width_of_lock_button); 1204 view->SetLayoutManager(layout); 1205 } else { 1206 layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1207 } 1208 1209 base::string16 text = browser_->profile()->IsGuestSession() ? 1210 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST) : 1211 l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON, 1212 profiles::GetAvatarNameForProfile( 1213 browser_->profile()->GetPath())); 1214 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1215 users_button_ = new BackgroundColorHoverButton( 1216 this, 1217 text, 1218 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR), 1219 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR)); 1220 users_button_->set_min_size(gfx::Size( 1221 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1222 1223 layout->StartRow(1, 0); 1224 layout->AddView(users_button_); 1225 1226 if (enable_lock) { 1227 lock_button_ = new BackgroundColorHoverButton( 1228 this, 1229 base::string16(), 1230 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK), 1231 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK)); 1232 lock_button_->set_min_size(gfx::Size( 1233 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1234 layout->AddView(lock_button_); 1235 } 1236 return view; 1237} 1238 1239views::View* ProfileChooserView::CreateSupervisedUserDisclaimerView() { 1240 views::View* view = new views::View(); 1241 views::GridLayout* layout = CreateSingleColumnLayout( 1242 view, kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew); 1243 layout->SetInsets(views::kRelatedControlVerticalSpacing, 1244 views::kButtonHEdgeMarginNew, 1245 views::kRelatedControlVerticalSpacing, 1246 views::kButtonHEdgeMarginNew); 1247 views::Label* disclaimer = new views::Label( 1248 avatar_menu_->GetSupervisedUserInformation()); 1249 disclaimer->SetMultiLine(true); 1250 disclaimer->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1251 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1252 disclaimer->SetFontList(rb->GetFontList(ui::ResourceBundle::SmallFont)); 1253 layout->StartRow(1, 0); 1254 layout->AddView(disclaimer); 1255 1256 return view; 1257} 1258 1259views::View* ProfileChooserView::CreateCurrentProfileAccountsView( 1260 const AvatarMenu::Item& avatar_item) { 1261 DCHECK(avatar_item.signed_in); 1262 views::View* view = new views::View(); 1263 view->set_background(views::Background::CreateSolidBackground( 1264 profiles::kAvatarBubbleAccountsBackgroundColor)); 1265 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1266 1267 Profile* profile = browser_->profile(); 1268 std::string primary_account = 1269 SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername(); 1270 DCHECK(!primary_account.empty()); 1271 std::vector<std::string>accounts = 1272 profiles::GetSecondaryAccountsForProfile(profile, primary_account); 1273 1274 // Get state of authentication error, if any. 1275 std::string error_account_id = GetAuthErrorAccountId(profile); 1276 1277 // The primary account should always be listed first. 1278 // TODO(rogerta): we still need to further differentiate the primary account 1279 // from the others in the UI, so more work is likely required here: 1280 // crbug.com/311124. 1281 CreateAccountButton(layout, primary_account, true, 1282 error_account_id == primary_account, kFixedMenuWidth); 1283 for (size_t i = 0; i < accounts.size(); ++i) 1284 CreateAccountButton(layout, accounts[i], false, 1285 error_account_id == accounts[i], kFixedMenuWidth); 1286 1287 if (!profile->IsSupervised()) { 1288 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 1289 1290 add_account_link_ = CreateLink(l10n_util::GetStringFUTF16( 1291 IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, avatar_item.name), this); 1292 add_account_link_->SetBorder(views::Border::CreateEmptyBorder( 1293 0, views::kButtonVEdgeMarginNew, 1294 views::kRelatedControlVerticalSpacing, 0)); 1295 layout->StartRow(1, 0); 1296 layout->AddView(add_account_link_); 1297 } 1298 1299 return view; 1300} 1301 1302void ProfileChooserView::CreateAccountButton(views::GridLayout* layout, 1303 const std::string& account, 1304 bool is_primary_account, 1305 bool reauth_required, 1306 int width) { 1307 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1308 const gfx::ImageSkia* delete_default_image = 1309 rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia(); 1310 const int kDeleteButtonWidth = delete_default_image->width(); 1311 const gfx::ImageSkia warning_default_image = reauth_required ? 1312 *rb->GetImageNamed(IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR).ToImageSkia() : 1313 gfx::ImageSkia(); 1314 const int kWarningButtonWidth = reauth_required ? 1315 warning_default_image.width() + views::kRelatedButtonHSpacing : 0; 1316 int available_width = width - 2 * views::kButtonHEdgeMarginNew 1317 - kDeleteButtonWidth - kWarningButtonWidth; 1318 views::LabelButton* email_button = new BackgroundColorHoverButton( 1319 reauth_required ? this : NULL, 1320 gfx::ElideText(base::UTF8ToUTF16(account), gfx::FontList(), 1321 available_width, gfx::ELIDE_EMAIL), 1322 warning_default_image, 1323 warning_default_image); 1324 layout->StartRow(1, 0); 1325 layout->AddView(email_button); 1326 1327 if (reauth_required) 1328 reauth_account_button_map_[email_button] = account; 1329 1330 // Delete button. 1331 if (!browser_->profile()->IsSupervised()) { 1332 views::ImageButton* delete_button = new views::ImageButton(this); 1333 delete_button->SetImageAlignment(views::ImageButton::ALIGN_RIGHT, 1334 views::ImageButton::ALIGN_MIDDLE); 1335 delete_button->SetImage(views::ImageButton::STATE_NORMAL, 1336 delete_default_image); 1337 delete_button->SetImage(views::ImageButton::STATE_HOVERED, 1338 rb->GetImageSkiaNamed(IDR_CLOSE_1_H)); 1339 delete_button->SetImage(views::ImageButton::STATE_PRESSED, 1340 rb->GetImageSkiaNamed(IDR_CLOSE_1_P)); 1341 delete_button->SetBounds( 1342 width - views::kButtonHEdgeMarginNew - kDeleteButtonWidth, 1343 0, kDeleteButtonWidth, kButtonHeight); 1344 1345 email_button->set_notify_enter_exit_on_child(true); 1346 email_button->AddChildView(delete_button); 1347 1348 // Save the original email address, as the button text could be elided. 1349 delete_account_button_map_[delete_button] = account; 1350 } 1351} 1352 1353views::View* ProfileChooserView::CreateGaiaSigninView() { 1354 GURL url; 1355 int message_id; 1356 1357 switch (view_mode_) { 1358 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: 1359 url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_SIGN_IN, 1360 false /* auto_close */, 1361 true /* is_constrained */); 1362 message_id = IDS_PROFILES_GAIA_SIGNIN_TITLE; 1363 break; 1364 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: 1365 url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT, 1366 false /* auto_close */, 1367 true /* is_constrained */); 1368 message_id = IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE; 1369 break; 1370 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: { 1371 DCHECK(HasAuthError(browser_->profile())); 1372 url = signin::GetReauthURL(browser_->profile(), 1373 GetAuthErrorUsername(browser_->profile())); 1374 message_id = IDS_PROFILES_GAIA_REAUTH_TITLE; 1375 break; 1376 } 1377 default: 1378 NOTREACHED() << "Called with invalid mode=" << view_mode_; 1379 return NULL; 1380 } 1381 1382 // Adds Gaia signin webview 1383 Profile* profile = browser_->profile(); 1384 views::WebView* web_view = new views::WebView(profile); 1385 web_view->LoadInitialURL(url); 1386 web_view->SetPreferredSize( 1387 gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight)); 1388 1389 TitleCard* title_card = new TitleCard(message_id, this, 1390 &gaia_signin_cancel_button_); 1391 return TitleCard::AddPaddedTitleCard( 1392 web_view, title_card, kFixedGaiaViewWidth); 1393} 1394 1395views::View* ProfileChooserView::CreateAccountRemovalView() { 1396 views::View* view = new views::View(); 1397 views::GridLayout* layout = CreateSingleColumnLayout( 1398 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew); 1399 layout->SetInsets(0, 1400 views::kButtonHEdgeMarginNew, 1401 views::kButtonVEdgeMarginNew, 1402 views::kButtonHEdgeMarginNew); 1403 1404 const std::string& primary_account = SigninManagerFactory::GetForProfile( 1405 browser_->profile())->GetAuthenticatedUsername(); 1406 bool is_primary_account = primary_account == account_id_to_remove_; 1407 1408 // Adds main text. 1409 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1410 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1411 const gfx::FontList& small_font_list = 1412 rb->GetFontList(ui::ResourceBundle::SmallFont); 1413 1414 if (is_primary_account) { 1415 std::vector<size_t> offsets; 1416 const base::string16 settings_text = 1417 l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK); 1418 const base::string16 primary_account_removal_text = 1419 l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT, 1420 base::UTF8ToUTF16(account_id_to_remove_), settings_text, &offsets); 1421 views::StyledLabel* primary_account_removal_label = 1422 new views::StyledLabel(primary_account_removal_text, this); 1423 primary_account_removal_label->AddStyleRange( 1424 gfx::Range(offsets[1], offsets[1] + settings_text.size()), 1425 views::StyledLabel::RangeStyleInfo::CreateForLink()); 1426 primary_account_removal_label->SetBaseFontList(small_font_list); 1427 layout->AddView(primary_account_removal_label); 1428 } else { 1429 views::Label* content_label = new views::Label( 1430 l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT)); 1431 content_label->SetMultiLine(true); 1432 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1433 content_label->SetFontList(small_font_list); 1434 layout->AddView(content_label); 1435 } 1436 1437 // Adds button. 1438 if (!is_primary_account) { 1439 remove_account_button_ = new views::BlueButton( 1440 this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON)); 1441 remove_account_button_->SetHorizontalAlignment( 1442 gfx::ALIGN_CENTER); 1443 layout->StartRowWithPadding( 1444 1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1445 layout->AddView(remove_account_button_); 1446 } else { 1447 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); 1448 } 1449 1450 TitleCard* title_card = new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, 1451 this, &account_removal_cancel_button_); 1452 return TitleCard::AddPaddedTitleCard(view, title_card, 1453 kFixedAccountRemovalViewWidth); 1454} 1455 1456views::View* ProfileChooserView::CreateNewProfileManagementPreviewView() { 1457 return CreateTutorialView( 1458 profiles::TUTORIAL_MODE_ENABLE_PREVIEW, 1459 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE), 1460 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT), 1461 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE), 1462 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON), 1463 &tutorial_learn_more_link_, 1464 &tutorial_enable_new_profile_management_button_); 1465} 1466 1467views::View* ProfileChooserView::CreateEndPreviewView() { 1468 views::View* view = new views::View(); 1469 views::GridLayout* layout = CreateSingleColumnLayout( 1470 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew); 1471 layout->SetInsets(0, 1472 views::kButtonHEdgeMarginNew, 1473 views::kButtonVEdgeMarginNew, 1474 views::kButtonHEdgeMarginNew); 1475 1476 // Adds main text. 1477 views::Label* content_label = new views::Label( 1478 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT)); 1479 content_label->SetMultiLine(true); 1480 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1481 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1482 const gfx::FontList& small_font_list = 1483 rb->GetFontList(ui::ResourceBundle::SmallFont); 1484 content_label->SetFontList(small_font_list); 1485 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1486 layout->AddView(content_label); 1487 1488 // Adds button. 1489 end_preview_and_relaunch_button_ = new views::BlueButton( 1490 this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH)); 1491 end_preview_and_relaunch_button_->SetHorizontalAlignment( 1492 gfx::ALIGN_CENTER); 1493 layout->StartRowWithPadding( 1494 1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1495 layout->AddView(end_preview_and_relaunch_button_); 1496 1497 TitleCard* title_card = new TitleCard( 1498 IDS_PROFILES_END_PREVIEW, this, &end_preview_cancel_button_); 1499 return TitleCard::AddPaddedTitleCard( 1500 view, title_card, kFixedAccountRemovalViewWidth); 1501} 1502 1503