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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java

Issue 2844893002: [Home] Infobars appear more often (Closed)
Patch Set: Created 3 years, 8 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 | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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.banners; 5 package org.chromium.chrome.browser.banners;
6 6
7 import android.animation.Animator; 7 import android.animation.Animator;
8 import android.animation.Animator.AnimatorListener; 8 import android.animation.Animator.AnimatorListener;
9 import android.animation.AnimatorListenerAdapter; 9 import android.animation.AnimatorListenerAdapter;
10 import android.animation.ObjectAnimator; 10 import android.animation.ObjectAnimator;
11 import android.content.Context; 11 import android.content.Context;
12 import android.graphics.Region; 12 import android.graphics.Region;
13 import android.util.AttributeSet; 13 import android.util.AttributeSet;
14 import android.view.Gravity; 14 import android.view.Gravity;
15 import android.view.View; 15 import android.view.View;
16 import android.view.ViewGroup; 16 import android.view.ViewGroup;
17 import android.view.animation.DecelerateInterpolator; 17 import android.view.animation.DecelerateInterpolator;
18 import android.view.animation.Interpolator; 18 import android.view.animation.Interpolator;
19 import android.widget.FrameLayout; 19 import android.widget.FrameLayout;
20 20
21 import org.chromium.chrome.browser.util.MathUtils;
21 import org.chromium.content.browser.ContentViewCore; 22 import org.chromium.content.browser.ContentViewCore;
22 import org.chromium.content_public.browser.GestureStateListener; 23 import org.chromium.content_public.browser.GestureStateListener;
23 24
24 /** 25 /**
25 * View that slides up from the bottom of the page and slides away as the user s crolls the page. 26 * View that slides up from the bottom of the page and slides away as the user s crolls the page.
26 * Meant to be tacked onto the {@link org.chromium.content.browser.ContentViewCo re}'s view and 27 * Meant to be tacked onto the {@link org.chromium.content.browser.ContentViewCo re}'s view and
27 * alerted when either the page scroll position or viewport size changes. 28 * alerted when either the page scroll position or viewport size changes.
28 * 29 *
29 * GENERAL BEHAVIOR 30 * GENERAL BEHAVIOR
30 * This View is brought onto the screen by sliding upwards from the bottom of th e screen. Afterward 31 * This View is brought onto the screen by sliding upwards from the bottom of th e screen. Afterward
31 * the View slides onto and off of the screen vertically as the user scrolls upw ards or 32 * the View slides onto and off of the screen vertically as the user scrolls upw ards or
32 * downwards on the page. 33 * downwards on the page.
33 * 34 *
34 * VERTICAL SCROLL CALCULATIONS
35 * To determine how close the user is to the top of the page, the View must not only be informed of
36 * page scroll position changes, but also of changes in the viewport size (which happens as the
37 * omnibox appears and disappears, or as the page rotates e.g.). When the viewp ort size gradually
38 * shrinks, the user is most likely to be scrolling the page downwards while the omnibox comes back
39 * into view.
40 *
41 * When the user first begins scrolling the page, both the scroll position and t he viewport size are
42 * summed and recorded together. This is because a pixel change in the viewport height is
43 * equivalent to a pixel change in the content's scroll offset:
44 * - As the user scrolls the page downward, either the viewport height will incr ease (as the omnibox
45 * is slid off of the screen) or the content scroll offset will increase.
46 * - As the user scrolls the page upward, either the viewport height will decrea se (as the omnibox
47 * is brought back onto the screen) or the content scroll offset will decrease .
48 *
49 * As the scroll offset or the viewport height are updated via a scroll or fling , the difference 35 * As the scroll offset or the viewport height are updated via a scroll or fling , the difference
50 * from the initial value is used to determine the View's Y-translation. If a g esture is stopped, 36 * from the initial value is used to determine the View's Y-translation. If a g esture is stopped,
51 * the View will be snapped back into the center of the screen or entirely off o f the screen, based 37 * the View will be snapped back into the center of the screen or entirely off o f the screen, based
52 * on how much of the View is visible, or where the user is currently located on the page. 38 * on how much of the View is visible, or where the user is currently located on the page.
53 */ 39 */
54 public abstract class SwipableOverlayView extends FrameLayout { 40 public abstract class SwipableOverlayView extends FrameLayout {
55 private static final float FULL_THRESHOLD = 0.5f; 41 private static final float FULL_THRESHOLD = 0.5f;
56 private static final float VERTICAL_FLING_SHOW_THRESHOLD = 0.2f; 42 private static final float VERTICAL_FLING_SHOW_THRESHOLD = 0.2f;
57 private static final float VERTICAL_FLING_HIDE_THRESHOLD = 0.9f; 43 private static final float VERTICAL_FLING_HIDE_THRESHOLD = 0.9f;
58 44
(...skipping 17 matching lines...) Expand all
76 62
77 // Tracks whether the user is scrolling or flinging. 63 // Tracks whether the user is scrolling or flinging.
78 private int mGestureState; 64 private int mGestureState;
79 65
80 // Animation currently being used to translate the View. 66 // Animation currently being used to translate the View.
81 private Animator mCurrentAnimation; 67 private Animator mCurrentAnimation;
82 68
83 // Used to determine when the layout has changed and the Viewport must be up dated. 69 // Used to determine when the layout has changed and the Viewport must be up dated.
84 private int mParentHeight; 70 private int mParentHeight;
85 71
86 // Location of the View when the current gesture was first started.
87 private float mInitialTranslationY;
88
89 // Offset from the top of the page when the current gesture was first starte d. 72 // Offset from the top of the page when the current gesture was first starte d.
90 private int mInitialOffsetY; 73 private int mInitialOffsetY;
91 74
92 // How tall the View is, including its margins. 75 // How tall the View is, including its margins.
93 private int mTotalHeight; 76 private int mTotalHeight;
94 77
95 // Whether or not the View ever been fully displayed. 78 // Whether or not the View ever been fully displayed.
96 private boolean mIsBeingDisplayedForFirstTime; 79 private boolean mIsBeingDisplayedForFirstTime;
97 80
98 // The ContentViewCore to which the overlay is added. 81 // The ContentViewCore to which the overlay is added.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 super.onLayout(changed, l, t, r, b); 183 super.onLayout(changed, l, t, r, b);
201 } 184 }
202 185
203 /** 186 /**
204 * Creates a listener than monitors the ContentViewCore for scrolls and flin gs. 187 * Creates a listener than monitors the ContentViewCore for scrolls and flin gs.
205 * The listener updates the location of this View to account for the user's gestures. 188 * The listener updates the location of this View to account for the user's gestures.
206 * @return GestureStateListener to send to the ContentViewCore. 189 * @return GestureStateListener to send to the ContentViewCore.
207 */ 190 */
208 private GestureStateListener createGestureStateListener() { 191 private GestureStateListener createGestureStateListener() {
209 return new GestureStateListener() { 192 return new GestureStateListener() {
193 // Tracks the previous event's scroll offset to determine if a scrol l is up or down.
gone 2017/05/08 17:01:53 javadoc syntax instead
mdjones 2017/05/08 18:13:06 Done.
194 private int mLastScrollOffsetY;
195
196 // Location of the View when the current gesture was first started.
197 private float mInitialTranslationY;
198
199 // The initial extent of the scroll when triggered.
200 private float mInitialExtentY;
201
210 @Override 202 @Override
211 public void onFlingStartGesture(int scrollOffsetY, int scrollExtentY ) { 203 public void onFlingStartGesture(int scrollOffsetY, int scrollExtentY ) {
212 if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return; 204 if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return;
213 beginGesture(scrollOffsetY, scrollExtentY); 205 resetInitialOffsets(scrollOffsetY, scrollExtentY);
214 mGestureState = GESTURE_FLINGING; 206 mGestureState = GESTURE_FLINGING;
215 } 207 }
216 208
217 @Override 209 @Override
218 public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) { 210 public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
219 if (mGestureState != GESTURE_FLINGING) return; 211 if (mGestureState != GESTURE_FLINGING) return;
220 mGestureState = GESTURE_NONE; 212 mGestureState = GESTURE_NONE;
221 213
222 int finalOffsetY = computeScrollDifference(scrollOffsetY, scroll ExtentY);
223 updateTranslation(scrollOffsetY, scrollExtentY); 214 updateTranslation(scrollOffsetY, scrollExtentY);
224 215
225 boolean isScrollingDownward = finalOffsetY > 0; 216 boolean isScrollingDownward = scrollOffsetY > mLastScrollOffsetY ;
226 217
227 boolean isVisibleInitially = mInitialTranslationY < mTotalHeight ; 218 boolean isVisibleInitially = mInitialTranslationY < mTotalHeight ;
228 float percentageVisible = 1.0f - (getTranslationY() / mTotalHeig ht); 219 float percentageVisible = 1.0f - (getTranslationY() / mTotalHeig ht);
229 float visibilityThreshold = isVisibleInitially 220 float visibilityThreshold = isVisibleInitially
230 ? VERTICAL_FLING_HIDE_THRESHOLD : VERTICAL_FLING_SHOW_TH RESHOLD; 221 ? VERTICAL_FLING_HIDE_THRESHOLD : VERTICAL_FLING_SHOW_TH RESHOLD;
231 boolean isVisibleEnough = percentageVisible > visibilityThreshol d; 222 boolean isVisibleEnough = percentageVisible > visibilityThreshol d;
223 boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_T HRESHOLD);
232 224
233 boolean show = !isScrollingDownward; 225 boolean show = (!isScrollingDownward && isVisibleEnough) || isNe arTopOfPage;
234 if (isVisibleInitially) { 226
235 // Check if the View was moving off-screen.
236 boolean isHiding = getTranslationY() > mInitialTranslationY;
237 show &= isVisibleEnough || !isHiding;
238 } else {
239 // When near the top of the page, there's not much room left to scroll.
240 boolean isNearTopOfPage = finalOffsetY < (mTotalHeight * FUL L_THRESHOLD);
241 show &= isVisibleEnough || isNearTopOfPage;
242 }
243 createVerticalSnapAnimation(show); 227 createVerticalSnapAnimation(show);
244 } 228 }
245 229
246 @Override 230 @Override
247 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { 231 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
248 if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return; 232 if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return;
249 beginGesture(scrollOffsetY, scrollExtentY); 233 resetInitialOffsets(scrollOffsetY, scrollExtentY);
234 mLastScrollOffsetY = scrollOffsetY;
250 mGestureState = GESTURE_SCROLLING; 235 mGestureState = GESTURE_SCROLLING;
251 } 236 }
252 237
253 @Override 238 @Override
254 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) { 239 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
255 if (mGestureState != GESTURE_SCROLLING) return; 240 if (mGestureState != GESTURE_SCROLLING) return;
256 mGestureState = GESTURE_NONE; 241 mGestureState = GESTURE_NONE;
257 242
258 int finalOffsetY = computeScrollDifference(scrollOffsetY, scroll ExtentY);
259 updateTranslation(scrollOffsetY, scrollExtentY); 243 updateTranslation(scrollOffsetY, scrollExtentY);
260 244
261 boolean isNearTopOfPage = finalOffsetY < (mTotalHeight * FULL_TH RESHOLD); 245 boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_T HRESHOLD);
262 boolean isVisibleEnough = getTranslationY() < mTotalHeight * FUL L_THRESHOLD; 246 boolean isVisibleEnough = getTranslationY() < mTotalHeight * FUL L_THRESHOLD;
263 createVerticalSnapAnimation(isNearTopOfPage || isVisibleEnough); 247 createVerticalSnapAnimation(isNearTopOfPage || isVisibleEnough);
264 } 248 }
265 249
266 @Override 250 @Override
267 public void onScrollOffsetOrExtentChanged(int scrollOffsetY, int scr ollExtentY) { 251 public void onScrollOffsetOrExtentChanged(int scrollOffsetY, int scr ollExtentY) {
268 // This function is called for both fling and scrolls. 252 // This function is called for both fling and scrolls.
269 if (mGestureState == GESTURE_NONE || !cancelCurrentAnimation()) return; 253 if (mGestureState == GESTURE_NONE || !cancelCurrentAnimation()) return;
254 mLastScrollOffsetY = scrollOffsetY;
270 updateTranslation(scrollOffsetY, scrollExtentY); 255 updateTranslation(scrollOffsetY, scrollExtentY);
271 } 256 }
272 257
273 private void updateTranslation(int scrollOffsetY, int scrollExtentY) { 258 private void updateTranslation(int scrollOffsetY, int scrollExtentY) {
274 float translation = mInitialTranslationY 259 float scrollDiff =
275 + computeScrollDifference(scrollOffsetY, scrollExtentY); 260 (scrollOffsetY - mInitialOffsetY) + (scrollExtentY - mIn itialExtentY);
276 translation = Math.max(0.0f, Math.min(mTotalHeight, translation) ); 261 float translation =
262 MathUtils.clamp(mInitialTranslationY + scrollDiff, mTota lHeight, 0);
263
264 // If the container has reached the completely shown position, r eset the initial
265 // scroll so any movement will start hiding it again.
266 if (translation <= 0f) resetInitialOffsets(scrollOffsetY, scroll ExtentY);
267
277 setTranslationY(translation); 268 setTranslationY(translation);
278 } 269 }
270
271 /**
272 * Records the conditions of the page to handle scrolls appropriatel y.
273 */
274 private void resetInitialOffsets(int scrollOffsetY, int scrollExtent Y) {
275 mInitialOffsetY = scrollOffsetY;
276 mInitialExtentY = scrollExtentY;
277 mInitialTranslationY = getTranslationY();
278 }
279 }; 279 };
280 } 280 }
281 281
282 /** 282 /**
283 * Creates a listener that is used only to animate the View coming onto the screen. 283 * Creates a listener that is used only to animate the View coming onto the screen.
284 * @return The SimpleOnGestureListener that will monitor the View. 284 * @return The SimpleOnGestureListener that will monitor the View.
285 */ 285 */
286 private View.OnLayoutChangeListener createLayoutChangeListener() { 286 private View.OnLayoutChangeListener createLayoutChangeListener() {
287 return new View.OnLayoutChangeListener() { 287 return new View.OnLayoutChangeListener() {
288 @Override 288 @Override
(...skipping 21 matching lines...) Expand all
310 float yDifference = Math.abs(translationY - getTranslationY()) / mTotalH eight; 310 float yDifference = Math.abs(translationY - getTranslationY()) / mTotalH eight;
311 long duration = Math.max(0, (long) (ANIMATION_DURATION_MS * yDifference) ); 311 long duration = Math.max(0, (long) (ANIMATION_DURATION_MS * yDifference) );
312 312
313 mCurrentAnimation = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, tra nslationY); 313 mCurrentAnimation = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, tra nslationY);
314 mCurrentAnimation.setDuration(duration); 314 mCurrentAnimation.setDuration(duration);
315 mCurrentAnimation.addListener(mAnimatorListener); 315 mCurrentAnimation.addListener(mAnimatorListener);
316 mCurrentAnimation.setInterpolator(mInterpolator); 316 mCurrentAnimation.setInterpolator(mInterpolator);
317 mCurrentAnimation.start(); 317 mCurrentAnimation.start();
318 } 318 }
319 319
320 private int computeScrollDifference(int scrollOffsetY, int scrollExtentY) {
321 return scrollOffsetY + scrollExtentY - mInitialOffsetY;
322 }
323
324 /** 320 /**
325 * Creates an AnimatorListenerAdapter that cleans up after an animation is c ompleted. 321 * Creates an AnimatorListenerAdapter that cleans up after an animation is c ompleted.
326 * @return {@link AnimatorListenerAdapter} to use for animations. 322 * @return {@link AnimatorListenerAdapter} to use for animations.
327 */ 323 */
328 private AnimatorListener createAnimatorListener() { 324 private AnimatorListener createAnimatorListener() {
329 return new AnimatorListenerAdapter() { 325 return new AnimatorListenerAdapter() {
330 @Override 326 @Override
331 public void onAnimationEnd(Animator animation) { 327 public void onAnimationEnd(Animator animation) {
332 mGestureState = GESTURE_NONE; 328 mGestureState = GESTURE_NONE;
333 mCurrentAnimation = null; 329 mCurrentAnimation = null;
334 mIsBeingDisplayedForFirstTime = false; 330 mIsBeingDisplayedForFirstTime = false;
335 } 331 }
336 }; 332 };
337 } 333 }
338 334
339 /** 335 /**
340 * Records the conditions of the page when a gesture is initiated.
341 */
342 private void beginGesture(int scrollOffsetY, int scrollExtentY) {
343 mInitialTranslationY = getTranslationY();
344 boolean isInitiallyVisible = mInitialTranslationY < mTotalHeight;
345 int startingY = isInitiallyVisible ? scrollOffsetY : Math.min(scrollOffs etY, mTotalHeight);
346 mInitialOffsetY = startingY + scrollExtentY;
347 }
348
349 /**
350 * Cancels the current animation, unless the View is coming onto the screen for the first time. 336 * Cancels the current animation, unless the View is coming onto the screen for the first time.
351 * @return True if the animation was canceled or wasn't running, false other wise. 337 * @return True if the animation was canceled or wasn't running, false other wise.
352 */ 338 */
353 private boolean cancelCurrentAnimation() { 339 private boolean cancelCurrentAnimation() {
354 if (mIsBeingDisplayedForFirstTime) return false; 340 if (mIsBeingDisplayedForFirstTime) return false;
355 if (mCurrentAnimation != null) mCurrentAnimation.cancel(); 341 if (mCurrentAnimation != null) mCurrentAnimation.cancel();
356 return true; 342 return true;
357 } 343 }
358 344
359 /** 345 /**
(...skipping 13 matching lines...) Expand all
373 public boolean gatherTransparentRegion(Region region) { 359 public boolean gatherTransparentRegion(Region region) {
374 float translationY = getTranslationY(); 360 float translationY = getTranslationY();
375 setTranslationY(0); 361 setTranslationY(0);
376 boolean result = super.gatherTransparentRegion(region); 362 boolean result = super.gatherTransparentRegion(region);
377 // Restoring TranslationY invalidates this view unnecessarily. However, this function 363 // Restoring TranslationY invalidates this view unnecessarily. However, this function
378 // is called as part of layout, which implies a full redraw is about to occur anyway. 364 // is called as part of layout, which implies a full redraw is about to occur anyway.
379 setTranslationY(translationY); 365 setTranslationY(translationY);
380 return result; 366 return result;
381 } 367 }
382 } 368 }
OLDNEW
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698