Commit 04afdb9e authored by siyua's avatar siyua Committed by Commit Bot

[Upstream Feedback] Add loading indicator and failure bubble for card icon

The loading indicator will be a circle throbber on top of the icon. It
will be shown when a upload save is in progress. The icon will be
disabled (not clickable) during the animation.

If upload save succeeded, dismiss the icon and shows the avatar
highlight, otherwise stop the animation and shows the failure bubble/icon
(credit card icon with a failure badge). The failure bubble will be shown
only once. Once the bubble is dismissed, the icon will be as well.

Bug: 932818
Change-Id: I305a5998d216546c697f35ffae18ec80ded05292
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1808168
Commit-Queue: Siyu An <siyua@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Reviewed-by: default avatarJared Saul <jsaul@google.com>
Cr-Commit-Position: refs/heads/master@{#700081}
parent 78d1fcdf
...@@ -2934,6 +2934,8 @@ jumbo_split_static_library("ui") { ...@@ -2934,6 +2934,8 @@ jumbo_split_static_library("ui") {
"views/overlay/track_image_button.h", "views/overlay/track_image_button.h",
"views/page_action/page_action_icon_container_view.cc", "views/page_action/page_action_icon_container_view.cc",
"views/page_action/page_action_icon_container_view.h", "views/page_action/page_action_icon_container_view.h",
"views/page_action/page_action_icon_loading_indicator_view.cc",
"views/page_action/page_action_icon_loading_indicator_view.h",
"views/page_action/page_action_icon_view.cc", "views/page_action/page_action_icon_view.cc",
"views/page_action/page_action_icon_view.h", "views/page_action/page_action_icon_view.h",
"views/page_action/pwa_install_view.cc", "views/page_action/pwa_install_view.cc",
......
...@@ -81,8 +81,16 @@ class SaveCardBubbleController { ...@@ -81,8 +81,16 @@ class SaveCardBubbleController {
// Returns whether or not a sign in / sync promo needs to be shown. // Returns whether or not a sign in / sync promo needs to be shown.
virtual bool ShouldShowSignInPromo() const = 0; virtual bool ShouldShowSignInPromo() const = 0;
// Returns true iff credit card upload save is in progress and the saving
// animation should be shown.
virtual bool ShouldShowSavingCardAnimation() const = 0;
// Returns true iff the card saved animation should be shown. // Returns true iff the card saved animation should be shown.
virtual bool ShouldShowCardSavedAnimation() const = 0; virtual bool ShouldShowCardSavedLabelAnimation() const = 0;
// Returns true iff credit card upload save failed and the failure badge on
// the icon should be shown.
virtual bool ShouldShowSaveFailureBadge() const = 0;
// Interaction. // Interaction.
// OnSyncPromoAccepted is called when the Dice Sign-in promo is clicked. // OnSyncPromoAccepted is called when the Dice Sign-in promo is clicked.
......
...@@ -173,8 +173,8 @@ void SaveCardBubbleControllerImpl::ShowBubbleForManageCardsForTesting( ...@@ -173,8 +173,8 @@ void SaveCardBubbleControllerImpl::ShowBubbleForManageCardsForTesting(
} }
void SaveCardBubbleControllerImpl::UpdateIconForSaveCardSuccess() { void SaveCardBubbleControllerImpl::UpdateIconForSaveCardSuccess() {
// TODO(crbug.com/964127): Dismisses the icon and triggers a highlight current_bubble_type_ = BubbleType::INACTIVE;
// animation of the avatar button. UpdateSaveCardIcon();
} }
void SaveCardBubbleControllerImpl::UpdateIconForSaveCardFailure() { void SaveCardBubbleControllerImpl::UpdateIconForSaveCardFailure() {
...@@ -270,6 +270,7 @@ base::string16 SaveCardBubbleControllerImpl::GetWindowTitle() const { ...@@ -270,6 +270,7 @@ base::string16 SaveCardBubbleControllerImpl::GetWindowTitle() const {
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_SAVED); return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_SAVED);
case BubbleType::FAILURE: case BubbleType::FAILURE:
return l10n_util::GetStringUTF16(IDS_AUTOFILL_FAILURE_BUBBLE_TITLE); return l10n_util::GetStringUTF16(IDS_AUTOFILL_FAILURE_BUBBLE_TITLE);
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
NOTREACHED(); NOTREACHED();
return base::string16(); return base::string16();
...@@ -372,6 +373,7 @@ base::string16 SaveCardBubbleControllerImpl::GetAcceptButtonText() const { ...@@ -372,6 +373,7 @@ base::string16 SaveCardBubbleControllerImpl::GetAcceptButtonText() const {
} }
case BubbleType::MANAGE_CARDS: case BubbleType::MANAGE_CARDS:
return l10n_util::GetStringUTF16(IDS_AUTOFILL_DONE); return l10n_util::GetStringUTF16(IDS_AUTOFILL_DONE);
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::SIGN_IN_PROMO: case BubbleType::SIGN_IN_PROMO:
case BubbleType::FAILURE: case BubbleType::FAILURE:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
...@@ -410,6 +412,7 @@ base::string16 SaveCardBubbleControllerImpl::GetDeclineButtonText() const { ...@@ -410,6 +412,7 @@ base::string16 SaveCardBubbleControllerImpl::GetDeclineButtonText() const {
return l10n_util::GetStringUTF16( return l10n_util::GetStringUTF16(
IDS_AUTOFILL_NO_THANKS_DESKTOP_UPLOAD_SAVE); IDS_AUTOFILL_NO_THANKS_DESKTOP_UPLOAD_SAVE);
} }
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::MANAGE_CARDS: case BubbleType::MANAGE_CARDS:
case BubbleType::SIGN_IN_PROMO: case BubbleType::SIGN_IN_PROMO:
case BubbleType::FAILURE: case BubbleType::FAILURE:
...@@ -460,8 +463,16 @@ bool SaveCardBubbleControllerImpl::ShouldShowSignInPromo() const { ...@@ -460,8 +463,16 @@ bool SaveCardBubbleControllerImpl::ShouldShowSignInPromo() const {
syncer::SyncService::DISABLE_REASON_USER_CHOICE); syncer::SyncService::DISABLE_REASON_USER_CHOICE);
} }
bool SaveCardBubbleControllerImpl::ShouldShowCardSavedAnimation() const { bool SaveCardBubbleControllerImpl::ShouldShowSavingCardAnimation() const {
return should_show_card_saved_animation_; return current_bubble_type_ == BubbleType::UPLOAD_IN_PROGRESS;
}
bool SaveCardBubbleControllerImpl::ShouldShowCardSavedLabelAnimation() const {
return should_show_card_saved_label_animation_;
}
bool SaveCardBubbleControllerImpl::ShouldShowSaveFailureBadge() const {
return current_bubble_type_ == BubbleType::FAILURE;
} }
void SaveCardBubbleControllerImpl::OnSyncPromoAccepted( void SaveCardBubbleControllerImpl::OnSyncPromoAccepted(
...@@ -504,7 +515,7 @@ void SaveCardBubbleControllerImpl::OnSaveButton( ...@@ -504,7 +515,7 @@ void SaveCardBubbleControllerImpl::OnSaveButton(
DCHECK(!local_save_card_prompt_callback_.is_null()); DCHECK(!local_save_card_prompt_callback_.is_null());
// Show an animated card saved confirmation message next time // Show an animated card saved confirmation message next time
// UpdateSaveCardIcon() is called. // UpdateSaveCardIcon() is called.
should_show_card_saved_animation_ = true; should_show_card_saved_label_animation_ = true;
std::move(local_save_card_prompt_callback_).Run(AutofillClient::ACCEPTED); std::move(local_save_card_prompt_callback_).Run(AutofillClient::ACCEPTED);
break; break;
...@@ -512,6 +523,7 @@ void SaveCardBubbleControllerImpl::OnSaveButton( ...@@ -512,6 +523,7 @@ void SaveCardBubbleControllerImpl::OnSaveButton(
AutofillMetrics::LogManageCardsPromptMetric( AutofillMetrics::LogManageCardsPromptMetric(
AutofillMetrics::MANAGE_CARDS_DONE, is_upload_save_); AutofillMetrics::MANAGE_CARDS_DONE, is_upload_save_);
return; return;
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::SIGN_IN_PROMO: case BubbleType::SIGN_IN_PROMO:
case BubbleType::FAILURE: case BubbleType::FAILURE:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
...@@ -519,7 +531,18 @@ void SaveCardBubbleControllerImpl::OnSaveButton( ...@@ -519,7 +531,18 @@ void SaveCardBubbleControllerImpl::OnSaveButton(
} }
const BubbleType previous_bubble_type = current_bubble_type_; const BubbleType previous_bubble_type = current_bubble_type_;
current_bubble_type_ = BubbleType::INACTIVE;
// If experiment is disabled or bubble type is not BubbleType::UPLOAD_SAVE, we
// change it to BubbleType::INACTIVE to dismiss the icon. Otherwise,
// |current_bubble_type_| will remain BubbleType::UPLOAD_SAVE, so that the
// icon is still visible during the credit card upload process.
if (base::FeatureList::IsEnabled(
features::kAutofillCreditCardUploadFeedback) &&
previous_bubble_type == BubbleType::UPLOAD_SAVE) {
current_bubble_type_ = BubbleType::UPLOAD_IN_PROGRESS;
} else {
current_bubble_type_ = BubbleType::INACTIVE;
}
// If user just saved a card locally, the next bubble can either be a sign-in // If user just saved a card locally, the next bubble can either be a sign-in
// promo or a manage cards view. If we need to show a sign-in promo, that // promo or a manage cards view. If we need to show a sign-in promo, that
...@@ -597,6 +620,13 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() { ...@@ -597,6 +620,13 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() {
// reopening the bubble will show the card management bubble. // reopening the bubble will show the card management bubble.
if (current_bubble_type_ == BubbleType::SIGN_IN_PROMO) if (current_bubble_type_ == BubbleType::SIGN_IN_PROMO)
current_bubble_type_ = BubbleType::MANAGE_CARDS; current_bubble_type_ = BubbleType::MANAGE_CARDS;
// Unlike other bubbles, the save failure bubble should not be reshown. If the
// save card failure bubble is closed, the credit card icon should be
// dismissed as well.
if (current_bubble_type_ == BubbleType::FAILURE)
current_bubble_type_ = BubbleType::INACTIVE;
UpdateSaveCardIcon(); UpdateSaveCardIcon();
if (observer_for_testing_) if (observer_for_testing_)
observer_for_testing_->OnBubbleClosed(); observer_for_testing_->OnBubbleClosed();
...@@ -605,7 +635,7 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() { ...@@ -605,7 +635,7 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() {
void SaveCardBubbleControllerImpl::OnAnimationEnded() { void SaveCardBubbleControllerImpl::OnAnimationEnded() {
// Do not repeat the animation next time UpdateSaveCardIcon() is called, // Do not repeat the animation next time UpdateSaveCardIcon() is called,
// unless explicitly set somewhere else. // unless explicitly set somewhere else.
should_show_card_saved_animation_ = false; should_show_card_saved_label_animation_ = false;
// We do not want to show the promo if the user clicked on the icon and the // We do not want to show the promo if the user clicked on the icon and the
// manage cards bubble started to show. // manage cards bubble started to show.
...@@ -760,6 +790,7 @@ void SaveCardBubbleControllerImpl::ShowBubble() { ...@@ -760,6 +790,7 @@ void SaveCardBubbleControllerImpl::ShowBubble() {
case BubbleType::FAILURE: case BubbleType::FAILURE:
// TODO(crbug.com/964127): Add metrics. // TODO(crbug.com/964127): Add metrics.
break; break;
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
NOTREACHED(); NOTREACHED();
} }
...@@ -798,6 +829,7 @@ void SaveCardBubbleControllerImpl::ShowIconOnly() { ...@@ -798,6 +829,7 @@ void SaveCardBubbleControllerImpl::ShowIconOnly() {
case BubbleType::FAILURE: case BubbleType::FAILURE:
// TODO(crbug.com/964127): Add metrics. // TODO(crbug.com/964127): Add metrics.
break; break;
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::MANAGE_CARDS: case BubbleType::MANAGE_CARDS:
case BubbleType::SIGN_IN_PROMO: case BubbleType::SIGN_IN_PROMO:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
......
...@@ -123,7 +123,9 @@ class SaveCardBubbleControllerImpl ...@@ -123,7 +123,9 @@ class SaveCardBubbleControllerImpl
// to the server -- this should change. // to the server -- this should change.
// TODO(crbug.com/864702): Don't show promo if user is a butter user. // TODO(crbug.com/864702): Don't show promo if user is a butter user.
bool ShouldShowSignInPromo() const override; bool ShouldShowSignInPromo() const override;
bool ShouldShowCardSavedAnimation() const override; bool ShouldShowSavingCardAnimation() const override;
bool ShouldShowCardSavedLabelAnimation() const override;
bool ShouldShowSaveFailureBadge() const override;
void OnSyncPromoAccepted(const AccountInfo& account, void OnSyncPromoAccepted(const AccountInfo& account,
signin_metrics::AccessPoint access_point, signin_metrics::AccessPoint access_point,
bool is_default_promo_account) override; bool is_default_promo_account) override;
...@@ -179,8 +181,8 @@ class SaveCardBubbleControllerImpl ...@@ -179,8 +181,8 @@ class SaveCardBubbleControllerImpl
// Should outlive this object. // Should outlive this object.
PersonalDataManager* personal_data_manager_; PersonalDataManager* personal_data_manager_;
// Is true only if the card saved animation should be shown. // Is true only if the [Card saved] label animation should be shown.
bool should_show_card_saved_animation_ = false; bool should_show_card_saved_label_animation_ = false;
// Weak reference. Will be nullptr if no bubble is currently shown. // Weak reference. Will be nullptr if no bubble is currently shown.
SaveCardBubbleView* save_card_bubble_view_ = nullptr; SaveCardBubbleView* save_card_bubble_view_ = nullptr;
......
...@@ -106,6 +106,7 @@ class SaveCardBubbleControllerImplTest : public DialogBrowserTest { ...@@ -106,6 +106,7 @@ class SaveCardBubbleControllerImplTest : public DialogBrowserTest {
case BubbleType::FAILURE: case BubbleType::FAILURE:
controller_->ShowBubbleForSaveCardFailureForTesting(); controller_->ShowBubbleForSaveCardFailureForTesting();
break; break;
case BubbleType::UPLOAD_IN_PROGRESS:
case BubbleType::INACTIVE: case BubbleType::INACTIVE:
break; break;
} }
......
...@@ -142,7 +142,7 @@ class SaveCardBubbleControllerImplTest : public BrowserWithTestWindowTest { ...@@ -142,7 +142,7 @@ class SaveCardBubbleControllerImplTest : public BrowserWithTestWindowTest {
void ClickSaveButton() { void ClickSaveButton() {
controller()->OnSaveButton({}); controller()->OnSaveButton({});
if (controller()->ShouldShowCardSavedAnimation()) if (controller()->ShouldShowCardSavedLabelAnimation())
controller()->OnAnimationEnded(); controller()->OnAnimationEnded();
} }
......
...@@ -15,6 +15,10 @@ enum class BubbleType { ...@@ -15,6 +15,10 @@ enum class BubbleType {
// Save prompt when uploading a card to Google payments. // Save prompt when uploading a card to Google payments.
UPLOAD_SAVE, UPLOAD_SAVE,
// Credit card upload is in progress. No bubble visible but show the credit
// card icon with the loading indicator animation.
UPLOAD_IN_PROGRESS,
// The sign-in promo that is shown after local save. // The sign-in promo that is shown after local save.
SIGN_IN_PROMO, SIGN_IN_PROMO,
......
...@@ -108,11 +108,15 @@ const char kResponseGetUploadDetailsSuccess[] = ...@@ -108,11 +108,15 @@ const char kResponseGetUploadDetailsSuccess[] =
"link: " "link: "
"{0}.\",\"template_parameter\":[{\"display_text\":\"Link\",\"url\":\"https:" "{0}.\",\"template_parameter\":[{\"display_text\":\"Link\",\"url\":\"https:"
"//www.example.com/\"}]}]},\"context_token\":\"dummy_context_token\"}"; "//www.example.com/\"}]}]},\"context_token\":\"dummy_context_token\"}";
const char kURLUploadCardRequest[] =
"https://payments.google.com/payments/apis-secure/chromepaymentsservice/"
"savecard"
"?s7e_suffix=chromewallet";
const char kResponsePaymentsSuccess[] =
"{ \"credit_card_id\": \"InstrumentData:1\" }";
const char kResponsePaymentsFailure[] = const char kResponsePaymentsFailure[] =
"{\"error\":{\"code\":\"FAILED_PRECONDITION\",\"user_error_message\":\"An " "{\"error\":{\"code\":\"FAILED_PRECONDITION\",\"user_error_message\":\"An "
"unexpected error has occurred. Please try again later.\"}}"; "unexpected error has occurred. Please try again later.\"}}";
const char kURLUploadCardRequest[] =
"https://payments.google.com/payments/apis/chromepaymentsservice/savecard";
const double kFakeGeolocationLatitude = 1.23; const double kFakeGeolocationLatitude = 1.23;
const double kFakeGeolocationLongitude = 4.56; const double kFakeGeolocationLongitude = 4.56;
...@@ -138,6 +142,7 @@ class SaveCardBubbleViewsFullFormBrowserTest ...@@ -138,6 +142,7 @@ class SaveCardBubbleViewsFullFormBrowserTest
RECEIVED_GET_UPLOAD_DETAILS_RESPONSE, RECEIVED_GET_UPLOAD_DETAILS_RESPONSE,
SENT_UPLOAD_CARD_REQUEST, SENT_UPLOAD_CARD_REQUEST,
RECEIVED_UPLOAD_CARD_RESPONSE, RECEIVED_UPLOAD_CARD_RESPONSE,
SHOW_CARD_SAVED_FEEDBACK,
STRIKE_CHANGE_COMPLETE, STRIKE_CHANGE_COMPLETE,
BUBBLE_SHOWN, BUBBLE_SHOWN,
BUBBLE_CLOSED BUBBLE_CLOSED
...@@ -250,6 +255,12 @@ class SaveCardBubbleViewsFullFormBrowserTest ...@@ -250,6 +255,12 @@ class SaveCardBubbleViewsFullFormBrowserTest
event_waiter_->OnEvent(DialogEvent::RECEIVED_UPLOAD_CARD_RESPONSE); event_waiter_->OnEvent(DialogEvent::RECEIVED_UPLOAD_CARD_RESPONSE);
} }
// CreditCardSaveManager::ObserverForTest:
void OnShowCardSavedFeedback() override {
if (event_waiter_)
event_waiter_->OnEvent(DialogEvent::SHOW_CARD_SAVED_FEEDBACK);
}
// CreditCardSaveManager::ObserverForTest: // CreditCardSaveManager::ObserverForTest:
void OnStrikeChangeComplete() override { void OnStrikeChangeComplete() override {
if (event_waiter_) if (event_waiter_)
...@@ -577,6 +588,11 @@ class SaveCardBubbleViewsFullFormBrowserTest ...@@ -577,6 +588,11 @@ class SaveCardBubbleViewsFullFormBrowserTest
net::HTTP_INTERNAL_SERVER_ERROR); net::HTTP_INTERNAL_SERVER_ERROR);
} }
void SetUploadCardRpcPaymentsSucceeds() {
test_url_loader_factory()->AddResponse(kURLUploadCardRequest,
kResponsePaymentsSuccess);
}
void SetUploadCardRpcPaymentsFails() { void SetUploadCardRpcPaymentsFails() {
test_url_loader_factory()->AddResponse(kURLUploadCardRequest, test_url_loader_factory()->AddResponse(kURLUploadCardRequest,
kResponsePaymentsFailure); kResponsePaymentsFailure);
...@@ -688,7 +704,7 @@ class SaveCardBubbleViewsFullFormBrowserTest ...@@ -688,7 +704,7 @@ class SaveCardBubbleViewsFullFormBrowserTest
return static_cast<SaveCardBubbleViews*>(save_card_bubble_view); return static_cast<SaveCardBubbleViews*>(save_card_bubble_view);
} }
PageActionIconView* GetSaveCardIconView() { SaveCardIconView* GetSaveCardIconView() {
BrowserView* browser_view = BrowserView* browser_view =
BrowserView::GetBrowserViewForBrowser(browser()); BrowserView::GetBrowserViewForBrowser(browser());
PageActionIconView* icon = PageActionIconView* icon =
...@@ -702,7 +718,7 @@ class SaveCardBubbleViewsFullFormBrowserTest ...@@ -702,7 +718,7 @@ class SaveCardBubbleViewsFullFormBrowserTest
} else { } else {
DCHECK(browser_view->GetLocationBarView()->Contains(icon)); DCHECK(browser_view->GetLocationBarView()->Contains(icon));
} }
return icon; return static_cast<SaveCardIconView*>(icon);
} }
void OpenSettingsFromManageCardsPrompt() { void OpenSettingsFromManageCardsPrompt() {
...@@ -795,8 +811,11 @@ class SaveCardBubbleViewsFullFormBrowserTestForStatusChip ...@@ -795,8 +811,11 @@ class SaveCardBubbleViewsFullFormBrowserTestForStatusChip
void SetUp() override { void SetUp() override {
base::test::ScopedFeatureList scoped_feature_list; base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature( scoped_feature_list.InitWithFeatures(
features::kAutofillEnableToolbarStatusChip); /*enabled_features=*/{features::kAutofillCreditCardUploadFeedback,
features::kAutofillEnableToolbarStatusChip,
features::kAutofillUpstream},
/*disabled_features=*/{});
SaveCardBubbleViewsFullFormBrowserTest::SetUp(); SaveCardBubbleViewsFullFormBrowserTest::SetUp();
} }
...@@ -2734,6 +2753,41 @@ IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTestForStatusChip, ...@@ -2734,6 +2753,41 @@ IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTestForStatusChip,
EXPECT_TRUE(GetSaveCardIconView()->GetVisible()); EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
EXPECT_FALSE(GetSaveCardBubbleViews()); EXPECT_FALSE(GetSaveCardBubbleViews());
} }
// Ensures the card saving throbber animation in the status chip behaves
// correctly during credit card upload process.
IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTestForStatusChip,
Feedback_CardSavingAnimation) {
// Start sync.
harness_->SetupSync();
FillForm();
SubmitFormAndWaitForCardUploadSaveBubble();
// Ensures icon is visible and animation is not.
EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
EXPECT_FALSE(
GetSaveCardIconView()->loading_indicator_for_testing()->IsAnimating());
ResetEventWaiterForSequence({DialogEvent::SENT_UPLOAD_CARD_REQUEST});
ClickOnDialogViewWithIdAndWait(DialogViewId::OK_BUTTON);
WaitForObservedEvent();
// Ensures icon and the animation are visible.
EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
EXPECT_TRUE(
GetSaveCardIconView()->loading_indicator_for_testing()->IsAnimating());
SetUploadCardRpcPaymentsSucceeds();
ResetEventWaiterForSequence({DialogEvent::RECEIVED_UPLOAD_CARD_RESPONSE,
DialogEvent::SHOW_CARD_SAVED_FEEDBACK});
WaitForObservedEvent();
// Ensures the animation should not be animating.
EXPECT_FALSE(
GetSaveCardIconView()->loading_indicator_for_testing()->IsAnimating());
}
#endif // !defined(OS_CHROMEOS) #endif // !defined(OS_CHROMEOS)
} // namespace autofill } // namespace autofill
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
namespace autofill { namespace autofill {
...@@ -26,9 +27,16 @@ SaveCardIconView::SaveCardIconView(CommandUpdater* command_updater, ...@@ -26,9 +27,16 @@ SaveCardIconView::SaveCardIconView(CommandUpdater* command_updater,
SetID(VIEW_ID_SAVE_CREDIT_CARD_BUTTON); SetID(VIEW_ID_SAVE_CREDIT_CARD_BUTTON);
SetUpForInOutAnimation(); SetUpForInOutAnimation();
loading_indicator_ =
AddChildView(std::make_unique<PageActionIconLoadingIndicatorView>());
loading_indicator_->SetVisible(false);
AddObserver(loading_indicator_);
} }
SaveCardIconView::~SaveCardIconView() {} SaveCardIconView::~SaveCardIconView() {
RemoveObserver(loading_indicator_);
}
views::BubbleDialogDelegateView* SaveCardIconView::GetBubble() const { views::BubbleDialogDelegateView* SaveCardIconView::GetBubble() const {
SaveCardBubbleController* controller = GetController(); SaveCardBubbleController* controller = GetController();
...@@ -47,12 +55,21 @@ bool SaveCardIconView::Update() { ...@@ -47,12 +55,21 @@ bool SaveCardIconView::Update() {
// |controller| may be nullptr due to lazy initialization. // |controller| may be nullptr due to lazy initialization.
SaveCardBubbleController* controller = GetController(); SaveCardBubbleController* controller = GetController();
bool enabled = controller && controller->IsIconVisible();
enabled &= SetCommandEnabled(enabled); bool command_enabled =
SetVisible(enabled); SetCommandEnabled(controller && controller->IsIconVisible());
SetVisible(command_enabled);
if (enabled && controller->ShouldShowCardSavedAnimation()) if (command_enabled && controller->ShouldShowSavingCardAnimation()) {
SetEnabled(false);
loading_indicator_->ShowAnimation();
} else {
loading_indicator_->StopAnimation();
UpdateIconImage();
SetEnabled(true);
}
if (command_enabled && controller->ShouldShowCardSavedLabelAnimation())
AnimateIn(IDS_AUTOFILL_CARD_SAVED); AnimateIn(IDS_AUTOFILL_CARD_SAVED);
return was_visible != GetVisible(); return was_visible != GetVisible();
...@@ -65,6 +82,14 @@ const gfx::VectorIcon& SaveCardIconView::GetVectorIcon() const { ...@@ -65,6 +82,14 @@ const gfx::VectorIcon& SaveCardIconView::GetVectorIcon() const {
return kCreditCardIcon; return kCreditCardIcon;
} }
const gfx::VectorIcon& SaveCardIconView::GetVectorIconBadge() const {
SaveCardBubbleController* controller = GetController();
if (controller && controller->ShouldShowSaveFailureBadge())
return kBlockedBadgeIcon;
return gfx::kNoneIcon;
}
base::string16 SaveCardIconView::GetTextForTooltipAndAccessibleName() const { base::string16 SaveCardIconView::GetTextForTooltipAndAccessibleName() const {
return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_CREDIT_CARD); return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_CREDIT_CARD);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_CARD_ICON_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_CARD_ICON_VIEW_H_
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_loading_indicator_view.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_view.h" #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
class CommandUpdater; class CommandUpdater;
...@@ -28,10 +29,15 @@ class SaveCardIconView : public PageActionIconView { ...@@ -28,10 +29,15 @@ class SaveCardIconView : public PageActionIconView {
bool Update() override; bool Update() override;
base::string16 GetTextForTooltipAndAccessibleName() const override; base::string16 GetTextForTooltipAndAccessibleName() const override;
PageActionIconLoadingIndicatorView* loading_indicator_for_testing() {
return loading_indicator_;
}
protected: protected:
// PageActionIconView: // PageActionIconView:
void OnExecuting(PageActionIconView::ExecuteSource execute_source) override; void OnExecuting(PageActionIconView::ExecuteSource execute_source) override;
const gfx::VectorIcon& GetVectorIcon() const override; const gfx::VectorIcon& GetVectorIcon() const override;
const gfx::VectorIcon& GetVectorIconBadge() const override;
private: private:
SaveCardBubbleController* GetController() const; SaveCardBubbleController* GetController() const;
...@@ -39,6 +45,10 @@ class SaveCardIconView : public PageActionIconView { ...@@ -39,6 +45,10 @@ class SaveCardIconView : public PageActionIconView {
// gfx::AnimationDelegate: // gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override;
// The loading indicator. Its animation will be triggered when upload save is
// in progress.
PageActionIconLoadingIndicatorView* loading_indicator_;
DISALLOW_COPY_AND_ASSIGN(SaveCardIconView); DISALLOW_COPY_AND_ASSIGN(SaveCardIconView);
}; };
......
...@@ -1388,6 +1388,7 @@ autofill::SaveCardBubbleView* BrowserView::ShowSaveCreditCardBubble( ...@@ -1388,6 +1388,7 @@ autofill::SaveCardBubbleView* BrowserView::ShowSaveCreditCardBubble(
bubble = new autofill::SaveCardFailureBubbleViews( bubble = new autofill::SaveCardFailureBubbleViews(
anchor_view, web_contents, controller); anchor_view, web_contents, controller);
break; break;
case autofill::BubbleType::UPLOAD_IN_PROGRESS:
case autofill::BubbleType::INACTIVE: case autofill::BubbleType::INACTIVE:
break; break;
} }
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/page_action/page_action_icon_loading_indicator_view.h"
#include "base/location.h"
#include "base/time/default_tick_clock.h"
#include "chrome/browser/themes/theme_properties.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_throbber.h"
PageActionIconLoadingIndicatorView::PageActionIconLoadingIndicatorView() =
default;
PageActionIconLoadingIndicatorView::~PageActionIconLoadingIndicatorView() =
default;
void PageActionIconLoadingIndicatorView::ShowAnimation() {
if (!throbber_start_time_.has_value())
throbber_start_time_ = base::TimeTicks::Now();
SetVisible(true);
animation_.StartThrobbing(-1);
}
void PageActionIconLoadingIndicatorView::StopAnimation() {
throbber_start_time_.reset();
SetVisible(false);
animation_.Reset();
}
bool PageActionIconLoadingIndicatorView::IsAnimating() {
return animation_.is_animating();
}
void PageActionIconLoadingIndicatorView::OnPaint(gfx::Canvas* canvas) {
const SkColor color = GetThemeProvider()->GetColor(
ThemeProperties::COLOR_TAB_THROBBER_SPINNING);
constexpr int kThrobberStrokeWidth = 2;
gfx::PaintThrobberSpinning(
canvas, GetLocalBounds(), color,
base::TimeTicks::Now() - throbber_start_time_.value_or(base::TimeTicks()),
kThrobberStrokeWidth);
}
void PageActionIconLoadingIndicatorView::OnViewBoundsChanged(
views::View* observed_view) {
SetBoundsRect(observed_view->GetLocalBounds());
}
void PageActionIconLoadingIndicatorView::AnimationProgressed(
const gfx::Animation* animation) {
DCHECK_EQ(animation, &animation_);
SchedulePaint();
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_LOADING_INDICATOR_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_LOADING_INDICATOR_VIEW_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
// The view that contains a throbber animation. It is shown when the action
// related to the page action icon is in progress.
// TODO(crbug.com/932818): Investigate the possibility of making this a layer
// instead of a view.
class PageActionIconLoadingIndicatorView : public views::View,
public views::ViewObserver,
public gfx::AnimationDelegate {
public:
PageActionIconLoadingIndicatorView();
~PageActionIconLoadingIndicatorView() override;
void ShowAnimation();
void StopAnimation();
bool IsAnimating();
// views::View:
void OnPaint(gfx::Canvas* canvas) override;
// views::ViewObserver:
void OnViewBoundsChanged(views::View* observed_view) override;
// gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override;
private:
base::Optional<base::TimeTicks> throbber_start_time_ = base::nullopt;
gfx::ThrobAnimation animation_{this};
DISALLOW_COPY_AND_ASSIGN(PageActionIconLoadingIndicatorView);
};
#endif // CHROME_BROWSER_UI_VIEWS_PAGE_ACTION_PAGE_ACTION_ICON_LOADING_INDICATOR_VIEW_H_
...@@ -329,6 +329,9 @@ void CreditCardSaveManager::OnDidUploadCard( ...@@ -329,6 +329,9 @@ void CreditCardSaveManager::OnDidUploadCard(
// Show credit card upload feedback. // Show credit card upload feedback.
client_->CreditCardUploadCompleted(result == AutofillClient::SUCCESS); client_->CreditCardUploadCompleted(result == AutofillClient::SUCCESS);
if (observer_for_testing_)
observer_for_testing_->OnShowCardSavedFeedback();
} }
CreditCardSaveStrikeDatabase* CreditCardSaveStrikeDatabase*
......
...@@ -88,6 +88,7 @@ class CreditCardSaveManager { ...@@ -88,6 +88,7 @@ class CreditCardSaveManager {
virtual void OnReceivedGetUploadDetailsResponse() {} virtual void OnReceivedGetUploadDetailsResponse() {}
virtual void OnSentUploadCardRequest() {} virtual void OnSentUploadCardRequest() {}
virtual void OnReceivedUploadCardResponse() {} virtual void OnReceivedUploadCardResponse() {}
virtual void OnShowCardSavedFeedback() {}
virtual void OnStrikeChangeComplete() {} virtual void OnStrikeChangeComplete() {}
}; };
...@@ -147,6 +148,8 @@ class CreditCardSaveManager { ...@@ -147,6 +148,8 @@ class CreditCardSaveManager {
StrikeDatabase_Local_FullFlowTest); StrikeDatabase_Local_FullFlowTest);
FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest, FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest,
StrikeDatabase_Upload_FullFlowTest); StrikeDatabase_Upload_FullFlowTest);
FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTestForStatusChip,
Feedback_CardSavingAnimation);
// Returns the CreditCardSaveStrikeDatabase for |client_|. // Returns the CreditCardSaveStrikeDatabase for |client_|.
CreditCardSaveStrikeDatabase* GetCreditCardSaveStrikeDatabase(); CreditCardSaveStrikeDatabase* GetCreditCardSaveStrikeDatabase();
......
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