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