Commit 1a4aa621 authored by Liquan (Max) Gu's avatar Liquan (Max) Gu Committed by Commit Bot

[PRImpl] Move PRImpl#buildUI UI logic into PaymentUIsManager

Change:
* Move the UI logic of PaymentRequestImpl#buildUI() into
PaymentUIsManager#buildPaymentRequestUI()
* Change buildPaymentRequestUI()'s structure to return an error
message. This way, PRImpl#buildUI()'s size can be reduced.
* PRImpl no longer depends on:
 - org.chromium.chrome.browser.tab
 - org.chromium.chrome.browser.tabmodel
 - org.chromium.chrome.browser.compositor.layouts
 - org.chromium.chrome.browser.ChromeTabbedActivity

Bug: 1102522

Change-Id: I5f61a57b7891ec06a59527852d1a37f14bda031a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2384315
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805524}
parent c97a51a3
......@@ -18,14 +18,10 @@ import org.chromium.base.LocaleUtils;
import org.chromium.base.Log;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.NormalizedAddressRequestDelegate;
import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator;
import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.PaymentHandlerWebContentsObserver;
import org.chromium.chrome.browser.payments.minimal.MinimalUICoordinator;
......@@ -38,13 +34,6 @@ import org.chromium.chrome.browser.payments.ui.SectionInformation;
import org.chromium.chrome.browser.payments.ui.ShoppingCart;
import org.chromium.chrome.browser.settings.SettingsLauncher;
import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
import org.chromium.components.autofill.EditableOption;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
import org.chromium.components.embedder_support.util.UrlConstants;
......@@ -167,35 +156,6 @@ public class PaymentRequestImpl
@Nullable
private ComponentPaymentRequestImpl mComponentPaymentRequestImpl;
/** Monitors changes in the TabModelSelector. */
private final TabModelSelectorObserver mSelectorObserver = new EmptyTabModelSelectorObserver() {
@Override
public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
disconnectFromClientWithDebugMessage(ErrorStrings.TAB_SWITCH);
}
};
/** Monitors changes in the current TabModel. */
private final TabModelObserver mTabModelObserver = new TabModelObserver() {
@Override
public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
if (tab == null || tab.getId() != lastId) {
mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
disconnectFromClientWithDebugMessage(ErrorStrings.TAB_SWITCH);
}
}
};
/** Monitors changes in the tab overview. */
private final OverviewModeObserver mOverviewModeObserver = new EmptyOverviewModeObserver() {
@Override
public void onOverviewModeStartedShowing(boolean showToolbar) {
mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
disconnectFromClientWithDebugMessage(ErrorStrings.TAB_OVERVIEW_MODE);
}
};
private final Handler mHandler = new Handler();
private final RenderFrameHost mRenderFrameHost;
private final Delegate mDelegate;
......@@ -232,9 +192,6 @@ public class PaymentRequestImpl
private PaymentApp mInvokedPaymentApp;
private boolean mHideServerAutofillCards;
private boolean mWaitForUpdatedDetails;
private TabModelSelector mObservedTabModelSelector;
private TabModel mObservedTabModel;
private OverviewModeBehavior mOverviewModeBehavior;
private PaymentHandlerHost mPaymentHandlerHost;
/**
......@@ -448,62 +405,18 @@ public class PaymentRequestImpl
/** @return Whether the UI was built. */
private boolean buildUI(ChromeActivity activity) {
// Payment methods section must be ready before building the rest of the UI. This is because
// shipping and contact sections (when requested by merchant) are populated depending on
// whether or not the selected payment app (if such exists) can provide the required
// information.
assert mPaymentUIsManager.getPaymentMethodsSection() != null;
assert activity != null;
// Catch any time the user switches tabs. Because the dialog is modal, a user shouldn't be
// allowed to switch tabs, which can happen if the user receives an external Intent.
mObservedTabModelSelector = activity.getTabModelSelector();
mObservedTabModel = activity.getCurrentTabModel();
mObservedTabModelSelector.addObserver(mSelectorObserver);
mObservedTabModel.addObserver(mTabModelObserver);
// Only the currently selected tab is allowed to show the payment UI.
if (!mDelegate.isWebContentsActive(activity)) {
String error = mPaymentUIsManager.buildPaymentRequestUI(activity,
/*isWebContentsActive=*/mDelegate.isWebContentsActive(activity),
/*waitForUpdatedDetails=*/mWaitForUpdatedDetails);
if (error != null) {
mJourneyLogger.setNotShown(NotShownReason.OTHER);
disconnectFromClientWithDebugMessage(ErrorStrings.CANNOT_SHOW_IN_BACKGROUND_TAB);
disconnectFromClientWithDebugMessage(error);
if (ComponentPaymentRequestImpl.getObserverForTest() != null) {
ComponentPaymentRequestImpl.getObserverForTest()
.onPaymentRequestServiceShowFailed();
}
return false;
}
// Catch any time the user enters the overview mode and dismiss the payment UI.
if (activity instanceof ChromeTabbedActivity) {
mOverviewModeBehavior =
((ChromeTabbedActivity) activity).getOverviewModeBehaviorSupplier().get();
assert mOverviewModeBehavior != null;
if (mOverviewModeBehavior.overviewVisible()) {
mJourneyLogger.setNotShown(NotShownReason.OTHER);
disconnectFromClientWithDebugMessage(ErrorStrings.TAB_OVERVIEW_MODE);
if (ComponentPaymentRequestImpl.getObserverForTest() != null) {
ComponentPaymentRequestImpl.getObserverForTest()
.onPaymentRequestServiceShowFailed();
}
return false;
}
mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
}
if (shouldShowShippingSection() && !mWaitForUpdatedDetails) {
mPaymentUIsManager.createShippingSectionForPaymentRequestUI(activity);
}
if (shouldShowContactSection()) {
mPaymentUIsManager.setContactSection(
new ContactDetailsSection(activity, mPaymentUIsManager.getAutofillProfiles(),
mPaymentUIsManager.getContactEditor(), mJourneyLogger));
}
mPaymentUIsManager.buildPaymentRequestUI(activity);
return true;
}
......@@ -2113,20 +2026,7 @@ public class PaymentRequestImpl
mPaymentUIsManager.setPaymentMethodsSection(null);
}
if (mObservedTabModelSelector != null) {
mObservedTabModelSelector.removeObserver(mSelectorObserver);
mObservedTabModelSelector = null;
}
if (mObservedTabModel != null) {
mObservedTabModel.removeObserver(mTabModelObserver);
mObservedTabModel = null;
}
if (mOverviewModeBehavior != null) {
mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
mOverviewModeBehavior = null;
}
mPaymentUIsManager.removeLeavingTabObservers();
SettingsAutofillAndPaymentsObserver.getInstance().unregisterObserver(mPaymentUIsManager);
......@@ -2193,4 +2093,12 @@ public class PaymentRequestImpl
if (mComponentPaymentRequestImpl == null) return;
mComponentPaymentRequestImpl.warnNoFavicon();
}
// Implement PaymentUIsObserver.onLeavingCurrentTab:
@Override
public void onLeavingCurrentTab(String reason) {
if (mComponentPaymentRequestImpl == null) return;
mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
disconnectFromClientWithDebugMessage(reason);
}
}
......@@ -14,10 +14,14 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
import org.chromium.chrome.browser.payments.AddressEditor;
import org.chromium.chrome.browser.payments.AutofillAddress;
import org.chromium.chrome.browser.payments.AutofillContact;
......@@ -35,10 +39,19 @@ import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.Pa
import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.PaymentHandlerWebContentsObserver;
import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection.FocusChangedObserver;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
import org.chromium.components.autofill.Completable;
import org.chromium.components.autofill.EditableOption;
import org.chromium.components.payments.BrowserPaymentRequest;
import org.chromium.components.payments.CurrencyFormatter;
import org.chromium.components.payments.ErrorStrings;
import org.chromium.components.payments.JourneyLogger;
import org.chromium.components.payments.PaymentApp;
import org.chromium.components.payments.PaymentAppType;
......@@ -94,6 +107,9 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
private final boolean mIsOffTheRecord;
private final Handler mHandler = new Handler();
private final Queue<Runnable> mRetryQueue = new LinkedList<>();
private final OverviewModeObserver mOverviewModeObserver;
private final TabModelSelectorObserver mSelectorObserver;
private final TabModelObserver mTabModelObserver;
private ContactEditor mContactEditor;
private PaymentHandlerCoordinator mPaymentHandlerUi;
private Callback<PaymentInformation> mPaymentInformationCallback;
......@@ -122,6 +138,9 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
private boolean mCanUserAddCreditCard;
private final JourneyLogger mJourneyLogger;
private PaymentUIsObserver mObserver;
private TabModelSelector mObservedTabModelSelector;
private TabModel mObservedTabModel;
private OverviewModeBehavior mOverviewModeBehavior;
/**
* True if we should skip showing PaymentRequest UI.
......@@ -231,6 +250,26 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
mIsOffTheRecord = isOffTheRecord;
mPaymentAppComparator = new PaymentAppComparator(/*params=*/mParams);
mObserver = observer;
mSelectorObserver = new EmptyTabModelSelectorObserver() {
@Override
public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
mObserver.onLeavingCurrentTab(ErrorStrings.TAB_SWITCH);
}
};
mTabModelObserver = new TabModelObserver() {
@Override
public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
if (tab == null || tab.getId() != lastId) {
mObserver.onLeavingCurrentTab(ErrorStrings.TAB_SWITCH);
}
}
};
mOverviewModeObserver = new EmptyOverviewModeObserver() {
@Override
public void onOverviewModeStartedShowing(boolean showToolbar) {
mObserver.onLeavingCurrentTab(ErrorStrings.TAB_OVERVIEW_MODE);
}
};
}
/** @return The PaymentRequestUI. */
......@@ -947,10 +986,61 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
/**
* Build the PaymentRequest UI.
* @param activity The ChromeActivity for the payment request.
* @param activity The ChromeActivity for the payment request, cannot be null.
* @param isWebContentsActive Whether the merchant's WebContents is active.
* @param waitForUpdatedDetails Whether to wait for updated details. See {@link
* BrowserPaymentRequest#show}'s waitForUpdatedDetails.
* @return The error message if built unsuccessfully; null otherwise.
*/
public void buildPaymentRequestUI(ChromeActivity activity) {
assert mIsPaymentRequestParamsInitiated;
@Nullable
public String buildPaymentRequestUI(
ChromeActivity activity, boolean isWebContentsActive, boolean waitForUpdatedDetails) {
// Payment methods section must be ready before building the rest of the UI. This is because
// shipping and contact sections (when requested by merchant) are populated depending on
// whether or not the selected payment app (if such exists) can provide the required
// information.
assert mPaymentMethodsSection != null;
assert activity != null;
// Only the currently selected tab is allowed to show the payment UI.
if (!isWebContentsActive) return ErrorStrings.CANNOT_SHOW_IN_BACKGROUND_TAB;
// Catch any time the user switches tabs. Because the dialog is modal, a user shouldn't be
// allowed to switch tabs, which can happen if the user receives an external Intent.
if (mObservedTabModelSelector != null) {
mObservedTabModelSelector.removeObserver(mSelectorObserver);
}
mObservedTabModelSelector = activity.getTabModelSelector();
mObservedTabModelSelector.addObserver(mSelectorObserver);
if (mObservedTabModel != null) {
mObservedTabModel.removeObserver(mTabModelObserver);
}
mObservedTabModel = activity.getCurrentTabModel();
mObservedTabModel.addObserver(mTabModelObserver);
// Catch any time the user enters the overview mode and dismiss the payment UI.
if (activity instanceof ChromeTabbedActivity) {
if (mOverviewModeBehavior != null) {
mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
}
mOverviewModeBehavior =
((ChromeTabbedActivity) activity).getOverviewModeBehaviorSupplier().get();
assert mOverviewModeBehavior != null;
if (mOverviewModeBehavior.overviewVisible()) return ErrorStrings.TAB_OVERVIEW_MODE;
mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
}
if (shouldShowShippingSection() && !waitForUpdatedDetails) {
createShippingSectionForPaymentRequestUI(activity);
}
if (shouldShowContactSection()) {
mContactSection = new ContactDetailsSection(
activity, mAutofillProfiles, mContactEditor, mJourneyLogger);
}
mPaymentRequestUI = new PaymentRequestUI(activity, mDelegate.getPaymentRequestUIClient(),
mMerchantSupportsAutofillCards, !PaymentPreferencesUtil.isPaymentCompleteOnce(),
mMerchantName, mTopLevelOriginFormattedForDisplay,
......@@ -985,6 +1075,7 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
if (mContactEditor != null) {
mContactEditor.setEditorDialog(mPaymentRequestUI.getEditorDialog());
}
return null;
}
/** Create a shipping section for PaymentRequest UI. */
......@@ -1291,4 +1382,22 @@ public class PaymentUIsManager implements SettingsAutofillAndPaymentsObserver.Ob
// skip-UI.
&& (isUserGestureShow || !selectedApp.isUserGestureRequiredToSkipUi());
}
/** Removes all of the observers that observe users leaving the tab. */
public void removeLeavingTabObservers() {
if (mObservedTabModelSelector != null) {
mObservedTabModelSelector.removeObserver(mSelectorObserver);
mObservedTabModelSelector = null;
}
if (mObservedTabModel != null) {
mObservedTabModel.removeObserver(mTabModelObserver);
mObservedTabModel = null;
}
if (mOverviewModeBehavior != null) {
mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
mOverviewModeBehavior = null;
}
}
}
......@@ -8,4 +8,12 @@ package org.chromium.components.payments;
public interface PaymentUIsObserver {
/** Called when favicon not available for payment request UI. */
void onPaymentRequestUIFaviconNotAvailable();
/**
* Called when the user is leaving the current tab (e.g., tab switched or tab overview mode is
* shown), upon which the PaymentRequest service should be closed.
* @param reason The reason of leaving the current tab, to be used as debug message for the
* developers.
*/
void onLeavingCurrentTab(String reason);
}
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