ConversationWebView.java revision c1fb9a9c2730178105977fca629e80951bfc3cdc
1f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang/*
2f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Copyright (C) 2012 Google Inc.
3f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed to The Android Open Source Project.
4f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang *
5f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed under the Apache License, Version 2.0 (the "License");
6f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * you may not use this file except in compliance with the License.
7f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * You may obtain a copy of the License at
8f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang *
9f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang *      http://www.apache.org/licenses/LICENSE-2.0
10f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang *
11f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Unless required by applicable law or agreed to in writing, software
12f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * distributed under the License is distributed on an "AS IS" BASIS,
13f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * See the License for the specific language governing permissions and
15f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * limitations under the License.
16f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */
17f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
185ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangpackage com.android.mail.browse;
19f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
20f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.content.Context;
21afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindypimport android.content.res.Resources;
22afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindypimport android.graphics.Bitmap;
23afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindypimport android.graphics.Canvas;
24f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.util.AttributeSet;
25bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huangimport android.view.MotionEvent;
2602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangimport android.view.ScaleGestureDetector;
2702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangimport android.view.ScaleGestureDetector.OnScaleGestureListener;
28f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
2923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huangimport com.android.mail.R;
30b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
31632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
32632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
33f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.Set;
34f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.concurrent.CopyOnWriteArraySet;
35f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
36c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huangpublic class ConversationWebView extends MailWebView implements ScrollNotifier {
37afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /** The initial delay when rendering in hardware layer. */
3830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    private final int mWebviewInitialDelay;
39afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
40afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private Bitmap mBitmap;
41afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private Canvas mCanvas;
42afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
43afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private boolean mUseSoftwareLayer;
4430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    /**
4530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * Whether this view is user-visible; we don't bother doing supplemental software drawing
4630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * if the view is off-screen.
4730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     */
4830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    private boolean mVisible;
49afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
50afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /** {@link Runnable} to be run when the page is rendered in hardware layer. */
51afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private final Runnable mNotifyPageRenderedInHardwareLayer = new Runnable() {
52afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        @Override
53afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        public void run() {
54afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            // Switch to hardware layer.
55afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            mUseSoftwareLayer = false;
56afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            destroyBitmap();
57afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            invalidate();
58afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
59afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    };
60afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
61afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    @Override
62afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void onDraw(Canvas canvas) {
63afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        // Always render in hardware layer to avoid flicker when switch.
64afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        super.onDraw(canvas);
65afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
6630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        // Render in software layer on top if needed, and we're visible (i.e. it's worthwhile to
6730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        // do all this)
6830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        if (mUseSoftwareLayer && mVisible && getWidth() > 0 && getHeight() > 0) {
6930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            if (mBitmap == null) {
7030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                try {
7130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    // Create an offscreen bitmap.
7230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
7330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mCanvas = new Canvas(mBitmap);
7430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                } catch (OutOfMemoryError e) {
7530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    // just give up
7630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mBitmap = null;
7730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mCanvas = null;
7830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mUseSoftwareLayer = false;
7930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                }
8030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            }
8130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
8230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            if (mBitmap != null) {
8330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                final int x = getScrollX();
8430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                final int y = getScrollY();
8530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
8630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.save();
8730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.translate(-x, -y);
8830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                super.onDraw(mCanvas);
8930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.restore();
9030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
9130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                canvas.drawBitmap(mBitmap, x, y, null /* paint */);
9230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            }
93afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
94afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
95afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
96afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    @Override
97afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void destroy() {
98afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        destroyBitmap();
9930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        removeCallbacks(mNotifyPageRenderedInHardwareLayer);
100afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
101afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        super.destroy();
102afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
103afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
104afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
105afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * Destroys the {@link Bitmap} used for software layer.
106afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
107afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private void destroyBitmap() {
108afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        if (mBitmap != null) {
109afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            mBitmap = null;
11030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            mCanvas = null;
111afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
112afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
113afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
114afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
115afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * Enable this WebView to also draw to an internal software canvas until
11630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * {@link #onRenderComplete()} is called. The software draw will happen every time
117afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * a normal {@link #onDraw(Canvas)} happens, and will overwrite whatever is normally drawn
118afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * (i.e. drawn in hardware) with the results of software rendering.
119afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * <p>
120afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * This is useful when you know that the WebView draws sooner to a software layer than it does
121afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * to its normal hardware layer.
122afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
123afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void setUseSoftwareLayer(boolean useSoftware) {
124afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        mUseSoftwareLayer = useSoftware;
125afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
126afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
127afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
12830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * Notifies the {@link ConversationWebView} that it has become visible. It can use this signal
12930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * to switch between software and hardware layer.
130afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
13130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    public void onRenderComplete() {
13230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        if (mUseSoftwareLayer) {
133afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            // Schedule to switch from software layer to hardware layer in 1s.
13430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            postDelayed(mNotifyPageRenderedInHardwareLayer, mWebviewInitialDelay);
135afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
136afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
137f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
13830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    public void onUserVisibilityChanged(boolean visible) {
13930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mVisible = visible;
14030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    }
14130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
14202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang    private ScaleGestureDetector mScaleDetector;
14302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
14423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final int mViewportWidth;
14523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final float mDensity;
14623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
147f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    private final Set<ScrollListener> mScrollListeners =
148f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang            new CopyOnWriteArraySet<ScrollListener>();
149f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
150632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
151632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * True when WebView is handling a touch-- in between POINTER_DOWN and
152632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * POINTER_UP/POINTER_CANCEL.
153632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
154632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mHandlingTouch;
155ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang    private boolean mIgnoringTouch;
156632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
157b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
158632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
159f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c) {
160f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        this(c, null);
161f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
162f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
163f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c, AttributeSet attrs) {
164f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super(c, attrs);
16523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
16630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        final Resources r = getResources();
16730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mViewportWidth = r.getInteger(R.integer.conversation_webview_viewport_px);
16830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mWebviewInitialDelay = r.getInteger(R.integer.webview_initial_delay);
16930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mDensity = r.getDisplayMetrics().density;
170f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
171f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
172f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
173f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void addScrollListener(ScrollListener l) {
174f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.add(l);
175f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
176f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
177f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
178f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void removeScrollListener(ScrollListener l) {
179f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.remove(l);
180f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
181f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
18202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang    public void setOnScaleGestureListener(OnScaleGestureListener l) {
18302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        if (l == null) {
18402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang            mScaleDetector = null;
18502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        } else {
18602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang            mScaleDetector = new ScaleGestureDetector(getContext(), l);
18702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        }
18802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang    }
18956d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei
19056d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei    @Override
191f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
192f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super.onScrollChanged(l, t, oldl, oldt);
193f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
194f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        for (ScrollListener listener : mScrollListeners) {
195f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang            listener.onNotifierScroll(l, t);
196f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        }
197f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
198f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
199bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    @Override
200bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    public boolean onTouchEvent(MotionEvent ev) {
201632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final int action = ev.getActionMasked();
202bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
203632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        switch (action) {
204632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_DOWN:
205632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = true;
206632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
207632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_POINTER_DOWN:
208632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.d(LOG_TAG, "WebView disabling intercepts: POINTER_DOWN");
209632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                requestDisallowInterceptTouchEvent(true);
210ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                if (mScaleDetector != null) {
211ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                    mIgnoringTouch = true;
212ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                    final MotionEvent fakeCancel = MotionEvent.obtain(ev);
213ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                    fakeCancel.setAction(MotionEvent.ACTION_CANCEL);
214ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                    super.onTouchEvent(fakeCancel);
215ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                }
216632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
217632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_CANCEL:
218632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_UP:
219632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = false;
220ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                mIgnoringTouch = false;
221632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
222bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang        }
223bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
224ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang        final boolean handled = mIgnoringTouch || super.onTouchEvent(ev);
22502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
22602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        if (mScaleDetector != null) {
22702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang            mScaleDetector.onTouchEvent(ev);
22802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        }
22902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
23002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        return handled;
231632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
232632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
233632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public boolean isHandlingTouch() {
234632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return mHandlingTouch;
235bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    }
236f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
23723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int getViewportWidth() {
23823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return mViewportWidth;
23923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
24023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
24123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    /**
24223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * Similar to {@link #getScale()}, except that it returns the initially expected scale, as
24323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * determined by the ratio of actual screen pixels to logical HTML pixels.
24423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * <p>This assumes that we are able to control the logical HTML viewport with a meta-viewport
24523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * tag.
24623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     */
24723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public float getInitialScale() {
24823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        // an HTML meta-viewport width of "device-width" and unspecified (medium) density means
24923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        // that the default scale is effectively the screen density.
25023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return mDensity;
25123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
25223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
25323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int screenPxToWebPx(int screenPx) {
25423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (screenPx / getInitialScale());
25523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
25623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
25723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int webPxToScreenPx(int webPx) {
25823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (webPx * getInitialScale());
25923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
26023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
2612b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    public float screenPxToWebPxError(int screenPx) {
2622b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei        return screenPx / getInitialScale() - screenPxToWebPx(screenPx);
2632b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    }
2642b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei
2652b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    public float webPxToScreenPxError(int webPx) {
2662b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei        return webPx * getInitialScale() - webPxToScreenPx(webPx);
2672b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    }
26802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
269f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang}
270