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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
45 * - HALF: The sheet is expanded to consume around half of the screen. | 45 * - HALF: The sheet is expanded to consume around half of the screen. |
46 * - FULL: The sheet is expanded to its full height. | 46 * - FULL: The sheet is expanded to its full height. |
47 * | 47 * |
48 * All the computation in this file is based off of the bottom of the screen ins tead of the top | 48 * All the computation in this file is based off of the bottom of the screen ins tead of the top |
49 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. | 49 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. |
50 */ | 50 */ |
51 | 51 |
52 public class BottomSheet | 52 public class BottomSheet |
53 extends FrameLayout implements FadingBackgroundView.FadingViewObserver, NativePageHost { | 53 extends FrameLayout implements FadingBackgroundView.FadingViewObserver, NativePageHost { |
54 /** The different states that the bottom sheet can have. */ | 54 /** The different states that the bottom sheet can have. */ |
55 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}) | 55 @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL, SHEET_STATE_S CROLLING}) |
56 @Retention(RetentionPolicy.SOURCE) | 56 @Retention(RetentionPolicy.SOURCE) |
57 public @interface SheetState {} | 57 public @interface SheetState {} |
58 public static final int SHEET_STATE_PEEK = 0; | 58 public static final int SHEET_STATE_PEEK = 0; |
59 public static final int SHEET_STATE_HALF = 1; | 59 public static final int SHEET_STATE_HALF = 1; |
60 public static final int SHEET_STATE_FULL = 2; | 60 public static final int SHEET_STATE_FULL = 2; |
61 public static final int SHEET_STATE_SCROLLING = 3; | |
61 | 62 |
62 /** | 63 /** |
63 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material | 64 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material |
64 * design (this is the minimum time a user is guaranteed to pay attention to something). | 65 * design (this is the minimum time a user is guaranteed to pay attention to something). |
65 */ | 66 */ |
66 private static final long BASE_ANIMATION_DURATION_MS = 218; | 67 private static final long BASE_ANIMATION_DURATION_MS = 218; |
67 | 68 |
68 /** | 69 /** |
69 * The fraction of the way to the next state the sheet must be swiped to ani mate there when | 70 * The fraction of the way to the next state the sheet must be swiped to ani mate there when |
70 * released. A smaller value here means a smaller swipe is needed to move th e sheet around. | 71 * 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... | |
105 | 106 |
106 /** The height of the toolbar. */ | 107 /** The height of the toolbar. */ |
107 private float mToolbarHeight; | 108 private float mToolbarHeight; |
108 | 109 |
109 /** The width of the view that contains the bottom sheet. */ | 110 /** The width of the view that contains the bottom sheet. */ |
110 private float mContainerWidth; | 111 private float mContainerWidth; |
111 | 112 |
112 /** The height of the view that contains the bottom sheet. */ | 113 /** The height of the view that contains the bottom sheet. */ |
113 private float mContainerHeight; | 114 private float mContainerHeight; |
114 | 115 |
115 /** The current sheet state. If the sheet is moving, this will be the target state. */ | 116 /** The current state that the sheet is in. */ |
116 private int mCurrentState; | 117 private int mCurrentState; |
117 | 118 |
119 /** The target sheet state. This is the state that the sheet is currently mo ving to. */ | |
120 private int mTargetState; | |
121 | |
118 /** Used for getting the current tab. */ | 122 /** Used for getting the current tab. */ |
119 private TabModelSelector mTabModelSelector; | 123 private TabModelSelector mTabModelSelector; |
120 | 124 |
121 /** The fullscreen manager for information about toolbar offsets. */ | 125 /** The fullscreen manager for information about toolbar offsets. */ |
122 private ChromeFullscreenManager mFullscreenManager; | 126 private ChromeFullscreenManager mFullscreenManager; |
123 | 127 |
124 /** A handle to the content being shown by the sheet. */ | 128 /** A handle to the content being shown by the sheet. */ |
125 private BottomSheetContent mSheetContent; | 129 private BottomSheetContent mSheetContent; |
126 | 130 |
127 /** A handle to the toolbar control container. */ | 131 /** A handle to the toolbar control container. */ |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 | 235 |
232 // Similarly, if the sheet is in the min position, don't move if the scroll is downward. | 236 // Similarly, if the sheet is in the min position, don't move if the scroll is downward. |
233 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { | 237 if (currentShownRatio <= getPeekRatio() && distanceY < 0) { |
234 mIsScrolling = false; | 238 mIsScrolling = false; |
235 return false; | 239 return false; |
236 } | 240 } |
237 | 241 |
238 float newOffset = getSheetOffsetFromBottom() + distanceY; | 242 float newOffset = getSheetOffsetFromBottom() + distanceY; |
239 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset())); | 243 setSheetOffsetFromBottom(MathUtils.clamp(newOffset, getMinOffset(), getMaxOffset())); |
240 | 244 |
245 setInternalCurrentState(SHEET_STATE_SCROLLING); | |
241 mIsScrolling = true; | 246 mIsScrolling = true; |
242 return true; | 247 return true; |
243 } | 248 } |
244 | 249 |
245 @Override | 250 @Override |
246 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { | 251 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { |
247 cancelAnimation(); | 252 cancelAnimation(); |
248 | 253 |
249 // Figure out the projected state of the sheet and animate there. No te that a swipe up | 254 // Figure out the projected state of the sheet and animate there. No te that a swipe up |
250 // will have a negative velocity, swipe down will have a positive ve locity. Negate this | 255 // will have a negative velocity, swipe down will have a positive ve locity. Negate this |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
352 * calculations in this class. | 357 * calculations in this class. |
353 * @param root The container of the bottom sheet. | 358 * @param root The container of the bottom sheet. |
354 * @param controlContainer The container for the toolbar. | 359 * @param controlContainer The container for the toolbar. |
355 */ | 360 */ |
356 public void init(View root, View controlContainer) { | 361 public void init(View root, View controlContainer) { |
357 mControlContainer = controlContainer; | 362 mControlContainer = controlContainer; |
358 mToolbarHeight = mControlContainer.getHeight(); | 363 mToolbarHeight = mControlContainer.getHeight(); |
359 | 364 |
360 mBottomSheetContentContainer = (FrameLayout) findViewById(R.id.bottom_sh eet_content); | 365 mBottomSheetContentContainer = (FrameLayout) findViewById(R.id.bottom_sh eet_content); |
361 | 366 |
362 mCurrentState = SHEET_STATE_PEEK; | 367 setInternalCurrentState(SHEET_STATE_SCROLLING); |
Theresa
2017/03/17 23:36:19
I would have expected this to stay SHEET_STATE_PEE
mdjones
2017/03/20 16:09:16
Whoops, definitely a mistake. Fixed.
| |
363 | 368 |
364 // Listen to height changes on the root. | 369 // Listen to height changes on the root. |
365 root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { | 370 root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |
366 @Override | 371 @Override |
367 public void onLayoutChange(View v, int left, int top, int right, int bottom, | 372 public void onLayoutChange(View v, int left, int top, int right, int bottom, |
368 int oldLeft, int oldTop, int oldRight, int oldBottom) { | 373 int oldLeft, int oldTop, int oldRight, int oldBottom) { |
369 // Make sure the size of the layout actually changed. | 374 // Make sure the size of the layout actually changed. |
370 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { | 375 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { |
371 return; | 376 return; |
372 } | 377 } |
373 | 378 |
374 mContainerWidth = right - left; | 379 mContainerWidth = right - left; |
375 mContainerHeight = bottom - top; | 380 mContainerHeight = bottom - top; |
376 updateSheetDimensions(); | 381 updateSheetDimensions(); |
377 | 382 |
378 cancelAnimation(); | 383 cancelAnimation(); |
379 setSheetState(mCurrentState, false); | 384 setSheetState(mTargetState, false); |
380 } | 385 } |
381 }); | 386 }); |
382 | 387 |
383 // Listen to height changes on the toolbar. | 388 // Listen to height changes on the toolbar. |
384 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen er() { | 389 controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListen er() { |
385 @Override | 390 @Override |
386 public void onLayoutChange(View v, int left, int top, int right, int bottom, | 391 public void onLayoutChange(View v, int left, int top, int right, int bottom, |
387 int oldLeft, int oldTop, int oldRight, int oldBottom) { | 392 int oldLeft, int oldTop, int oldRight, int oldBottom) { |
388 // Make sure the size of the layout actually changed. | 393 // Make sure the size of the layout actually changed. |
389 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { | 394 if (bottom - top == oldBottom - oldTop && right - left == oldRig ht - oldLeft) { |
390 return; | 395 return; |
391 } | 396 } |
392 | 397 |
393 mToolbarHeight = bottom - top; | 398 mToolbarHeight = bottom - top; |
394 updateSheetDimensions(); | 399 updateSheetDimensions(); |
395 | 400 |
396 cancelAnimation(); | 401 cancelAnimation(); |
397 setSheetState(mCurrentState, false); | 402 setSheetState(mTargetState, false); |
398 } | 403 } |
399 }); | 404 }); |
400 | 405 |
401 mPlaceholder = new View(getContext()); | 406 mPlaceholder = new View(getContext()); |
402 LayoutParams placeHolderParams = | 407 LayoutParams placeHolderParams = |
403 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT); | 408 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT); |
404 mPlaceholder.setBackgroundColor( | 409 mPlaceholder.setBackgroundColor( |
405 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w hite)); | 410 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w hite)); |
406 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); | 411 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); |
407 | 412 |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
580 if (mSettleAnimator == null) return; | 585 if (mSettleAnimator == null) return; |
581 mSettleAnimator.cancel(); | 586 mSettleAnimator.cancel(); |
582 mSettleAnimator = null; | 587 mSettleAnimator = null; |
583 } | 588 } |
584 | 589 |
585 /** | 590 /** |
586 * Creates the sheet's animation to a target state. | 591 * Creates the sheet's animation to a target state. |
587 * @param targetState The target state. | 592 * @param targetState The target state. |
588 */ | 593 */ |
589 private void createSettleAnimation(@SheetState int targetState) { | 594 private void createSettleAnimation(@SheetState int targetState) { |
590 mCurrentState = targetState; | 595 mTargetState = targetState; |
591 mSettleAnimator = ValueAnimator.ofFloat( | 596 mSettleAnimator = ValueAnimator.ofFloat( |
592 getSheetOffsetFromBottom(), getSheetHeightForState(targetState)) ; | 597 getSheetOffsetFromBottom(), getSheetHeightForState(targetState)) ; |
593 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); | 598 mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); |
594 mSettleAnimator.setInterpolator(mInterpolator); | 599 mSettleAnimator.setInterpolator(mInterpolator); |
595 | 600 |
596 // When the animation is canceled or ends, reset the handle to null. | 601 // When the animation is canceled or ends, reset the handle to null. |
597 mSettleAnimator.addListener(new AnimatorListenerAdapter() { | 602 mSettleAnimator.addListener(new AnimatorListenerAdapter() { |
598 @Override | 603 @Override |
599 public void onAnimationEnd(Animator animator) { | 604 public void onAnimationEnd(Animator animator) { |
600 mSettleAnimator = null; | 605 mSettleAnimator = null; |
606 setInternalCurrentState(mTargetState); | |
601 } | 607 } |
602 }); | 608 }); |
603 | 609 |
604 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen er() { | 610 mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListen er() { |
605 @Override | 611 @Override |
606 public void onAnimationUpdate(ValueAnimator animator) { | 612 public void onAnimationUpdate(ValueAnimator animator) { |
607 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); | 613 setSheetOffsetFromBottom((Float) animator.getAnimatedValue()); |
608 } | 614 } |
609 }); | 615 }); |
610 | 616 |
611 mSettleAnimator.start(); | 617 mSettleAnimator.start(); |
618 setInternalCurrentState(SHEET_STATE_SCROLLING); | |
612 } | 619 } |
613 | 620 |
614 /** | 621 /** |
615 * Gets the distance of a fling based on the velocity and the base animation time. This formula | 622 * Gets the distance of a fling based on the velocity and the base animation time. This formula |
616 * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: | 623 * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: |
617 * displacement = initialVelocity * duration / 2. | 624 * displacement = initialVelocity * duration / 2. |
618 * @param velocity The velocity of the fling. | 625 * @param velocity The velocity of the fling. |
619 * @return The distance the fling would cover. | 626 * @return The distance the fling would cover. |
620 */ | 627 */ |
621 private float getFlingDistance(float velocity) { | 628 private float getFlingDistance(float velocity) { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
716 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { | 723 if (mLastPeekToHalfRatioSent < 1f || peekHalfRatio < 1f) { |
717 mLastPeekToHalfRatioSent = peekHalfRatio; | 724 mLastPeekToHalfRatioSent = peekHalfRatio; |
718 for (BottomSheetObserver o : mObservers) { | 725 for (BottomSheetObserver o : mObservers) { |
719 o.onTransitionPeekToHalf(peekHalfRatio); | 726 o.onTransitionPeekToHalf(peekHalfRatio); |
720 } | 727 } |
721 } | 728 } |
722 } | 729 } |
723 | 730 |
724 /** | 731 /** |
725 * Moves the sheet to the provided state. | 732 * Moves the sheet to the provided state. |
726 * @param state The state to move the panel to. | 733 * @param state The state to move the panel to. |
Theresa
2017/03/17 23:36:19
nit: It may be good to mention that SCROLLING is n
mdjones
2017/03/20 16:09:17
Done.
| |
727 * @param animate If true, the sheet will animate to the provided state, oth erwise it will | 734 * @param animate If true, the sheet will animate to the provided state, oth erwise it will |
728 * move there instantly. | 735 * move there instantly. |
729 */ | 736 */ |
730 public void setSheetState(@SheetState int state, boolean animate) { | 737 public void setSheetState(@SheetState int state, boolean animate) { |
731 mCurrentState = state; | 738 assert state != SHEET_STATE_SCROLLING; |
739 | |
740 mTargetState = state; | |
732 | 741 |
733 if (animate) { | 742 if (animate) { |
734 createSettleAnimation(state); | 743 createSettleAnimation(state); |
735 } else { | 744 } else { |
736 setSheetOffsetFromBottom(getSheetHeightForState(state)); | 745 setSheetOffsetFromBottom(getSheetHeightForState(state)); |
746 setInternalCurrentState(mTargetState); | |
737 } | 747 } |
738 } | 748 } |
739 | 749 |
740 /** | 750 /** |
741 * @return The current state of the bottom sheet. If the sheet is animating, this will be the | 751 * @return The current state of the bottom sheet. If the sheet is animating, this will be the |
742 * state the sheet is animating to. | 752 * state the sheet is animating to. |
743 */ | 753 */ |
744 public int getSheetState() { | 754 public int getSheetState() { |
745 return mCurrentState; | 755 return mCurrentState; |
746 } | 756 } |
747 | 757 |
748 /** | 758 /** |
759 * Set the current state of the bottom sheet. This is for internal use to no tify observers of | |
760 * state change events. | |
761 * @param state The current state of the sheet. | |
762 */ | |
763 private void setInternalCurrentState(@SheetState int state) { | |
764 // TODO(mdjones): Trigger onSheetStateChanged here. | |
765 mCurrentState = state; | |
766 } | |
767 | |
768 /** | |
749 * If the animation to settle the sheet in one of its states is running. | 769 * If the animation to settle the sheet in one of its states is running. |
750 * @return True if the animation is running. | 770 * @return True if the animation is running. |
751 */ | 771 */ |
752 public boolean isRunningSettleAnimation() { | 772 public boolean isRunningSettleAnimation() { |
753 return mSettleAnimator != null; | 773 return mSettleAnimator != null; |
754 } | 774 } |
755 | 775 |
756 @VisibleForTesting | 776 @VisibleForTesting |
757 public BottomSheetContent getCurrentSheetContent() { | 777 public BottomSheetContent getCurrentSheetContent() { |
758 return mSheetContent; | 778 return mSheetContent; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
826 @Override | 846 @Override |
827 public void onFadingViewVisibilityChanged(boolean visible) {} | 847 public void onFadingViewVisibilityChanged(boolean visible) {} |
828 | 848 |
829 private boolean canMoveSheet() { | 849 private boolean canMoveSheet() { |
830 boolean isInOverviewMode = mTabModelSelector != null | 850 boolean isInOverviewMode = mTabModelSelector != null |
831 && (mTabModelSelector.getCurrentTab() == null | 851 && (mTabModelSelector.getCurrentTab() == null |
832 || mTabModelSelector.getCurrentTab().getActivity().is InOverviewMode()); | 852 || mTabModelSelector.getCurrentTab().getActivity().is InOverviewMode()); |
833 return !isToolbarAndroidViewHidden() && !isInOverviewMode; | 853 return !isToolbarAndroidViewHidden() && !isInOverviewMode; |
834 } | 854 } |
835 } | 855 } |
OLD | NEW |