| 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.ObjectAnimator; | 9 import android.animation.ObjectAnimator; |
| 10 import android.animation.ValueAnimator; | 10 import android.animation.ValueAnimator; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 * - HALF: The sheet is expanded to consume around half of the screen. | 47 * - HALF: The sheet is expanded to consume around half of the screen. |
| 48 * - FULL: The sheet is expanded to its full height. | 48 * - FULL: The sheet is expanded to its full height. |
| 49 * | 49 * |
| 50 * All the computation in this file is based off of the bottom of the screen ins
tead of the top | 50 * All the computation in this file is based off of the bottom of the screen ins
tead of the top |
| 51 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. | 51 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. |
| 52 */ | 52 */ |
| 53 | 53 |
| 54 public class BottomSheet | 54 public class BottomSheet |
| 55 extends FrameLayout implements FadingBackgroundView.FadingViewObserver,
NativePageHost { | 55 extends FrameLayout implements FadingBackgroundView.FadingViewObserver,
NativePageHost { |
| 56 /** The different states that the bottom sheet can have. */ | 56 /** The different states that the bottom sheet can have. */ |
| 57 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}) | 57 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL, SHEET_STATE_S
CROLLING}) |
| 58 @Retention(RetentionPolicy.SOURCE) | 58 @Retention(RetentionPolicy.SOURCE) |
| 59 public @interface SheetState {} | 59 public @interface SheetState {} |
| 60 public static final int SHEET_STATE_PEEK = 0; | 60 public static final int SHEET_STATE_PEEK = 0; |
| 61 public static final int SHEET_STATE_HALF = 1; | 61 public static final int SHEET_STATE_HALF = 1; |
| 62 public static final int SHEET_STATE_FULL = 2; | 62 public static final int SHEET_STATE_FULL = 2; |
| 63 public static final int SHEET_STATE_SCROLLING = 3; |
| 63 | 64 |
| 64 /** | 65 /** |
| 65 * The base duration of the settling animation of the sheet. 218 ms is a spe
c for material | 66 * The base duration of the settling animation of the sheet. 218 ms is a spe
c for material |
| 66 * design (this is the minimum time a user is guaranteed to pay attention to
something). | 67 * design (this is the minimum time a user is guaranteed to pay attention to
something). |
| 67 */ | 68 */ |
| 68 private static final long BASE_ANIMATION_DURATION_MS = 218; | 69 private static final long BASE_ANIMATION_DURATION_MS = 218; |
| 69 | 70 |
| 70 /** | 71 /** |
| 71 * The fraction of the way to the next state the sheet must be swiped to ani
mate there when | 72 * The fraction of the way to the next state the sheet must be swiped to ani
mate there when |
| 72 * released. This is the value used when there are 3 active states. A smalle
r value here means | 73 * released. This is the value used when there are 3 active states. A smalle
r value here means |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 | 112 |
| 112 /** The height of the toolbar. */ | 113 /** The height of the toolbar. */ |
| 113 private float mToolbarHeight; | 114 private float mToolbarHeight; |
| 114 | 115 |
| 115 /** The width of the view that contains the bottom sheet. */ | 116 /** The width of the view that contains the bottom sheet. */ |
| 116 private float mContainerWidth; | 117 private float mContainerWidth; |
| 117 | 118 |
| 118 /** The height of the view that contains the bottom sheet. */ | 119 /** The height of the view that contains the bottom sheet. */ |
| 119 private float mContainerHeight; | 120 private float mContainerHeight; |
| 120 | 121 |
| 121 /** The current sheet state. If the sheet is moving, this will be the target
state. */ | 122 /** The current state that the sheet is in. */ |
| 122 private int mCurrentState; | 123 private int mCurrentState; |
| 123 | 124 |
| 125 /** The target sheet state. This is the state that the sheet is currently mo
ving to. */ |
| 126 private int mTargetState; |
| 127 |
| 124 /** Used for getting the current tab. */ | 128 /** Used for getting the current tab. */ |
| 125 private TabModelSelector mTabModelSelector; | 129 private TabModelSelector mTabModelSelector; |
| 126 | 130 |
| 127 /** The fullscreen manager for information about toolbar offsets. */ | 131 /** The fullscreen manager for information about toolbar offsets. */ |
| 128 private ChromeFullscreenManager mFullscreenManager; | 132 private ChromeFullscreenManager mFullscreenManager; |
| 129 | 133 |
| 130 /** A handle to the content being shown by the sheet. */ | 134 /** A handle to the content being shown by the sheet. */ |
| 131 private BottomSheetContent mSheetContent; | 135 private BottomSheetContent mSheetContent; |
| 132 | 136 |
| 133 /** A handle to the toolbar control container. */ | 137 /** A handle to the toolbar control container. */ |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 | 247 |
| 244 // Similarly, if the sheet is in the min position, don't move if the
scroll is downward. | 248 // Similarly, if the sheet is in the min position, don't move if the
scroll is downward. |
| 245 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { | 249 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { |
| 246 mIsScrolling = false; | 250 mIsScrolling = false; |
| 247 return false; | 251 return false; |
| 248 } | 252 } |
| 249 | 253 |
| 250 float newOffset = getSheetOffsetFromBottom() + distanceY; | 254 float newOffset = getSheetOffsetFromBottom() + distanceY; |
| 251 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(),
getMaxOffset())); | 255 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(),
getMaxOffset())); |
| 252 | 256 |
| 257 setInternalCurrentState(SHEET_STATE_SCROLLING); |
| 253 mIsScrolling = true; | 258 mIsScrolling = true; |
| 254 return true; | 259 return true; |
| 255 } | 260 } |
| 256 | 261 |
| 257 @Override | 262 @Override |
| 258 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) { | 263 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) { |
| 259 cancelAnimation(); | 264 cancelAnimation(); |
| 260 | 265 |
| 261 // Figure out the projected state of the sheet and animate there. No
te that a swipe up | 266 // Figure out the projected state of the sheet and animate there. No
te that a swipe up |
| 262 // will have a negative velocity, swipe down will have a positive ve
locity. Negate this | 267 // 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... |
| 383 // Make sure the size of the layout actually changed. | 388 // Make sure the size of the layout actually changed. |
| 384 if (bottom - top == oldBottom - oldTop && right - left == oldRig
ht - oldLeft) { | 389 if (bottom - top == oldBottom - oldTop && right - left == oldRig
ht - oldLeft) { |
| 385 return; | 390 return; |
| 386 } | 391 } |
| 387 | 392 |
| 388 mContainerWidth = right - left; | 393 mContainerWidth = right - left; |
| 389 mContainerHeight = bottom - top; | 394 mContainerHeight = bottom - top; |
| 390 updateSheetDimensions(); | 395 updateSheetDimensions(); |
| 391 | 396 |
| 392 cancelAnimation(); | 397 cancelAnimation(); |
| 393 setSheetState(mCurrentState, false); | 398 setSheetState(mTargetState, false); |
| 394 } | 399 } |
| 395 }); | 400 }); |
| 396 | 401 |
| 397 // Listen to height changes on the toolbar. | 402 // Listen to height changes on the toolbar. |
| 398 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen
er() { | 403 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen
er() { |
| 399 @Override | 404 @Override |
| 400 public void onLayoutChange(View v, int left, int top, int right, int
bottom, | 405 public void onLayoutChange(View v, int left, int top, int right, int
bottom, |
| 401 int oldLeft, int oldTop, int oldRight, int oldBottom) { | 406 int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| 402 // Make sure the size of the layout actually changed. | 407 // Make sure the size of the layout actually changed. |
| 403 if (bottom - top == oldBottom - oldTop && right - left == oldRig
ht - oldLeft) { | 408 if (bottom - top == oldBottom - oldTop && right - left == oldRig
ht - oldLeft) { |
| 404 return; | 409 return; |
| 405 } | 410 } |
| 406 | 411 |
| 407 mToolbarHeight = bottom - top; | 412 mToolbarHeight = bottom - top; |
| 408 updateSheetDimensions(); | 413 updateSheetDimensions(); |
| 409 | 414 |
| 410 cancelAnimation(); | 415 cancelAnimation(); |
| 411 setSheetState(mCurrentState, false); | 416 setSheetState(mTargetState, false); |
| 412 } | 417 } |
| 413 }); | 418 }); |
| 414 | 419 |
| 415 mPlaceholder = new View(getContext()); | 420 mPlaceholder = new View(getContext()); |
| 416 LayoutParams placeHolderParams = | 421 LayoutParams placeHolderParams = |
| 417 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P
ARENT); | 422 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P
ARENT); |
| 418 mPlaceholder.setBackgroundColor( | 423 mPlaceholder.setBackgroundColor( |
| 419 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w
hite)); | 424 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w
hite)); |
| 420 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); | 425 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); |
| 421 | 426 |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 if (mSettleAnimator == null) return; | 653 if (mSettleAnimator == null) return; |
| 649 mSettleAnimator.cancel(); | 654 mSettleAnimator.cancel(); |
| 650 mSettleAnimator = null; | 655 mSettleAnimator = null; |
| 651 } | 656 } |
| 652 | 657 |
| 653 /** | 658 /** |
| 654 * Creates the sheet's animation to a target state. | 659 * Creates the sheet's animation to a target state. |
| 655 * @param targetState The target state. | 660 * @param targetState The target state. |
| 656 */ | 661 */ |
| 657 private void createSettleAnimation(@SheetState int targetState) { | 662 private void createSettleAnimation(@SheetState int targetState) { |
| 658 mCurrentState = targetState; | 663 mTargetState = targetState; |
| 659 mSettleAnimator = ValueAnimator.ofFloat( | 664 mSettleAnimator = ValueAnimator.ofFloat( |
| 660 getSheetOffsetFromBottom(), getSheetHeightForState(targetState))
; | 665 getSheetOffsetFromBottom(), getSheetHeightForState(targetState))
; |
| 661 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); | 666 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); |
| 662 mSettleAnimator.setInterpolator(mInterpolator); | 667 mSettleAnimator.setInterpolator(mInterpolator); |
| 663 | 668 |
| 664 // When the animation is canceled or ends, reset the handle to null. | 669 // When the animation is canceled or ends, reset the handle to null. |
| 665 mSettleAnimator.addListener(new AnimatorListenerAdapter() { | 670 mSettleAnimator.addListener(new AnimatorListenerAdapter() { |
| 666 @Override | 671 @Override |
| 667 public void onAnimationEnd(Animator animator) { | 672 public void onAnimationEnd(Animator animator) { |
| 668 mSettleAnimator = null; | 673 mSettleAnimator = null; |
| 674 setInternalCurrentState(mTargetState); |
| 669 } | 675 } |
| 670 }); | 676 }); |
| 671 | 677 |
| 672 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen
er() { | 678 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen
er() { |
| 673 @Override | 679 @Override |
| 674 public void onAnimationUpdate(ValueAnimator animator) { | 680 public void onAnimationUpdate(ValueAnimator animator) { |
| 675 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); | 681 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); |
| 676 } | 682 } |
| 677 }); | 683 }); |
| 678 | 684 |
| 679 mSettleAnimator.start(); | 685 mSettleAnimator.start(); |
| 686 setInternalCurrentState(SHEET_STATE_SCROLLING); |
| 680 } | 687 } |
| 681 | 688 |
| 682 /** | 689 /** |
| 683 * Gets the distance of a fling based on the velocity and the base animation
time. This formula | 690 * Gets the distance of a fling based on the velocity and the base animation
time. This formula |
| 684 * assumes the deceleration curve is quadratic (t^2), hence the displacement
formula should be: | 691 * assumes the deceleration curve is quadratic (t^2), hence the displacement
formula should be: |
| 685 * displacement = initialVelocity * duration / 2. | 692 * displacement = initialVelocity * duration / 2. |
| 686 * @param velocity The velocity of the fling. | 693 * @param velocity The velocity of the fling. |
| 687 * @return The distance the fling would cover. | 694 * @return The distance the fling would cover. |
| 688 */ | 695 */ |
| 689 private float getFlingDistance(float velocity) { | 696 private float getFlingDistance(float velocity) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 784 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { | 791 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { |
| 785 mLastPeekToHalfRatioSent = peekHalfRatio; | 792 mLastPeekToHalfRatioSent = peekHalfRatio; |
| 786 for (BottomSheetObserver o : mObservers) { | 793 for (BottomSheetObserver o : mObservers) { |
| 787 o.onTransitionPeekToHalf(peekHalfRatio); | 794 o.onTransitionPeekToHalf(peekHalfRatio); |
| 788 } | 795 } |
| 789 } | 796 } |
| 790 } | 797 } |
| 791 | 798 |
| 792 /** | 799 /** |
| 793 * Moves the sheet to the provided state. | 800 * Moves the sheet to the provided state. |
| 794 * @param state The state to move the panel to. | 801 * @param state The state to move the panel to. This cannot be SHEET_STATE_S
CROLLING. |
| 795 * @param animate If true, the sheet will animate to the provided state, oth
erwise it will | 802 * @param animate If true, the sheet will animate to the provided state, oth
erwise it will |
| 796 * move there instantly. | 803 * move there instantly. |
| 797 */ | 804 */ |
| 798 public void setSheetState(@SheetState int state, boolean animate) { | 805 public void setSheetState(@SheetState int state, boolean animate) { |
| 799 boolean stateChanged = state != mCurrentState; | 806 assert state != SHEET_STATE_SCROLLING; |
| 800 mCurrentState = state; | 807 mTargetState = state; |
| 801 | 808 |
| 802 if (animate) { | 809 if (animate) { |
| 803 createSettleAnimation(state); | 810 createSettleAnimation(state); |
| 804 } else { | 811 } else { |
| 805 setSheetOffsetFromBottom(getSheetHeightForState(state)); | 812 setSheetOffsetFromBottom(getSheetHeightForState(state)); |
| 806 } | 813 setInternalCurrentState(mTargetState); |
| 807 | |
| 808 if (!stateChanged) return; | |
| 809 | |
| 810 for (BottomSheetObserver o : mObservers) { | |
| 811 o.onSheetStateChanged(mCurrentState); | |
| 812 } | 814 } |
| 813 } | 815 } |
| 814 | 816 |
| 815 /** | 817 /** |
| 816 * @return The current state of the bottom sheet. If the sheet is animating,
this will be the | 818 * @return The current state of the bottom sheet. If the sheet is animating,
this will be the |
| 817 * state the sheet is animating to. | 819 * state the sheet is animating to. |
| 818 */ | 820 */ |
| 819 public int getSheetState() { | 821 public int getSheetState() { |
| 820 return mCurrentState; | 822 return mCurrentState; |
| 821 } | 823 } |
| 822 | 824 |
| 823 /** | 825 /** |
| 826 * Set the current state of the bottom sheet. This is for internal use to no
tify observers of |
| 827 * state change events. |
| 828 * @param state The current state of the sheet. |
| 829 */ |
| 830 private void setInternalCurrentState(@SheetState int state) { |
| 831 if (state == mCurrentState) return; |
| 832 mCurrentState = state; |
| 833 |
| 834 for (BottomSheetObserver o : mObservers) { |
| 835 o.onSheetStateChanged(mCurrentState); |
| 836 } |
| 837 } |
| 838 |
| 839 /** |
| 824 * If the animation to settle the sheet in one of its states is running. | 840 * If the animation to settle the sheet in one of its states is running. |
| 825 * @return True if the animation is running. | 841 * @return True if the animation is running. |
| 826 */ | 842 */ |
| 827 public boolean isRunningSettleAnimation() { | 843 public boolean isRunningSettleAnimation() { |
| 828 return mSettleAnimator != null; | 844 return mSettleAnimator != null; |
| 829 } | 845 } |
| 830 | 846 |
| 831 @VisibleForTesting | 847 @VisibleForTesting |
| 832 public BottomSheetContent getCurrentSheetContent() { | 848 public BottomSheetContent getCurrentSheetContent() { |
| 833 return mSheetContent; | 849 return mSheetContent; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 906 @Override | 922 @Override |
| 907 public void onFadingViewVisibilityChanged(boolean visible) {} | 923 public void onFadingViewVisibilityChanged(boolean visible) {} |
| 908 | 924 |
| 909 private boolean canMoveSheet() { | 925 private boolean canMoveSheet() { |
| 910 boolean isInOverviewMode = mTabModelSelector != null | 926 boolean isInOverviewMode = mTabModelSelector != null |
| 911 && (mTabModelSelector.getCurrentTab() == null | 927 && (mTabModelSelector.getCurrentTab() == null |
| 912 || mTabModelSelector.getCurrentTab().getActivity().is
InOverviewMode()); | 928 || mTabModelSelector.getCurrentTab().getActivity().is
InOverviewMode()); |
| 913 return !isToolbarAndroidViewHidden() && !isInOverviewMode; | 929 return !isToolbarAndroidViewHidden() && !isInOverviewMode; |
| 914 } | 930 } |
| 915 } | 931 } |
| OLD | NEW |