Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java

Issue 2839673002: 🏠 Polish bottom sheet content transitions (Closed)
Patch Set: end animation immediately if no previous content Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/android/java/res/layout/bottom_control_container.xml ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.AnimatorSet;
9 import android.animation.ObjectAnimator; 10 import android.animation.ObjectAnimator;
10 import android.animation.ValueAnimator; 11 import android.animation.ValueAnimator;
11 import android.content.Context; 12 import android.content.Context;
12 import android.content.SharedPreferences; 13 import android.content.SharedPreferences;
13 import android.graphics.Color; 14 import android.graphics.Color;
14 import android.graphics.Region; 15 import android.graphics.Region;
15 import android.os.Build; 16 import android.os.Build;
16 import android.support.annotation.IntDef; 17 import android.support.annotation.IntDef;
17 import android.support.annotation.Nullable; 18 import android.support.annotation.Nullable;
18 import android.util.AttributeSet; 19 import android.util.AttributeSet;
19 import android.view.GestureDetector; 20 import android.view.GestureDetector;
20 import android.view.MotionEvent; 21 import android.view.MotionEvent;
21 import android.view.VelocityTracker; 22 import android.view.VelocityTracker;
22 import android.view.View; 23 import android.view.View;
24 import android.view.ViewGroup;
23 import android.view.Window; 25 import android.view.Window;
24 import android.view.animation.DecelerateInterpolator; 26 import android.view.animation.DecelerateInterpolator;
25 import android.view.animation.Interpolator; 27 import android.view.animation.Interpolator;
26 import android.widget.FrameLayout; 28 import android.widget.FrameLayout;
27 29
28 import org.chromium.base.ApiCompatibilityUtils; 30 import org.chromium.base.ApiCompatibilityUtils;
29 import org.chromium.base.ContextUtils; 31 import org.chromium.base.ContextUtils;
30 import org.chromium.base.ObserverList; 32 import org.chromium.base.ObserverList;
31 import org.chromium.base.VisibleForTesting; 33 import org.chromium.base.VisibleForTesting;
32 import org.chromium.chrome.R; 34 import org.chromium.chrome.R;
33 import org.chromium.chrome.browser.NativePageHost; 35 import org.chromium.chrome.browser.NativePageHost;
34 import org.chromium.chrome.browser.TabLoadStatus; 36 import org.chromium.chrome.browser.TabLoadStatus;
35 import org.chromium.chrome.browser.firstrun.FirstRunStatus; 37 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
36 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; 38 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
37 import org.chromium.chrome.browser.ntp.NativePageFactory; 39 import org.chromium.chrome.browser.ntp.NativePageFactory;
38 import org.chromium.chrome.browser.ntp.NewTabPage; 40 import org.chromium.chrome.browser.ntp.NewTabPage;
39 import org.chromium.chrome.browser.tab.Tab; 41 import org.chromium.chrome.browser.tab.Tab;
40 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; 42 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
41 import org.chromium.chrome.browser.tabmodel.TabModel; 43 import org.chromium.chrome.browser.tabmodel.TabModel;
42 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 44 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
43 import org.chromium.chrome.browser.toolbar.BottomToolbarPhone; 45 import org.chromium.chrome.browser.toolbar.BottomToolbarPhone;
44 import org.chromium.chrome.browser.util.MathUtils; 46 import org.chromium.chrome.browser.util.MathUtils;
45 import org.chromium.chrome.browser.widget.FadingBackgroundView; 47 import org.chromium.chrome.browser.widget.FadingBackgroundView;
46 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContentControll er.ContentType; 48 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContentControll er.ContentType;
47 import org.chromium.chrome.browser.widget.textbubble.ViewAnchoredTextBubble; 49 import org.chromium.chrome.browser.widget.textbubble.ViewAnchoredTextBubble;
48 import org.chromium.content_public.browser.LoadUrlParams; 50 import org.chromium.content_public.browser.LoadUrlParams;
49 51
50 import java.lang.annotation.Retention; 52 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy; 53 import java.lang.annotation.RetentionPolicy;
54 import java.util.ArrayList;
55 import java.util.List;
52 56
53 /** 57 /**
54 * This class defines the bottom sheet that has multiple states and a persistent ly showing toolbar. 58 * This class defines the bottom sheet that has multiple states and a persistent ly showing toolbar.
55 * Namely, the states are: 59 * Namely, the states are:
56 * - PEEK: Only the toolbar is visible at the bottom of the screen. 60 * - PEEK: Only the toolbar is visible at the bottom of the screen.
57 * - HALF: The sheet is expanded to consume around half of the screen. 61 * - HALF: The sheet is expanded to consume around half of the screen.
58 * - FULL: The sheet is expanded to its full height. 62 * - FULL: The sheet is expanded to its full height.
59 * 63 *
60 * All the computation in this file is based off of the bottom of the screen ins tead of the top 64 * All the computation in this file is based off of the bottom of the screen ins tead of the top
61 * for simplicity. This means that the bottom of the screen is 0 on the Y axis. 65 * for simplicity. This means that the bottom of the screen is 0 on the Y axis.
(...skipping 17 matching lines...) Expand all
79 83
80 /** Shared preference key for tracking whether the help bubble has been show n. */ 84 /** Shared preference key for tracking whether the help bubble has been show n. */
81 private static final String BOTTOM_SHEET_HELP_BUBBLE_SHOWN = "bottom_sheet_h elp_bubble_shown"; 85 private static final String BOTTOM_SHEET_HELP_BUBBLE_SHOWN = "bottom_sheet_h elp_bubble_shown";
82 86
83 /** 87 /**
84 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material 88 * The base duration of the settling animation of the sheet. 218 ms is a spe c for material
85 * design (this is the minimum time a user is guaranteed to pay attention to something). 89 * design (this is the minimum time a user is guaranteed to pay attention to something).
86 */ 90 */
87 private static final long BASE_ANIMATION_DURATION_MS = 218; 91 private static final long BASE_ANIMATION_DURATION_MS = 218;
88 92
93 /** The amount of time it takes to transition sheet content in or out. */
94 private static final long TRANSITION_DURATION_MS = 150;
95
89 /** 96 /**
90 * The fraction of the way to the next state the sheet must be swiped to ani mate there when 97 * The fraction of the way to the next state the sheet must be swiped to ani mate there when
91 * released. This is the value used when there are 3 active states. A smalle r value here means 98 * released. This is the value used when there are 3 active states. A smalle r value here means
92 * a smaller swipe is needed to move the sheet around. 99 * a smaller swipe is needed to move the sheet around.
93 */ 100 */
94 private static final float THRESHOLD_TO_NEXT_STATE_3 = 0.5f; 101 private static final float THRESHOLD_TO_NEXT_STATE_3 = 0.5f;
95 102
96 /** This is similar to {@link #THRESHOLD_TO_NEXT_STATE_3} but for 2 states i nstead of 3. */ 103 /** This is similar to {@link #THRESHOLD_TO_NEXT_STATE_3} but for 2 states i nstead of 3. */
97 private static final float THRESHOLD_TO_NEXT_STATE_2 = 0.3f; 104 private static final float THRESHOLD_TO_NEXT_STATE_2 = 0.3f;
98 105
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 140
134 /** Whether or not the user is scrolling the bottom sheet. */ 141 /** Whether or not the user is scrolling the bottom sheet. */
135 private boolean mIsScrolling; 142 private boolean mIsScrolling;
136 143
137 /** Track the velocity of the user's scrolls to determine up or down directi on. */ 144 /** Track the velocity of the user's scrolls to determine up or down directi on. */
138 private VelocityTracker mVelocityTracker; 145 private VelocityTracker mVelocityTracker;
139 146
140 /** The animator used to move the sheet to a fixed state when released by th e user. */ 147 /** The animator used to move the sheet to a fixed state when released by th e user. */
141 private ValueAnimator mSettleAnimator; 148 private ValueAnimator mSettleAnimator;
142 149
143 /** The animator used for the toolbar fades. */ 150 /** The animator set responsible for swapping the bottom sheet content. */
144 private ValueAnimator mToolbarFadeAnimator; 151 private AnimatorSet mContentSwapAnimatorSet;
145 152
146 /** The height of the toolbar. */ 153 /** The height of the toolbar. */
147 private float mToolbarHeight; 154 private float mToolbarHeight;
148 155
149 /** The width of the view that contains the bottom sheet. */ 156 /** The width of the view that contains the bottom sheet. */
150 private float mContainerWidth; 157 private float mContainerWidth;
151 158
152 /** The height of the view that contains the bottom sheet. */ 159 /** The height of the view that contains the bottom sheet. */
153 private float mContainerHeight; 160 private float mContainerHeight;
154 161
(...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 557
551 cancelAnimation(); 558 cancelAnimation();
552 setSheetState(mCurrentState, false); 559 setSheetState(mCurrentState, false);
553 } 560 }
554 }); 561 });
555 562
556 mPlaceholder = new View(getContext()); 563 mPlaceholder = new View(getContext());
557 LayoutParams placeHolderParams = 564 LayoutParams placeHolderParams =
558 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT); 565 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT);
559 mPlaceholder.setBackgroundColor( 566 mPlaceholder.setBackgroundColor(
560 ApiCompatibilityUtils.getColor(getResources(), android.R.color.w hite)); 567 ApiCompatibilityUtils.getColor(getResources(), R.color.default_p rimary_color));
561 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams); 568 mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams);
562 569
563 mToolbarHolder = (FrameLayout) mControlContainer.findViewById(R.id.toolb ar_holder); 570 mToolbarHolder = (FrameLayout) mControlContainer.findViewById(R.id.toolb ar_holder);
564 mDefaultToolbarView = (BottomToolbarPhone) mControlContainer.findViewByI d(R.id.toolbar); 571 mDefaultToolbarView = (BottomToolbarPhone) mControlContainer.findViewByI d(R.id.toolbar);
565 } 572 }
566 573
567 /** 574 /**
568 * Set the color of the pull handle used by the toolbar. 575 * Set the color of the pull handle used by the toolbar.
569 */ 576 */
570 public void updateHandleTint() { 577 public void updateHandleTint() {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
638 * @return The sheet's distance from the bottom of the screen. 645 * @return The sheet's distance from the bottom of the screen.
639 */ 646 */
640 public float getSheetOffsetFromBottom() { 647 public float getSheetOffsetFromBottom() {
641 return mContainerHeight - getTranslationY(); 648 return mContainerHeight - getTranslationY();
642 } 649 }
643 650
644 /** 651 /**
645 * Show content in the bottom sheet's content area. 652 * Show content in the bottom sheet's content area.
646 * @param content The {@link BottomSheetContent} to show. 653 * @param content The {@link BottomSheetContent} to show.
647 */ 654 */
648 public void showContent(BottomSheetContent content) { 655 public void showContent(final BottomSheetContent content) {
649 // If the desired content is already showing, do nothing. 656 // If the desired content is already showing, do nothing.
650 if (mSheetContent == content) return; 657 if (mSheetContent == content) return;
651 658
652 View newToolbar = content.getToolbarView(); 659 mBottomSheetContentContainer.removeView(mPlaceholder);
653 View oldToolbar = null;
654 660
655 if (mSheetContent != null) { 661 View newToolbar =
656 oldToolbar = mSheetContent.getToolbarView(); 662 content.getToolbarView() != null ? content.getToolbarView() : mD efaultToolbarView;
657 mBottomSheetContentContainer.removeView(mSheetContent.getContentView ()); 663 View oldToolbar = mSheetContent != null && mSheetContent.getToolbarView( ) != null
658 mSheetContent = null; 664 ? mSheetContent.getToolbarView()
659 } 665 : mDefaultToolbarView;
666 View oldContent = mSheetContent != null ? mSheetContent.getContentView() : null;
660 667
661 mBottomSheetContentContainer.removeView(mPlaceholder); 668 // If an animation is already running, end it.
662 mSheetContent = content; 669 if (mContentSwapAnimatorSet != null) mContentSwapAnimatorSet.end();
663 mBottomSheetContentContainer.addView(mSheetContent.getContentView());
664 670
665 doToolbarSwap(newToolbar, oldToolbar); 671 List<Animator> animators = new ArrayList<>();
672 mContentSwapAnimatorSet = new AnimatorSet();
673 mContentSwapAnimatorSet.addListener(new AnimatorListenerAdapter() {
674 @Override
675 public void onAnimationEnd(Animator animation) {
676 mSheetContent = content;
677 for (BottomSheetObserver o : mObservers) {
678 o.onSheetContentChanged(content);
679 }
680 updateHandleTint();
681 mContentSwapAnimatorSet = null;
682 }
683 });
666 684
667 for (BottomSheetObserver o : mObservers) { 685 // For the toolbar transition, make sure we don't detach the default too lbar view.
668 o.onSheetContentChanged(mSheetContent); 686 animators.add(getViewTransitionAnimator(
669 } 687 newToolbar, oldToolbar, mToolbarHolder, mDefaultToolbarView != o ldToolbar));
688 animators.add(getViewTransitionAnimator(
689 content.getContentView(), oldContent, mBottomSheetContentContain er, true));
690
691 mContentSwapAnimatorSet.playTogether(animators);
692 mContentSwapAnimatorSet.start();
693
694 // If the existing content is null, end the animation immediately.
695 if (mSheetContent == null) mContentSwapAnimatorSet.end();
670 } 696 }
671 697
672 /** 698 /**
673 * Fade between a new toolbar and the old toolbar to be shown. A null parame ter can be used to 699 * Creates a transition animation between two views. The old view is faded o ut completely
674 * refer to the default omnibox toolbar. Normally, the new toolbar is attach ed to the toolbar 700 * before the new view is faded in. There is an option to detach the old vie w or not.
675 * container and faded in. In the case of the default toolbar, the old toolb ar is faded out. 701 * @param newView The new view to transition to.
676 * This is because the default toolbar is always attached to the view hierar chy and sits behind 702 * @param oldView The old view to transition from.
677 * the attach point for the other toolbars. 703 * @param detachOldView Whether or not to detach the old view once faded out .
678 * @param newToolbar The toolbar that will be shown. 704 * @return An animator that runs the specified animation.
679 * @param oldToolbar The toolbar being replaced.
680 */ 705 */
681 private void doToolbarSwap(View newToolbar, View oldToolbar) { 706 private Animator getViewTransitionAnimator(final View newView, final View ol dView,
682 if (mToolbarFadeAnimator != null) mToolbarFadeAnimator.end(); 707 final ViewGroup parent, final boolean detachOldView) {
708 if (newView == oldView) return null;
683 709
684 final View targetToolbar = newToolbar != null ? newToolbar : mDefaultToo lbarView; 710 AnimatorSet animatorSet = new AnimatorSet();
685 final View currentToolbar = oldToolbar != null ? oldToolbar : mDefaultTo olbarView; 711 List<Animator> animators = new ArrayList<>();
686 712
687 if (targetToolbar == currentToolbar) return; 713 // Fade out the old view.
688 714 if (oldView != null) {
689 if (targetToolbar != mDefaultToolbarView) { 715 ValueAnimator fadeOutAnimator = ObjectAnimator.ofFloat(oldView, View .ALPHA, 0);
690 mToolbarHolder.addView(targetToolbar); 716 fadeOutAnimator.setDuration(TRANSITION_DURATION_MS);
691 targetToolbar.setAlpha(0f); 717 fadeOutAnimator.addListener(new AnimatorListenerAdapter() {
692 } else { 718 @Override
693 targetToolbar.setVisibility(View.VISIBLE); 719 public void onAnimationEnd(Animator animation) {
694 targetToolbar.setAlpha(1f); 720 if (detachOldView && oldView.getParent() != null) {
721 parent.removeView(oldView);
722 }
723 }
724 });
725 animators.add(fadeOutAnimator);
695 } 726 }
696 727
697 mToolbarFadeAnimator = ObjectAnimator.ofFloat(0, 1); 728 // Fade in the new view.
698 mToolbarFadeAnimator.setDuration(BASE_ANIMATION_DURATION_MS); 729 if (parent != newView.getParent()) parent.addView(newView);
699 mToolbarFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateL istener() { 730 newView.setAlpha(0);
700 @Override 731 ValueAnimator fadeInAnimator = ObjectAnimator.ofFloat(newView, View.ALPH A, 1);
701 public void onAnimationUpdate(ValueAnimator animator) { 732 fadeInAnimator.setDuration(TRANSITION_DURATION_MS);
702 if (targetToolbar == mDefaultToolbarView) { 733 animators.add(fadeInAnimator);
703 currentToolbar.setAlpha(1f - animator.getAnimatedFraction()) ;
704 } else {
705 targetToolbar.setAlpha(animator.getAnimatedFraction());
706 }
707 }
708 });
709 mToolbarFadeAnimator.addListener(new AnimatorListenerAdapter() {
710 @Override
711 public void onAnimationEnd(Animator animation) {
712 targetToolbar.setAlpha(1f);
713 currentToolbar.setAlpha(0f);
714 if (currentToolbar != mDefaultToolbarView) {
715 mToolbarHolder.removeView(currentToolbar);
716 } else {
717 currentToolbar.setVisibility(View.GONE);
718 }
719 mToolbarFadeAnimator = null;
720 updateHandleTint();
721 }
722 });
723 734
724 mToolbarFadeAnimator.start(); 735 animatorSet.playSequentially(animators);
736
737 return animatorSet;
725 } 738 }
726 739
727 /** 740 /**
728 * Determines if a touch event is inside the toolbar. This assumes the toolb ar is the full 741 * Determines if a touch event is inside the toolbar. This assumes the toolb ar is the full
729 * width of the screen and that the toolbar is at the top of the bottom shee t. 742 * width of the screen and that the toolbar is at the top of the bottom shee t.
730 * @param e The motion event to test. 743 * @param e The motion event to test.
731 * @return True if the event occured in the toolbar region. 744 * @return True if the event occured in the toolbar region.
732 */ 745 */
733 private boolean isTouchEventInToolbar(MotionEvent e) { 746 private boolean isTouchEventInToolbar(MotionEvent e) {
734 if (mControlContainer == null) return false; 747 if (mControlContainer == null) return false;
(...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after
1141 getContext(), mControlContainer, R.string.bottom_sheet_help_bubb le_message); 1154 getContext(), mControlContainer, R.string.bottom_sheet_help_bubb le_message);
1142 int inset = getContext().getResources().getDimensionPixelSize( 1155 int inset = getContext().getResources().getDimensionPixelSize(
1143 R.dimen.bottom_sheet_help_bubble_inset); 1156 R.dimen.bottom_sheet_help_bubble_inset);
1144 helpBubble.setInsetPx(0, inset, 0, inset); 1157 helpBubble.setInsetPx(0, inset, 0, inset);
1145 helpBubble.setDismissOnTouchInteraction(true); 1158 helpBubble.setDismissOnTouchInteraction(true);
1146 helpBubble.show(); 1159 helpBubble.show();
1147 1160
1148 preferences.edit().putBoolean(BOTTOM_SHEET_HELP_BUBBLE_SHOWN, true).appl y(); 1161 preferences.edit().putBoolean(BOTTOM_SHEET_HELP_BUBBLE_SHOWN, true).appl y();
1149 } 1162 }
1150 } 1163 }
OLDNEW
« no previous file with comments | « chrome/android/java/res/layout/bottom_control_container.xml ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698