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

[PaymentHandler] Add web-content to bottom-sheet

As part of the Scroll-to-expand payment handler project, this patch
will embed the web-content of the payment handler web-app into the
payment-handler bottom-sheet.

This patch also adds the CustomTabToolbar view (without the coordinator
yet) as the payment-handler's toolbar.

Bug: 999196

Change-Id: I403de9c01c3598bb120f0fb51dc7872adda8c8a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1872167
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarDanyao Wang <danyao@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709082}
parent 15d575f9
......@@ -2,9 +2,13 @@
<!-- 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. -->
<RelativeLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/sheet_bg_color"
android:layout_height="match_parent"
android:layout_width="match_parent">
</RelativeLayout>
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.chromium.chrome.browser.ui.widget.FadingShadowView
android:id="@+id/shadow"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_shadow_height"/>
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</RelativeLayout>
......@@ -1189,7 +1189,8 @@ public class PaymentRequestImpl
mPaymentHandlerUi = new PaymentHandlerCoordinator();
ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents);
if (chromeActivity == null) return false;
return mPaymentHandlerUi.show(chromeActivity, this::onPaymentHandlerUiDismissed);
return mPaymentHandlerUi.show(
chromeActivity, this::onPaymentHandlerUiDismissed, url, mIsIncognito);
}
private void onPaymentHandlerUiDismissed() {
......
......@@ -6,10 +6,18 @@ package org.chromium.chrome.browser.payments.handler;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeVersionInfo;
import org.chromium.chrome.browser.WebContentsFactory;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import java.net.URI;
/**
* PaymentHandler coordinator, which owns the component overall, i.e., creates other objects in the
* component and connects them. It decouples the implementation of this component from other
......@@ -18,6 +26,7 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
*/
public class PaymentHandlerCoordinator {
private Runnable mHider;
private WebContents mWebContents;
/** Observer for the dismissal of the payment-handler UI. */
public interface DismissObserver {
......@@ -36,23 +45,38 @@ public class PaymentHandlerCoordinator {
/**
* Shows the payment-handler UI.
*
* @param chromeActivity The activity where the UI should be shown.
* @param chromeActivity The activity where the UI should be shown.
* @param dismissObserver The observer to be notified when the user has dismissed the UI.
* @param url The url of the payment handler app, i.e., that of
* "PaymentRequestEvent.openWindow(url)".
* @param isIncognito Whether the tab is in incognito mode.
* @return Whether the payment-handler UI was shown. Can be false if the UI was suppressed.
*/
public boolean show(ChromeActivity activity, DismissObserver dismissObserver) {
public boolean show(ChromeActivity activity, DismissObserver dismissObserver, URI url,
boolean isIncognito) {
assert mHider == null : "Already showing payment-handler UI";
PaymentHandlerMediator mediator = new PaymentHandlerMediator(this::hide, dismissObserver);
PropertyModel model = new PropertyModel.Builder(PaymentHandlerProperties.ALL_KEYS).build();
PaymentHandlerMediator mediator =
new PaymentHandlerMediator(model, this::hide, dismissObserver);
BottomSheetController bottomSheetController = activity.getBottomSheetController();
bottomSheetController.getBottomSheet().addObserver(mediator);
PropertyModel model = new PropertyModel.Builder(PaymentHandlerProperties.ALL_KEYS).build();
PaymentHandlerView view = new PaymentHandlerView(activity);
mWebContents = WebContentsFactory.createWebContents(isIncognito, /*initiallyHidden=*/false);
ContentView webContentView = ContentView.createContentView(activity, mWebContents);
mWebContents.initialize(ChromeVersionInfo.getProductVersion(),
ViewAndroidDelegate.createBasicDelegate(webContentView), webContentView,
activity.getWindowAndroid(), WebContents.createDefaultInternalsHolder());
mWebContents.getNavigationController().loadUrl(new LoadUrlParams(url.toString()));
PaymentHandlerView view = new PaymentHandlerView(
activity, bottomSheetController.getBottomSheet(), mWebContents, webContentView);
PropertyModelChangeProcessor changeProcessor =
PropertyModelChangeProcessor.create(model, view, PaymentHandlerViewBinder::bind);
mHider = () -> {
changeProcessor.destroy();
bottomSheetController.getBottomSheet().removeObserver(mediator);
bottomSheetController.hideContent(/*content=*/view, /*animate=*/true);
mWebContents.destroy();
};
boolean result = bottomSheetController.requestShowContent(view, /*animate=*/true);
if (result) bottomSheetController.expandSheet();
......
......@@ -7,16 +7,20 @@ package org.chromium.chrome.browser.payments.handler;
import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.DismissObserver;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.SheetState;
import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.ui.modelutil.PropertyModel;
/**
* PaymentHandler mediator, which is responsible for receiving events from the view and notifies the
* backend (the coordinator).
*/
/* package */ class PaymentHandlerMediator extends EmptyBottomSheetObserver {
private final PropertyModel mModel;
private final Runnable mHider;
private final DismissObserver mDismissObserver;
/* package */ PaymentHandlerMediator(Runnable hider, DismissObserver dismissObserver) {
/* package */ PaymentHandlerMediator(
PropertyModel model, Runnable hider, DismissObserver dismissObserver) {
mModel = model;
mHider = hider;
mDismissObserver = dismissObserver;
}
......@@ -31,4 +35,9 @@ import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
break;
}
}
@Override
public void onSheetOffsetChanged(float heightFraction, float offsetPx) {
mModel.set(PaymentHandlerProperties.BOTTOM_SHEET_HEIGHT_FRACTION, heightFraction);
}
}
......@@ -5,11 +5,16 @@
package org.chromium.chrome.browser.payments.handler;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
/** PaymentHandler UI properties, which fully describe the state of the UI. */
/* package */ class PaymentHandlerProperties {
// TODO(maxlg): Should add more keys after we add more states to the widget .
/* package */ static final PropertyKey[] ALL_KEYS = new PropertyKey[] {};
/** The height fraction defined in {@link BottomSHeetObserver#onSheetOffsetChanged} */
/* package */ static final WritableFloatPropertyKey BOTTOM_SHEET_HEIGHT_FRACTION =
new WritableFloatPropertyKey();
/* package */ static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {BOTTOM_SHEET_HEIGHT_FRACTION};
// Prevent instantiation.
private PaymentHandlerProperties() {}
......
......@@ -4,23 +4,91 @@
package org.chromium.chrome.browser.payments.handler;
import android.content.Context;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.thinwebview.ThinWebView;
import org.chromium.chrome.browser.thinwebview.ThinWebViewFactory;
import org.chromium.chrome.browser.ui.widget.FadingShadow;
import org.chromium.chrome.browser.ui.widget.FadingShadowView;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetContent;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ActivityWindowAndroid;
/** PaymentHandler UI. */
/* package */ class PaymentHandlerView implements BottomSheetContent {
private final int mToolbarHeightPx;
private final View mToolbarView;
private final View mContentView;
private final FrameLayout mContentView;
private final ThinWebView mThinWebView;
private final Handler mReflowHandler = new Handler();
private final int mTabHeight;
/**
* Construct the PaymentHandlerView.
*
* @param activity The activity where the bottome-sheet should be shown.
* @param bottomSheet The bottom-sheet where the web-content should be shown.
* @param webContents The web-content of the payment-handler web-app.
* @param webContentView The {@link ContentView} that has been contructed with the web-content.
*/
/* package */ PaymentHandlerView(ChromeActivity activity, BottomSheet bottomSheet,
WebContents webContents, ContentView webContentView) {
mToolbarHeightPx = activity.getResources().getDimensionPixelSize(
R.dimen.custom_tabs_control_container_height);
mTabHeight = activity.getActivityTab().getHeight();
mToolbarView = LayoutInflater.from(activity).inflate(R.layout.custom_tabs_toolbar, null);
mContentView = (FrameLayout) LayoutInflater.from(activity).inflate(
R.layout.payment_handler_content, null);
mThinWebView = ThinWebViewFactory.create(activity, new ActivityWindowAndroid(activity));
initContentView(activity, mThinWebView, webContents, webContentView);
}
/**
* Initialize the content view.
*
* @param activity The activity where the bottome-sheet should be shown.
* @param thinWebView The {@link ThinWebView} that was created with the activity.
* @param webContents The web-content of the payment-handler web-app.
* @param webContentView The {@link ContentView} that has been contructed with the web-content.
*/
private void initContentView(ChromeActivity activity, ThinWebView thinWebView,
WebContents webContents, ContentView webContentView) {
FadingShadowView shadow = mContentView.findViewById(R.id.shadow);
shadow.init(ApiCompatibilityUtils.getColor(
activity.getResources(), R.color.toolbar_shadow_color),
FadingShadow.POSITION_TOP);
assert webContentView.getParent() == null;
thinWebView.attachWebContents(webContents, webContentView);
mContentView.setPadding(/*left=*/0, /*top=*/mToolbarHeightPx, /*right=*/0, /*bottom=*/0);
mContentView.addView(thinWebView.getView(), /*index=*/0);
}
/* package */ PaymentHandlerView(Context context) {
mToolbarView = LayoutInflater.from(context).inflate(R.layout.payment_handler_toolbar, null);
mContentView = LayoutInflater.from(context).inflate(R.layout.payment_handler_content, null);
/* A callback when the heightFraction property changed.*/
/* package */ void onHeightFractionChanged(float heightFraction) {
// Reflow the web-content when the bottom-sheet size stops changing.
mReflowHandler.removeCallbacksAndMessages(null);
mReflowHandler.postDelayed(() -> reflowWebContents(heightFraction), /*delayMillis=*/100);
}
/* Resize ThinWebView to reflow the web-contents. */
private void reflowWebContents(float heightFraction) {
// Scale mThinWebView to make the web-content fit into the visible area of the bottom-sheet.
if (mThinWebView.getView() == null) return;
LayoutParams params = (LayoutParams) mThinWebView.getView().getLayoutParams();
params.height = Math.max(0, (int) (mTabHeight * heightFraction) - mToolbarHeightPx);
mThinWebView.getView().setLayoutParams(params);
}
// BottomSheetContent:
......@@ -41,7 +109,10 @@ import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetCon
}
@Override
public void destroy() {}
public void destroy() {
mReflowHandler.removeCallbacksAndMessages(null);
mThinWebView.destroy();
}
@Override
@BottomSheet.ContentPriority
......@@ -63,22 +134,22 @@ import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetCon
@Override
public int getSheetContentDescriptionStringId() {
return R.string.payment_request_payment_method_section_name;
return R.string.payment_handler_sheet_description;
}
@Override
public int getSheetHalfHeightAccessibilityStringId() {
return R.string.payment_request_payment_method_section_name;
return R.string.payment_handler_sheet_opened_half;
}
@Override
public int getSheetFullHeightAccessibilityStringId() {
return R.string.payment_request_payment_method_section_name;
return R.string.payment_handler_sheet_opened_full;
}
@Override
public int getSheetClosedAccessibilityStringId() {
return R.string.payment_request_payment_method_section_name;
return R.string.payment_handler_sheet_closed;
}
@Override
......
......@@ -14,6 +14,9 @@ import org.chromium.ui.modelutil.PropertyModel;
/* package */ class PaymentHandlerViewBinder {
/* package */ static void bind(
PropertyModel model, PaymentHandlerView view, PropertyKey propertyKey) {
// TODO(maxlg): bind model properties to view after adding view widgets.
if (PaymentHandlerProperties.BOTTOM_SHEET_HEIGHT_FRACTION == propertyKey) {
view.onHeightFractionChanged(
model.get(PaymentHandlerProperties.BOTTOM_SHEET_HEIGHT_FRACTION));
}
}
}
gogerald@chromium.org
rouslan@chromium.org
danyao@chromium.org
maxlg@chromium.org
# COMPONENT: UI>Browser>Payments
......@@ -652,4 +652,18 @@
<message name="IDS_PAYMENTS_ORDER_SUMMARY_ACCESSIBLE_LABEL" desc="The string used to format what the screenreader reads out for the order summary section of the Payment Sheet.">
Order Summary, <ph name="TOTAL_LABEL">$1<ex>Total, USD $10.00</ex></ph>, More Details
</message>
<!-- Payment Handler strings -->
<message name="IDS_PAYMENT_HANDLER_SHEET_DESCRIPTION" desc="Accessibility string read when the payment-handler bottom sheet is opened. It describes the payment-handler where users can fill in the payment info." formatter_data="android_java">
Payment handler sheet
</message>
<message name="IDS_PAYMENT_HANDLER_SHEET_OPENED_HALF" desc="Accessibility string read when the payment-handler bottom sheet is opened at half height. The sheet will occupy up to the half screen." formatter_data="android_java">
Payment handler sheet is half-opened
</message>
<message name="IDS_PAYMENT_HANDLER_SHEET_OPENED_FULL" desc="Accessibility string read when the payment-handler bottom sheet is opened at full height. The sheet will occupy up to the full screen." formatter_data="android_java">
Payment handler sheet is opened
</message>
<message name="IDS_PAYMENT_HANDLER_SHEET_CLOSED" desc="Accessibility string read when the payment-handler bottom sheet is closed." formatter_data="android_java">
Payment handler sheet is closed
</message>
</grit-part>
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