// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.h"
#include <optional>
#include "base/feature_list.h"
#include "base/notimplemented.h"
#include "chrome/browser/plus_addresses/plus_address_service_factory.h"
#include "chrome/browser/plus_addresses/plus_address_setting_service_factory.h"
#include "chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h"
#include "components/plus_addresses/features.h"
#include "components/plus_addresses/metrics/plus_address_metrics.h"
#include "components/plus_addresses/plus_address_service.h"
#include "components/plus_addresses/plus_address_types.h"
#include "components/plus_addresses/settings/plus_address_setting_service.h"
namespace plus_addresses {
// static
PlusAddressCreationController* PlusAddressCreationController::GetOrCreate(
content::WebContents* web_contents) {
PlusAddressCreationControllerAndroid::CreateForWebContents(web_contents);
return PlusAddressCreationControllerAndroid::FromWebContents(web_contents);
}
PlusAddressCreationControllerAndroid::PlusAddressCreationControllerAndroid(
content::WebContents* web_contents)
: content::WebContentsUserData<PlusAddressCreationControllerAndroid>(
*web_contents) {}
PlusAddressCreationControllerAndroid::~PlusAddressCreationControllerAndroid() =
default;
void PlusAddressCreationControllerAndroid::OfferCreation(
const url::Origin& main_frame_origin,
PlusAddressCallback callback) {
if (view_) {
return;
}
PlusAddressService* plus_address_service =
PlusAddressServiceFactory::GetForBrowserContext(
GetWebContents().GetBrowserContext());
if (!plus_address_service) {
// TODO(crbug.com/40276862): Verify expected behavior in this case and the
// missing email case below.
return;
}
std::optional<std::string> maybe_email =
plus_address_service->GetPrimaryEmail();
if (maybe_email == std::nullopt) {
return;
}
callback_ = std::move(callback);
relevant_origin_ = main_frame_origin;
const bool should_show_notice = ShouldShowNotice();
metrics::RecordModalEvent(metrics::PlusAddressModalEvent::kModalShown,
should_show_notice);
modal_shown_time_ = base::TimeTicks::Now();
if (!suppress_ui_for_testing_) {
view_ = std::make_unique<PlusAddressCreationViewAndroid>(GetWeakPtr(),
&GetWebContents());
view_->ShowInit(
maybe_email.value(),
plus_address_service->IsRefreshingSupported(relevant_origin_),
/*has_accepted_notice=*/!should_show_notice);
}
plus_address_service->ReservePlusAddress(
relevant_origin_,
base::BindOnce(
&PlusAddressCreationControllerAndroid::OnPlusAddressReserved,
GetWeakPtr()));
}
void PlusAddressCreationControllerAndroid::OnRefreshClicked() {
PlusAddressService* plus_address_service = GetPlusAddressService();
if (!plus_address_service) {
return;
}
plus_address_service->RefreshPlusAddress(
relevant_origin_,
base::BindOnce(
&PlusAddressCreationControllerAndroid::OnPlusAddressReserved,
GetWeakPtr()));
}
void PlusAddressCreationControllerAndroid::OnConfirmed() {
CHECK(plus_profile_.has_value());
metrics::RecordModalEvent(metrics::PlusAddressModalEvent::kModalConfirmed,
ShouldShowNotice());
if (plus_profile_->is_confirmed) {
OnPlusAddressConfirmed(plus_profile_.value());
return;
}
if (PlusAddressService* plus_address_service = GetPlusAddressService()) {
// Note: this call may fail if this modal is confirmed on the same
// `relevant_origin_` from another device.
plus_address_service->ConfirmPlusAddress(
relevant_origin_, plus_profile_->plus_address,
base::BindOnce(
&PlusAddressCreationControllerAndroid::OnPlusAddressConfirmed,
GetWeakPtr()));
}
}
void PlusAddressCreationControllerAndroid::OnCanceled() {
// TODO(b/320541525) ModalEvent is in sync with actual user action. May
// re-evaluate the use of this metric when modal becomes more complex.
const bool was_notice_shown = ShouldShowNotice();
metrics::RecordModalEvent(metrics::PlusAddressModalEvent::kModalCanceled,
was_notice_shown);
if (modal_error_status_.has_value()) {
RecordModalShownOutcome(modal_error_status_.value(), was_notice_shown);
modal_error_status_.reset();
} else {
RecordModalShownOutcome(
metrics::PlusAddressModalCompletionStatus::kModalCanceled,
was_notice_shown);
}
}
void PlusAddressCreationControllerAndroid::OnDialogDestroyed() {
view_.reset();
plus_profile_.reset();
}
void PlusAddressCreationControllerAndroid::set_suppress_ui_for_testing(
bool should_suppress) {
suppress_ui_for_testing_ = should_suppress;
}
std::optional<PlusProfile>
PlusAddressCreationControllerAndroid::get_plus_profile_for_testing() {
return plus_profile_;
}
void PlusAddressCreationControllerAndroid::OnPlusAddressReserved(
const PlusProfileOrError& maybe_plus_profile) {
// Note that in case of `suppress_ui_for_testing_` or bottom sheet dismissal
// prior to service response, `view_` will be null.
if (view_) {
view_->ShowReserveResult(maybe_plus_profile);
if (PlusAddressService* service = GetPlusAddressService();
service && !service->IsRefreshingSupported(relevant_origin_)) {
view_->HideRefreshButton();
}
}
if (maybe_plus_profile.has_value()) {
plus_profile_ = maybe_plus_profile.value();
++reserve_response_count_;
} else {
modal_error_status_ =
metrics::PlusAddressModalCompletionStatus::kReservePlusAddressError;
}
}
void PlusAddressCreationControllerAndroid::OnPlusAddressConfirmed(
const PlusProfileOrError& maybe_plus_profile) {
if (maybe_plus_profile.has_value()) {
const bool was_notice_shown = ShouldShowNotice();
if (was_notice_shown) {
GetPlusAddressSettingService()->SetHasAcceptedNotice();
}
std::move(callback_).Run(*maybe_plus_profile->plus_address);
RecordModalShownOutcome(
metrics::PlusAddressModalCompletionStatus::kModalConfirmed,
was_notice_shown);
} else {
modal_error_status_ =
metrics::PlusAddressModalCompletionStatus::kConfirmPlusAddressError;
}
// Note that in case of `suppress_ui_for_testing_` or bottom sheet dismissal
// prior to service response, `view_` will be null.
if (view_) {
view_->ShowConfirmResult(maybe_plus_profile);
}
}
void PlusAddressCreationControllerAndroid::RecordModalShownOutcome(
metrics::PlusAddressModalCompletionStatus status,
bool was_notice_shown) {
if (modal_shown_time_.has_value()) {
metrics::RecordModalShownOutcome(
status, base::TimeTicks::Now() - *modal_shown_time_,
std::max(reserve_response_count_ - 1, 0), was_notice_shown);
modal_shown_time_.reset();
reserve_response_count_ = 0;
}
}
bool PlusAddressCreationControllerAndroid::ShouldShowNotice() const {
// `this` is never created as a `const` member - therefore the cast is safe.
const PlusAddressSettingService* setting_service =
const_cast<PlusAddressCreationControllerAndroid*>(this)
->GetPlusAddressSettingService();
return setting_service && !setting_service->GetHasAcceptedNotice() &&
base::FeatureList::IsEnabled(
features::kPlusAddressUserOnboardingEnabled);
}
PlusAddressService*
PlusAddressCreationControllerAndroid::GetPlusAddressService() {
return PlusAddressServiceFactory::GetForBrowserContext(
GetWebContents().GetBrowserContext());
}
PlusAddressSettingService*
PlusAddressCreationControllerAndroid::GetPlusAddressSettingService() {
return PlusAddressSettingServiceFactory::GetForBrowserContext(
GetWebContents().GetBrowserContext());
}
base::WeakPtr<PlusAddressCreationControllerAndroid>
PlusAddressCreationControllerAndroid::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(PlusAddressCreationControllerAndroid);
} // namespace plus_addresses