1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.webkit; 17 18 import com.android.internal.R; 19 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.graphics.Canvas; 23 import android.graphics.drawable.Drawable; 24 import android.view.View; 25 import android.widget.EdgeEffect; 26 27 /** 28 * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges. 29 * @hide 30 */ 31 public class OverScrollGlow { 32 private WebViewClassic mHostView; 33 34 private EdgeEffect mEdgeGlowTop; 35 private EdgeEffect mEdgeGlowBottom; 36 private EdgeEffect mEdgeGlowLeft; 37 private EdgeEffect mEdgeGlowRight; 38 39 private int mOverScrollDeltaX; 40 private int mOverScrollDeltaY; 41 OverScrollGlow(WebViewClassic host)42 public OverScrollGlow(WebViewClassic host) { 43 mHostView = host; 44 Context context = host.getContext(); 45 mEdgeGlowTop = new EdgeEffect(context); 46 mEdgeGlowBottom = new EdgeEffect(context); 47 mEdgeGlowLeft = new EdgeEffect(context); 48 mEdgeGlowRight = new EdgeEffect(context); 49 } 50 51 /** 52 * Pull leftover touch scroll distance into one of the edge glows as appropriate. 53 * 54 * @param x Current X scroll offset 55 * @param y Current Y scroll offset 56 * @param oldX Old X scroll offset 57 * @param oldY Old Y scroll offset 58 * @param maxX Maximum range for horizontal scrolling 59 * @param maxY Maximum range for vertical scrolling 60 */ pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY)61 public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) { 62 // Only show overscroll bars if there was no movement in any direction 63 // as a result of scrolling. 64 if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) { 65 // Don't show left/right glows if we fit the whole content. 66 // Also don't show if there was vertical movement. 67 if (maxX > 0) { 68 final int pulledToX = oldX + mOverScrollDeltaX; 69 if (pulledToX < 0) { 70 mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); 71 if (!mEdgeGlowRight.isFinished()) { 72 mEdgeGlowRight.onRelease(); 73 } 74 } else if (pulledToX > maxX) { 75 mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); 76 if (!mEdgeGlowLeft.isFinished()) { 77 mEdgeGlowLeft.onRelease(); 78 } 79 } 80 mOverScrollDeltaX = 0; 81 } 82 83 if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { 84 final int pulledToY = oldY + mOverScrollDeltaY; 85 if (pulledToY < 0) { 86 mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); 87 if (!mEdgeGlowBottom.isFinished()) { 88 mEdgeGlowBottom.onRelease(); 89 } 90 } else if (pulledToY > maxY) { 91 mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); 92 if (!mEdgeGlowTop.isFinished()) { 93 mEdgeGlowTop.onRelease(); 94 } 95 } 96 mOverScrollDeltaY = 0; 97 } 98 } 99 } 100 101 /** 102 * Set touch delta values indicating the current amount of overscroll. 103 * 104 * @param deltaX 105 * @param deltaY 106 */ setOverScrollDeltas(int deltaX, int deltaY)107 public void setOverScrollDeltas(int deltaX, int deltaY) { 108 mOverScrollDeltaX = deltaX; 109 mOverScrollDeltaY = deltaY; 110 } 111 112 /** 113 * Absorb leftover fling velocity into one of the edge glows as appropriate. 114 * 115 * @param x Current X scroll offset 116 * @param y Current Y scroll offset 117 * @param oldX Old X scroll offset 118 * @param oldY Old Y scroll offset 119 * @param rangeX Maximum range for horizontal scrolling 120 * @param rangeY Maximum range for vertical scrolling 121 */ absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY)122 public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) { 123 if (rangeY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { 124 if (y < 0 && oldY >= 0) { 125 mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); 126 if (!mEdgeGlowBottom.isFinished()) { 127 mEdgeGlowBottom.onRelease(); 128 } 129 } else if (y > rangeY && oldY <= rangeY) { 130 mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); 131 if (!mEdgeGlowTop.isFinished()) { 132 mEdgeGlowTop.onRelease(); 133 } 134 } 135 } 136 137 if (rangeX > 0) { 138 if (x < 0 && oldX >= 0) { 139 mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); 140 if (!mEdgeGlowRight.isFinished()) { 141 mEdgeGlowRight.onRelease(); 142 } 143 } else if (x > rangeX && oldX <= rangeX) { 144 mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); 145 if (!mEdgeGlowLeft.isFinished()) { 146 mEdgeGlowLeft.onRelease(); 147 } 148 } 149 } 150 } 151 152 /** 153 * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null. 154 * 155 * @param canvas Canvas to draw into, transformed into view coordinates. 156 * @return true if glow effects are still animating and the view should invalidate again. 157 */ drawEdgeGlows(Canvas canvas)158 public boolean drawEdgeGlows(Canvas canvas) { 159 final int scrollX = mHostView.getScrollX(); 160 final int scrollY = mHostView.getScrollY(); 161 final int width = mHostView.getWidth(); 162 int height = mHostView.getHeight(); 163 164 boolean invalidateForGlow = false; 165 if (!mEdgeGlowTop.isFinished()) { 166 final int restoreCount = canvas.save(); 167 168 canvas.translate(scrollX, mHostView.getVisibleTitleHeight() + Math.min(0, scrollY)); 169 mEdgeGlowTop.setSize(width, height); 170 invalidateForGlow |= mEdgeGlowTop.draw(canvas); 171 canvas.restoreToCount(restoreCount); 172 } 173 if (!mEdgeGlowBottom.isFinished()) { 174 final int restoreCount = canvas.save(); 175 176 canvas.translate(-width + scrollX, Math.max(mHostView.computeMaxScrollY(), scrollY) 177 + height); 178 canvas.rotate(180, width, 0); 179 mEdgeGlowBottom.setSize(width, height); 180 invalidateForGlow |= mEdgeGlowBottom.draw(canvas); 181 canvas.restoreToCount(restoreCount); 182 } 183 if (!mEdgeGlowLeft.isFinished()) { 184 final int restoreCount = canvas.save(); 185 186 canvas.rotate(270); 187 canvas.translate(-height - scrollY, Math.min(0, scrollX)); 188 mEdgeGlowLeft.setSize(height, width); 189 invalidateForGlow |= mEdgeGlowLeft.draw(canvas); 190 canvas.restoreToCount(restoreCount); 191 } 192 if (!mEdgeGlowRight.isFinished()) { 193 final int restoreCount = canvas.save(); 194 195 canvas.rotate(90); 196 canvas.translate(scrollY, 197 -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width)); 198 mEdgeGlowRight.setSize(height, width); 199 invalidateForGlow |= mEdgeGlowRight.draw(canvas); 200 canvas.restoreToCount(restoreCount); 201 } 202 return invalidateForGlow; 203 } 204 205 /** 206 * @return True if any glow is still animating 207 */ isAnimating()208 public boolean isAnimating() { 209 return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() || 210 !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()); 211 } 212 213 /** 214 * Release all glows from any touch pulls in progress. 215 */ releaseAll()216 public void releaseAll() { 217 mEdgeGlowTop.onRelease(); 218 mEdgeGlowBottom.onRelease(); 219 mEdgeGlowLeft.onRelease(); 220 mEdgeGlowRight.onRelease(); 221 } 222 } 223