| Index: chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
|
| index 66470b75c4ee773e8df4bd47bdc572fdf8fd43ff..dade115b6bb2bc4ac751da5e833297b1402c37f8 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
|
| @@ -9,15 +9,22 @@ import android.animation.AnimatorListenerAdapter;
|
| import android.animation.ObjectAnimator;
|
| import android.content.Context;
|
| import android.graphics.Region;
|
| +import android.support.v4.view.ScrollingView;
|
| import android.util.AttributeSet;
|
| import android.view.GestureDetector;
|
| import android.view.MotionEvent;
|
| import android.view.VelocityTracker;
|
| import android.view.View;
|
| +import android.view.ViewGroup;
|
| import android.view.animation.DecelerateInterpolator;
|
| import android.view.animation.Interpolator;
|
| +import android.widget.FrameLayout;
|
| import android.widget.LinearLayout;
|
|
|
| +import org.chromium.chrome.R;
|
| +import org.chromium.chrome.browser.NativePage;
|
| +import org.chromium.chrome.browser.ntp.NewTabPage;
|
| +import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
| import org.chromium.chrome.browser.util.MathUtils;
|
|
|
| /**
|
| @@ -48,6 +55,9 @@ public class BottomSheet extends LinearLayout {
|
| /** The minimum y/x ratio that a scroll must have to be considered vertical. */
|
| private static final float MIN_VERTICAL_SCROLL_SLOPE = 2.0f;
|
|
|
| + /** The minimum difference that two floats must have to be considered different. */
|
| + private static final float EPSILON = 0.0001f;
|
| +
|
| /**
|
| * Information about the different scroll states of the sheet. Order is important for these,
|
| * they go from smallest to largest.
|
| @@ -73,12 +83,31 @@ public class BottomSheet extends LinearLayout {
|
| /** The height of the toolbar. */
|
| private float mToolbarHeight;
|
|
|
| - /** The height of the view that contains the bottom sheet. */
|
| + /** The width and height of the view that contains the bottom sheet. */
|
| + private float mContainerWidth;
|
| private float mContainerHeight;
|
|
|
| /** The current sheet state. If the sheet is moving, this will be the target state. */
|
| private int mCurrentState;
|
|
|
| + /** Used for getting the current tab. */
|
| + private TabModelSelector mTabModelSelector;
|
| +
|
| + /** A handle to the native page being shown by the sheet. */
|
| + private NativePage mNativePage;
|
| +
|
| + /** A handle to the toolbar control container. */
|
| + private View mControlContainer;
|
| +
|
| + /** A handle to the FrameLayout that holds the content of the bottom sheet. */
|
| + private FrameLayout mBottomSheetContent;
|
| +
|
| + /** A handle to the main scrolling view in the bottom sheet's content. */
|
| + private ScrollingView mScrollingContentView;
|
| +
|
| + /** This is a cached array for getting the window location of different views. */
|
| + private final int[] mLocationArr;
|
| +
|
| /**
|
| * This class is responsible for detecting swipe and scroll events on the bottom sheet or
|
| * ignoring them when appropriate.
|
| @@ -100,18 +129,27 @@ public class BottomSheet extends LinearLayout {
|
| return false;
|
| }
|
|
|
| - // Cancel the settling animation if it is running so it doesn't conflict with where the
|
| + // Cancel the settling animation if it is running so it doesn't conflict with where the
|
| // user wants to move the sheet.
|
| + boolean wasSettleAnimatorRunning = mSettleAnimator != null;
|
| cancelAnimation();
|
|
|
| mVelocityTracker.addMovement(e2);
|
|
|
| float currentShownRatio =
|
| mContainerHeight > 0 ? getSheetOffsetFromBottom() / mContainerHeight : 0;
|
| + boolean isSheetInMaxPosition =
|
| + areFloatsEqual(currentShownRatio, mStateRatios[mStateRatios.length - 1]);
|
| +
|
| + // Allow the bottom sheet's content to be scrolled up without dragging the sheet down.
|
| + if (!isTouchEventInToolbar(e2) && isSheetInMaxPosition && mScrollingContentView != null
|
| + && mScrollingContentView.computeVerticalScrollOffset() > 0) {
|
| + mIsScrolling = false;
|
| + return false;
|
| + }
|
|
|
| // If the sheet is in the max position, don't move if the scroll is upward.
|
| - if (currentShownRatio >= mStateRatios[mStateRatios.length - 1]
|
| - && distanceY > 0) {
|
| + if (isSheetInMaxPosition && distanceY > 0) {
|
| mIsScrolling = false;
|
| return false;
|
| }
|
| @@ -122,6 +160,12 @@ public class BottomSheet extends LinearLayout {
|
| return false;
|
| }
|
|
|
| + // Send a notification that the sheet is exiting the peeking state into something that
|
| + // will show content.
|
| + if (!mIsScrolling && mCurrentState == STATE_PEEK && !wasSettleAnimatorRunning) {
|
| + onExitPeekState();
|
| + }
|
| +
|
| float newOffset = getSheetOffsetFromBottom() + distanceY;
|
| setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset()));
|
|
|
| @@ -154,6 +198,7 @@ public class BottomSheet extends LinearLayout {
|
| public BottomSheet(Context context, AttributeSet atts) {
|
| super(context, atts);
|
|
|
| + mLocationArr = new int[2];
|
| mVelocityTracker = VelocityTracker.obtain();
|
|
|
| mGestureDetector = new GestureDetector(context, new BottomSheetSwipeDetector());
|
| @@ -207,6 +252,13 @@ public class BottomSheet extends LinearLayout {
|
| }
|
|
|
| /**
|
| + * @param tabModelSelector A TabModelSelector for getting the current tab and activity.
|
| + */
|
| + public void setTabModelSelector(TabModelSelector tabModelSelector) {
|
| + mTabModelSelector = tabModelSelector;
|
| + }
|
| +
|
| + /**
|
| * Adds layout change listeners to the views that the bottom sheet depends on. Namely the
|
| * heights of the root view and control container are important as they are used in many of the
|
| * calculations in this class.
|
| @@ -214,7 +266,9 @@ public class BottomSheet extends LinearLayout {
|
| * @param controlContainer The container for the toolbar.
|
| */
|
| public void init(View root, View controlContainer) {
|
| - mToolbarHeight = controlContainer.getHeight();
|
| + mControlContainer = controlContainer;
|
| + mToolbarHeight = mControlContainer.getHeight();
|
| + mBottomSheetContent = (FrameLayout) findViewById(R.id.bottom_sheet_content);
|
| mCurrentState = STATE_PEEK;
|
|
|
| // Listen to height changes on the root.
|
| @@ -227,8 +281,9 @@ public class BottomSheet extends LinearLayout {
|
| return;
|
| }
|
|
|
| + mContainerWidth = right - left;
|
| mContainerHeight = bottom - top;
|
| - updateSheetPeekHeight(mToolbarHeight, mContainerHeight);
|
| + updateSheetDimensions(mToolbarHeight, mContainerWidth, mContainerHeight);
|
|
|
| cancelAnimation();
|
| setSheetState(mCurrentState, false);
|
| @@ -246,7 +301,7 @@ public class BottomSheet extends LinearLayout {
|
| }
|
|
|
| mToolbarHeight = bottom - top;
|
| - updateSheetPeekHeight(mToolbarHeight, mContainerHeight);
|
| + updateSheetDimensions(mToolbarHeight, mContainerWidth, mContainerHeight);
|
|
|
| cancelAnimation();
|
| setSheetState(mCurrentState, false);
|
| @@ -255,6 +310,57 @@ public class BottomSheet extends LinearLayout {
|
| }
|
|
|
| /**
|
| + * Determines if a touch event is inside the toolbar. This assumes the toolbar is the full
|
| + * width of the screen and that the toolbar is at the top of the bottom sheet.
|
| + * @param e The motion event to test.
|
| + * @return True if the event occured in the toolbar region.
|
| + */
|
| + private boolean isTouchEventInToolbar(MotionEvent e) {
|
| + // Is the touch in the toolbar area?
|
| + if (mControlContainer != null) {
|
| + mControlContainer.getLocationInWindow(mLocationArr);
|
| + } else {
|
| + mLocationArr[0] = mLocationArr[1] = 0;
|
| + }
|
| +
|
| + return e.getRawY() < mLocationArr[1] + mToolbarHeight;
|
| + }
|
| +
|
| + /**
|
| + * Determine if two floats are equal.
|
| + * @param f1 The first float to compare.
|
| + * @param f2 The second float to compare.
|
| + * @return True if the floats are equal.
|
| + */
|
| + private boolean areFloatsEqual(float f1, float f2) {
|
| + return Math.abs(f1 - f2) < EPSILON;
|
| + }
|
| +
|
| + /**
|
| + * A notification that the sheet is exiting the peek state into one that shows content.
|
| + */
|
| + private void onExitPeekState() {
|
| + if (mNativePage == null) {
|
| + showNativePage(new NewTabPage(mTabModelSelector.getCurrentTab().getActivity(),
|
| + mTabModelSelector.getCurrentTab(), mTabModelSelector));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Show a native page in the bottom sheet's content area.
|
| + * @param page The NativePage to show.
|
| + */
|
| + private void showNativePage(NativePage page) {
|
| + if (mNativePage != null) mBottomSheetContent.removeView(mNativePage.getView());
|
| +
|
| + mNativePage = page;
|
| + mBottomSheetContent.addView(mNativePage.getView());
|
| + mScrollingContentView = findScrollingChild(mNativePage.getView());
|
| +
|
| + mNativePage.updateForUrl("");
|
| + }
|
| +
|
| + /**
|
| * Creates an unadjusted version of a MotionEvent.
|
| * @param e The original event.
|
| * @return The unadjusted version of the event.
|
| @@ -266,14 +372,45 @@ public class BottomSheet extends LinearLayout {
|
| }
|
|
|
| /**
|
| - * Updates the bottom sheet's peeking height.
|
| + * Updates the bottom sheet's peeking and content height.
|
| * @param toolbarHeight The height of the toolbar control container.
|
| + * @param containerWidth The width of the bottom sheet's container.
|
| * @param containerHeight The height of the bottom sheet's container.
|
| */
|
| - private void updateSheetPeekHeight(float toolbarHeight, float containerHeight) {
|
| + private void updateSheetDimensions(float toolbarHeight, float containerWidth,
|
| + float containerHeight) {
|
| if (containerHeight <= 0) return;
|
|
|
| mStateRatios[0] = toolbarHeight / containerHeight;
|
| +
|
| + // Compute the height that the content section of the bottom sheet should be.
|
| + float contentHeight =
|
| + (containerHeight * mStateRatios[mStateRatios.length - 1]) - toolbarHeight;
|
| + mBottomSheetContent.setLayoutParams(
|
| + new LinearLayout.LayoutParams((int) containerWidth, (int) contentHeight));
|
| + }
|
| +
|
| + /**
|
| + * Find the first ScrollingView in a view hierarchy.
|
| + * TODO(mdjones): The root of native pages should be a ScrollingView so this logic is not
|
| + * necessary.
|
| + * @param view The root of the tree or subtree.
|
| + * @return The first scrolling view or null.
|
| + */
|
| + private ScrollingView findScrollingChild(View view) {
|
| + if (view instanceof ScrollingView) {
|
| + return (ScrollingView) view;
|
| + }
|
| + if (view instanceof ViewGroup) {
|
| + ViewGroup group = (ViewGroup) view;
|
| + for (int i = 0, count = group.getChildCount(); i < count; i++) {
|
| + ScrollingView scrollingChild = findScrollingChild(group.getChildAt(i));
|
| + if (scrollingChild != null) {
|
| + return scrollingChild;
|
| + }
|
| + }
|
| + }
|
| + return null;
|
| }
|
|
|
| /**
|
|
|