blob: b459842f76f2c0cd763675c9055432cbf12d0e71 [file] [log] [blame]
// Copyright 2018 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.
package org.chromium.chrome.browser.autofill_assistant;
import android.content.Context;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.CoordinatorLayout;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselCoordinator;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsCoordinator;
import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderCoordinator;
import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxCoordinator;
import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator;
import org.chromium.ui.modelutil.ListModel;
/**
* Coordinator responsible for the Autofill Assistant bottom bar. This coordinator allows to enable
* or disable the swipeable behavior of the bottom bar and ensures that the bottom bar height is
* constant during the script execution (if possible) by adapting the spacing between its child
* views (details, infobox, payment request and carousel).
*/
class AssistantBottomBarCoordinator {
// The top padding that should be applied to the bottom bar when the swiping indicator is
// hidden.
private static final int BOTTOM_BAR_WITHOUT_INDICATOR_PADDING_TOP_DP = 16;
private final ViewGroup mBottomBarView;
private final ViewGroup mBottomBarContainerView;
private final View mSwipeIndicatorView;
private final BottomSheetBehavior mBottomBarBehavior;
// Dimensions in device pixels.
private final int mBottomBarWithoutIndicatorPaddingTop;
// Child coordinators.
private final AssistantHeaderCoordinator mHeaderCoordinator;
private final AssistantInfoBoxCoordinator mInfoBoxCoordinator;
private final AssistantDetailsCoordinator mDetailsCoordinator;
private final AssistantPaymentRequestCoordinator mPaymentRequestCoordinator;
private final AssistantCarouselCoordinator mSuggestionsCoordinator;
private final AssistantCarouselCoordinator mActionsCoordinator;
AssistantBottomBarCoordinator(Context context, View assistantView, AssistantModel model) {
mBottomBarView = assistantView.findViewById(R.id.autofill_assistant_bottombar);
mBottomBarContainerView =
mBottomBarView.findViewById(R.id.autofill_assistant_bottombar_container);
mSwipeIndicatorView = mBottomBarView.findViewById(R.id.swipe_indicator);
mBottomBarBehavior = BottomSheetBehavior.from(mBottomBarView);
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
mBottomBarWithoutIndicatorPaddingTop =
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
BOTTOM_BAR_WITHOUT_INDICATOR_PADDING_TOP_DP, displayMetrics);
// Instantiate child components.
mHeaderCoordinator =
new AssistantHeaderCoordinator(context, mBottomBarView, model.getHeaderModel());
mInfoBoxCoordinator = new AssistantInfoBoxCoordinator(context, model.getInfoBoxModel());
mDetailsCoordinator = new AssistantDetailsCoordinator(context, model.getDetailsModel());
mPaymentRequestCoordinator =
new AssistantPaymentRequestCoordinator(context, model.getPaymentRequestModel());
mSuggestionsCoordinator =
new AssistantCarouselCoordinator(context, model.getSuggestionsModel());
mActionsCoordinator = new AssistantCarouselCoordinator(context, model.getActionsModel());
// Add child views to bottom bar container.
mBottomBarContainerView.addView(mInfoBoxCoordinator.getView());
mBottomBarContainerView.addView(mDetailsCoordinator.getView());
mBottomBarContainerView.addView(mPaymentRequestCoordinator.getView());
mBottomBarContainerView.addView(mSuggestionsCoordinator.getView());
mBottomBarContainerView.addView(mActionsCoordinator.getView());
// Set children top margins to have a spacing between them. For the carousels, we set their
// margin only when they are not empty given that they are always shown, even if empty. We
// do not hide them because there is an incompatibility bug between the animateLayoutChanges
// attribute set on mBottomBarContainerView and the animations ran by the carousels
// RecyclerView.
int childSpacing = context.getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_bottombar_vertical_spacing);
setChildMarginTop(mDetailsCoordinator.getView(), childSpacing);
setChildMarginTop(mPaymentRequestCoordinator.getView(), childSpacing);
setCarouselMarginTop(mSuggestionsCoordinator.getView(),
model.getSuggestionsModel().getChipsModel(), childSpacing);
setCarouselMarginTop(mActionsCoordinator.getView(), model.getActionsModel().getChipsModel(),
childSpacing);
// We set the horizontal margins of the details and payment request. We don't set a padding
// to the container as we want the carousels children to be scrolled at the limit of the
// screen.
setHorizontalMargins(mInfoBoxCoordinator.getView());
setHorizontalMargins(mDetailsCoordinator.getView());
setHorizontalMargins(mPaymentRequestCoordinator.getView());
}
/**
* Return the container view representing the bottom bar. Adding child views to this view should
* add them below the header.
*/
public ViewGroup getView() {
return mBottomBarView;
}
/**
* Returns the view container inside the bottom bar view.
*/
public ViewGroup getContainerView() {
return mBottomBarContainerView;
}
/**
* Make sure the bottom bar is expanded and text is visible.
*/
public void expand() {
mBottomBarBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
/**
* Enable or disable to swipeable behavior of the bottom bar.
*/
public void allowSwipingBottomSheet(boolean allowed) {
CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mBottomBarView.getLayoutParams();
if (allowed) {
params.setBehavior(mBottomBarBehavior);
mSwipeIndicatorView.setVisibility(View.VISIBLE);
setBottomBarPaddingTop(0);
} else {
params.setBehavior(null);
mSwipeIndicatorView.setVisibility(View.GONE);
setBottomBarPaddingTop(mBottomBarWithoutIndicatorPaddingTop);
}
}
private void setChildMarginTop(View child, int marginTop) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) child.getLayoutParams();
params.topMargin = marginTop;
child.setLayoutParams(params);
}
/**
* Observe {@code model} such that we set the topMargin of {@code carouselView} to {@code
* marginTop} when {@code model} is not empty and set it to 0 otherwise.
*/
private void setCarouselMarginTop(
View carouselView, ListModel<AssistantChip> chipsModel, int marginTop) {
chipsModel.addObserver(new AbstractListObserver<Void>() {
@Override
public void onDataSetChanged() {
setChildMarginTop(carouselView, chipsModel.size() > 0 ? marginTop : 0);
}
});
}
@VisibleForTesting
public AssistantCarouselCoordinator getSuggestionsCoordinator() {
return mSuggestionsCoordinator;
}
@VisibleForTesting
public AssistantCarouselCoordinator getActionsCoordinator() {
return mActionsCoordinator;
}
private void setBottomBarPaddingTop(int paddingPx) {
mBottomBarView.setPadding(0, paddingPx, 0, 0);
}
private void setHorizontalMargins(View view) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
int horizontalMargin = view.getContext().getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_bottombar_horizontal_spacing);
layoutParams.setMarginStart(horizontalMargin);
layoutParams.setMarginEnd(horizontalMargin);
view.setLayoutParams(layoutParams);
}
}