• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.android_webview;
6 
7 import android.content.Context;
8 import android.graphics.Canvas;
9 import android.view.View;
10 import android.widget.EdgeEffect;
11 
12 /**
13  * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
14  */
15 class OverScrollGlow {
16     private View mHostView;
17 
18     private EdgeEffect mEdgeGlowTop;
19     private EdgeEffect mEdgeGlowBottom;
20     private EdgeEffect mEdgeGlowLeft;
21     private EdgeEffect mEdgeGlowRight;
22 
23     private int mOverScrollDeltaX;
24     private int mOverScrollDeltaY;
25 
26     private boolean mShouldPull;
27 
OverScrollGlow(Context context, View host)28     public OverScrollGlow(Context context, View host) {
29         mHostView = host;
30         mEdgeGlowTop = new EdgeEffect(context);
31         mEdgeGlowBottom = new EdgeEffect(context);
32         mEdgeGlowLeft = new EdgeEffect(context);
33         mEdgeGlowRight = new EdgeEffect(context);
34     }
35 
setShouldPull(boolean shouldPull)36     public void setShouldPull(boolean shouldPull) {
37         mShouldPull = shouldPull;
38     }
39 
40     /**
41      * Pull leftover touch scroll distance into one of the edge glows as appropriate.
42      *
43      * @param x Current X scroll offset
44      * @param y Current Y scroll offset
45      * @param oldX Old X scroll offset
46      * @param oldY Old Y scroll offset
47      * @param maxX Maximum range for horizontal scrolling
48      * @param maxY Maximum range for vertical scrolling
49      */
pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY)50     public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) {
51         if (!mShouldPull) return;
52         // Only show overscroll bars if there was no movement in any direction
53         // as a result of scrolling.
54         if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) {
55             // Don't show left/right glows if we fit the whole content.
56             // Also don't show if there was vertical movement.
57             if (maxX > 0) {
58                 final int pulledToX = oldX + mOverScrollDeltaX;
59                 if (pulledToX < 0) {
60                     mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
61                     if (!mEdgeGlowRight.isFinished()) {
62                         mEdgeGlowRight.onRelease();
63                     }
64                 } else if (pulledToX > maxX) {
65                     mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
66                     if (!mEdgeGlowLeft.isFinished()) {
67                         mEdgeGlowLeft.onRelease();
68                     }
69                 }
70                 mOverScrollDeltaX = 0;
71             }
72 
73             if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
74                 final int pulledToY = oldY + mOverScrollDeltaY;
75                 if (pulledToY < 0) {
76                     mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
77                     if (!mEdgeGlowBottom.isFinished()) {
78                         mEdgeGlowBottom.onRelease();
79                     }
80                 } else if (pulledToY > maxY) {
81                     mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
82                     if (!mEdgeGlowTop.isFinished()) {
83                         mEdgeGlowTop.onRelease();
84                     }
85                 }
86                 mOverScrollDeltaY = 0;
87             }
88         }
89     }
90 
91     /**
92      * Absorb leftover fling velocity into one of the edge glows as appropriate.
93      *
94      * @param x Current X scroll offset
95      * @param y Current Y scroll offset
96      * @param oldX Old X scroll offset
97      * @param oldY Old Y scroll offset
98      * @param rangeX Maximum range for horizontal scrolling
99      * @param rangeY Maximum range for vertical scrolling
100      * @param currentFlingVelocity Current fling velocity
101      */
absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY, float currentFlingVelocity)102     public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY,
103             float currentFlingVelocity) {
104         if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
105             if (y < 0 && oldY >= 0) {
106                 mEdgeGlowTop.onAbsorb((int) currentFlingVelocity);
107                 if (!mEdgeGlowBottom.isFinished()) {
108                     mEdgeGlowBottom.onRelease();
109                 }
110             } else if (y > rangeY && oldY <= rangeY) {
111                 mEdgeGlowBottom.onAbsorb((int) currentFlingVelocity);
112                 if (!mEdgeGlowTop.isFinished()) {
113                     mEdgeGlowTop.onRelease();
114                 }
115             }
116         }
117 
118         if (rangeX > 0) {
119             if (x < 0 && oldX >= 0) {
120                 mEdgeGlowLeft.onAbsorb((int) currentFlingVelocity);
121                 if (!mEdgeGlowRight.isFinished()) {
122                     mEdgeGlowRight.onRelease();
123                 }
124             } else if (x > rangeX && oldX <= rangeX) {
125                 mEdgeGlowRight.onAbsorb((int) currentFlingVelocity);
126                 if (!mEdgeGlowLeft.isFinished()) {
127                     mEdgeGlowLeft.onRelease();
128                 }
129             }
130         }
131     }
132 
133     /**
134      * Set touch delta values indicating the current amount of overscroll.
135      *
136      * @param deltaX
137      * @param deltaY
138      */
setOverScrollDeltas(int deltaX, int deltaY)139     public void setOverScrollDeltas(int deltaX, int deltaY) {
140         mOverScrollDeltaX += deltaX;
141         mOverScrollDeltaY += deltaY;
142     }
143 
144     /**
145      * Draw the glow effect along the sides of the widget.
146      *
147      * @param canvas Canvas to draw into, transformed into view coordinates.
148      * @param maxScrollX maximum horizontal scroll offset
149      * @param maxScrollY maximum vertical scroll offset
150      * @return true if glow effects are still animating and the view should invalidate again.
151      */
drawEdgeGlows(Canvas canvas, int maxScrollX, int maxScrollY)152     public boolean drawEdgeGlows(Canvas canvas, int maxScrollX, int maxScrollY) {
153         final int scrollX = mHostView.getScrollX();
154         final int scrollY = mHostView.getScrollY();
155         final int width = mHostView.getWidth();
156         int height = mHostView.getHeight();
157 
158         boolean invalidateForGlow = false;
159         if (!mEdgeGlowTop.isFinished()) {
160             final int restoreCount = canvas.save();
161 
162             canvas.translate(scrollX, Math.min(0, scrollY));
163             mEdgeGlowTop.setSize(width, height);
164             invalidateForGlow |= mEdgeGlowTop.draw(canvas);
165             canvas.restoreToCount(restoreCount);
166         }
167         if (!mEdgeGlowBottom.isFinished()) {
168             final int restoreCount = canvas.save();
169 
170             canvas.translate(-width + scrollX, Math.max(maxScrollY, scrollY) + height);
171             canvas.rotate(180, width, 0);
172             mEdgeGlowBottom.setSize(width, height);
173             invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
174             canvas.restoreToCount(restoreCount);
175         }
176         if (!mEdgeGlowLeft.isFinished()) {
177             final int restoreCount = canvas.save();
178 
179             canvas.rotate(270);
180             canvas.translate(-height - scrollY, Math.min(0, scrollX));
181             mEdgeGlowLeft.setSize(height, width);
182             invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
183             canvas.restoreToCount(restoreCount);
184         }
185         if (!mEdgeGlowRight.isFinished()) {
186             final int restoreCount = canvas.save();
187 
188             canvas.rotate(90);
189             canvas.translate(scrollY, -(Math.max(scrollX, maxScrollX) + width));
190             mEdgeGlowRight.setSize(height, width);
191             invalidateForGlow |= mEdgeGlowRight.draw(canvas);
192             canvas.restoreToCount(restoreCount);
193         }
194         return invalidateForGlow;
195     }
196 
197     /**
198      * @return True if any glow is still animating
199      */
isAnimating()200     public boolean isAnimating() {
201         return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() ||
202                 !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished());
203     }
204 
205     /**
206      * Release all glows from any touch pulls in progress.
207      */
releaseAll()208     public void releaseAll() {
209         mEdgeGlowTop.onRelease();
210         mEdgeGlowBottom.onRelease();
211         mEdgeGlowLeft.onRelease();
212         mEdgeGlowRight.onRelease();
213     }
214 }
215