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 |
deleted file mode 100644 |
index c1f73b8fda59a5d8c8cceeabd0423e06777ed0f0..0000000000000000000000000000000000000000 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java |
+++ /dev/null |
@@ -1,836 +0,0 @@ |
-// Copyright 2017 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.widget; |
- |
-import android.animation.Animator; |
-import android.animation.AnimatorListenerAdapter; |
-import android.animation.ValueAnimator; |
-import android.content.Context; |
-import android.graphics.Region; |
-import android.support.annotation.IntDef; |
-import android.support.annotation.Nullable; |
-import android.util.AttributeSet; |
-import android.view.GestureDetector; |
-import android.view.MotionEvent; |
-import android.view.VelocityTracker; |
-import android.view.View; |
-import android.view.animation.DecelerateInterpolator; |
-import android.view.animation.Interpolator; |
-import android.widget.FrameLayout; |
- |
-import org.chromium.base.ApiCompatibilityUtils; |
-import org.chromium.base.ObserverList; |
-import org.chromium.base.VisibleForTesting; |
-import org.chromium.chrome.R; |
-import org.chromium.chrome.browser.NativePageHost; |
-import org.chromium.chrome.browser.TabLoadStatus; |
-import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; |
-import org.chromium.chrome.browser.ntp.NativePageFactory; |
-import org.chromium.chrome.browser.tab.Tab; |
-import org.chromium.chrome.browser.tabmodel.TabModel; |
-import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
-import org.chromium.chrome.browser.util.MathUtils; |
-import org.chromium.content_public.browser.LoadUrlParams; |
- |
-import java.lang.annotation.Retention; |
-import java.lang.annotation.RetentionPolicy; |
- |
-/** |
- * This class defines the bottom sheet that has multiple states and a persistently showing toolbar. |
- * Namely, the states are: |
- * - PEEK: Only the toolbar is visible at the bottom of the screen. |
- * - HALF: The sheet is expanded to consume around half of the screen. |
- * - FULL: The sheet is expanded to its full height. |
- * |
- * All the computation in this file is based off of the bottom of the screen instead of the top |
- * for simplicity. This means that the bottom of the screen is 0 on the Y axis. |
- */ |
- |
-public class BottomSheet |
- extends FrameLayout implements FadingBackgroundView.FadingViewObserver, NativePageHost { |
- /** The different states that the bottom sheet can have. */ |
- @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}) |
- @Retention(RetentionPolicy.SOURCE) |
- public @interface SheetState {} |
- public static final int SHEET_STATE_PEEK = 0; |
- public static final int SHEET_STATE_HALF = 1; |
- public static final int SHEET_STATE_FULL = 2; |
- |
- /** |
- * The base duration of the settling animation of the sheet. 218 ms is a spec for material |
- * design (this is the minimum time a user is guaranteed to pay attention to something). |
- */ |
- private static final long BASE_ANIMATION_DURATION_MS = 218; |
- |
- /** |
- * The fraction of the way to the next state the sheet must be swiped to animate there when |
- * released. A smaller value here means a smaller swipe is needed to move the sheet around. |
- */ |
- private static final float THRESHOLD_TO_NEXT_STATE = 0.5f; |
- |
- /** The minimum y/x ratio that a scroll must have to be considered vertical. */ |
- private static final float MIN_VERTICAL_SCROLL_SLOPE = 2.0f; |
- |
- /** |
- * Information about the different scroll states of the sheet. Order is important for these, |
- * they go from smallest to largest. |
- */ |
- private static final int[] sStates = |
- new int[] {SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}; |
- private final float[] mStateRatios = new float[] {0.0f, 0.55f, 0.95f}; |
- |
- /** The interpolator that the height animator uses. */ |
- private final Interpolator mInterpolator = new DecelerateInterpolator(1.0f); |
- |
- /** The list of observers of this sheet. */ |
- private final ObserverList<BottomSheetObserver> mObservers = new ObserverList<>(); |
- |
- /** This is a cached array for getting the window location of different views. */ |
- private final int[] mLocationArray = new int[2]; |
- |
- /** For detecting scroll and fling events on the bottom sheet. */ |
- private GestureDetector mGestureDetector; |
- |
- /** Whether or not the user is scrolling the bottom sheet. */ |
- private boolean mIsScrolling; |
- |
- /** Track the velocity of the user's scrolls to determine up or down direction. */ |
- private VelocityTracker mVelocityTracker; |
- |
- /** The animator used to move the sheet to a fixed state when released by the user. */ |
- private ValueAnimator mSettleAnimator; |
- |
- /** The height of the toolbar. */ |
- private float mToolbarHeight; |
- |
- /** The width of the view that contains the bottom sheet. */ |
- private float mContainerWidth; |
- |
- /** The height of the view that contains the bottom sheet. */ |
- 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; |
- |
- /** The fullscreen manager for information about toolbar offsets. */ |
- private ChromeFullscreenManager mFullscreenManager; |
- |
- /** A handle to the content being shown by the sheet. */ |
- private BottomSheetContent mSheetContent; |
- |
- /** A handle to the toolbar control container. */ |
- private View mControlContainer; |
- |
- /** A placeholder for if there is no content in the bottom sheet. */ |
- private View mPlaceholder; |
- |
- /** A handle to the FrameLayout that holds the content of the bottom sheet. */ |
- private FrameLayout mBottomSheetContentContainer; |
- |
- /** |
- * The last ratio sent to observers of onTransitionPeekToHalf(). This is used to ensure the |
- * final value sent to these observers is 1.0f. |
- */ |
- private float mLastPeekToHalfRatioSent; |
- |
- /** The FrameLayout used to hold the bottom sheet toolbar. */ |
- private FrameLayout mToolbarHolder; |
- |
- /** |
- * The default toolbar view. This is shown when the current bottom sheet content doesn't have |
- * its own toolbar and when the bottom sheet is closed. |
- */ |
- private View mDefaultToolbarView; |
- |
- /** The last non-default toolbar view that was attached to mToolbarHolder. */ |
- private View mLastToolbarView; |
- |
- /** |
- * An interface defining content that can be displayed inside of the bottom sheet for Chrome |
- * Home. |
- */ |
- public interface BottomSheetContent { |
- /** |
- * Gets the {@link View} that holds the content to be displayed in the Chrome Home bottom |
- * sheet. |
- * @return The content view. |
- */ |
- View getContentView(); |
- |
- /** |
- * Get the {@link View} that contains the toolbar specific to the content being displayed. |
- * If null is returned, the omnibox is used. |
- * TODO(mdjones): This still needs implementation in the sheet. |
- * |
- * @return The toolbar view. |
- */ |
- @Nullable |
- View getToolbarView(); |
- |
- /** |
- * @return The vertical scroll offset of the content view. |
- */ |
- int getVerticalScrollOffset(); |
- |
- /** |
- * Called to destroy the BottomSheetContent when it is no longer in use. |
- */ |
- void destroy(); |
- } |
- |
- /** |
- * This class is responsible for detecting swipe and scroll events on the bottom sheet or |
- * ignoring them when appropriate. |
- */ |
- private class BottomSheetSwipeDetector extends GestureDetector.SimpleOnGestureListener { |
- @Override |
- public boolean onDown(MotionEvent e) { |
- return true; |
- } |
- |
- @Override |
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, |
- float distanceY) { |
- // Only start scrolling if the scroll is up or down. If the user is already scrolling, |
- // continue moving the sheet. |
- float slope = Math.abs(distanceX) > 0f ? Math.abs(distanceY) / Math.abs(distanceX) : 0f; |
- if (!mIsScrolling && slope < MIN_VERTICAL_SCROLL_SLOPE) { |
- mVelocityTracker.clear(); |
- return false; |
- } |
- |
- // Cancel the settling animation if it is running so it doesn't conflict with where the |
- // user wants to move the sheet. |
- cancelAnimation(); |
- |
- mVelocityTracker.addMovement(e2); |
- |
- float currentShownRatio = |
- mContainerHeight > 0 ? getSheetOffsetFromBottom() / mContainerHeight : 0; |
- boolean isSheetInMaxPosition = |
- MathUtils.areFloatsEqual(currentShownRatio, getFullRatio()); |
- |
- // Allow the bottom sheet's content to be scrolled up without dragging the sheet down. |
- if (!isTouchEventInToolbar(e2) && isSheetInMaxPosition && mSheetContent != null |
- && mSheetContent.getVerticalScrollOffset() > 0) { |
- mIsScrolling = false; |
- return false; |
- } |
- |
- // If the sheet is in the max position, don't move the sheet if the scroll is upward. |
- // Instead, allow the sheet's content to handle it if it needs to. |
- if (isSheetInMaxPosition && distanceY > 0) { |
- mIsScrolling = false; |
- return false; |
- } |
- |
- // Similarly, if the sheet is in the min position, don't move if the scroll is downward. |
- if (currentShownRatio <= getPeekRatio() && distanceY < 0) { |
- mIsScrolling = false; |
- return false; |
- } |
- |
- float newOffset = getSheetOffsetFromBottom() + distanceY; |
- setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset())); |
- |
- mIsScrolling = true; |
- return true; |
- } |
- |
- @Override |
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, |
- float velocityY) { |
- cancelAnimation(); |
- |
- // Figure out the projected state of the sheet and animate there. Note that a swipe up |
- // will have a negative velocity, swipe down will have a positive velocity. Negate this |
- // values so that the logic is more intuitive. |
- @SheetState |
- int targetState = getTargetSheetState( |
- getSheetOffsetFromBottom() + getFlingDistance(-velocityY), -velocityY); |
- setSheetState(targetState, true); |
- mIsScrolling = false; |
- |
- return true; |
- } |
- } |
- |
- /** |
- * Constructor for inflation from XML. |
- * @param context An Android context. |
- * @param atts The XML attributes. |
- */ |
- public BottomSheet(Context context, AttributeSet atts) { |
- super(context, atts); |
- |
- mVelocityTracker = VelocityTracker.obtain(); |
- |
- mGestureDetector = new GestureDetector(context, new BottomSheetSwipeDetector()); |
- mGestureDetector.setIsLongpressEnabled(false); |
- } |
- |
- @Override |
- public boolean onInterceptTouchEvent(MotionEvent e) { |
- if (!canMoveSheet()) return false; |
- |
- // The incoming motion event may have been adjusted by the view sending it down. Create a |
- // motion event with the raw (x, y) coordinates of the original so the gesture detector |
- // functions properly. |
- mGestureDetector.onTouchEvent(createRawMotionEvent(e)); |
- return mIsScrolling; |
- } |
- |
- @Override |
- public boolean onTouchEvent(MotionEvent e) { |
- if (isToolbarAndroidViewHidden()) return false; |
- |
- // The down event is interpreted above in onInterceptTouchEvent, it does not need to be |
- // interpreted a second time. |
- if (e.getActionMasked() != MotionEvent.ACTION_DOWN) { |
- mGestureDetector.onTouchEvent(createRawMotionEvent(e)); |
- } |
- |
- // If the user is scrolling and the event is a cancel or up action, update scroll state |
- // and return. |
- if (e.getActionMasked() == MotionEvent.ACTION_UP |
- || e.getActionMasked() == MotionEvent.ACTION_CANCEL) { |
- mIsScrolling = false; |
- |
- mVelocityTracker.computeCurrentVelocity(1000); |
- |
- // If an animation was not created to settle the sheet at some state, do it now. |
- if (mSettleAnimator == null) { |
- // Negate velocity so a positive number indicates a swipe up. |
- float currentVelocity = -mVelocityTracker.getYVelocity(); |
- @SheetState |
- int targetState = getTargetSheetState(getSheetOffsetFromBottom(), currentVelocity); |
- |
- setSheetState(targetState, true); |
- } |
- } |
- |
- return true; |
- } |
- |
- @Override |
- public boolean gatherTransparentRegion(Region region) { |
- // TODO(mdjones): Figure out what this should actually be set to since the view animates |
- // without necessarily calling this method again. |
- region.setEmpty(); |
- return true; |
- } |
- |
- /** |
- * @param tabModelSelector A TabModelSelector for getting the current tab and activity. |
- */ |
- public void setTabModelSelector(TabModelSelector tabModelSelector) { |
- mTabModelSelector = tabModelSelector; |
- } |
- |
- /** |
- * @param fullscreenManager Chrome's fullscreen manager for information about toolbar offsets. |
- */ |
- public void setFullscreenManager(ChromeFullscreenManager fullscreenManager) { |
- mFullscreenManager = fullscreenManager; |
- } |
- |
- /** |
- * @return Whether or not the toolbar Android View is hidden due to being scrolled off-screen. |
- */ |
- private boolean isToolbarAndroidViewHidden() { |
- return mFullscreenManager == null || mFullscreenManager.getBottomControlOffset() > 0 |
- || mControlContainer.getVisibility() != VISIBLE; |
- } |
- |
- /** |
- * 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. |
- * @param root The container of the bottom sheet. |
- * @param controlContainer The container for the toolbar. |
- */ |
- public void init(View root, View controlContainer) { |
- mControlContainer = controlContainer; |
- mToolbarHeight = mControlContainer.getHeight(); |
- |
- mBottomSheetContentContainer = (FrameLayout) findViewById(R.id.bottom_sheet_content); |
- |
- mCurrentState = SHEET_STATE_PEEK; |
- |
- // Listen to height changes on the root. |
- root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |
- @Override |
- public void onLayoutChange(View v, int left, int top, int right, int bottom, |
- int oldLeft, int oldTop, int oldRight, int oldBottom) { |
- // Make sure the size of the layout actually changed. |
- if (bottom - top == oldBottom - oldTop && right - left == oldRight - oldLeft) { |
- return; |
- } |
- |
- mContainerWidth = right - left; |
- mContainerHeight = bottom - top; |
- updateSheetDimensions(); |
- |
- cancelAnimation(); |
- setSheetState(mCurrentState, false); |
- } |
- }); |
- |
- // Listen to height changes on the toolbar. |
- controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |
- @Override |
- public void onLayoutChange(View v, int left, int top, int right, int bottom, |
- int oldLeft, int oldTop, int oldRight, int oldBottom) { |
- // Make sure the size of the layout actually changed. |
- if (bottom - top == oldBottom - oldTop && right - left == oldRight - oldLeft) { |
- return; |
- } |
- |
- mToolbarHeight = bottom - top; |
- updateSheetDimensions(); |
- |
- cancelAnimation(); |
- setSheetState(mCurrentState, false); |
- } |
- }); |
- |
- mPlaceholder = new View(getContext()); |
- LayoutParams placeHolderParams = |
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); |
- mPlaceholder.setBackgroundColor( |
- ApiCompatibilityUtils.getColor(getResources(), android.R.color.white)); |
- mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); |
- |
- mToolbarHolder = (FrameLayout) mControlContainer.findViewById(R.id.toolbar_holder); |
- mDefaultToolbarView = mControlContainer.findViewById(R.id.toolbar); |
- } |
- |
- @Override |
- public int loadUrl(LoadUrlParams params, boolean incognito) { |
- for (BottomSheetObserver o : mObservers) o.onLoadUrl(params.getUrl()); |
- |
- // Native page URLs in this context do not need to communicate with the tab. |
- if (NativePageFactory.isNativePageUrl(params.getUrl(), incognito)) { |
- return TabLoadStatus.PAGE_LOAD_FAILED; |
- } |
- |
- // In all non-native cases, minimize the sheet. |
- setSheetState(SHEET_STATE_PEEK, true); |
- |
- assert mTabModelSelector != null; |
- |
- // First try to get the tab behind the sheet. |
- if (getActiveTab() != null && getActiveTab().isIncognito() == incognito) { |
- return getActiveTab().loadUrl(params); |
- } |
- |
- // If no compatible tab is active behind the sheet, open a new one. |
- mTabModelSelector.openNewTab( |
- params, TabModel.TabLaunchType.FROM_CHROME_UI, getActiveTab(), incognito); |
- return TabLoadStatus.DEFAULT_PAGE_LOAD; |
- } |
- |
- @Override |
- public boolean isIncognito() { |
- if (getActiveTab() == null) return false; |
- return getActiveTab().isIncognito(); |
- } |
- |
- @Override |
- public int getParentId() { |
- return Tab.INVALID_TAB_ID; |
- } |
- |
- @Override |
- public Tab getActiveTab() { |
- return mTabModelSelector.getCurrentTab(); |
- } |
- |
- @Override |
- public boolean isVisible() { |
- return mCurrentState != SHEET_STATE_PEEK; |
- } |
- |
- /** |
- * Gets the minimum offset of the bottom sheet. |
- * @return The min offset. |
- */ |
- public float getMinOffset() { |
- return getPeekRatio() * mContainerHeight; |
- } |
- |
- /** |
- * Gets the sheet's offset from the bottom of the screen. |
- * @return The sheet's distance from the bottom of the screen. |
- */ |
- public float getSheetOffsetFromBottom() { |
- return mContainerHeight - getTranslationY(); |
- } |
- |
- /** |
- * Show content in the bottom sheet's content area. |
- * @param content The {@link BottomSheetContent} to show. |
- */ |
- public void showContent(BottomSheetContent content) { |
- // If the desired content is already showing, do nothing. |
- if (mSheetContent == content) return; |
- |
- if (mSheetContent != null) { |
- mBottomSheetContentContainer.removeView(mSheetContent.getContentView()); |
- mSheetContent = null; |
- } |
- |
- if (content == null) { |
- mBottomSheetContentContainer.addView(mPlaceholder); |
- return; |
- } |
- |
- mBottomSheetContentContainer.removeView(mPlaceholder); |
- mSheetContent = content; |
- mBottomSheetContentContainer.addView(mSheetContent.getContentView()); |
- |
- if (mLastToolbarView != null) { |
- mToolbarHolder.removeView(mLastToolbarView); |
- mLastToolbarView = null; |
- } |
- |
- if (mSheetContent.getToolbarView() != null) { |
- mLastToolbarView = mSheetContent.getToolbarView(); |
- mToolbarHolder.addView(mSheetContent.getToolbarView()); |
- mDefaultToolbarView.setVisibility(View.GONE); |
- } else { |
- mDefaultToolbarView.setVisibility(View.VISIBLE); |
- } |
- } |
- |
- /** |
- * 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) { |
- if (mControlContainer == null) return false; |
- |
- mControlContainer.getLocationInWindow(mLocationArray); |
- |
- return e.getRawY() < mLocationArray[1] + mToolbarHeight; |
- } |
- |
- /** |
- * A notification that the sheet is exiting the peek state into one that shows content. |
- */ |
- private void onSheetOpened() { |
- for (BottomSheetObserver o : mObservers) o.onSheetOpened(); |
- } |
- |
- /** |
- * A notification that the sheet has returned to the peeking state. |
- */ |
- private void onSheetClosed() { |
- for (BottomSheetObserver o : mObservers) o.onSheetClosed(); |
- } |
- |
- /** |
- * Creates an unadjusted version of a MotionEvent. |
- * @param e The original event. |
- * @return The unadjusted version of the event. |
- */ |
- private MotionEvent createRawMotionEvent(MotionEvent e) { |
- MotionEvent rawEvent = MotionEvent.obtain(e); |
- rawEvent.setLocation(e.getRawX(), e.getRawY()); |
- return rawEvent; |
- } |
- |
- /** |
- * Updates the bottom sheet's peeking and content height. |
- */ |
- private void updateSheetDimensions() { |
- if (mContainerHeight <= 0) return; |
- |
- // Though mStateRatios is a static constant, the peeking ratio is computed here because |
- // the correct toolbar height and container height are not know until those views are |
- // inflated. |
- mStateRatios[0] = mToolbarHeight / mContainerHeight; |
- |
- // Compute the height that the content section of the bottom sheet. |
- float contentHeight = (mContainerHeight * getFullRatio()) - mToolbarHeight; |
- |
- MarginLayoutParams sheetContentParams = |
- (MarginLayoutParams) mBottomSheetContentContainer.getLayoutParams(); |
- sheetContentParams.width = (int) mContainerWidth; |
- sheetContentParams.height = (int) contentHeight; |
- sheetContentParams.topMargin = (int) mToolbarHeight; |
- |
- MarginLayoutParams toolbarShadowParams = |
- (MarginLayoutParams) findViewById(R.id.toolbar_shadow).getLayoutParams(); |
- toolbarShadowParams.topMargin = (int) mToolbarHeight; |
- |
- mBottomSheetContentContainer.requestLayout(); |
- } |
- |
- /** |
- * Cancels and nulls the height animation if it exists. |
- */ |
- private void cancelAnimation() { |
- if (mSettleAnimator == null) return; |
- mSettleAnimator.cancel(); |
- mSettleAnimator = null; |
- } |
- |
- /** |
- * Creates the sheet's animation to a target state. |
- * @param targetState The target state. |
- */ |
- private void createSettleAnimation(@SheetState int targetState) { |
- mCurrentState = targetState; |
- mSettleAnimator = ValueAnimator.ofFloat(getSheetOffsetFromBottom(), |
- getSheetHeightForState(targetState)); |
- mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); |
- mSettleAnimator.setInterpolator(mInterpolator); |
- |
- // When the animation is canceled or ends, reset the handle to null. |
- mSettleAnimator.addListener(new AnimatorListenerAdapter() { |
- @Override |
- public void onAnimationEnd(Animator animator) { |
- mSettleAnimator = null; |
- } |
- }); |
- |
- mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
- @Override |
- public void onAnimationUpdate(ValueAnimator animator) { |
- setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); |
- } |
- }); |
- |
- mSettleAnimator.start(); |
- } |
- |
- /** |
- * Gets the distance of a fling based on the velocity and the base animation time. This formula |
- * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: |
- * displacement = initialVelocity * duration / 2. |
- * @param velocity The velocity of the fling. |
- * @return The distance the fling would cover. |
- */ |
- private float getFlingDistance(float velocity) { |
- // This includes conversion from seconds to ms. |
- return velocity * BASE_ANIMATION_DURATION_MS / 2000f; |
- } |
- |
- /** |
- * Gets the maximum offset of the bottom sheet. |
- * @return The max offset. |
- */ |
- private float getMaxOffset() { |
- return getFullRatio() * mContainerHeight; |
- } |
- |
- /** |
- * Sets the sheet's offset relative to the bottom of the screen. |
- * @param offset The offset that the sheet should be. |
- */ |
- private void setSheetOffsetFromBottom(float offset) { |
- if (MathUtils.areFloatsEqual(getSheetOffsetFromBottom(), getMinOffset()) |
- && offset > getMinOffset()) { |
- onSheetOpened(); |
- } else if (MathUtils.areFloatsEqual(offset, getMinOffset()) |
- && getSheetOffsetFromBottom() > getMinOffset()) { |
- onSheetClosed(); |
- } |
- |
- setTranslationY(mContainerHeight - offset); |
- sendOffsetChangeEvents(); |
- } |
- |
- /** |
- * This is the same as {@link #setSheetOffsetFromBottom(float)} but exclusively for testing. |
- * @param offset The offset to set the sheet to. |
- */ |
- @VisibleForTesting |
- public void setSheetOffsetFromBottomForTesting(float offset) { |
- setSheetOffsetFromBottom(offset); |
- } |
- |
- /** |
- * @return The ratio of the height of the screen that the peeking state is. |
- */ |
- @VisibleForTesting |
- public float getPeekRatio() { |
- return mStateRatios[0]; |
- } |
- |
- /** |
- * @return The ratio of the height of the screen that the half expanded state is. |
- */ |
- @VisibleForTesting |
- public float getHalfRatio() { |
- return mStateRatios[1]; |
- } |
- |
- /** |
- * @return The ratio of the height of the screen that the fully expanded state is. |
- */ |
- @VisibleForTesting |
- public float getFullRatio() { |
- return mStateRatios[2]; |
- } |
- |
- /** |
- * @return The height of the container that the bottom sheet exists in. |
- */ |
- @VisibleForTesting |
- public float getSheetContainerHeight() { |
- return mContainerHeight; |
- } |
- |
- /** |
- * Sends notifications if the sheet is transitioning from the peeking to half expanded state and |
- * from the peeking to fully expanded state. The peek to half events are only sent when the |
- * sheet is between the peeking and half states. |
- */ |
- private void sendOffsetChangeEvents() { |
- float screenRatio = |
- mContainerHeight > 0 ? getSheetOffsetFromBottom() / mContainerHeight : 0; |
- |
- // This ratio is relative to the peek and full positions of the sheet. |
- float peekFullRatio = MathUtils.clamp( |
- (screenRatio - getPeekRatio()) / (getFullRatio() - getPeekRatio()), 0, 1); |
- |
- for (BottomSheetObserver o : mObservers) { |
- o.onSheetOffsetChanged(MathUtils.areFloatsEqual(peekFullRatio, 0) ? 0 : peekFullRatio); |
- } |
- |
- // This ratio is relative to the peek and half positions of the sheet. |
- float peekHalfRatio = MathUtils.clamp( |
- (screenRatio - getPeekRatio()) / (getHalfRatio() - getPeekRatio()), 0, 1); |
- |
- // If the ratio is close enough to zero, just set it to zero. |
- if (MathUtils.areFloatsEqual(peekHalfRatio, 0f)) peekHalfRatio = 0f; |
- |
- if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { |
- mLastPeekToHalfRatioSent = peekHalfRatio; |
- for (BottomSheetObserver o : mObservers) { |
- o.onTransitionPeekToHalf(peekHalfRatio); |
- } |
- } |
- } |
- |
- /** |
- * Moves the sheet to the provided state. |
- * @param state The state to move the panel to. |
- * @param animate If true, the sheet will animate to the provided state, otherwise it will |
- * move there instantly. |
- */ |
- public void setSheetState(@SheetState int state, boolean animate) { |
- mCurrentState = state; |
- |
- if (animate) { |
- createSettleAnimation(state); |
- } else { |
- setSheetOffsetFromBottom(getSheetHeightForState(state)); |
- } |
- } |
- |
- /** |
- * @return The current state of the bottom sheet. If the sheet is animating, this will be the |
- * state the sheet is animating to. |
- */ |
- public int getSheetState() { |
- return mCurrentState; |
- } |
- |
- /** |
- * If the animation to settle the sheet in one of its states is running. |
- * @return True if the animation is running. |
- */ |
- public boolean isRunningSettleAnimation() { |
- return mSettleAnimator != null; |
- } |
- |
- @VisibleForTesting |
- public BottomSheetContent getCurrentSheetContent() { |
- return mSheetContent; |
- } |
- |
- /** |
- * Gets the height of the bottom sheet based on a provided state. |
- * @param state The state to get the height from. |
- * @return The height of the sheet at the provided state. |
- */ |
- private float getSheetHeightForState(@SheetState int state) { |
- return mStateRatios[state] * mContainerHeight; |
- } |
- |
- /** |
- * Adds an observer to the bottom sheet. |
- * @param observer The observer to add. |
- */ |
- public void addObserver(BottomSheetObserver observer) { |
- mObservers.addObserver(observer); |
- } |
- |
- /** |
- * Gets the target state of the sheet based on the sheet's height and velocity. |
- * @param sheetHeight The current height of the sheet. |
- * @param yVelocity The current Y velocity of the sheet. This is only used for determining the |
- * scroll or fling direction. If this value is positive, the movement is from |
- * bottom to top. |
- * @return The target state of the bottom sheet. |
- */ |
- private int getTargetSheetState(float sheetHeight, float yVelocity) { |
- if (sheetHeight <= getMinOffset()) return SHEET_STATE_PEEK; |
- if (sheetHeight >= getMaxOffset()) return SHEET_STATE_FULL; |
- |
- // First, find the two states that the sheet height is between. |
- @SheetState |
- int nextState = sStates[0]; |
- |
- @SheetState |
- int prevState = nextState; |
- for (int i = 0; i < sStates.length; i++) { |
- prevState = nextState; |
- nextState = sStates[i]; |
- // The values in PanelState are ascending, they should be kept that way in order for |
- // this to work. |
- if (sheetHeight >= getSheetHeightForState(prevState) |
- && sheetHeight < getSheetHeightForState(nextState)) { |
- break; |
- } |
- } |
- |
- // If the desired height is close enough to a certain state, depending on the direction of |
- // the velocity, move to that state. |
- float lowerBound = getSheetHeightForState(prevState); |
- float distance = getSheetHeightForState(nextState) - lowerBound; |
- |
- float inverseThreshold = 1.0f - THRESHOLD_TO_NEXT_STATE; |
- float thresholdToNextState = yVelocity < 0.0f ? THRESHOLD_TO_NEXT_STATE : inverseThreshold; |
- |
- if ((sheetHeight - lowerBound) / distance > thresholdToNextState) { |
- return nextState; |
- } |
- return prevState; |
- } |
- |
- @Override |
- public void onFadingViewClick() { |
- setSheetState(SHEET_STATE_PEEK, true); |
- } |
- |
- @Override |
- public void onFadingViewVisibilityChanged(boolean visible) {} |
- |
- private boolean canMoveSheet() { |
- boolean isInOverviewMode = mTabModelSelector != null |
- && (mTabModelSelector.getCurrentTab() == null |
- || mTabModelSelector.getCurrentTab().getActivity().isInOverviewMode()); |
- return !isToolbarAndroidViewHidden() && !isInOverviewMode; |
- } |
-} |