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.
5 #include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
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"
63 // Helpers --------------------------------------------------------------------
65 const int kFixedMenuWidth
= 250;
66 const int kButtonHeight
= 29;
67 const int kProfileAvatarTutorialShowMax
= 1;
68 const int kFixedGaiaViewHeight
= 400;
69 const int kFixedGaiaViewWidth
= 360;
70 const int kFixedAccountRemovalViewWidth
= 280;
71 const int kFixedEndPreviewViewWidth
= 280;
72 const int kLargeImageSide
= 88;
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.
76 views::GridLayout
* CreateSingleColumnLayout(views::View
* view
, int width
) {
77 views::GridLayout
* layout
= new views::GridLayout(view
);
78 view
->SetLayoutManager(layout
);
80 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
81 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
82 views::GridLayout::FIXED
, width
, width
);
86 views::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
);
95 gfx::ImageSkia
CreateSquarePlaceholderImage(int size
) {
97 bitmap
.setConfig(SkBitmap::kA8_Config
, size
, size
);
99 bitmap
.eraseARGB(0, 0, 0, 0);
100 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap
);
103 bool HasAuthError(Profile
* profile
) {
104 const SigninErrorController
* error
=
105 profiles::GetSigninErrorController(profile
);
106 return error
&& error
->HasError();
109 std::string
GetAuthErrorAccountId(Profile
* profile
) {
110 const SigninErrorController
* error
=
111 profiles::GetSigninErrorController(profile
);
113 return std::string();
115 return error
->error_account_id();
118 std::string
GetAuthErrorUsername(Profile
* profile
) {
119 const SigninErrorController
* error
=
120 profiles::GetSigninErrorController(profile
);
122 return std::string();
124 return error
->error_username();
127 // BackgroundColorHoverButton -------------------------------------------------
129 // A custom button that allows for setting a background color when hovered over.
130 class BackgroundColorHoverButton
: public views::LabelButton
{
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();
139 // views::LabelButton:
140 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
;
142 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton
);
145 BackgroundColorHoverButton::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
);
159 BackgroundColorHoverButton::~BackgroundColorHoverButton() {}
161 void BackgroundColorHoverButton::OnPaint(gfx::Canvas
* canvas
) {
162 if ((state() == STATE_PRESSED
) || (state() == STATE_HOVERED
) || HasFocus()) {
163 canvas
->DrawColor(GetNativeTheme()->GetSystemColor(
164 ui::NativeTheme::kColorId_ButtonHoverBackgroundColor
));
166 LabelButton::OnPaint(canvas
);
169 // SizedContainer -------------------------------------------------
171 // A simple container view that takes an explicit preferred size.
172 class SizedContainer
: public views::View
{
174 explicit SizedContainer(const gfx::Size
& preferred_size
)
175 : preferred_size_(preferred_size
) {}
177 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
178 return preferred_size_
;
182 gfx::Size preferred_size_
;
188 // EditableProfilePhoto -------------------------------------------------
190 // A custom Image control that shows a "change" button when moused over.
191 class EditableProfilePhoto
: public views::ImageView
{
193 EditableProfilePhoto(views::ButtonListener
* listener
,
194 const gfx::Image
& icon
,
195 bool is_editing_allowed
,
196 const gfx::Rect
& bounds
)
197 : views::ImageView(),
198 change_photo_button_(NULL
) {
199 gfx::Image image
= profiles::GetSizedAvatarIcon(
200 icon
, true, kLargeImageSide
, kLargeImageSide
);
201 SetImage(image
.ToImageSkia());
202 SetBoundsRect(bounds
);
204 // Calculate the circular mask that will be used to display the photo.
205 circular_mask_
.addCircle(SkIntToScalar(bounds
.width() / 2),
206 SkIntToScalar(bounds
.height() / 2),
207 SkIntToScalar(bounds
.width() / 2));
209 if (!is_editing_allowed
)
212 set_notify_enter_exit_on_child(true);
214 // Button overlay that appears when hovering over the image.
215 change_photo_button_
= new views::LabelButton(listener
, base::string16());
216 change_photo_button_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
217 change_photo_button_
->SetBorder(views::Border::NullBorder());
219 const SkColor kBackgroundColor
= SkColorSetARGB(65, 255, 255, 255);
220 change_photo_button_
->set_background(
221 views::Background::CreateSolidBackground(kBackgroundColor
));
222 change_photo_button_
->SetImage(views::LabelButton::STATE_NORMAL
,
223 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
224 IDR_ICON_PROFILES_EDIT_CAMERA
));
226 change_photo_button_
->SetSize(bounds
.size());
227 change_photo_button_
->SetVisible(false);
228 AddChildView(change_photo_button_
);
231 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
{
232 // Display the profile picture as a circle.
233 canvas
->ClipPath(circular_mask_
, true);
234 views::ImageView::OnPaint(canvas
);
237 virtual void PaintChildren(gfx::Canvas
* canvas
,
238 const views::CullSet
& cull_set
) OVERRIDE
{
239 // Display any children (the "change photo" overlay) as a circle.
240 canvas
->ClipPath(circular_mask_
, true);
241 View::PaintChildren(canvas
, cull_set
);
244 views::LabelButton
* change_photo_button() { return change_photo_button_
; }
248 virtual void OnMouseEntered(const ui::MouseEvent
& event
) OVERRIDE
{
249 if (change_photo_button_
)
250 change_photo_button_
->SetVisible(true);
253 virtual void OnMouseExited(const ui::MouseEvent
& event
) OVERRIDE
{
254 if (change_photo_button_
)
255 change_photo_button_
->SetVisible(false);
258 gfx::Path circular_mask_
;
260 // Button that is shown when hovering over the image view. Can be NULL if
261 // the photo isn't allowed to be edited (e.g. for guest profiles).
262 views::LabelButton
* change_photo_button_
;
264 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto
);
268 // EditableProfileName -------------------------------------------------
270 // A custom text control that turns into a textfield for editing when clicked.
271 class EditableProfileName
: public views::LabelButton
,
272 public views::ButtonListener
{
274 EditableProfileName(views::TextfieldController
* controller
,
275 const base::string16
& text
,
276 bool is_editing_allowed
)
277 : views::LabelButton(this, text
),
278 profile_name_textfield_(NULL
) {
279 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
280 const gfx::FontList
& medium_font_list
=
281 rb
->GetFontList(ui::ResourceBundle::MediumFont
);
282 SetFontList(medium_font_list
);
283 SetHorizontalAlignment(gfx::ALIGN_CENTER
);
285 if (!is_editing_allowed
) {
286 SetBorder(views::Border::CreateEmptyBorder(2, 0, 2, 0));
290 // Show an "edit" pencil icon when hovering over. In the default state,
291 // we need to create an empty placeholder of the correct size, so that
292 // the text doesn't jump around when the hovered icon appears.
293 gfx::ImageSkia hover_image
=
294 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER
);
295 SetImage(STATE_NORMAL
, CreateSquarePlaceholderImage(hover_image
.width()));
296 SetImage(STATE_HOVERED
, hover_image
);
297 SetImage(STATE_PRESSED
,
298 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED
));
299 // To center the text, we need to offest it by the width of the icon we
300 // are adding. We need to also add a small top/bottom padding to account
301 // for the textfield's border.
302 SetBorder(views::Border::CreateEmptyBorder(2, hover_image
.width(), 2, 0));
304 // Textfield that overlaps the button.
305 profile_name_textfield_
= new views::Textfield();
306 profile_name_textfield_
->set_controller(controller
);
307 profile_name_textfield_
->SetFontList(medium_font_list
);
308 profile_name_textfield_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
310 profile_name_textfield_
->SetVisible(false);
311 AddChildView(profile_name_textfield_
);
314 views::Textfield
* profile_name_textfield() {
315 return profile_name_textfield_
;
318 // Hide the editable textfield to show the profile name button instead.
319 void ShowReadOnlyView() {
320 if (profile_name_textfield_
)
321 profile_name_textfield_
->SetVisible(false);
325 // views::ButtonListener:
326 virtual void ButtonPressed(views::Button
* sender
,
327 const ui::Event
& event
) OVERRIDE
{
328 if (profile_name_textfield_
) {
329 profile_name_textfield_
->SetVisible(true);
330 profile_name_textfield_
->SetText(GetText());
331 profile_name_textfield_
->SelectAll(false);
332 profile_name_textfield_
->RequestFocus();
336 // views::LabelButton:
337 virtual bool OnKeyReleased(const ui::KeyEvent
& event
) OVERRIDE
{
338 // Override CustomButton's implementation, which presses the button when
339 // you press space and clicks it when you release space, as the space can be
340 // part of the new profile name typed in the textfield.
344 virtual void Layout() OVERRIDE
{
345 if (profile_name_textfield_
)
346 profile_name_textfield_
->SetBounds(0, 0, width(), height());
347 // This layout trick keeps the text left-aligned and the icon right-aligned.
348 SetHorizontalAlignment(gfx::ALIGN_RIGHT
);
349 views::LabelButton::Layout();
350 label()->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
353 // Textfield that is shown when editing the profile name. Can be NULL if
354 // the profile name isn't allowed to be edited (e.g. for guest profiles).
355 views::Textfield
* profile_name_textfield_
;
357 DISALLOW_COPY_AND_ASSIGN(EditableProfileName
);
360 // A title card with one back button right aligned and one label center aligned.
361 class TitleCard
: public views::View
{
363 TitleCard(int message_id
, views::ButtonListener
* listener
,
364 views::ImageButton
** back_button
) {
365 back_button_
= new views::ImageButton(listener
);
366 back_button_
->SetImageAlignment(views::ImageButton::ALIGN_LEFT
,
367 views::ImageButton::ALIGN_MIDDLE
);
368 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
369 back_button_
->SetImage(views::ImageButton::STATE_NORMAL
,
370 rb
->GetImageSkiaNamed(IDR_BACK
));
371 back_button_
->SetImage(views::ImageButton::STATE_HOVERED
,
372 rb
->GetImageSkiaNamed(IDR_BACK_H
));
373 back_button_
->SetImage(views::ImageButton::STATE_PRESSED
,
374 rb
->GetImageSkiaNamed(IDR_BACK_P
));
375 back_button_
->SetImage(views::ImageButton::STATE_DISABLED
,
376 rb
->GetImageSkiaNamed(IDR_BACK_D
));
377 *back_button
= back_button_
;
379 title_label_
= new views::Label(l10n_util::GetStringUTF16(message_id
));
380 title_label_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
381 const gfx::FontList
& medium_font_list
=
382 rb
->GetFontList(ui::ResourceBundle::MediumFont
);
383 title_label_
->SetFontList(medium_font_list
);
385 AddChildView(back_button_
);
386 AddChildView(title_label_
);
389 // Creates a new view that has the |title_card| with padding at the top, an
390 // edge-to-edge separator below, and the specified |view| at the bottom.
391 static views::View
* AddPaddedTitleCard(views::View
* view
,
392 TitleCard
* title_card
,
394 views::View
* titled_view
= new views::View();
395 views::GridLayout
* layout
= new views::GridLayout(titled_view
);
396 titled_view
->SetLayoutManager(layout
);
398 // Column set 0 is a single column layout with horizontal padding at left
399 // and right, and column set 1 is a single column layout with no padding.
400 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
401 columns
->AddPaddingColumn(1, views::kButtonHEdgeMarginNew
);
402 int available_width
= width
- 2 * views::kButtonHEdgeMarginNew
;
403 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
404 views::GridLayout::FIXED
, available_width
, available_width
);
405 columns
->AddPaddingColumn(1, views::kButtonHEdgeMarginNew
);
406 layout
->AddColumnSet(1)->AddColumn(views::GridLayout::FILL
,
407 views::GridLayout::FILL
, 0,views::GridLayout::FIXED
, width
, width
);
409 layout
->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew
);
410 layout
->AddView(title_card
);
411 layout
->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing
);
412 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
414 layout
->StartRow(1, 1);
415 layout
->AddView(view
);
421 virtual void Layout() OVERRIDE
{
422 back_button_
->SetBounds(
423 0, 0, back_button_
->GetPreferredSize().width(), height());
424 title_label_
->SetBoundsRect(GetContentsBounds());
427 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
428 int height
= std::max(title_label_
->GetPreferredSize().height(),
429 back_button_
->GetPreferredSize().height());
430 return gfx::Size(width(), height
);
433 views::ImageButton
* back_button_
;
434 views::Label
* title_label_
;
436 DISALLOW_COPY_AND_ASSIGN(TitleCard
);
439 // ProfileChooserView ---------------------------------------------------------
442 ProfileChooserView
* ProfileChooserView::profile_bubble_
= NULL
;
443 bool ProfileChooserView::close_on_deactivate_for_testing_
= true;
446 void ProfileChooserView::ShowBubble(
447 profiles::BubbleViewMode view_mode
,
448 signin::GAIAServiceType service_type
,
449 views::View
* anchor_view
,
450 views::BubbleBorder::Arrow arrow
,
451 views::BubbleBorder::BubbleAlignment border_alignment
,
452 const gfx::Rect
& anchor_rect
,
457 profile_bubble_
= new ProfileChooserView(anchor_view
, arrow
, anchor_rect
,
458 browser
, view_mode
, service_type
);
459 views::BubbleDelegateView::CreateBubble(profile_bubble_
);
460 profile_bubble_
->set_close_on_deactivate(close_on_deactivate_for_testing_
);
461 profile_bubble_
->SetAlignment(border_alignment
);
462 profile_bubble_
->GetWidget()->Show();
463 profile_bubble_
->SetArrowPaintType(views::BubbleBorder::PAINT_NONE
);
467 bool ProfileChooserView::IsShowing() {
468 return profile_bubble_
!= NULL
;
472 void ProfileChooserView::Hide() {
474 profile_bubble_
->GetWidget()->Close();
477 ProfileChooserView::ProfileChooserView(views::View
* anchor_view
,
478 views::BubbleBorder::Arrow arrow
,
479 const gfx::Rect
& anchor_rect
,
481 profiles::BubbleViewMode view_mode
,
482 signin::GAIAServiceType service_type
)
483 : BubbleDelegateView(anchor_view
, arrow
),
485 view_mode_(view_mode
),
486 tutorial_mode_(profiles::TUTORIAL_MODE_NONE
),
487 gaia_service_type_(service_type
) {
488 // Reset the default margins inherited from the BubbleDelegateView.
489 set_margins(gfx::Insets());
493 avatar_menu_
.reset(new AvatarMenu(
494 &g_browser_process
->profile_manager()->GetProfileInfoCache(),
497 avatar_menu_
->RebuildMenu();
499 ProfileOAuth2TokenService
* oauth2_token_service
=
500 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
501 if (oauth2_token_service
)
502 oauth2_token_service
->AddObserver(this);
505 ProfileChooserView::~ProfileChooserView() {
506 ProfileOAuth2TokenService
* oauth2_token_service
=
507 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
508 if (oauth2_token_service
)
509 oauth2_token_service
->RemoveObserver(this);
512 void ProfileChooserView::ResetView() {
513 question_mark_button_
= NULL
;
514 manage_accounts_link_
= NULL
;
515 signin_current_profile_link_
= NULL
;
516 users_button_
= NULL
;
518 add_account_link_
= NULL
;
519 current_profile_photo_
= NULL
;
520 current_profile_name_
= NULL
;
521 tutorial_ok_button_
= NULL
;
522 tutorial_learn_more_link_
= NULL
;
523 tutorial_enable_new_profile_management_button_
= NULL
;
524 tutorial_end_preview_link_
= NULL
;
525 tutorial_send_feedback_button_
= NULL
;
526 end_preview_and_relaunch_button_
= NULL
;
527 end_preview_cancel_button_
= NULL
;
528 remove_account_button_
= NULL
;
529 account_removal_cancel_button_
= NULL
;
530 gaia_signin_cancel_button_
= NULL
;
531 open_other_profile_indexes_map_
.clear();
532 delete_account_button_map_
.clear();
533 reauth_account_button_map_
.clear();
534 tutorial_mode_
= profiles::TUTORIAL_MODE_NONE
;
537 void ProfileChooserView::Init() {
538 // If view mode is PROFILE_CHOOSER but there is an auth error, force
539 // ACCOUNT_MANAGEMENT mode.
540 if (view_mode_
== profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
&&
541 HasAuthError(browser_
->profile())) {
542 view_mode_
= profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
;
545 ShowView(view_mode_
, avatar_menu_
.get());
548 void ProfileChooserView::OnAvatarMenuChanged(
549 AvatarMenu
* avatar_menu
) {
550 // Refresh the view with the new menu. We can't just update the local copy
551 // as this may have been triggered by a sign out action, in which case
552 // the view is being destroyed.
553 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu
);
556 void ProfileChooserView::OnRefreshTokenAvailable(
557 const std::string
& account_id
) {
558 // Refresh the account management view when a new account is added to the
560 if (view_mode_
== profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
||
561 view_mode_
== profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN
||
562 view_mode_
== profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
||
563 view_mode_
== profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
) {
564 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
568 void ProfileChooserView::OnRefreshTokenRevoked(const std::string
& account_id
) {
569 // Refresh the account management view when an account is removed from the
571 if (view_mode_
== profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
)
572 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
575 void ProfileChooserView::ShowView(profiles::BubbleViewMode view_to_display
,
576 AvatarMenu
* avatar_menu
) {
577 // The account management view should only be displayed if the active profile
579 if (view_to_display
== profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
) {
580 const AvatarMenu::Item
& active_item
= avatar_menu
->GetItemAt(
581 avatar_menu
->GetActiveProfileIndex());
582 DCHECK(active_item
.signed_in
);
585 // Records the last tutorial mode.
586 profiles::TutorialMode last_tutorial_mode
= tutorial_mode_
;
588 RemoveAllChildViews(true);
589 view_mode_
= view_to_display
;
591 views::GridLayout
* layout
;
592 views::View
* sub_view
;
593 switch (view_mode_
) {
594 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN
:
595 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
:
596 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
:
597 layout
= CreateSingleColumnLayout(this, kFixedGaiaViewWidth
);
598 sub_view
= CreateGaiaSigninView();
600 case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL
:
601 layout
= CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth
);
602 sub_view
= CreateAccountRemovalView();
604 case profiles::BUBBLE_VIEW_MODE_END_PREVIEW
:
605 layout
= CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth
);
606 sub_view
= CreateEndPreviewView();
609 layout
= CreateSingleColumnLayout(this, kFixedMenuWidth
);
610 sub_view
= CreateProfileChooserView(avatar_menu
, last_tutorial_mode
);
612 sub_view
->set_background(views::Background::CreateSolidBackground(
613 GetNativeTheme()->GetSystemColor(
614 ui::NativeTheme::kColorId_DialogBackground
)));
616 layout
->StartRow(1, 0);
617 layout
->AddView(sub_view
);
619 if (GetBubbleFrameView())
623 void ProfileChooserView::WindowClosing() {
624 DCHECK_EQ(profile_bubble_
, this);
625 profile_bubble_
= NULL
;
628 void ProfileChooserView::ButtonPressed(views::Button
* sender
,
629 const ui::Event
& event
) {
630 // Disable button after clicking so that it doesn't get clicked twice and
631 // start a second action... which can crash Chrome. But don't disable if it
632 // has no parent (like in tests) because that will also crash.
633 if (sender
->parent())
634 sender
->SetEnabled(false);
636 if (sender
== users_button_
) {
637 profiles::ShowUserManagerMaybeWithTutorial(browser_
->profile());
638 // If this is a guest session, also close all the guest browser windows.
639 if (browser_
->profile()->IsGuestSession())
640 profiles::CloseGuestProfileWindows();
641 } else if (sender
== lock_button_
) {
642 profiles::LockProfile(browser_
->profile());
643 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK
);
644 } else if (sender
== tutorial_ok_button_
) {
645 // If the user manually dismissed the tutorial, never show it again by
646 // setting the number of times shown to the maximum plus 1, so that later we
647 // could distinguish between the dismiss case and the case when the tutorial
648 // is indeed shown for the maximum number of times.
649 browser_
->profile()->GetPrefs()->SetInteger(
650 prefs::kProfileAvatarTutorialShown
, kProfileAvatarTutorialShowMax
+ 1);
652 ProfileMetrics::LogProfileUpgradeEnrollment(
653 ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD
);
654 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
655 } else if (sender
== tutorial_enable_new_profile_management_button_
) {
656 ProfileMetrics::LogProfileUpgradeEnrollment(
657 ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT
);
658 profiles::EnableNewProfileManagementPreview();
659 } else if (sender
== remove_account_button_
) {
661 } else if (sender
== account_removal_cancel_button_
) {
662 account_id_to_remove_
.clear();
663 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
664 } else if (sender
== gaia_signin_cancel_button_
) {
665 std::string primary_account
=
666 SigninManagerFactory::GetForProfile(browser_
->profile())->
667 GetAuthenticatedUsername();
668 ShowView(primary_account
.empty() ?
669 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
:
670 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
,
672 } else if (sender
== question_mark_button_
) {
673 tutorial_mode_
= profiles::TUTORIAL_MODE_SEND_FEEDBACK
;
674 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
675 } else if (sender
== tutorial_send_feedback_button_
) {
676 ProfileMetrics::LogProfileUpgradeEnrollment(
677 ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK
);
678 chrome::OpenFeedbackDialog(browser_
);
679 } else if (sender
== end_preview_and_relaunch_button_
) {
680 ProfileMetrics::LogProfileUpgradeEnrollment(
681 ProfileMetrics::PROFILE_ENROLLMENT_DISABLE_NEW_PROFILE_MGMT
);
682 profiles::DisableNewProfileManagementPreview();
683 } else if (sender
== end_preview_cancel_button_
) {
684 tutorial_mode_
= profiles::TUTORIAL_MODE_SEND_FEEDBACK
;
685 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
686 } else if (current_profile_photo_
&&
687 sender
== current_profile_photo_
->change_photo_button()) {
688 avatar_menu_
->EditProfile(avatar_menu_
->GetActiveProfileIndex());
689 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE
);
690 } else if (sender
== signin_current_profile_link_
) {
691 // Only show the inline signin if the new UI flag is flipped. Otherwise,
692 // use the tab signin page.
693 if (switches::IsNewProfileManagement())
694 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN
, avatar_menu_
.get());
696 chrome::ShowBrowserSignin(browser_
, signin::SOURCE_MENU
);
698 // Either one of the "other profiles", or one of the profile accounts
699 // buttons was pressed.
700 ButtonIndexes::const_iterator profile_match
=
701 open_other_profile_indexes_map_
.find(sender
);
702 if (profile_match
!= open_other_profile_indexes_map_
.end()) {
703 avatar_menu_
->SwitchToProfile(
704 profile_match
->second
,
705 ui::DispositionFromEventFlags(event
.flags()) == NEW_WINDOW
,
706 ProfileMetrics::SWITCH_PROFILE_ICON
);
708 // This was a profile accounts button.
709 AccountButtonIndexes::const_iterator account_match
=
710 delete_account_button_map_
.find(sender
);
711 if (account_match
!= delete_account_button_map_
.end()) {
712 account_id_to_remove_
= account_match
->second
;
713 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL
,
716 account_match
= reauth_account_button_map_
.find(sender
);
717 DCHECK(account_match
!= reauth_account_button_map_
.end());
718 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
, avatar_menu_
.get());
724 void ProfileChooserView::RemoveAccount() {
725 DCHECK(!account_id_to_remove_
.empty());
726 MutableProfileOAuth2TokenService
* oauth2_token_service
=
727 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
728 browser_
->profile());
729 if (oauth2_token_service
) {
730 oauth2_token_service
->RevokeCredentials(account_id_to_remove_
);
731 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_REMOVE_ACCT
);
733 account_id_to_remove_
.clear();
735 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
738 void ProfileChooserView::LinkClicked(views::Link
* sender
, int event_flags
) {
739 if (sender
== manage_accounts_link_
) {
740 // This link can either mean show/hide the account management view,
741 // depending on which view it is displayed. ShowView() will DCHECK if
742 // the account management view is displayed for non signed-in users.
744 view_mode_
== profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
?
745 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
:
746 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
,
748 } else if (sender
== add_account_link_
) {
749 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
, avatar_menu_
.get());
750 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT
);
751 } else if (sender
== tutorial_learn_more_link_
) {
752 ProfileMetrics::LogProfileUpgradeEnrollment(
753 ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE
);
754 // TODO(guohui): update |learn_more_url| once it is decided.
755 const GURL
lear_more_url("https://support.google.com/chrome/?hl=en#to");
756 chrome::NavigateParams
params(
759 content::PAGE_TRANSITION_LINK
);
760 params
.disposition
= NEW_FOREGROUND_TAB
;
761 chrome::Navigate(¶ms
);
763 DCHECK(sender
== tutorial_end_preview_link_
);
764 ShowView(profiles::BUBBLE_VIEW_MODE_END_PREVIEW
, avatar_menu_
.get());
768 void ProfileChooserView::StyledLabelLinkClicked(
769 const gfx::Range
& range
, int event_flags
) {
770 chrome::ShowSettings(browser_
);
773 bool ProfileChooserView::HandleKeyEvent(views::Textfield
* sender
,
774 const ui::KeyEvent
& key_event
) {
775 views::Textfield
* name_textfield
=
776 current_profile_name_
->profile_name_textfield();
777 DCHECK(sender
== name_textfield
);
779 if (key_event
.key_code() == ui::VKEY_RETURN
||
780 key_event
.key_code() == ui::VKEY_TAB
) {
781 // Pressing Tab/Enter commits the new profile name, unless it's empty.
782 base::string16 new_profile_name
= name_textfield
->text();
783 if (new_profile_name
.empty())
786 const AvatarMenu::Item
& active_item
= avatar_menu_
->GetItemAt(
787 avatar_menu_
->GetActiveProfileIndex());
788 Profile
* profile
= g_browser_process
->profile_manager()->GetProfile(
789 active_item
.profile_path
);
792 if (profile
->IsSupervised())
795 profiles::UpdateProfileName(profile
, new_profile_name
);
796 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME
);
797 current_profile_name_
->ShowReadOnlyView();
803 void ProfileChooserView::PostActionPerformed(
804 ProfileMetrics::ProfileDesktopMenu action_performed
) {
805 ProfileMetrics::LogProfileDesktopMenu(action_performed
, gaia_service_type_
);
806 gaia_service_type_
= signin::GAIA_SERVICE_TYPE_NONE
;
809 views::View
* ProfileChooserView::CreateProfileChooserView(
810 AvatarMenu
* avatar_menu
,
811 profiles::TutorialMode last_tutorial_mode
) {
812 // TODO(guohui, noms): the view should be customized based on whether new
813 // profile management preview is enabled or not.
815 views::View
* view
= new views::View();
816 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
817 // Separate items into active and alternatives.
818 Indexes other_profiles
;
819 views::View
* tutorial_view
= NULL
;
820 views::View
* current_profile_view
= NULL
;
821 views::View
* current_profile_accounts
= NULL
;
822 views::View
* option_buttons_view
= NULL
;
823 bool is_new_profile_management
= switches::IsNewProfileManagement();
824 for (size_t i
= 0; i
< avatar_menu
->GetNumberOfItems(); ++i
) {
825 const AvatarMenu::Item
& item
= avatar_menu
->GetItemAt(i
);
827 option_buttons_view
= CreateOptionsView(item
.signed_in
);
828 current_profile_view
= CreateCurrentProfileView(item
, false);
829 if (view_mode_
== profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
) {
830 if (is_new_profile_management
) {
832 last_tutorial_mode
== profiles::TUTORIAL_MODE_SEND_FEEDBACK
?
833 CreateSendPreviewFeedbackView() :
834 CreatePreviewEnabledTutorialView(
835 item
, last_tutorial_mode
== profiles::TUTORIAL_MODE_WELCOME
);
837 tutorial_view
= CreateNewProfileManagementPreviewView();
840 current_profile_accounts
= CreateCurrentProfileAccountsView(item
);
843 other_profiles
.push_back(i
);
848 // Be sure not to track the tutorial display on View refresh, and only count
849 // the preview-promo view, shown when New Profile Management is off.
850 if (tutorial_mode_
!= last_tutorial_mode
&& !is_new_profile_management
) {
851 ProfileMetrics::LogProfileUpgradeEnrollment(
852 ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO
);
854 layout
->StartRow(1, 0);
855 layout
->AddView(tutorial_view
);
858 if (!current_profile_view
) {
859 // Guest windows don't have an active profile.
860 current_profile_view
= CreateGuestProfileView();
861 option_buttons_view
= CreateOptionsView(false);
864 layout
->StartRow(1, 0);
865 layout
->AddView(current_profile_view
);
867 if (browser_
->profile()->IsSupervised()) {
868 layout
->StartRow(0, 0);
869 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
870 layout
->StartRow(1, 0);
871 layout
->AddView(CreateSupervisedUserDisclaimerView());
874 if (view_mode_
== profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
) {
875 layout
->StartRow(1, 0);
876 if (switches::IsFastUserSwitching())
877 layout
->AddView(CreateOtherProfilesView(other_profiles
));
879 DCHECK(current_profile_accounts
);
880 layout
->StartRow(0, 0);
881 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
882 layout
->StartRow(1, 0);
883 layout
->AddView(current_profile_accounts
);
886 layout
->StartRow(0, 0);
887 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
889 // Option buttons. Only available with the new profile management flag.
890 if (option_buttons_view
) {
891 layout
->StartRow(0, 0);
892 layout
->AddView(option_buttons_view
);
898 views::View
* ProfileChooserView::CreatePreviewEnabledTutorialView(
899 const AvatarMenu::Item
& current_avatar_item
,
900 bool tutorial_shown
) {
901 if (!switches::IsNewProfileManagementPreviewEnabled())
904 Profile
* profile
= browser_
->profile();
905 const int show_count
= profile
->GetPrefs()->GetInteger(
906 prefs::kProfileAvatarTutorialShown
);
907 // Do not show the tutorial if user has dismissed it.
908 if (show_count
> kProfileAvatarTutorialShowMax
)
911 if (!tutorial_shown
) {
912 if (show_count
== kProfileAvatarTutorialShowMax
)
914 profile
->GetPrefs()->SetInteger(
915 prefs::kProfileAvatarTutorialShown
, show_count
+ 1);
918 return CreateTutorialView(
919 profiles::TUTORIAL_MODE_WELCOME
,
920 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE
),
921 l10n_util::GetStringUTF16(
922 IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT
),
923 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
),
924 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON
),
925 &tutorial_learn_more_link_
,
926 &tutorial_ok_button_
);
929 views::View
* ProfileChooserView::CreateSendPreviewFeedbackView() {
930 return CreateTutorialView(
931 profiles::TUTORIAL_MODE_SEND_FEEDBACK
,
932 l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE
),
933 l10n_util::GetStringUTF16(
934 IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT
),
935 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW
),
936 l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON
),
937 &tutorial_end_preview_link_
,
938 &tutorial_send_feedback_button_
);
941 views::View
* ProfileChooserView::CreateTutorialView(
942 profiles::TutorialMode tutorial_mode
,
943 const base::string16
& title_text
,
944 const base::string16
& content_text
,
945 const base::string16
& link_text
,
946 const base::string16
& button_text
,
948 views::LabelButton
** button
) {
949 tutorial_mode_
= tutorial_mode
;
951 views::View
* view
= new views::View();
952 view
->set_background(views::Background::CreateSolidBackground(
953 profiles::kAvatarTutorialBackgroundColor
));
954 views::GridLayout
* layout
= CreateSingleColumnLayout(view
,
955 kFixedMenuWidth
- 2 * views::kButtonHEdgeMarginNew
);
956 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
957 views::kButtonHEdgeMarginNew
,
958 views::kButtonVEdgeMarginNew
,
959 views::kButtonHEdgeMarginNew
);
962 views::Label
* title_label
= new views::Label(title_text
);
963 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
964 title_label
->SetAutoColorReadabilityEnabled(false);
965 title_label
->SetEnabledColor(SK_ColorWHITE
);
966 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
967 ui::ResourceBundle::MediumFont
));
968 layout
->StartRow(1, 0);
969 layout
->AddView(title_label
);
971 // Adds body content.
972 views::Label
* content_label
= new views::Label(content_text
);
973 content_label
->SetMultiLine(true);
974 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
975 content_label
->SetAutoColorReadabilityEnabled(false);
976 content_label
->SetEnabledColor(profiles::kAvatarTutorialContentTextColor
);
977 layout
->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing
);
978 layout
->AddView(content_label
);
980 // Adds links and buttons.
981 views::View
* button_row
= new views::View();
982 views::GridLayout
* button_layout
= new views::GridLayout(button_row
);
983 views::ColumnSet
* button_columns
= button_layout
->AddColumnSet(0);
984 button_columns
->AddColumn(views::GridLayout::LEADING
,
985 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
986 button_columns
->AddPaddingColumn(
987 1, views::kUnrelatedControlHorizontalSpacing
);
988 button_columns
->AddColumn(views::GridLayout::TRAILING
,
989 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
990 button_row
->SetLayoutManager(button_layout
);
992 *link
= CreateLink(link_text
, this);
993 (*link
)->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
994 (*link
)->SetAutoColorReadabilityEnabled(false);
995 (*link
)->SetEnabledColor(SK_ColorWHITE
);
996 button_layout
->StartRow(1, 0);
997 button_layout
->AddView(*link
);
999 *button
= new views::LabelButton(this, button_text
);
1000 (*button
)->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
1001 (*button
)->SetStyle(views::Button::STYLE_BUTTON
);
1002 button_layout
->AddView(*button
);
1004 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1005 layout
->AddView(button_row
);
1007 // Adds a padded caret image at the bottom.
1008 views::View
* padded_caret_view
= new views::View();
1009 views::GridLayout
* padded_caret_layout
=
1010 new views::GridLayout(padded_caret_view
);
1011 views::ColumnSet
* padded_columns
= padded_caret_layout
->AddColumnSet(0);
1012 padded_columns
->AddPaddingColumn(0, views::kButtonHEdgeMarginNew
);
1013 padded_columns
->AddColumn(views::GridLayout::LEADING
,
1014 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
1015 padded_caret_view
->SetLayoutManager(padded_caret_layout
);
1017 views::ImageView
* caret_image_view
= new views::ImageView();
1018 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1019 caret_image_view
->SetImage(
1020 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET
));
1022 padded_caret_layout
->StartRow(1, 0);
1023 padded_caret_layout
->AddView(caret_image_view
);
1025 views::View
* view_with_caret
= new views::View();
1026 views::GridLayout
* layout_with_caret
=
1027 CreateSingleColumnLayout(view_with_caret
, kFixedMenuWidth
);
1028 layout_with_caret
->StartRow(1, 0);
1029 layout_with_caret
->AddView(view
);
1030 layout_with_caret
->StartRow(1, 0);
1031 layout_with_caret
->AddView(padded_caret_view
);
1032 return view_with_caret
;
1035 views::View
* ProfileChooserView::CreateCurrentProfileView(
1036 const AvatarMenu::Item
& avatar_item
,
1038 views::View
* view
= new views::View();
1039 int column_width
= kFixedMenuWidth
- 2 * views::kButtonHEdgeMarginNew
;
1040 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, column_width
);
1041 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
1042 views::kButtonHEdgeMarginNew
,
1043 views::kUnrelatedControlVerticalSpacing
,
1044 views::kButtonHEdgeMarginNew
);
1046 // Profile icon, centered.
1047 int x_offset
= (column_width
- kLargeImageSide
) / 2;
1048 current_profile_photo_
= new EditableProfilePhoto(
1049 this, avatar_item
.icon
, !is_guest
,
1050 gfx::Rect(x_offset
, 0, kLargeImageSide
, kLargeImageSide
));
1051 SizedContainer
* profile_icon_container
=
1052 new SizedContainer(gfx::Size(column_width
, kLargeImageSide
));
1053 profile_icon_container
->AddChildView(current_profile_photo_
);
1055 if (switches::IsNewProfileManagementPreviewEnabled()) {
1056 question_mark_button_
= new views::ImageButton(this);
1057 question_mark_button_
->SetImageAlignment(
1058 views::ImageButton::ALIGN_LEFT
, views::ImageButton::ALIGN_MIDDLE
);
1059 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1060 question_mark_button_
->SetImage(views::ImageButton::STATE_NORMAL
,
1061 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE
));
1062 question_mark_button_
->SetImage(views::ImageButton::STATE_HOVERED
,
1063 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER
));
1064 question_mark_button_
->SetImage(views::ImageButton::STATE_PRESSED
,
1065 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT
));
1066 gfx::Size preferred_size
= question_mark_button_
->GetPreferredSize();
1067 question_mark_button_
->SetBounds(
1068 0, 0, preferred_size
.width(), preferred_size
.height());
1069 profile_icon_container
->AddChildView(question_mark_button_
);
1072 if (browser_
->profile()->IsSupervised()) {
1073 views::ImageView
* supervised_icon
= new views::ImageView();
1074 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1075 supervised_icon
->SetImage(
1076 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_SUPERVISED
));
1077 gfx::Size preferred_size
= supervised_icon
->GetPreferredSize();
1078 gfx::Rect parent_bounds
= current_profile_photo_
->bounds();
1079 supervised_icon
->SetBounds(
1080 parent_bounds
.right() - preferred_size
.width(),
1081 parent_bounds
.bottom() - preferred_size
.height(),
1082 preferred_size
.width(),
1083 preferred_size
.height());
1084 profile_icon_container
->AddChildView(supervised_icon
);
1087 layout
->StartRow(1, 0);
1088 layout
->AddView(profile_icon_container
);
1090 // Profile name, centered.
1091 bool editing_allowed
= !is_guest
&& !browser_
->profile()->IsSupervised();
1092 current_profile_name_
= new EditableProfileName(
1093 this, profiles::GetAvatarNameForProfile(browser_
->profile()),
1095 layout
->StartRow(1, 0);
1096 layout
->AddView(current_profile_name_
);
1101 // The available links depend on the type of profile that is active.
1102 if (avatar_item
.signed_in
) {
1103 layout
->StartRow(1, 0);
1104 if (switches::IsNewProfileManagement()) {
1105 base::string16 link_title
= l10n_util::GetStringUTF16(
1106 view_mode_
== profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
?
1107 IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON
:
1108 IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON
);
1109 manage_accounts_link_
= CreateLink(link_title
, this);
1110 manage_accounts_link_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
1111 layout
->AddView(manage_accounts_link_
);
1113 views::Label
* email_label
= new views::Label(avatar_item
.sync_state
);
1114 email_label
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
1115 layout
->AddView(email_label
);
1118 SigninManagerBase
* signin_manager
=
1119 SigninManagerFactory::GetForProfile(
1120 browser_
->profile()->GetOriginalProfile());
1121 if (signin_manager
->IsSigninAllowed()) {
1122 signin_current_profile_link_
= new views::BlueButton(
1123 this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL
,
1124 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
)));
1125 layout
->StartRow(1, 0);
1126 layout
->AddView(signin_current_profile_link_
);
1133 views::View
* ProfileChooserView::CreateGuestProfileView() {
1134 gfx::Image guest_icon
=
1135 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1136 profiles::GetPlaceholderAvatarIconResourceID());
1137 AvatarMenu::Item
guest_avatar_item(0, 0, guest_icon
);
1138 guest_avatar_item
.active
= true;
1139 guest_avatar_item
.name
= l10n_util::GetStringUTF16(
1140 IDS_PROFILES_GUEST_PROFILE_NAME
);
1141 guest_avatar_item
.signed_in
= false;
1143 return CreateCurrentProfileView(guest_avatar_item
, true);
1146 views::View
* ProfileChooserView::CreateOtherProfilesView(
1147 const Indexes
& avatars_to_show
) {
1148 views::View
* view
= new views::View();
1149 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1151 int num_avatars_to_show
= avatars_to_show
.size();
1152 for (int i
= 0; i
< num_avatars_to_show
; ++i
) {
1153 const size_t index
= avatars_to_show
[i
];
1154 const AvatarMenu::Item
& item
= avatar_menu_
->GetItemAt(index
);
1155 const int kSmallImageSide
= 32;
1157 gfx::Image image
= profiles::GetSizedAvatarIcon(
1158 item
.icon
, true, kSmallImageSide
, kSmallImageSide
);
1160 views::LabelButton
* button
= new BackgroundColorHoverButton(
1163 *image
.ToImageSkia(),
1164 *image
.ToImageSkia());
1165 button
->set_min_size(gfx::Size(
1166 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1168 open_other_profile_indexes_map_
[button
] = index
;
1170 layout
->StartRow(1, 0);
1171 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
1172 layout
->StartRow(1, 0);
1173 layout
->AddView(button
);
1179 views::View
* ProfileChooserView::CreateOptionsView(bool enable_lock
) {
1180 if (!switches::IsNewProfileManagement())
1183 views::View
* view
= new views::View();
1184 views::GridLayout
* layout
;
1186 // Only signed-in users have the ability to lock.
1188 layout
= new views::GridLayout(view
);
1189 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
1190 int width_of_lock_button
=
1191 2 * views::kUnrelatedControlLargeHorizontalSpacing
+ 12;
1192 int width_of_users_button
= kFixedMenuWidth
- width_of_lock_button
;
1193 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
1194 views::GridLayout::FIXED
, width_of_users_button
,
1195 width_of_users_button
);
1196 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
1197 views::GridLayout::FIXED
, width_of_lock_button
,
1198 width_of_lock_button
);
1199 view
->SetLayoutManager(layout
);
1201 layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1204 base::string16 text
= browser_
->profile()->IsGuestSession() ?
1205 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST
) :
1206 l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON
,
1207 profiles::GetAvatarNameForProfile(browser_
->profile()));
1208 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1209 users_button_
= new BackgroundColorHoverButton(
1212 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR
),
1213 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR
));
1214 users_button_
->set_min_size(gfx::Size(
1215 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1217 layout
->StartRow(1, 0);
1218 layout
->AddView(users_button_
);
1221 lock_button_
= new BackgroundColorHoverButton(
1224 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK
),
1225 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK
));
1226 lock_button_
->set_min_size(gfx::Size(
1227 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1228 layout
->AddView(lock_button_
);
1233 views::View
* ProfileChooserView::CreateSupervisedUserDisclaimerView() {
1234 views::View
* view
= new views::View();
1235 views::GridLayout
* layout
= CreateSingleColumnLayout(
1236 view
, kFixedMenuWidth
- 2 * views::kButtonHEdgeMarginNew
);
1237 layout
->SetInsets(views::kRelatedControlVerticalSpacing
,
1238 views::kButtonHEdgeMarginNew
,
1239 views::kRelatedControlVerticalSpacing
,
1240 views::kButtonHEdgeMarginNew
);
1241 views::Label
* disclaimer
= new views::Label(
1242 avatar_menu_
->GetSupervisedUserInformation());
1243 disclaimer
->SetMultiLine(true);
1244 disclaimer
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
1245 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1246 disclaimer
->SetFontList(rb
->GetFontList(ui::ResourceBundle::SmallFont
));
1247 layout
->StartRow(1, 0);
1248 layout
->AddView(disclaimer
);
1253 views::View
* ProfileChooserView::CreateCurrentProfileAccountsView(
1254 const AvatarMenu::Item
& avatar_item
) {
1255 DCHECK(avatar_item
.signed_in
);
1256 views::View
* view
= new views::View();
1257 view
->set_background(views::Background::CreateSolidBackground(
1258 profiles::kAvatarBubbleAccountsBackgroundColor
));
1259 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1261 Profile
* profile
= browser_
->profile();
1262 std::string primary_account
=
1263 SigninManagerFactory::GetForProfile(profile
)->GetAuthenticatedUsername();
1264 DCHECK(!primary_account
.empty());
1265 std::vector
<std::string
>accounts
=
1266 profiles::GetSecondaryAccountsForProfile(profile
, primary_account
);
1268 // Get state of authentication error, if any.
1269 std::string error_account_id
= GetAuthErrorAccountId(profile
);
1271 // The primary account should always be listed first.
1272 // TODO(rogerta): we still need to further differentiate the primary account
1273 // from the others in the UI, so more work is likely required here:
1274 // crbug.com/311124.
1275 CreateAccountButton(layout
, primary_account
, true,
1276 error_account_id
== primary_account
, kFixedMenuWidth
);
1277 for (size_t i
= 0; i
< accounts
.size(); ++i
)
1278 CreateAccountButton(layout
, accounts
[i
], false,
1279 error_account_id
== accounts
[i
], kFixedMenuWidth
);
1280 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
1282 add_account_link_
= CreateLink(l10n_util::GetStringFUTF16(
1283 IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON
, avatar_item
.name
), this);
1284 add_account_link_
->SetBorder(views::Border::CreateEmptyBorder(
1285 0, views::kButtonVEdgeMarginNew
,
1286 views::kRelatedControlVerticalSpacing
, 0));
1287 layout
->StartRow(1, 0);
1288 layout
->AddView(add_account_link_
);
1292 void ProfileChooserView::CreateAccountButton(views::GridLayout
* layout
,
1293 const std::string
& account
,
1294 bool is_primary_account
,
1295 bool reauth_required
,
1297 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1298 const gfx::ImageSkia
* delete_default_image
=
1299 rb
->GetImageNamed(IDR_CLOSE_1
).ToImageSkia();
1300 const int kDeleteButtonWidth
= delete_default_image
->width();
1301 const gfx::ImageSkia
* warning_default_image
= reauth_required
?
1302 rb
->GetImageNamed(IDR_WARNING
).ToImageSkia() : NULL
;
1303 const int kWarningButtonWidth
= reauth_required
?
1304 warning_default_image
->width() + views::kRelatedButtonHSpacing
: 0;
1305 int available_width
= width
-
1306 kDeleteButtonWidth
- kWarningButtonWidth
- views::kButtonHEdgeMarginNew
;
1308 views::LabelButton
* email_button
= new BackgroundColorHoverButton(
1310 gfx::ElideText(base::UTF8ToUTF16(account
), gfx::FontList(),
1311 available_width
, gfx::ELIDE_EMAIL
),
1314 layout
->StartRow(1, 0);
1315 layout
->AddView(email_button
);
1318 views::ImageButton
* delete_button
= new views::ImageButton(this);
1319 delete_button
->SetImageAlignment(views::ImageButton::ALIGN_RIGHT
,
1320 views::ImageButton::ALIGN_MIDDLE
);
1321 delete_button
->SetImage(views::ImageButton::STATE_NORMAL
,
1322 delete_default_image
);
1323 delete_button
->SetImage(views::ImageButton::STATE_HOVERED
,
1324 rb
->GetImageSkiaNamed(IDR_CLOSE_1_H
));
1325 delete_button
->SetImage(views::ImageButton::STATE_PRESSED
,
1326 rb
->GetImageSkiaNamed(IDR_CLOSE_1_P
));
1327 delete_button
->SetBounds(
1328 available_width
+ kWarningButtonWidth
, 0,
1329 kDeleteButtonWidth
, kButtonHeight
);
1331 email_button
->set_notify_enter_exit_on_child(true);
1332 email_button
->AddChildView(delete_button
);
1334 // Save the original email address, as the button text could be elided.
1335 delete_account_button_map_
[delete_button
] = account
;
1338 if (reauth_required
) {
1339 views::ImageButton
* reauth_button
= new views::ImageButton(this);
1340 reauth_button
->SetImageAlignment(views::ImageButton::ALIGN_LEFT
,
1341 views::ImageButton::ALIGN_MIDDLE
);
1342 reauth_button
->SetImage(views::ImageButton::STATE_NORMAL
,
1343 warning_default_image
);
1344 reauth_button
->SetBounds(
1345 available_width
, 0, kWarningButtonWidth
, kButtonHeight
);
1347 email_button
->AddChildView(reauth_button
);
1348 reauth_account_button_map_
[reauth_button
] = account
;
1352 views::View
* ProfileChooserView::CreateGaiaSigninView() {
1356 switch (view_mode_
) {
1357 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN
:
1358 url
= signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_SIGN_IN
,
1359 false /* auto_close */,
1360 true /* is_constrained */);
1361 message_id
= IDS_PROFILES_GAIA_SIGNIN_TITLE
;
1363 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
:
1364 url
= signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
,
1365 false /* auto_close */,
1366 true /* is_constrained */);
1367 message_id
= IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE
;
1369 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
: {
1370 DCHECK(HasAuthError(browser_
->profile()));
1371 url
= signin::GetReauthURL(browser_
->profile(),
1372 GetAuthErrorUsername(browser_
->profile()));
1373 message_id
= IDS_PROFILES_GAIA_REAUTH_TITLE
;
1377 NOTREACHED() << "Called with invalid mode=" << view_mode_
;
1381 // Adds Gaia signin webview
1382 Profile
* profile
= browser_
->profile();
1383 views::WebView
* web_view
= new views::WebView(profile
);
1384 web_view
->LoadInitialURL(url
);
1385 web_view
->SetPreferredSize(
1386 gfx::Size(kFixedGaiaViewWidth
, kFixedGaiaViewHeight
));
1388 TitleCard
* title_card
= new TitleCard(message_id
, this,
1389 &gaia_signin_cancel_button_
);
1390 return TitleCard::AddPaddedTitleCard(
1391 web_view
, title_card
, kFixedGaiaViewWidth
);
1394 views::View
* ProfileChooserView::CreateAccountRemovalView() {
1395 views::View
* view
= new views::View();
1396 views::GridLayout
* layout
= CreateSingleColumnLayout(
1397 view
, kFixedAccountRemovalViewWidth
- 2 * views::kButtonHEdgeMarginNew
);
1398 layout
->SetInsets(0,
1399 views::kButtonHEdgeMarginNew
,
1400 views::kButtonVEdgeMarginNew
,
1401 views::kButtonHEdgeMarginNew
);
1403 const std::string
& primary_account
= SigninManagerFactory::GetForProfile(
1404 browser_
->profile())->GetAuthenticatedUsername();
1405 bool is_primary_account
= primary_account
== account_id_to_remove_
;
1408 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1409 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1410 const gfx::FontList
& small_font_list
=
1411 rb
->GetFontList(ui::ResourceBundle::SmallFont
);
1413 if (is_primary_account
) {
1414 std::vector
<size_t> offsets
;
1415 const base::string16 settings_text
=
1416 l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK
);
1417 const base::string16 primary_account_removal_text
=
1418 l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT
,
1419 base::UTF8ToUTF16(account_id_to_remove_
), settings_text
, &offsets
);
1420 views::StyledLabel
* primary_account_removal_label
=
1421 new views::StyledLabel(primary_account_removal_text
, this);
1422 primary_account_removal_label
->AddStyleRange(
1423 gfx::Range(offsets
[1], offsets
[1] + settings_text
.size()),
1424 views::StyledLabel::RangeStyleInfo::CreateForLink());
1425 primary_account_removal_label
->SetBaseFontList(small_font_list
);
1426 layout
->AddView(primary_account_removal_label
);
1428 views::Label
* content_label
= new views::Label(
1429 l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT
));
1430 content_label
->SetMultiLine(true);
1431 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
1432 content_label
->SetFontList(small_font_list
);
1433 layout
->AddView(content_label
);
1437 if (!is_primary_account
) {
1438 remove_account_button_
= new views::BlueButton(
1439 this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON
));
1440 remove_account_button_
->SetHorizontalAlignment(
1442 layout
->StartRowWithPadding(
1443 1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1444 layout
->AddView(remove_account_button_
);
1446 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
1449 TitleCard
* title_card
= new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE
,
1450 this, &account_removal_cancel_button_
);
1451 return TitleCard::AddPaddedTitleCard(view
, title_card
,
1452 kFixedAccountRemovalViewWidth
);
1455 views::View
* ProfileChooserView::CreateNewProfileManagementPreviewView() {
1456 return CreateTutorialView(
1457 profiles::TUTORIAL_MODE_ENABLE_PREVIEW
,
1458 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE
),
1459 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT
),
1460 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
),
1461 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON
),
1462 &tutorial_learn_more_link_
,
1463 &tutorial_enable_new_profile_management_button_
);
1466 views::View
* ProfileChooserView::CreateEndPreviewView() {
1467 views::View
* view
= new views::View();
1468 views::GridLayout
* layout
= CreateSingleColumnLayout(
1469 view
, kFixedAccountRemovalViewWidth
- 2 * views::kButtonHEdgeMarginNew
);
1470 layout
->SetInsets(0,
1471 views::kButtonHEdgeMarginNew
,
1472 views::kButtonVEdgeMarginNew
,
1473 views::kButtonHEdgeMarginNew
);
1476 views::Label
* content_label
= new views::Label(
1477 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT
));
1478 content_label
->SetMultiLine(true);
1479 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
1480 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1481 const gfx::FontList
& small_font_list
=
1482 rb
->GetFontList(ui::ResourceBundle::SmallFont
);
1483 content_label
->SetFontList(small_font_list
);
1484 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1485 layout
->AddView(content_label
);
1488 end_preview_and_relaunch_button_
= new views::BlueButton(
1489 this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH
));
1490 end_preview_and_relaunch_button_
->SetHorizontalAlignment(
1492 layout
->StartRowWithPadding(
1493 1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1494 layout
->AddView(end_preview_and_relaunch_button_
);
1496 TitleCard
* title_card
= new TitleCard(
1497 IDS_PROFILES_END_PREVIEW
, this, &end_preview_cancel_button_
);
1498 return TitleCard::AddPaddedTitleCard(
1499 view
, title_card
, kFixedAccountRemovalViewWidth
);