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_manager_factory.h"
19 #include "chrome/browser/signin/signin_promo.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_commands.h"
22 #include "chrome/browser/ui/browser_dialogs.h"
23 #include "chrome/browser/ui/chrome_pages.h"
24 #include "chrome/browser/ui/singleton_tabs.h"
25 #include "chrome/browser/ui/views/profiles/user_manager_view.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/common/url_constants.h"
28 #include "components/signin/core/browser/mutable_profile_oauth2_token_service.h"
29 #include "components/signin/core/browser/profile_oauth2_token_service.h"
30 #include "components/signin/core/browser/signin_manager.h"
31 #include "components/signin/core/common/profile_management_switches.h"
32 #include "grit/chromium_strings.h"
33 #include "grit/generated_resources.h"
34 #include "grit/theme_resources.h"
35 #include "third_party/skia/include/core/SkColor.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/canvas.h"
39 #include "ui/gfx/image/image.h"
40 #include "ui/gfx/image/image_skia.h"
41 #include "ui/gfx/text_elider.h"
42 #include "ui/native_theme/native_theme.h"
43 #include "ui/views/controls/button/blue_button.h"
44 #include "ui/views/controls/button/image_button.h"
45 #include "ui/views/controls/button/label_button.h"
46 #include "ui/views/controls/button/menu_button.h"
47 #include "ui/views/controls/label.h"
48 #include "ui/views/controls/link.h"
49 #include "ui/views/controls/separator.h"
50 #include "ui/views/controls/styled_label.h"
51 #include "ui/views/controls/textfield/textfield.h"
52 #include "ui/views/controls/webview/webview.h"
53 #include "ui/views/layout/grid_layout.h"
54 #include "ui/views/layout/layout_constants.h"
55 #include "ui/views/widget/widget.h"
59 // Helpers --------------------------------------------------------------------
61 const int kFixedMenuWidth
= 250;
62 const int kButtonHeight
= 29;
63 const int kProfileAvatarTutorialShowMax
= 1;
64 const int kFixedGaiaViewHeight
= 400;
65 const int kFixedGaiaViewWidth
= 360;
66 const int kFixedAccountRemovalViewWidth
= 280;
67 const int kFixedEndPreviewViewWidth
= 280;
68 const int kLargeImageSide
= 88;
70 // Creates a GridLayout with a single column. This ensures that all the child
71 // views added get auto-expanded to fill the full width of the bubble.
72 views::GridLayout
* CreateSingleColumnLayout(views::View
* view
, int width
) {
73 views::GridLayout
* layout
= new views::GridLayout(view
);
74 view
->SetLayoutManager(layout
);
76 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
77 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
78 views::GridLayout::FIXED
, width
, width
);
82 views::Link
* CreateLink(const base::string16
& link_text
,
83 views::LinkListener
* listener
) {
84 views::Link
* link_button
= new views::Link(link_text
);
85 link_button
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
86 link_button
->SetUnderline(false);
87 link_button
->set_listener(listener
);
91 gfx::ImageSkia
CreateSquarePlaceholderImage(int size
) {
93 bitmap
.setConfig(SkBitmap::kA8_Config
, size
, size
);
95 bitmap
.eraseARGB(0, 0, 0, 0);
96 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap
);
99 // BackgroundColorHoverButton -------------------------------------------------
101 // A custom button that allows for setting a background color when hovered over.
102 class BackgroundColorHoverButton
: public views::LabelButton
{
104 BackgroundColorHoverButton(views::ButtonListener
* listener
,
105 const base::string16
& text
,
106 const gfx::ImageSkia
& normal_icon
,
107 const gfx::ImageSkia
& hover_icon
);
108 virtual ~BackgroundColorHoverButton();
111 // views::LabelButton:
112 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
;
114 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton
);
117 BackgroundColorHoverButton::BackgroundColorHoverButton(
118 views::ButtonListener
* listener
,
119 const base::string16
& text
,
120 const gfx::ImageSkia
& normal_icon
,
121 const gfx::ImageSkia
& hover_icon
)
122 : views::LabelButton(listener
, text
) {
123 SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew
,
124 0, views::kButtonHEdgeMarginNew
));
125 set_min_size(gfx::Size(0, kButtonHeight
));
126 SetImage(STATE_NORMAL
, normal_icon
);
127 SetImage(STATE_HOVERED
, hover_icon
);
128 SetImage(STATE_PRESSED
, hover_icon
);
131 BackgroundColorHoverButton::~BackgroundColorHoverButton() {}
133 void BackgroundColorHoverButton::OnPaint(gfx::Canvas
* canvas
) {
134 if ((state() == STATE_PRESSED
) || (state() == STATE_HOVERED
) || HasFocus()) {
135 canvas
->DrawColor(GetNativeTheme()->GetSystemColor(
136 ui::NativeTheme::kColorId_ButtonHoverBackgroundColor
));
138 LabelButton::OnPaint(canvas
);
144 // EditableProfilePhoto -------------------------------------------------
146 // A custom Image control that shows a "change" button when moused over.
147 class EditableProfilePhoto
: public views::ImageView
{
149 EditableProfilePhoto(views::ButtonListener
* listener
,
150 const gfx::Image
& icon
,
151 bool is_editing_allowed
,
152 const gfx::Rect
& bounds
)
153 : views::ImageView(),
154 change_photo_button_(NULL
) {
155 gfx::Image image
= profiles::GetSizedAvatarIcon(
156 icon
, true, kLargeImageSide
, kLargeImageSide
);
157 SetImage(image
.ToImageSkia());
158 SetBoundsRect(bounds
);
160 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
161 views::ImageView
* frame_overlay
= new views::ImageView();
162 frame_overlay
->SetImage(rb
->GetImageNamed(
163 IDR_ICON_PROFILES_AVATAR_PHOTO_FRAME
).ToImageSkia());
164 frame_overlay
->SetVerticalAlignment(views::ImageView::CENTER
);
165 frame_overlay
->SetBoundsRect(bounds
);
166 AddChildView(frame_overlay
);
168 if (!is_editing_allowed
)
171 set_notify_enter_exit_on_child(true);
173 // Button overlay that appears when hovering over the image.
174 change_photo_button_
= new views::LabelButton(listener
, base::string16());
175 change_photo_button_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
176 change_photo_button_
->SetBorder(views::Border::NullBorder());
178 const SkColor kBackgroundColor
= SkColorSetARGB(65, 255, 255, 255);
179 change_photo_button_
->set_background(
180 views::Background::CreateSolidBackground(kBackgroundColor
));
181 change_photo_button_
->SetImage(views::LabelButton::STATE_NORMAL
,
182 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_CAMERA
));
184 change_photo_button_
->SetBoundsRect(bounds
);
185 change_photo_button_
->SetVisible(false);
186 AddChildView(change_photo_button_
);
189 views::LabelButton
* change_photo_button() { return change_photo_button_
; }
193 virtual void OnMouseEntered(const ui::MouseEvent
& event
) OVERRIDE
{
194 if (change_photo_button_
)
195 change_photo_button_
->SetVisible(true);
198 virtual void OnMouseExited(const ui::MouseEvent
& event
) OVERRIDE
{
199 if (change_photo_button_
)
200 change_photo_button_
->SetVisible(false);
203 // Button that is shown when hovering over the image view. Can be NULL if
204 // the photo isn't allowed to be edited (e.g. for guest profiles).
205 views::LabelButton
* change_photo_button_
;
207 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto
);
211 // EditableProfileName -------------------------------------------------
213 // A custom text control that turns into a textfield for editing when clicked.
214 class EditableProfileName
: public views::LabelButton
,
215 public views::ButtonListener
{
217 EditableProfileName(views::TextfieldController
* controller
,
218 const base::string16
& text
,
219 bool is_editing_allowed
)
220 : views::LabelButton(this, text
),
221 profile_name_textfield_(NULL
) {
222 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
223 const gfx::FontList
& medium_font_list
=
224 rb
->GetFontList(ui::ResourceBundle::MediumFont
);
225 SetFontList(medium_font_list
);
226 SetHorizontalAlignment(gfx::ALIGN_CENTER
);
228 if (!is_editing_allowed
)
231 // Show an "edit" pencil icon when hovering over. In the default state,
232 // we need to create an empty placeholder of the correct size, so that
233 // the text doesn't jump around when the hovered icon appears.
234 gfx::ImageSkia hover_image
=
235 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER
);
236 SetImage(STATE_NORMAL
, CreateSquarePlaceholderImage(hover_image
.width()));
237 SetImage(STATE_HOVERED
, hover_image
);
238 SetImage(STATE_PRESSED
,
239 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED
));
240 // To center the text, we need to offest it by the width of the icon we
241 // are adding. We need to also add a small top/bottom padding to account
242 // for the textfield's border.
243 SetBorder(views::Border::CreateEmptyBorder(2, hover_image
.width(), 2, 0));
245 // Textfield that overlaps the button.
246 profile_name_textfield_
= new views::Textfield();
247 profile_name_textfield_
->set_controller(controller
);
248 profile_name_textfield_
->SetFontList(medium_font_list
);
249 profile_name_textfield_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
251 profile_name_textfield_
->SetVisible(false);
252 AddChildView(profile_name_textfield_
);
255 views::Textfield
* profile_name_textfield() {
256 return profile_name_textfield_
;
259 // Hide the editable textfield to show the profile name button instead.
260 void ShowReadOnlyView() {
261 if (profile_name_textfield_
)
262 profile_name_textfield_
->SetVisible(false);
266 // views::ButtonListener:
267 virtual void ButtonPressed(views::Button
* sender
,
268 const ui::Event
& event
) OVERRIDE
{
269 if (profile_name_textfield_
) {
270 profile_name_textfield_
->SetVisible(true);
271 profile_name_textfield_
->SetText(GetText());
272 profile_name_textfield_
->SelectAll(false);
273 profile_name_textfield_
->RequestFocus();
277 // views::LabelButton:
278 virtual bool OnKeyReleased(const ui::KeyEvent
& event
) OVERRIDE
{
279 // Override CustomButton's implementation, which presses the button when
280 // you press space and clicks it when you release space, as the space can be
281 // part of the new profile name typed in the textfield.
285 virtual void Layout() OVERRIDE
{
286 if (profile_name_textfield_
)
287 profile_name_textfield_
->SetBounds(0, 0, width(), height());
288 // This layout trick keeps the text left-aligned and the icon right-aligned.
289 SetHorizontalAlignment(gfx::ALIGN_RIGHT
);
290 views::LabelButton::Layout();
291 label()->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
294 // Textfield that is shown when editing the profile name. Can be NULL if
295 // the profile name isn't allowed to be edited (e.g. for guest profiles).
296 views::Textfield
* profile_name_textfield_
;
298 DISALLOW_COPY_AND_ASSIGN(EditableProfileName
);
301 // A title card with one back button right aligned and one label center aligned.
302 class TitleCard
: public views::View
{
304 TitleCard(int message_id
, views::ButtonListener
* listener
,
305 views::ImageButton
** back_button
) {
306 back_button_
= new views::ImageButton(listener
);
307 back_button_
->SetImageAlignment(views::ImageButton::ALIGN_LEFT
,
308 views::ImageButton::ALIGN_MIDDLE
);
309 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
310 back_button_
->SetImage(views::ImageButton::STATE_NORMAL
,
311 rb
->GetImageSkiaNamed(IDR_BACK
));
312 back_button_
->SetImage(views::ImageButton::STATE_HOVERED
,
313 rb
->GetImageSkiaNamed(IDR_BACK_H
));
314 back_button_
->SetImage(views::ImageButton::STATE_PRESSED
,
315 rb
->GetImageSkiaNamed(IDR_BACK_P
));
316 back_button_
->SetImage(views::ImageButton::STATE_DISABLED
,
317 rb
->GetImageSkiaNamed(IDR_BACK_D
));
318 *back_button
= back_button_
;
320 title_label_
= new views::Label(l10n_util::GetStringUTF16(message_id
));
321 title_label_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
322 const gfx::FontList
& medium_font_list
=
323 rb
->GetFontList(ui::ResourceBundle::MediumFont
);
324 title_label_
->SetFontList(medium_font_list
);
326 AddChildView(back_button_
);
327 AddChildView(title_label_
);
330 // Creates a new view that has the |title_card| with padding at the top, an
331 // edge-to-edge separator below, and the specified |view| at the bottom.
332 static views::View
* AddPaddedTitleCard(views::View
* view
,
333 TitleCard
* title_card
,
335 views::View
* titled_view
= new views::View();
336 views::GridLayout
* layout
= new views::GridLayout(titled_view
);
337 titled_view
->SetLayoutManager(layout
);
339 // Column set 0 is a single column layout with horizontal padding at left
340 // and right, and column set 1 is a single column layout with no padding.
341 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
342 columns
->AddPaddingColumn(1, views::kButtonHEdgeMarginNew
);
343 int available_width
= width
- 2 * views::kButtonHEdgeMarginNew
;
344 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
345 views::GridLayout::FIXED
, available_width
, available_width
);
346 columns
->AddPaddingColumn(1, views::kButtonHEdgeMarginNew
);
347 layout
->AddColumnSet(1)->AddColumn(views::GridLayout::FILL
,
348 views::GridLayout::FILL
, 0,views::GridLayout::FIXED
, width
, width
);
350 layout
->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew
);
351 layout
->AddView(title_card
);
352 layout
->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing
);
353 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
355 layout
->StartRow(1, 1);
356 layout
->AddView(view
);
362 virtual void Layout() OVERRIDE
{
363 back_button_
->SetBounds(
364 0, 0, back_button_
->GetPreferredSize().width(), height());
365 title_label_
->SetBoundsRect(GetContentsBounds());
368 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
369 int height
= std::max(title_label_
->GetPreferredSize().height(),
370 back_button_
->GetPreferredSize().height());
371 return gfx::Size(width(), height
);
374 views::ImageButton
* back_button_
;
375 views::Label
* title_label_
;
377 DISALLOW_COPY_AND_ASSIGN(TitleCard
);
380 // ProfileChooserView ---------------------------------------------------------
383 ProfileChooserView
* ProfileChooserView::profile_bubble_
= NULL
;
384 bool ProfileChooserView::close_on_deactivate_for_testing_
= true;
387 void ProfileChooserView::ShowBubble(
388 BubbleViewMode view_mode
,
389 views::View
* anchor_view
,
390 views::BubbleBorder::Arrow arrow
,
391 views::BubbleBorder::BubbleAlignment border_alignment
,
392 const gfx::Rect
& anchor_rect
,
397 profile_bubble_
= new ProfileChooserView(anchor_view
, arrow
, anchor_rect
,
399 views::BubbleDelegateView::CreateBubble(profile_bubble_
);
400 profile_bubble_
->set_close_on_deactivate(close_on_deactivate_for_testing_
);
401 profile_bubble_
->SetAlignment(border_alignment
);
402 profile_bubble_
->GetWidget()->Show();
403 profile_bubble_
->SetArrowPaintType(views::BubbleBorder::PAINT_NONE
);
407 bool ProfileChooserView::IsShowing() {
408 return profile_bubble_
!= NULL
;
412 void ProfileChooserView::Hide() {
414 profile_bubble_
->GetWidget()->Close();
417 ProfileChooserView::ProfileChooserView(views::View
* anchor_view
,
418 views::BubbleBorder::Arrow arrow
,
419 const gfx::Rect
& anchor_rect
,
421 BubbleViewMode view_mode
)
422 : BubbleDelegateView(anchor_view
, arrow
),
424 view_mode_(view_mode
),
425 tutorial_mode_(TUTORIAL_MODE_NONE
) {
426 // Reset the default margins inherited from the BubbleDelegateView.
427 set_margins(gfx::Insets());
431 avatar_menu_
.reset(new AvatarMenu(
432 &g_browser_process
->profile_manager()->GetProfileInfoCache(),
435 avatar_menu_
->RebuildMenu();
437 ProfileOAuth2TokenService
* oauth2_token_service
=
438 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
439 if (oauth2_token_service
)
440 oauth2_token_service
->AddObserver(this);
443 ProfileChooserView::~ProfileChooserView() {
444 ProfileOAuth2TokenService
* oauth2_token_service
=
445 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
446 if (oauth2_token_service
)
447 oauth2_token_service
->RemoveObserver(this);
450 void ProfileChooserView::ResetView() {
451 question_mark_button_
= NULL
;
452 manage_accounts_link_
= NULL
;
453 signin_current_profile_link_
= NULL
;
454 users_button_
= NULL
;
456 add_account_link_
= NULL
;
457 current_profile_photo_
= NULL
;
458 current_profile_name_
= NULL
;
459 tutorial_ok_button_
= NULL
;
460 tutorial_learn_more_link_
= NULL
;
461 tutorial_enable_new_profile_management_button_
= NULL
;
462 tutorial_end_preview_link_
= NULL
;
463 tutorial_send_feedback_button_
= NULL
;
464 end_preview_and_relaunch_button_
= NULL
;
465 end_preview_cancel_button_
= NULL
;
466 remove_account_button_
= NULL
;
467 account_removal_cancel_button_
= NULL
;
468 gaia_signin_cancel_button_
= NULL
;
469 open_other_profile_indexes_map_
.clear();
470 current_profile_accounts_map_
.clear();
471 tutorial_mode_
= TUTORIAL_MODE_NONE
;
474 void ProfileChooserView::Init() {
475 ShowView(view_mode_
, avatar_menu_
.get());
478 void ProfileChooserView::OnAvatarMenuChanged(
479 AvatarMenu
* avatar_menu
) {
480 // Refresh the view with the new menu. We can't just update the local copy
481 // as this may have been triggered by a sign out action, in which case
482 // the view is being destroyed.
483 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu
);
486 void ProfileChooserView::OnRefreshTokenAvailable(
487 const std::string
& account_id
) {
488 // Refresh the account management view when a new account is added to the
490 if (view_mode_
== BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
||
491 view_mode_
== BUBBLE_VIEW_MODE_GAIA_SIGNIN
||
492 view_mode_
== BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
) {
493 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
497 void ProfileChooserView::OnRefreshTokenRevoked(const std::string
& account_id
) {
498 // Refresh the account management view when an account is removed from the
500 if (view_mode_
== BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
)
501 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
504 void ProfileChooserView::ShowView(BubbleViewMode view_to_display
,
505 AvatarMenu
* avatar_menu
) {
506 // The account management view should only be displayed if the active profile
508 if (view_to_display
== BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
) {
509 const AvatarMenu::Item
& active_item
= avatar_menu
->GetItemAt(
510 avatar_menu
->GetActiveProfileIndex());
511 DCHECK(active_item
.signed_in
);
514 // Records the last tutorial mode.
515 TutorialMode last_tutorial_mode
= tutorial_mode_
;
517 RemoveAllChildViews(true);
518 view_mode_
= view_to_display
;
520 views::GridLayout
* layout
;
521 views::View
* sub_view
;
522 switch (view_mode_
) {
523 case BUBBLE_VIEW_MODE_GAIA_SIGNIN
:
524 case BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
:
525 layout
= CreateSingleColumnLayout(this, kFixedGaiaViewWidth
);
526 sub_view
= CreateGaiaSigninView(
527 view_mode_
== BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
);
529 case BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL
:
530 layout
= CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth
);
531 sub_view
= CreateAccountRemovalView();
533 case BUBBLE_VIEW_MODE_END_PREVIEW
:
534 layout
= CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth
);
535 sub_view
= CreateEndPreviewView();
538 layout
= CreateSingleColumnLayout(this, kFixedMenuWidth
);
539 sub_view
= CreateProfileChooserView(avatar_menu
, last_tutorial_mode
);
541 sub_view
->set_background(views::Background::CreateSolidBackground(
542 GetNativeTheme()->GetSystemColor(
543 ui::NativeTheme::kColorId_DialogBackground
)));
545 layout
->StartRow(1, 0);
546 layout
->AddView(sub_view
);
548 if (GetBubbleFrameView())
552 void ProfileChooserView::WindowClosing() {
553 DCHECK_EQ(profile_bubble_
, this);
554 profile_bubble_
= NULL
;
557 void ProfileChooserView::ButtonPressed(views::Button
* sender
,
558 const ui::Event
& event
) {
559 // Disable button after clicking so that it doesn't get clicked twice and
560 // start a second action... which can crash Chrome. But don't disable if it
561 // has no parent (like in tests) because that will also crash.
562 if (sender
->parent())
563 sender
->SetEnabled(false);
565 if (sender
== users_button_
) {
566 profiles::ShowUserManagerMaybeWithTutorial(browser_
->profile());
567 // If this is a guest session, also close all the guest browser windows.
568 if (browser_
->profile()->IsGuestSession())
569 profiles::CloseGuestProfileWindows();
570 } else if (sender
== lock_button_
) {
571 profiles::LockProfile(browser_
->profile());
572 } else if (sender
== tutorial_ok_button_
) {
573 // If the user manually dismissed the tutorial, never show it again by
574 // setting the number of times shown to the maximum plus 1, so that later we
575 // could distinguish between the dismiss case and the case when the tutorial
576 // is indeed shown for the maximum number of times.
577 browser_
->profile()->GetPrefs()->SetInteger(
578 prefs::kProfileAvatarTutorialShown
, kProfileAvatarTutorialShowMax
+ 1);
580 ProfileMetrics::LogProfileUpgradeEnrollment(
581 ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD
);
582 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
583 } else if (sender
== tutorial_enable_new_profile_management_button_
) {
584 ProfileMetrics::LogProfileUpgradeEnrollment(
585 ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT
);
586 profiles::EnableNewProfileManagementPreview();
587 } else if (sender
== remove_account_button_
) {
589 } else if (sender
== account_removal_cancel_button_
) {
590 account_id_to_remove_
.clear();
591 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
592 } else if (sender
== gaia_signin_cancel_button_
) {
593 std::string primary_account
=
594 SigninManagerFactory::GetForProfile(browser_
->profile())->
595 GetAuthenticatedUsername();
596 ShowView(primary_account
.empty() ? BUBBLE_VIEW_MODE_PROFILE_CHOOSER
:
597 BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
,
599 } else if (sender
== question_mark_button_
) {
600 tutorial_mode_
= TUTORIAL_MODE_SEND_FEEDBACK
;
601 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
602 } else if (sender
== tutorial_send_feedback_button_
) {
603 ProfileMetrics::LogProfileUpgradeEnrollment(
604 ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK
);
605 chrome::OpenFeedbackDialog(browser_
);
606 } else if (sender
== end_preview_and_relaunch_button_
) {
607 ProfileMetrics::LogProfileUpgradeEnrollment(
608 ProfileMetrics::PROFILE_ENROLLMENT_DISABLE_NEW_PROFILE_MGMT
);
609 profiles::DisableNewProfileManagementPreview();
610 } else if (sender
== end_preview_cancel_button_
) {
611 tutorial_mode_
= TUTORIAL_MODE_SEND_FEEDBACK
;
612 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER
, avatar_menu_
.get());
613 } else if (current_profile_photo_
&&
614 sender
== current_profile_photo_
->change_photo_button()) {
615 avatar_menu_
->EditProfile(avatar_menu_
->GetActiveProfileIndex());
616 } else if (sender
== signin_current_profile_link_
) {
617 // Only show the inline signin if the new UI flag is flipped. Otherwise,
618 // use the tab signin page.
619 if (switches::IsNewProfileManagement())
620 ShowView(BUBBLE_VIEW_MODE_GAIA_SIGNIN
, avatar_menu_
.get());
622 chrome::ShowBrowserSignin(browser_
, signin::SOURCE_MENU
);
624 // Either one of the "other profiles", or one of the profile accounts
625 // buttons was pressed.
626 ButtonIndexes::const_iterator profile_match
=
627 open_other_profile_indexes_map_
.find(sender
);
628 if (profile_match
!= open_other_profile_indexes_map_
.end()) {
629 avatar_menu_
->SwitchToProfile(
630 profile_match
->second
,
631 ui::DispositionFromEventFlags(event
.flags()) == NEW_WINDOW
,
632 ProfileMetrics::SWITCH_PROFILE_ICON
);
634 // This was a profile accounts button.
635 AccountButtonIndexes::const_iterator account_match
=
636 current_profile_accounts_map_
.find(sender
);
637 DCHECK(account_match
!= current_profile_accounts_map_
.end());
638 account_id_to_remove_
= account_match
->second
;
639 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL
, avatar_menu_
.get());
644 void ProfileChooserView::RemoveAccount() {
645 DCHECK(!account_id_to_remove_
.empty());
646 MutableProfileOAuth2TokenService
* oauth2_token_service
=
647 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
648 browser_
->profile());
649 if (oauth2_token_service
)
650 oauth2_token_service
->RevokeCredentials(account_id_to_remove_
);
651 account_id_to_remove_
.clear();
653 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
, avatar_menu_
.get());
656 void ProfileChooserView::LinkClicked(views::Link
* sender
, int event_flags
) {
657 if (sender
== manage_accounts_link_
) {
658 // This link can either mean show/hide the account management view,
659 // depending on which view it is displayed. ShowView() will DCHECK if
660 // the account management view is displayed for non signed-in users.
662 view_mode_
== BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
?
663 BUBBLE_VIEW_MODE_PROFILE_CHOOSER
:
664 BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
,
666 } else if (sender
== add_account_link_
) {
667 ShowView(BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT
, avatar_menu_
.get());
668 } else if (sender
== tutorial_learn_more_link_
) {
669 ProfileMetrics::LogProfileUpgradeEnrollment(
670 ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE
);
671 // TODO(guohui): update |learn_more_url| once it is decided.
672 const GURL
lear_more_url("https://support.google.com/chrome/?hl=en#to");
673 chrome::NavigateParams
params(
676 content::PAGE_TRANSITION_LINK
);
677 params
.disposition
= NEW_FOREGROUND_TAB
;
678 chrome::Navigate(¶ms
);
680 DCHECK(sender
== tutorial_end_preview_link_
);
681 ShowView(BUBBLE_VIEW_MODE_END_PREVIEW
, avatar_menu_
.get());
685 void ProfileChooserView::StyledLabelLinkClicked(
686 const gfx::Range
& range
, int event_flags
) {
687 chrome::ShowSettings(browser_
);
690 bool ProfileChooserView::HandleKeyEvent(views::Textfield
* sender
,
691 const ui::KeyEvent
& key_event
) {
692 views::Textfield
* name_textfield
=
693 current_profile_name_
->profile_name_textfield();
694 DCHECK(sender
== name_textfield
);
696 if (key_event
.key_code() == ui::VKEY_RETURN
||
697 key_event
.key_code() == ui::VKEY_TAB
) {
698 // Pressing Tab/Enter commits the new profile name, unless it's empty.
699 base::string16 new_profile_name
= name_textfield
->text();
700 if (new_profile_name
.empty())
703 const AvatarMenu::Item
& active_item
= avatar_menu_
->GetItemAt(
704 avatar_menu_
->GetActiveProfileIndex());
705 Profile
* profile
= g_browser_process
->profile_manager()->GetProfile(
706 active_item
.profile_path
);
709 if (profile
->IsManaged())
712 profiles::UpdateProfileName(profile
, new_profile_name
);
713 current_profile_name_
->ShowReadOnlyView();
719 views::View
* ProfileChooserView::CreateProfileChooserView(
720 AvatarMenu
* avatar_menu
,
721 TutorialMode last_tutorial_mode
) {
722 // TODO(guohui, noms): the view should be customized based on whether new
723 // profile management preview is enabled or not.
725 views::View
* view
= new views::View();
726 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
727 // Separate items into active and alternatives.
728 Indexes other_profiles
;
729 views::View
* tutorial_view
= NULL
;
730 views::View
* current_profile_view
= NULL
;
731 views::View
* current_profile_accounts
= NULL
;
732 views::View
* option_buttons_view
= NULL
;
733 bool is_new_profile_management
= switches::IsNewProfileManagement();
734 for (size_t i
= 0; i
< avatar_menu
->GetNumberOfItems(); ++i
) {
735 const AvatarMenu::Item
& item
= avatar_menu
->GetItemAt(i
);
737 option_buttons_view
= CreateOptionsView(item
.signed_in
);
738 current_profile_view
= CreateCurrentProfileView(item
, false);
739 if (view_mode_
== BUBBLE_VIEW_MODE_PROFILE_CHOOSER
) {
740 if (is_new_profile_management
) {
741 tutorial_view
= last_tutorial_mode
== TUTORIAL_MODE_SEND_FEEDBACK
?
742 CreateSendPreviewFeedbackView() :
743 CreatePreviewEnabledTutorialView(
744 item
, last_tutorial_mode
== TUTORIAL_MODE_PREVIEW_ENABLED
);
746 tutorial_view
= CreateNewProfileManagementPreviewView();
749 current_profile_accounts
= CreateCurrentProfileAccountsView(item
);
752 other_profiles
.push_back(i
);
757 // Be sure not to track the tutorial display on View refresh, and only count
758 // the preview-promo view, shown when New Profile Management is off.
759 if (tutorial_mode_
!= last_tutorial_mode
&& !is_new_profile_management
) {
760 ProfileMetrics::LogProfileUpgradeEnrollment(
761 ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO
);
763 layout
->StartRow(1, 0);
764 layout
->AddView(tutorial_view
);
767 if (!current_profile_view
) {
768 // Guest windows don't have an active profile.
769 current_profile_view
= CreateGuestProfileView();
770 option_buttons_view
= CreateOptionsView(false);
773 layout
->StartRow(1, 0);
774 layout
->AddView(current_profile_view
);
776 if (view_mode_
== BUBBLE_VIEW_MODE_PROFILE_CHOOSER
) {
777 layout
->StartRow(1, 0);
778 if (switches::IsFastUserSwitching())
779 layout
->AddView(CreateOtherProfilesView(other_profiles
));
781 DCHECK(current_profile_accounts
);
782 layout
->StartRow(0, 0);
783 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
784 layout
->StartRow(1, 0);
785 layout
->AddView(current_profile_accounts
);
788 layout
->StartRow(0, 0);
789 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
791 // Option buttons. Only available with the new profile management flag.
792 if (option_buttons_view
) {
793 layout
->StartRow(0, 0);
794 layout
->AddView(option_buttons_view
);
800 views::View
* ProfileChooserView::CreatePreviewEnabledTutorialView(
801 const AvatarMenu::Item
& current_avatar_item
,
802 bool tutorial_shown
) {
803 if (!switches::IsNewProfileManagementPreviewEnabled())
806 Profile
* profile
= browser_
->profile();
807 const int show_count
= profile
->GetPrefs()->GetInteger(
808 prefs::kProfileAvatarTutorialShown
);
809 // Do not show the tutorial if user has dismissed it.
810 if (show_count
> kProfileAvatarTutorialShowMax
)
813 if (!tutorial_shown
) {
814 if (show_count
== kProfileAvatarTutorialShowMax
)
816 profile
->GetPrefs()->SetInteger(
817 prefs::kProfileAvatarTutorialShown
, show_count
+ 1);
820 return CreateTutorialView(
821 TUTORIAL_MODE_PREVIEW_ENABLED
,
822 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE
),
823 l10n_util::GetStringUTF16(
824 IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT
),
825 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
),
826 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON
),
827 &tutorial_learn_more_link_
,
828 &tutorial_ok_button_
);
831 views::View
* ProfileChooserView::CreateSendPreviewFeedbackView() {
832 return CreateTutorialView(
833 TUTORIAL_MODE_SEND_FEEDBACK
,
834 l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE
),
835 l10n_util::GetStringUTF16(
836 IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT
),
837 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW
),
838 l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON
),
839 &tutorial_end_preview_link_
,
840 &tutorial_send_feedback_button_
);
843 views::View
* ProfileChooserView::CreateTutorialView(
844 TutorialMode tutorial_mode
,
845 const base::string16
& title_text
,
846 const base::string16
& content_text
,
847 const base::string16
& link_text
,
848 const base::string16
& button_text
,
850 views::LabelButton
** button
) {
851 tutorial_mode_
= tutorial_mode
;
853 views::View
* view
= new views::View();
854 view
->set_background(views::Background::CreateSolidBackground(
855 profiles::kAvatarTutorialBackgroundColor
));
856 views::GridLayout
* layout
= CreateSingleColumnLayout(view
,
857 kFixedMenuWidth
- 2 * views::kButtonHEdgeMarginNew
);
858 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
859 views::kButtonHEdgeMarginNew
,
860 views::kButtonVEdgeMarginNew
,
861 views::kButtonHEdgeMarginNew
);
864 views::Label
* title_label
= new views::Label(title_text
);
865 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
866 title_label
->SetAutoColorReadabilityEnabled(false);
867 title_label
->SetEnabledColor(SK_ColorWHITE
);
868 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
869 ui::ResourceBundle::MediumFont
));
870 layout
->StartRow(1, 0);
871 layout
->AddView(title_label
);
873 // Adds body content.
874 views::Label
* content_label
= new views::Label(content_text
);
875 content_label
->SetMultiLine(true);
876 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
877 content_label
->SetAutoColorReadabilityEnabled(false);
878 content_label
->SetEnabledColor(profiles::kAvatarTutorialContentTextColor
);
879 layout
->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing
);
880 layout
->AddView(content_label
);
882 // Adds links and buttons.
883 views::View
* button_row
= new views::View();
884 views::GridLayout
* button_layout
= new views::GridLayout(button_row
);
885 views::ColumnSet
* button_columns
= button_layout
->AddColumnSet(0);
886 button_columns
->AddColumn(views::GridLayout::LEADING
,
887 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
888 button_columns
->AddPaddingColumn(
889 1, views::kUnrelatedControlHorizontalSpacing
);
890 button_columns
->AddColumn(views::GridLayout::TRAILING
,
891 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
892 button_row
->SetLayoutManager(button_layout
);
894 *link
= CreateLink(link_text
, this);
895 (*link
)->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
896 (*link
)->SetAutoColorReadabilityEnabled(false);
897 (*link
)->SetEnabledColor(SK_ColorWHITE
);
898 button_layout
->StartRow(1, 0);
899 button_layout
->AddView(*link
);
901 *button
= new views::LabelButton(this, button_text
);
902 (*button
)->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
903 (*button
)->SetStyle(views::Button::STYLE_BUTTON
);
904 button_layout
->AddView(*button
);
906 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
907 layout
->AddView(button_row
);
909 // Adds a padded caret image at the bottom.
910 views::View
* padded_caret_view
= new views::View();
911 views::GridLayout
* padded_caret_layout
=
912 new views::GridLayout(padded_caret_view
);
913 views::ColumnSet
* padded_columns
= padded_caret_layout
->AddColumnSet(0);
914 padded_columns
->AddPaddingColumn(0, views::kButtonHEdgeMarginNew
);
915 padded_columns
->AddColumn(views::GridLayout::LEADING
,
916 views::GridLayout::CENTER
, 0, views::GridLayout::USE_PREF
, 0, 0);
917 padded_caret_view
->SetLayoutManager(padded_caret_layout
);
919 views::ImageView
* caret_image_view
= new views::ImageView();
920 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
921 caret_image_view
->SetImage(
922 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET
));
924 padded_caret_layout
->StartRow(1, 0);
925 padded_caret_layout
->AddView(caret_image_view
);
927 views::View
* view_with_caret
= new views::View();
928 views::GridLayout
* layout_with_caret
=
929 CreateSingleColumnLayout(view_with_caret
, kFixedMenuWidth
);
930 layout_with_caret
->StartRow(1, 0);
931 layout_with_caret
->AddView(view
);
932 layout_with_caret
->StartRow(1, 0);
933 layout_with_caret
->AddView(padded_caret_view
);
934 return view_with_caret
;
937 views::View
* ProfileChooserView::CreateCurrentProfileView(
938 const AvatarMenu::Item
& avatar_item
,
940 views::View
* view
= new views::View();
941 int column_width
= kFixedMenuWidth
- 2 * views::kButtonHEdgeMarginNew
;
942 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, column_width
);
943 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
944 views::kButtonHEdgeMarginNew
,
945 views::kUnrelatedControlVerticalSpacing
,
946 views::kButtonHEdgeMarginNew
);
948 // Profile icon, centered.
949 float x_offset
= (column_width
- kLargeImageSide
) / 2;
950 current_profile_photo_
= new EditableProfilePhoto(
951 this, avatar_item
.icon
, !is_guest
,
952 gfx::Rect(x_offset
, 0, kLargeImageSide
, kLargeImageSide
));
954 if (switches::IsNewProfileManagementPreviewEnabled()) {
955 question_mark_button_
= new views::ImageButton(this);
956 question_mark_button_
->SetImageAlignment(
957 views::ImageButton::ALIGN_LEFT
, views::ImageButton::ALIGN_MIDDLE
);
958 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
959 question_mark_button_
->SetImage(views::ImageButton::STATE_NORMAL
,
960 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE
));
961 question_mark_button_
->SetImage(views::ImageButton::STATE_HOVERED
,
962 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER
));
963 question_mark_button_
->SetImage(views::ImageButton::STATE_PRESSED
,
964 rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT
));
965 gfx::Size preferred_size
= question_mark_button_
->GetPreferredSize();
966 question_mark_button_
->SetBounds(
967 0, 0, preferred_size
.width(), preferred_size
.height());
968 current_profile_photo_
->AddChildView(question_mark_button_
);
971 layout
->StartRow(1, 0);
972 layout
->AddView(current_profile_photo_
);
974 // Profile name, centered.
975 current_profile_name_
= new EditableProfileName(
976 this, profiles::GetAvatarNameForProfile(browser_
->profile()), !is_guest
);
977 layout
->StartRow(1, 0);
978 layout
->AddView(current_profile_name_
);
983 // The available links depend on the type of profile that is active.
984 layout
->StartRow(1, 0);
985 if (avatar_item
.signed_in
) {
986 if (switches::IsNewProfileManagement()) {
987 base::string16 link_title
= l10n_util::GetStringUTF16(
988 view_mode_
== BUBBLE_VIEW_MODE_PROFILE_CHOOSER
?
989 IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON
:
990 IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON
);
991 manage_accounts_link_
= CreateLink(link_title
, this);
992 manage_accounts_link_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
993 layout
->AddView(manage_accounts_link_
);
995 views::Label
* email_label
= new views::Label(avatar_item
.sync_state
);
996 email_label
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
997 layout
->AddView(email_label
);
1000 signin_current_profile_link_
= new views::BlueButton(
1001 this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL
,
1002 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
)));
1003 layout
->AddView(signin_current_profile_link_
);
1009 views::View
* ProfileChooserView::CreateGuestProfileView() {
1010 gfx::Image guest_icon
=
1011 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1012 profiles::GetPlaceholderAvatarIconResourceID());
1013 AvatarMenu::Item
guest_avatar_item(0, 0, guest_icon
);
1014 guest_avatar_item
.active
= true;
1015 guest_avatar_item
.name
= l10n_util::GetStringUTF16(
1016 IDS_PROFILES_GUEST_PROFILE_NAME
);
1017 guest_avatar_item
.signed_in
= false;
1019 return CreateCurrentProfileView(guest_avatar_item
, true);
1022 views::View
* ProfileChooserView::CreateOtherProfilesView(
1023 const Indexes
& avatars_to_show
) {
1024 views::View
* view
= new views::View();
1025 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1027 int num_avatars_to_show
= avatars_to_show
.size();
1028 for (int i
= 0; i
< num_avatars_to_show
; ++i
) {
1029 const size_t index
= avatars_to_show
[i
];
1030 const AvatarMenu::Item
& item
= avatar_menu_
->GetItemAt(index
);
1031 const int kSmallImageSide
= 32;
1033 gfx::Image image
= profiles::GetSizedAvatarIcon(
1034 item
.icon
, true, kSmallImageSide
, kSmallImageSide
);
1036 views::LabelButton
* button
= new BackgroundColorHoverButton(
1039 *image
.ToImageSkia(),
1040 *image
.ToImageSkia());
1041 button
->set_min_size(gfx::Size(
1042 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1044 open_other_profile_indexes_map_
[button
] = index
;
1046 layout
->StartRow(1, 0);
1047 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
1048 layout
->StartRow(1, 0);
1049 layout
->AddView(button
);
1055 views::View
* ProfileChooserView::CreateOptionsView(bool enable_lock
) {
1056 if (!switches::IsNewProfileManagement())
1059 views::View
* view
= new views::View();
1060 views::GridLayout
* layout
;
1062 // Only signed-in users have the ability to lock.
1064 layout
= new views::GridLayout(view
);
1065 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
1066 int width_of_lock_button
=
1067 2 * views::kUnrelatedControlLargeHorizontalSpacing
+ 12;
1068 int width_of_users_button
= kFixedMenuWidth
- width_of_lock_button
;
1069 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
1070 views::GridLayout::FIXED
, width_of_users_button
,
1071 width_of_users_button
);
1072 columns
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 0,
1073 views::GridLayout::FIXED
, width_of_lock_button
,
1074 width_of_lock_button
);
1075 view
->SetLayoutManager(layout
);
1077 layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1080 base::string16 text
= browser_
->profile()->IsGuestSession() ?
1081 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST
) :
1082 l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON
,
1083 profiles::GetAvatarNameForProfile(browser_
->profile()));
1084 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1085 users_button_
= new BackgroundColorHoverButton(
1088 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR
),
1089 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR
));
1090 users_button_
->set_min_size(gfx::Size(
1091 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1093 layout
->StartRow(1, 0);
1094 layout
->AddView(users_button_
);
1097 lock_button_
= new BackgroundColorHoverButton(
1100 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK
),
1101 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK
));
1102 lock_button_
->set_min_size(gfx::Size(
1103 0, kButtonHeight
+ views::kRelatedControlVerticalSpacing
));
1104 layout
->AddView(lock_button_
);
1109 views::View
* ProfileChooserView::CreateCurrentProfileAccountsView(
1110 const AvatarMenu::Item
& avatar_item
) {
1111 DCHECK(avatar_item
.signed_in
);
1112 views::View
* view
= new views::View();
1113 view
->set_background(views::Background::CreateSolidBackground(
1114 profiles::kAvatarBubbleAccountsBackgroundColor
));
1115 views::GridLayout
* layout
= CreateSingleColumnLayout(view
, kFixedMenuWidth
);
1117 Profile
* profile
= browser_
->profile();
1118 std::string primary_account
=
1119 SigninManagerFactory::GetForProfile(profile
)->GetAuthenticatedUsername();
1120 DCHECK(!primary_account
.empty());
1121 std::vector
<std::string
>accounts
=
1122 profiles::GetSecondaryAccountsForProfile(profile
, primary_account
);
1124 // The primary account should always be listed first.
1125 // TODO(rogerta): we still need to further differentiate the primary account
1126 // from the others in the UI, so more work is likely required here:
1127 // crbug.com/311124.
1128 CreateAccountButton(layout
, primary_account
, true, kFixedMenuWidth
);
1129 for (size_t i
= 0; i
< accounts
.size(); ++i
)
1130 CreateAccountButton(layout
, accounts
[i
], false, kFixedMenuWidth
);
1131 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
1133 add_account_link_
= CreateLink(l10n_util::GetStringFUTF16(
1134 IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON
, avatar_item
.name
), this);
1135 add_account_link_
->SetBorder(views::Border::CreateEmptyBorder(
1136 0, views::kButtonVEdgeMarginNew
,
1137 views::kRelatedControlVerticalSpacing
, 0));
1138 layout
->StartRow(1, 0);
1139 layout
->AddView(add_account_link_
);
1143 void ProfileChooserView::CreateAccountButton(views::GridLayout
* layout
,
1144 const std::string
& account
,
1145 bool is_primary_account
,
1147 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1148 const gfx::ImageSkia
* default_image
=
1149 rb
->GetImageNamed(IDR_CLOSE_1
).ToImageSkia();
1150 int kDeleteButtonWidth
= default_image
->width();
1151 int available_width
= width
-
1152 kDeleteButtonWidth
- views::kButtonHEdgeMarginNew
;
1154 views::LabelButton
* email_button
= new BackgroundColorHoverButton(
1156 gfx::ElideEmail(base::UTF8ToUTF16(account
),
1157 rb
->GetFontList(ui::ResourceBundle::BaseFont
),
1161 layout
->StartRow(1, 0);
1162 layout
->AddView(email_button
);
1165 views::ImageButton
* delete_button
= new views::ImageButton(this);
1166 delete_button
->SetImageAlignment(views::ImageButton::ALIGN_RIGHT
,
1167 views::ImageButton::ALIGN_MIDDLE
);
1168 delete_button
->SetImage(views::ImageButton::STATE_NORMAL
,
1170 delete_button
->SetImage(views::ImageButton::STATE_HOVERED
,
1171 rb
->GetImageSkiaNamed(IDR_CLOSE_1_H
));
1172 delete_button
->SetImage(views::ImageButton::STATE_PRESSED
,
1173 rb
->GetImageSkiaNamed(IDR_CLOSE_1_P
));
1174 delete_button
->SetBounds(
1175 available_width
, 0, kDeleteButtonWidth
, kButtonHeight
);
1177 email_button
->set_notify_enter_exit_on_child(true);
1178 email_button
->AddChildView(delete_button
);
1180 // Save the original email address, as the button text could be elided.
1181 current_profile_accounts_map_
[delete_button
] = account
;
1184 views::View
* ProfileChooserView::CreateGaiaSigninView(
1185 bool add_secondary_account
) {
1186 // Adds Gaia signin webview
1187 Profile
* profile
= browser_
->profile();
1188 views::WebView
* web_view
= new views::WebView(profile
);
1189 signin::Source source
= add_secondary_account
?
1190 signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
:
1191 signin::SOURCE_AVATAR_BUBBLE_SIGN_IN
;
1192 GURL
url(signin::GetPromoURL(
1193 source
, false /* auto_close */, true /* is_constrained */));
1194 web_view
->LoadInitialURL(url
);
1195 web_view
->SetPreferredSize(
1196 gfx::Size(kFixedGaiaViewWidth
, kFixedGaiaViewHeight
));
1198 TitleCard
* title_card
= new TitleCard(
1199 add_secondary_account
? IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE
:
1200 IDS_PROFILES_GAIA_SIGNIN_TITLE
,
1201 this, &gaia_signin_cancel_button_
);
1202 return TitleCard::AddPaddedTitleCard(
1203 web_view
, title_card
, kFixedGaiaViewWidth
);
1206 views::View
* ProfileChooserView::CreateAccountRemovalView() {
1207 views::View
* view
= new views::View();
1208 views::GridLayout
* layout
= CreateSingleColumnLayout(
1209 view
, kFixedAccountRemovalViewWidth
- 2 * views::kButtonHEdgeMarginNew
);
1210 layout
->SetInsets(0,
1211 views::kButtonHEdgeMarginNew
,
1212 views::kButtonVEdgeMarginNew
,
1213 views::kButtonHEdgeMarginNew
);
1215 const std::string
& primary_account
= SigninManagerFactory::GetForProfile(
1216 browser_
->profile())->GetAuthenticatedUsername();
1217 bool is_primary_account
= primary_account
== account_id_to_remove_
;
1220 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1221 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1222 const gfx::FontList
& small_font_list
=
1223 rb
->GetFontList(ui::ResourceBundle::SmallFont
);
1225 if (is_primary_account
) {
1226 std::vector
<size_t> offsets
;
1227 const base::string16 settings_text
=
1228 l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK
);
1229 const base::string16 primary_account_removal_text
=
1230 l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT
,
1231 base::UTF8ToUTF16(account_id_to_remove_
), settings_text
, &offsets
);
1232 views::StyledLabel
* primary_account_removal_label
=
1233 new views::StyledLabel(primary_account_removal_text
, this);
1234 primary_account_removal_label
->AddStyleRange(
1235 gfx::Range(offsets
[1], offsets
[1] + settings_text
.size()),
1236 views::StyledLabel::RangeStyleInfo::CreateForLink());
1237 primary_account_removal_label
->SetBaseFontList(small_font_list
);
1238 layout
->AddView(primary_account_removal_label
);
1240 views::Label
* content_label
= new views::Label(
1241 l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT
));
1242 content_label
->SetMultiLine(true);
1243 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
1244 content_label
->SetFontList(small_font_list
);
1245 layout
->AddView(content_label
);
1249 if (!is_primary_account
) {
1250 remove_account_button_
= new views::BlueButton(
1251 this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON
));
1252 remove_account_button_
->SetHorizontalAlignment(
1254 layout
->StartRowWithPadding(
1255 1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1256 layout
->AddView(remove_account_button_
);
1258 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
1261 TitleCard
* title_card
= new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE
,
1262 this, &account_removal_cancel_button_
);
1263 return TitleCard::AddPaddedTitleCard(view
, title_card
,
1264 kFixedAccountRemovalViewWidth
);
1267 views::View
* ProfileChooserView::CreateNewProfileManagementPreviewView() {
1268 return CreateTutorialView(
1269 TUTORIAL_MODE_ENABLE_PREVIEW
,
1270 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE
),
1271 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT
),
1272 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
),
1273 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON
),
1274 &tutorial_learn_more_link_
,
1275 &tutorial_enable_new_profile_management_button_
);
1278 views::View
* ProfileChooserView::CreateEndPreviewView() {
1279 views::View
* view
= new views::View();
1280 views::GridLayout
* layout
= CreateSingleColumnLayout(
1281 view
, kFixedAccountRemovalViewWidth
- 2 * views::kButtonHEdgeMarginNew
);
1282 layout
->SetInsets(0,
1283 views::kButtonHEdgeMarginNew
,
1284 views::kButtonVEdgeMarginNew
,
1285 views::kButtonHEdgeMarginNew
);
1288 views::Label
* content_label
= new views::Label(
1289 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT
));
1290 content_label
->SetMultiLine(true);
1291 content_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
1292 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
1293 const gfx::FontList
& small_font_list
=
1294 rb
->GetFontList(ui::ResourceBundle::SmallFont
);
1295 content_label
->SetFontList(small_font_list
);
1296 layout
->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1297 layout
->AddView(content_label
);
1300 end_preview_and_relaunch_button_
= new views::BlueButton(
1301 this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH
));
1302 end_preview_and_relaunch_button_
->SetHorizontalAlignment(
1304 layout
->StartRowWithPadding(
1305 1, 0, 0, views::kUnrelatedControlVerticalSpacing
);
1306 layout
->AddView(end_preview_and_relaunch_button_
);
1308 TitleCard
* title_card
= new TitleCard(
1309 IDS_PROFILES_END_PREVIEW
, this, &end_preview_cancel_button_
);
1310 return TitleCard::AddPaddedTitleCard(
1311 view
, title_card
, kFixedAccountRemovalViewWidth
);