Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.widget.bottomsheet; | 5 package org.chromium.chrome.browser.widget.bottomsheet; |
| 6 | 6 |
| 7 import android.animation.Animator; | 7 import android.animation.Animator; |
| 8 import android.animation.AnimatorListenerAdapter; | 8 import android.animation.AnimatorListenerAdapter; |
| 9 import android.animation.ValueAnimator; | 9 import android.animation.ValueAnimator; |
| 10 import android.content.Context; | 10 import android.content.Context; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 46 * - HALF: The sheet is expanded to consume around half of the screen. | 46 * - HALF: The sheet is expanded to consume around half of the screen. |
| 47 * - FULL: The sheet is expanded to its full height. | 47 * - FULL: The sheet is expanded to its full height. |
| 48 * | 48 * |
| 49 * All the computation in this file is based off of the bottom of the screen ins tead of the top | 49 * All the computation in this file is based off of the bottom of the screen ins tead of the top |
| 50 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. | 50 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. |
| 51 */ | 51 */ |
| 52 | 52 |
| 53 public class BottomSheet | 53 public class BottomSheet |
| 54 extends FrameLayout implements FadingBackgroundView.FadingViewObserver, NativePageHost { | 54 extends FrameLayout implements FadingBackgroundView.FadingViewObserver, NativePageHost { |
| 55 /** The different states that the bottom sheet can have. */ | 55 /** The different states that the bottom sheet can have. */ |
| 56 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}) | 56 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL, SHEET_STATE_S CROLLING}) |
| 57 @Retention(RetentionPolicy.SOURCE) | 57 @Retention(RetentionPolicy.SOURCE) |
| 58 public @interface SheetState {} | 58 public @interface SheetState {} |
| 59 public static final int SHEET_STATE_PEEK = 0; | 59 public static final int SHEET_STATE_PEEK = 0; |
| 60 public static final int SHEET_STATE_HALF = 1; | 60 public static final int SHEET_STATE_HALF = 1; |
| 61 public static final int SHEET_STATE_FULL = 2; | 61 public static final int SHEET_STATE_FULL = 2; |
| 62 public static final int SHEET_STATE_SCROLLING = 3; | |
| 62 | 63 |
| 63 /** | 64 /** |
| 64 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material | 65 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material |
| 65 * design (this is the minimum time a user is guaranteed to pay attention to something). | 66 * design (this is the minimum time a user is guaranteed to pay attention to something). |
| 66 */ | 67 */ |
| 67 private static final long BASE_ANIMATION_DURATION_MS = 218; | 68 private static final long BASE_ANIMATION_DURATION_MS = 218; |
| 68 | 69 |
| 69 /** | 70 /** |
| 70 * The fraction of the way to the next state the sheet must be swiped to ani mate there when | 71 * The fraction of the way to the next state the sheet must be swiped to ani mate there when |
| 71 * released. A smaller value here means a smaller swipe is needed to move th e sheet around. | 72 * released. A smaller value here means a smaller swipe is needed to move th e sheet around. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 106 | 107 |
| 107 /** The height of the toolbar. */ | 108 /** The height of the toolbar. */ |
| 108 private float mToolbarHeight; | 109 private float mToolbarHeight; |
| 109 | 110 |
| 110 /** The width of the view that contains the bottom sheet. */ | 111 /** The width of the view that contains the bottom sheet. */ |
| 111 private float mContainerWidth; | 112 private float mContainerWidth; |
| 112 | 113 |
| 113 /** The height of the view that contains the bottom sheet. */ | 114 /** The height of the view that contains the bottom sheet. */ |
| 114 private float mContainerHeight; | 115 private float mContainerHeight; |
| 115 | 116 |
| 116 /** The current sheet state. If the sheet is moving, this will be the target state. */ | 117 /** The current state that the sheet is in. */ |
| 117 private int mCurrentState; | 118 private int mCurrentState; |
| 118 | 119 |
| 120 /** The target sheet state. This is the state that the sheet is currently mo ving to. */ | |
| 121 private int mTargetState; | |
| 122 | |
| 119 /** Used for getting the current tab. */ | 123 /** Used for getting the current tab. */ |
| 120 private TabModelSelector mTabModelSelector; | 124 private TabModelSelector mTabModelSelector; |
| 121 | 125 |
| 122 /** The fullscreen manager for information about toolbar offsets. */ | 126 /** The fullscreen manager for information about toolbar offsets. */ |
| 123 private ChromeFullscreenManager mFullscreenManager; | 127 private ChromeFullscreenManager mFullscreenManager; |
| 124 | 128 |
| 125 /** A handle to the content being shown by the sheet. */ | 129 /** A handle to the content being shown by the sheet. */ |
| 126 private BottomSheetContent mSheetContent; | 130 private BottomSheetContent mSheetContent; |
| 127 | 131 |
| 128 /** A handle to the toolbar control container. */ | 132 /** A handle to the toolbar control container. */ |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 | 245 |
| 242 // Similarly, if the sheet is in the min position, don't move if the scroll is downward. | 246 // Similarly, if the sheet is in the min position, don't move if the scroll is downward. |
| 243 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { | 247 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { |
| 244 mIsScrolling = false; | 248 mIsScrolling = false; |
| 245 return false; | 249 return false; |
| 246 } | 250 } |
| 247 | 251 |
| 248 float newOffset = getSheetOffsetFromBottom() + distanceY; | 252 float newOffset = getSheetOffsetFromBottom() + distanceY; |
| 249 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset())); | 253 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset())); |
| 250 | 254 |
| 255 setInternalCurrentState(SHEET_STATE_SCROLLING); | |
| 251 mIsScrolling = true; | 256 mIsScrolling = true; |
| 252 return true; | 257 return true; |
| 253 } | 258 } |
| 254 | 259 |
| 255 @Override | 260 @Override |
| 256 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { | 261 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { |
| 257 cancelAnimation(); | 262 cancelAnimation(); |
| 258 | 263 |
| 259 // Figure out the projected state of the sheet and animate there. No te that a swipe up | 264 // Figure out the projected state of the sheet and animate there. No te that a swipe up |
| 260 // will have a negative velocity, swipe down will have a positive ve locity. Negate this | 265 // will have a negative velocity, swipe down will have a positive ve locity. Negate this |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 // Make sure the size of the layout actually changed. | 386 // Make sure the size of the layout actually changed. |
| 382 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { | 387 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { |
| 383 return; | 388 return; |
| 384 } | 389 } |
| 385 | 390 |
| 386 mContainerWidth = right - left; | 391 mContainerWidth = right - left; |
| 387 mContainerHeight = bottom - top; | 392 mContainerHeight = bottom - top; |
| 388 updateSheetDimensions(); | 393 updateSheetDimensions(); |
| 389 | 394 |
| 390 cancelAnimation(); | 395 cancelAnimation(); |
| 391 setSheetState(mCurrentState, false); | 396 setSheetState(mTargetState, false); |
| 392 } | 397 } |
| 393 }); | 398 }); |
| 394 | 399 |
| 395 // Listen to height changes on the toolbar. | 400 // Listen to height changes on the toolbar. |
| 396 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen er() { | 401 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen er() { |
| 397 @Override | 402 @Override |
| 398 public void onLayoutChange(View v, int left, int top, int right, int bottom, | 403 public void onLayoutChange(View v, int left, int top, int right, int bottom, |
| 399 int oldLeft, int oldTop, int oldRight, int oldBottom) { | 404 int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| 400 // Make sure the size of the layout actually changed. | 405 // Make sure the size of the layout actually changed. |
| 401 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { | 406 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { |
| 402 return; | 407 return; |
| 403 } | 408 } |
| 404 | 409 |
| 405 mToolbarHeight = bottom - top; | 410 mToolbarHeight = bottom - top; |
| 406 updateSheetDimensions(); | 411 updateSheetDimensions(); |
| 407 | 412 |
| 408 cancelAnimation(); | 413 cancelAnimation(); |
| 409 setSheetState(mCurrentState, false); | 414 setSheetState(mTargetState, false); |
| 410 } | 415 } |
| 411 }); | 416 }); |
| 412 | 417 |
| 413 mPlaceholder = new View(getContext()); | 418 mPlaceholder = new View(getContext()); |
| 414 LayoutParams placeHolderParams = | 419 LayoutParams placeHolderParams = |
| 415 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT); | 420 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT); |
| 416 mPlaceholder.setBackgroundColor( | 421 mPlaceholder.setBackgroundColor( |
| 417 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w hite)); | 422 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w hite)); |
| 418 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); | 423 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); |
| 419 | 424 |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 602 if (mSettleAnimator == null) return; | 607 if (mSettleAnimator == null) return; |
| 603 mSettleAnimator.cancel(); | 608 mSettleAnimator.cancel(); |
| 604 mSettleAnimator = null; | 609 mSettleAnimator = null; |
| 605 } | 610 } |
| 606 | 611 |
| 607 /** | 612 /** |
| 608 * Creates the sheet's animation to a target state. | 613 * Creates the sheet's animation to a target state. |
| 609 * @param targetState The target state. | 614 * @param targetState The target state. |
| 610 */ | 615 */ |
| 611 private void createSettleAnimation(@SheetState int targetState) { | 616 private void createSettleAnimation(@SheetState int targetState) { |
| 612 mCurrentState = targetState; | 617 mTargetState = targetState; |
| 613 mSettleAnimator = ValueAnimator.ofFloat( | 618 mSettleAnimator = ValueAnimator.ofFloat( |
| 614 getSheetOffsetFromBottom(), getSheetHeightForState(targetState)) ; | 619 getSheetOffsetFromBottom(), getSheetHeightForState(targetState)) ; |
| 615 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); | 620 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); |
| 616 mSettleAnimator.setInterpolator(mInterpolator); | 621 mSettleAnimator.setInterpolator(mInterpolator); |
| 617 | 622 |
| 618 // When the animation is canceled or ends, reset the handle to null. | 623 // When the animation is canceled or ends, reset the handle to null. |
| 619 mSettleAnimator.addListener(new AnimatorListenerAdapter() { | 624 mSettleAnimator.addListener(new AnimatorListenerAdapter() { |
| 620 @Override | 625 @Override |
| 621 public void onAnimationEnd(Animator animator) { | 626 public void onAnimationEnd(Animator animator) { |
| 622 mSettleAnimator = null; | 627 mSettleAnimator = null; |
| 628 setInternalCurrentState(mTargetState); | |
| 623 } | 629 } |
| 624 }); | 630 }); |
| 625 | 631 |
| 626 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen er() { | 632 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen er() { |
| 627 @Override | 633 @Override |
| 628 public void onAnimationUpdate(ValueAnimator animator) { | 634 public void onAnimationUpdate(ValueAnimator animator) { |
| 629 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); | 635 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); |
| 630 } | 636 } |
| 631 }); | 637 }); |
| 632 | 638 |
| 633 mSettleAnimator.start(); | 639 mSettleAnimator.start(); |
| 640 setInternalCurrentState(SHEET_STATE_SCROLLING); | |
| 634 } | 641 } |
| 635 | 642 |
| 636 /** | 643 /** |
| 637 * Gets the distance of a fling based on the velocity and the base animation time. This formula | 644 * Gets the distance of a fling based on the velocity and the base animation time. This formula |
| 638 * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: | 645 * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: |
| 639 * displacement = initialVelocity * duration / 2. | 646 * displacement = initialVelocity * duration / 2. |
| 640 * @param velocity The velocity of the fling. | 647 * @param velocity The velocity of the fling. |
| 641 * @return The distance the fling would cover. | 648 * @return The distance the fling would cover. |
| 642 */ | 649 */ |
| 643 private float getFlingDistance(float velocity) { | 650 private float getFlingDistance(float velocity) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 738 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { | 745 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { |
| 739 mLastPeekToHalfRatioSent = peekHalfRatio; | 746 mLastPeekToHalfRatioSent = peekHalfRatio; |
| 740 for (BottomSheetObserver o : mObservers) { | 747 for (BottomSheetObserver o : mObservers) { |
| 741 o.onTransitionPeekToHalf(peekHalfRatio); | 748 o.onTransitionPeekToHalf(peekHalfRatio); |
| 742 } | 749 } |
| 743 } | 750 } |
| 744 } | 751 } |
| 745 | 752 |
| 746 /** | 753 /** |
| 747 * Moves the sheet to the provided state. | 754 * Moves the sheet to the provided state. |
| 748 * @param state The state to move the panel to. | 755 * @param state The state to move the panel to. This cannot be SHEET_STATE_S CROLLING. |
| 749 * @param animate If true, the sheet will animate to the provided state, oth erwise it will | 756 * @param animate If true, the sheet will animate to the provided state, oth erwise it will |
| 750 * move there instantly. | 757 * move there instantly. |
| 751 */ | 758 */ |
| 752 public void setSheetState(@SheetState int state, boolean animate) { | 759 public void setSheetState(@SheetState int state, boolean animate) { |
| 753 boolean stateChanged = state != mCurrentState; | 760 assert state != SHEET_STATE_SCROLLING; |
| 754 mCurrentState = state; | 761 mTargetState = state; |
| 755 | 762 |
| 756 if (animate) { | 763 if (animate) { |
| 757 createSettleAnimation(state); | 764 createSettleAnimation(state); |
| 758 } else { | 765 } else { |
| 759 setSheetOffsetFromBottom(getSheetHeightForState(state)); | 766 setSheetOffsetFromBottom(getSheetHeightForState(state)); |
| 760 } | 767 setInternalCurrentState(mTargetState); |
| 761 | |
| 762 if (!stateChanged) return; | |
| 763 | |
| 764 for (BottomSheetObserver o : mObservers) { | |
| 765 o.onSheetStateChanged(mCurrentState); | |
| 766 } | 768 } |
| 767 } | 769 } |
| 768 | 770 |
| 769 /** | 771 /** |
| 770 * @return The current state of the bottom sheet. If the sheet is animating, this will be the | 772 * @return The current state of the bottom sheet. If the sheet is animating, this will be the |
| 771 * state the sheet is animating to. | 773 * state the sheet is animating to. |
| 772 */ | 774 */ |
| 773 public int getSheetState() { | 775 public int getSheetState() { |
| 774 return mCurrentState; | 776 return mCurrentState; |
| 775 } | 777 } |
| 776 | 778 |
| 777 /** | 779 /** |
| 780 * Set the current state of the bottom sheet. This is for internal use to no tify observers of | |
| 781 * state change events. | |
| 782 * @param state The current state of the sheet. | |
| 783 */ | |
| 784 private void setInternalCurrentState(@SheetState int state) { | |
| 785 boolean stateChanged = state != mCurrentState; | |
| 786 mCurrentState = state; | |
| 787 | |
| 788 if (!stateChanged) return; | |
|
Theresa
2017/03/21 23:07:01
This early return can move up:
if (state == mCurr
mdjones
2017/03/21 23:32:42
Done.
| |
| 789 | |
| 790 for (BottomSheetObserver o : mObservers) { | |
| 791 o.onSheetStateChanged(mCurrentState); | |
| 792 } | |
| 793 } | |
| 794 | |
| 795 /** | |
| 778 * If the animation to settle the sheet in one of its states is running. | 796 * If the animation to settle the sheet in one of its states is running. |
| 779 * @return True if the animation is running. | 797 * @return True if the animation is running. |
| 780 */ | 798 */ |
| 781 public boolean isRunningSettleAnimation() { | 799 public boolean isRunningSettleAnimation() { |
| 782 return mSettleAnimator != null; | 800 return mSettleAnimator != null; |
| 783 } | 801 } |
| 784 | 802 |
| 785 @VisibleForTesting | 803 @VisibleForTesting |
| 786 public BottomSheetContent getCurrentSheetContent() { | 804 public BottomSheetContent getCurrentSheetContent() { |
| 787 return mSheetContent; | 805 return mSheetContent; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 855 @Override | 873 @Override |
| 856 public void onFadingViewVisibilityChanged(boolean visible) {} | 874 public void onFadingViewVisibilityChanged(boolean visible) {} |
| 857 | 875 |
| 858 private boolean canMoveSheet() { | 876 private boolean canMoveSheet() { |
| 859 boolean isInOverviewMode = mTabModelSelector != null | 877 boolean isInOverviewMode = mTabModelSelector != null |
| 860 && (mTabModelSelector.getCurrentTab() == null | 878 && (mTabModelSelector.getCurrentTab() == null |
| 861 || mTabModelSelector.getCurrentTab().getActivity().is InOverviewMode()); | 879 || mTabModelSelector.getCurrentTab().getActivity().is InOverviewMode()); |
| 862 return !isToolbarAndroidViewHidden() && !isInOverviewMode; | 880 return !isToolbarAndroidViewHidden() && !isInOverviewMode; |
| 863 } | 881 } |
| 864 } | 882 } |
| OLD | NEW |