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