Commit 1a5e0788 authored by Thomas Tangl's avatar Thomas Tangl Committed by Commit Bot

[profile-menu] Add account feature buttons

Account feature buttons are added at the bottom
of the bordered box.

Screenshot:
https://drive.google.com/file/d/17tsNrV3HmOZD9yVNp6VwfbJ46GOAuvQu/view?usp=sharing

Flag: profile-menu-revamp

Bug: 995720
Change-Id: Icbd286cdf388742d710688e6c93cd44d1ad16e9a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1803438
Commit-Queue: Thomas Tangl <tangltom@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#696737}
parent 1512a4cd
...@@ -170,6 +170,7 @@ void ProfileMenuView::BuildMenu() { ...@@ -170,6 +170,7 @@ void ProfileMenuView::BuildMenu() {
} }
BuildIdentity(); BuildIdentity();
BuildAutofillButtons(); BuildAutofillButtons();
BuildAccountFeatureButtons();
BuildSelectableProfiles(); BuildSelectableProfiles();
} }
...@@ -199,8 +200,14 @@ void ProfileMenuView::OnManageGoogleAccountButtonClicked() { ...@@ -199,8 +200,14 @@ void ProfileMenuView::OnManageGoogleAccountButtonClicked() {
// TODO(crbug.com/995757): Remove user action. // TODO(crbug.com/995757): Remove user action.
base::RecordAction( base::RecordAction(
base::UserMetricsAction("ProfileChooser_ManageGoogleAccountClicked")); base::UserMetricsAction("ProfileChooser_ManageGoogleAccountClicked"));
DCHECK(!dice_accounts_.empty());
NavigateToGoogleAccountPage(browser()->profile(), dice_accounts_[0].email); Profile* profile = browser()->profile();
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
DCHECK(identity_manager->HasUnconsentedPrimaryAccount());
NavigateToGoogleAccountPage(
profile, identity_manager->GetUnconsentedPrimaryAccountInfo().email);
} }
void ProfileMenuView::OnPasswordsButtonClicked() { void ProfileMenuView::OnPasswordsButtonClicked() {
...@@ -414,6 +421,34 @@ void ProfileMenuView::BuildAutofillButtons() { ...@@ -414,6 +421,34 @@ void ProfileMenuView::BuildAutofillButtons() {
base::Unretained(this))); base::Unretained(this)));
} }
void ProfileMenuView::BuildAccountFeatureButtons() {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(browser()->profile());
if (!identity_manager->HasUnconsentedPrimaryAccount())
return;
AddAccountFeatureButton(
#if defined(GOOGLE_CHROME_BUILD)
// The Google G icon needs to be shrunk, so it won't look too big
// compared to the other icons.
ImageForMenu(kGoogleGLogoIcon, /*icon_to_image_ratio=*/0.75),
#else
gfx::ImageSkia(),
#endif
l10n_util::GetStringUTF16(IDS_SETTINGS_MANAGE_GOOGLE_ACCOUNT),
base::BindRepeating(&ProfileMenuView::OnManageGoogleAccountButtonClicked,
base::Unretained(this)));
if (!identity_manager->HasPrimaryAccount()) {
// The sign-out button is only shown when sync is off.
AddAccountFeatureButton(
ImageForMenu(kSignOutIcon),
l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT),
base::BindRepeating(&ProfileMenuView::OnSignoutButtonClicked,
base::Unretained(this)));
}
}
void ProfileMenuView::BuildSelectableProfiles() { void ProfileMenuView::BuildSelectableProfiles() {
auto profile_entries = g_browser_process->profile_manager() auto profile_entries = g_browser_process->profile_manager()
->GetProfileAttributesStorage() ->GetProfileAttributesStorage()
......
...@@ -100,6 +100,7 @@ class ProfileMenuView : public ProfileMenuViewBase, public AvatarMenuObserver { ...@@ -100,6 +100,7 @@ class ProfileMenuView : public ProfileMenuViewBase, public AvatarMenuObserver {
// Helper methods for building the menu. // Helper methods for building the menu.
void BuildIdentity(); void BuildIdentity();
void BuildAutofillButtons(); void BuildAutofillButtons();
void BuildAccountFeatureButtons();
void BuildSelectableProfiles(); void BuildSelectableProfiles();
// Adds the profile chooser view. // Adds the profile chooser view.
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/accessibility/view_accessibility.h" #include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/button/md_text_button.h"
...@@ -47,6 +49,7 @@ ProfileMenuViewBase* g_profile_bubble_ = nullptr; ...@@ -47,6 +49,7 @@ ProfileMenuViewBase* g_profile_bubble_ = nullptr;
constexpr int kMenuWidth = 288; constexpr int kMenuWidth = 288;
constexpr int kIconSize = 16; constexpr int kIconSize = 16;
constexpr int kIdentityImageSize = 64; constexpr int kIdentityImageSize = 64;
constexpr int kMaxImageSize = kIdentityImageSize;
// If the bubble is too large to fit on the screen, it still needs to be at // If the bubble is too large to fit on the screen, it still needs to be at
// least this tall to show one row. // least this tall to show one row.
...@@ -56,6 +59,15 @@ constexpr int kMinimumScrollableContentHeight = 40; ...@@ -56,6 +59,15 @@ constexpr int kMinimumScrollableContentHeight = 40;
// the menu items. // the menu items.
constexpr int kMenuEdgeMargin = 16; constexpr int kMenuEdgeMargin = 16;
gfx::ImageSkia SizeImage(const gfx::ImageSkia& image, int size) {
return gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_BEST, gfx::Size(size, size));
}
gfx::ImageSkia ColorImage(const gfx::ImageSkia& image, SkColor color) {
return gfx::ImageSkiaOperations::CreateColorMask(image, color);
}
std::unique_ptr<views::BoxLayout> CreateBoxLayout( std::unique_ptr<views::BoxLayout> CreateBoxLayout(
views::BoxLayout::Orientation orientation, views::BoxLayout::Orientation orientation,
views::BoxLayout::CrossAxisAlignment cross_axis_alignment) { views::BoxLayout::CrossAxisAlignment cross_axis_alignment) {
...@@ -219,7 +231,7 @@ void ProfileMenuViewBase::AddShortcutFeatureButton( ...@@ -219,7 +231,7 @@ void ProfileMenuViewBase::AddShortcutFeatureButton(
const gfx::VectorIcon& icon, const gfx::VectorIcon& icon,
const base::string16& text, const base::string16& text,
base::RepeatingClosure action) { base::RepeatingClosure action) {
constexpr int kTopMargin = 8; constexpr int kVerticalMargin = 8;
constexpr int kButtonSpacing = 6; constexpr int kButtonSpacing = 6;
constexpr int kIconSize = 16; constexpr int kIconSize = 16;
constexpr int kIconPadding = 6; constexpr int kIconPadding = 6;
...@@ -235,7 +247,7 @@ void ProfileMenuViewBase::AddShortcutFeatureButton( ...@@ -235,7 +247,7 @@ void ProfileMenuViewBase::AddShortcutFeatureButton(
views::BoxLayout* layout = shortcut_features_container_->SetLayoutManager( views::BoxLayout* layout = shortcut_features_container_->SetLayoutManager(
std::make_unique<views::BoxLayout>( std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(kTopMargin, 0, 0, 0), kButtonSpacing)); gfx::Insets(kVerticalMargin, 0), kButtonSpacing));
layout->set_main_axis_alignment( layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter); views::BoxLayout::MainAxisAlignment::kCenter);
} }
...@@ -255,6 +267,32 @@ void ProfileMenuViewBase::AddShortcutFeatureButton( ...@@ -255,6 +267,32 @@ void ProfileMenuViewBase::AddShortcutFeatureButton(
RegisterClickAction(button, std::move(action)); RegisterClickAction(button, std::move(action));
} }
void ProfileMenuViewBase::AddAccountFeatureButton(
const gfx::ImageSkia& icon,
const base::string16& text,
base::RepeatingClosure action) {
constexpr int kIconSize = 16;
const SkColor kIconColor =
ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
ui::NativeTheme::kColorId_DefaultIconColor);
// Initialize layout if this is the first time a button is added.
if (!account_features_container_->GetLayoutManager()) {
account_features_container_->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
}
account_features_container_->AddChildView(
std::make_unique<views::Separator>());
views::Button* button =
account_features_container_->AddChildView(std::make_unique<HoverButton>(
this, SizeImage(ColorImage(icon, kIconColor), kIconSize), text));
RegisterClickAction(button, std::move(action));
}
void ProfileMenuViewBase::AddSelectableProfile(const gfx::Image& image, void ProfileMenuViewBase::AddSelectableProfile(const gfx::Image& image,
const base::string16& name, const base::string16& name,
base::RepeatingClosure action) { base::RepeatingClosure action) {
...@@ -277,6 +315,19 @@ void ProfileMenuViewBase::AddSelectableProfile(const gfx::Image& image, ...@@ -277,6 +315,19 @@ void ProfileMenuViewBase::AddSelectableProfile(const gfx::Image& image,
RegisterClickAction(button, std::move(action)); RegisterClickAction(button, std::move(action));
} }
gfx::ImageSkia ProfileMenuViewBase::ImageForMenu(const gfx::VectorIcon& icon,
float icon_to_image_ratio) {
const SkColor kIconColor =
ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
ui::NativeTheme::kColorId_DefaultIconColor);
const int padding =
static_cast<int>(kMaxImageSize * (1.0 - icon_to_image_ratio) / 2.0);
auto sized_icon =
gfx::CreateVectorIcon(icon, kMaxImageSize - 2 * padding, kIconColor);
return gfx::CanvasImageSource::CreatePadded(sized_icon, gfx::Insets(padding));
}
ax::mojom::Role ProfileMenuViewBase::GetAccessibleWindowRole() { ax::mojom::Role ProfileMenuViewBase::GetAccessibleWindowRole() {
// Return |ax::mojom::Role::kDialog| which will make screen readers announce // Return |ax::mojom::Role::kDialog| which will make screen readers announce
// the following in the listed order: // the following in the listed order:
...@@ -368,6 +419,8 @@ void ProfileMenuViewBase::Reset() { ...@@ -368,6 +419,8 @@ void ProfileMenuViewBase::Reset() {
bordered_box_container->AddChildView(std::make_unique<views::View>()); bordered_box_container->AddChildView(std::make_unique<views::View>());
shortcut_features_container_ = shortcut_features_container_ =
bordered_box_container->AddChildView(std::make_unique<views::View>()); bordered_box_container->AddChildView(std::make_unique<views::View>());
account_features_container_ =
bordered_box_container->AddChildView(std::make_unique<views::View>());
components->AddChildView( components->AddChildView(
CreateBorderedBoxView(std::move(bordered_box_container))); CreateBorderedBoxView(std::move(bordered_box_container)));
......
...@@ -100,9 +100,17 @@ class ProfileMenuViewBase : public content::WebContentsDelegate, ...@@ -100,9 +100,17 @@ class ProfileMenuViewBase : public content::WebContentsDelegate,
void AddShortcutFeatureButton(const gfx::VectorIcon& icon, void AddShortcutFeatureButton(const gfx::VectorIcon& icon,
const base::string16& text, const base::string16& text,
base::RepeatingClosure action); base::RepeatingClosure action);
void AddAccountFeatureButton(const gfx::ImageSkia& icon,
const base::string16& text,
base::RepeatingClosure action);
void AddSelectableProfile(const gfx::Image& image, void AddSelectableProfile(const gfx::Image& image,
const base::string16& name, const base::string16& name,
base::RepeatingClosure action); base::RepeatingClosure action);
// 0 < |icon_to_image_ratio| <= 1 is the size ratio of |icon| in the returned
// image. E.g. a value of 0.8 means that |icon| only takes up 80% of the
// returned image, with the rest being padding around it.
gfx::ImageSkia ImageForMenu(const gfx::VectorIcon& icon,
float icon_to_image_ratio = 1.0f);
// Initializes a new group of menu items. A separator is added before them if // Initializes a new group of menu items. A separator is added before them if
// |add_separator| is true. // |add_separator| is true.
...@@ -203,6 +211,7 @@ class ProfileMenuViewBase : public content::WebContentsDelegate, ...@@ -203,6 +211,7 @@ class ProfileMenuViewBase : public content::WebContentsDelegate,
// Component containers. // Component containers.
views::View* identity_info_container_ = nullptr; views::View* identity_info_container_ = nullptr;
views::View* shortcut_features_container_ = nullptr; views::View* shortcut_features_container_ = nullptr;
views::View* account_features_container_ = nullptr;
views::View* selectable_profiles_container_ = nullptr; views::View* selectable_profiles_container_ = nullptr;
CloseBubbleOnTabActivationHelper close_bubble_helper_; CloseBubbleOnTabActivationHelper close_bubble_helper_;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/scoped_account_consistency.h" #include "chrome/browser/signin/scoped_account_consistency.h"
#include "chrome/browser/sync/test/integration/secondary_account_helper.h"
#include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_finder.h"
...@@ -48,6 +49,7 @@ ...@@ -48,6 +49,7 @@
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "services/network/test/test_url_loader_factory.h"
#include "ui/events/event_utils.h" #include "ui/events/event_utils.h"
#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/webview/webview.h" #include "ui/views/controls/webview/webview.h"
...@@ -709,10 +711,11 @@ class ProfileMenuClickTest_WithPrimaryAccount : public ProfileMenuClickTest { ...@@ -709,10 +711,11 @@ class ProfileMenuClickTest_WithPrimaryAccount : public ProfileMenuClickTest {
public: public:
// List of actionable items in the correct order as they appear in the menu. // List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list. // If a new button is added to the menu, it should also be added to this list.
static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[4] = static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[5] =
{ProfileMenuView::ActionableItem::kPasswordsButton, {ProfileMenuView::ActionableItem::kPasswordsButton,
ProfileMenuView::ActionableItem::kCreditCardsButton, ProfileMenuView::ActionableItem::kCreditCardsButton,
ProfileMenuView::ActionableItem::kAddressesButton, ProfileMenuView::ActionableItem::kAddressesButton,
ProfileMenuView::ActionableItem::kManageGoogleAccountButton,
// The first button is added again to finish the cycle and test that // The first button is added again to finish the cycle and test that
// there are no other buttons at the end. // there are no other buttons at the end.
ProfileMenuView::ActionableItem::kPasswordsButton}; ProfileMenuView::ActionableItem::kPasswordsButton};
...@@ -748,3 +751,74 @@ INSTANTIATE_TEST_SUITE_P( ...@@ -748,3 +751,74 @@ INSTANTIATE_TEST_SUITE_P(
size_t(0), size_t(0),
base::size( base::size(
ProfileMenuClickTest_WithPrimaryAccount::kOrderedActionableItems))); ProfileMenuClickTest_WithPrimaryAccount::kOrderedActionableItems)));
class ProfileMenuClickTest_WithUnconsentedPrimaryAccount
: public ProfileMenuClickTest {
public:
// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[6] =
{ProfileMenuView::ActionableItem::kPasswordsButton,
ProfileMenuView::ActionableItem::kCreditCardsButton,
ProfileMenuView::ActionableItem::kAddressesButton,
ProfileMenuView::ActionableItem::kManageGoogleAccountButton,
ProfileMenuView::ActionableItem::kSignoutButton,
// The first button is added again to finish the cycle and test that
// there are no other buttons at the end.
ProfileMenuView::ActionableItem::kPasswordsButton};
ProfileMenuClickTest_WithUnconsentedPrimaryAccount() = default;
void SetUpInProcessBrowserTestFixture() override {
// This is required to support (fake) secondary-account-signin (based on
// cookies) in tests. Without this, the real GaiaCookieManagerService would
// try talking to Google servers which of course wouldn't work in tests.
test_signin_client_factory_ =
secondary_account_helper::SetUpSigninClient(&test_url_loader_factory_);
ProfileMenuClickTest::SetUpInProcessBrowserTestFixture();
}
ProfileMenuView::ActionableItem GetExpectedActionableItemAtIndex(
size_t index) override {
return kOrderedActionableItems[index];
}
void SetUnconsentedPrimaryAccount() {
signin::MakeAccountAvailableWithCookies(
IdentityManagerFactory::GetForProfile(browser()->profile()),
&test_url_loader_factory_, "account@example.com", "dummyId");
}
private:
secondary_account_helper::ScopedSigninClientFactory
test_signin_client_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(ProfileMenuClickTest_WithUnconsentedPrimaryAccount);
};
// static
constexpr ProfileMenuView::ActionableItem
ProfileMenuClickTest_WithUnconsentedPrimaryAccount::kOrderedActionableItems
[];
IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_WithUnconsentedPrimaryAccount,
SetupAndRunTest) {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(browser()->profile());
ASSERT_FALSE(identity_manager->HasUnconsentedPrimaryAccount());
SetUnconsentedPrimaryAccount();
ASSERT_FALSE(identity_manager->HasPrimaryAccount());
ASSERT_TRUE(identity_manager->HasUnconsentedPrimaryAccount());
RunTest();
}
INSTANTIATE_TEST_SUITE_P(
,
ProfileMenuClickTest_WithUnconsentedPrimaryAccount,
::testing::Range(
size_t(0),
base::size(ProfileMenuClickTest_WithUnconsentedPrimaryAccount::
kOrderedActionableItems)));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment