/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webkit; import com.android.internal.R; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.view.View; import android.widget.EdgeEffect; /** * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges. * @hide */ public class OverScrollGlow { private WebViewClassic mHostView; private EdgeEffect mEdgeGlowTop; private EdgeEffect mEdgeGlowBottom; private EdgeEffect mEdgeGlowLeft; private EdgeEffect mEdgeGlowRight; private int mOverScrollDeltaX; private int mOverScrollDeltaY; public OverScrollGlow(WebViewClassic host) { mHostView = host; Context context = host.getContext(); mEdgeGlowTop = new EdgeEffect(context); mEdgeGlowBottom = new EdgeEffect(context); mEdgeGlowLeft = new EdgeEffect(context); mEdgeGlowRight = new EdgeEffect(context); } /** * Pull leftover touch scroll distance into one of the edge glows as appropriate. * * @param x Current X scroll offset * @param y Current Y scroll offset * @param oldX Old X scroll offset * @param oldY Old Y scroll offset * @param maxX Maximum range for horizontal scrolling * @param maxY Maximum range for vertical scrolling */ public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) { // Only show overscroll bars if there was no movement in any direction // as a result of scrolling. if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) { // Don't show left/right glows if we fit the whole content. // Also don't show if there was vertical movement. if (maxX > 0) { final int pulledToX = oldX + mOverScrollDeltaX; if (pulledToX < 0) { mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); if (!mEdgeGlowRight.isFinished()) { mEdgeGlowRight.onRelease(); } } else if (pulledToX > maxX) { mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); if (!mEdgeGlowLeft.isFinished()) { mEdgeGlowLeft.onRelease(); } } mOverScrollDeltaX = 0; } if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { final int pulledToY = oldY + mOverScrollDeltaY; if (pulledToY < 0) { mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (pulledToY > maxY) { mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } } mOverScrollDeltaY = 0; } } } /** * Set touch delta values indicating the current amount of overscroll. * * @param deltaX * @param deltaY */ public void setOverScrollDeltas(int deltaX, int deltaY) { mOverScrollDeltaX = deltaX; mOverScrollDeltaY = deltaY; } /** * Absorb leftover fling velocity into one of the edge glows as appropriate. * * @param x Current X scroll offset * @param y Current Y scroll offset * @param oldX Old X scroll offset * @param oldY Old Y scroll offset * @param rangeX Maximum range for horizontal scrolling * @param rangeY Maximum range for vertical scrolling */ public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) { if (rangeY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { if (y < 0 && oldY >= 0) { mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (y > rangeY && oldY <= rangeY) { mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } } } if (rangeX > 0) { if (x < 0 && oldX >= 0) { mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); if (!mEdgeGlowRight.isFinished()) { mEdgeGlowRight.onRelease(); } } else if (x > rangeX && oldX <= rangeX) { mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); if (!mEdgeGlowLeft.isFinished()) { mEdgeGlowLeft.onRelease(); } } } } /** * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null. * * @param canvas Canvas to draw into, transformed into view coordinates. * @return true if glow effects are still animating and the view should invalidate again. */ public boolean drawEdgeGlows(Canvas canvas) { final int scrollX = mHostView.getScrollX(); final int scrollY = mHostView.getScrollY(); final int width = mHostView.getWidth(); int height = mHostView.getHeight(); boolean invalidateForGlow = false; if (!mEdgeGlowTop.isFinished()) { final int restoreCount = canvas.save(); canvas.translate(scrollX, mHostView.getVisibleTitleHeight() + Math.min(0, scrollY)); mEdgeGlowTop.setSize(width, height); invalidateForGlow |= mEdgeGlowTop.draw(canvas); canvas.restoreToCount(restoreCount); } if (!mEdgeGlowBottom.isFinished()) { final int restoreCount = canvas.save(); canvas.translate(-width + scrollX, Math.max(mHostView.computeMaxScrollY(), scrollY) + height); canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); invalidateForGlow |= mEdgeGlowBottom.draw(canvas); canvas.restoreToCount(restoreCount); } if (!mEdgeGlowLeft.isFinished()) { final int restoreCount = canvas.save(); canvas.rotate(270); canvas.translate(-height - scrollY, Math.min(0, scrollX)); mEdgeGlowLeft.setSize(height, width); invalidateForGlow |= mEdgeGlowLeft.draw(canvas); canvas.restoreToCount(restoreCount); } if (!mEdgeGlowRight.isFinished()) { final int restoreCount = canvas.save(); canvas.rotate(90); canvas.translate(scrollY, -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width)); mEdgeGlowRight.setSize(height, width); invalidateForGlow |= mEdgeGlowRight.draw(canvas); canvas.restoreToCount(restoreCount); } return invalidateForGlow; } /** * @return True if any glow is still animating */ public boolean isAnimating() { return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() || !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()); } /** * Release all glows from any touch pulls in progress. */ public void releaseAll() { mEdgeGlowTop.onRelease(); mEdgeGlowBottom.onRelease(); mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); } }