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;
26f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
2723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huangimport com.android.mail.R;
28b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
29632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
30632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
31f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.Set;
32f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.concurrent.CopyOnWriteArraySet;
33f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
34c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huangpublic class ConversationWebView extends MailWebView implements ScrollNotifier {
35afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /** The initial delay when rendering in hardware layer. */
3630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    private final int mWebviewInitialDelay;
37afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
38afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private Bitmap mBitmap;
39afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private Canvas mCanvas;
40afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
41afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private boolean mUseSoftwareLayer;
4230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    /**
4330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * Whether this view is user-visible; we don't bother doing supplemental software drawing
4430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * if the view is off-screen.
4530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     */
4630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    private boolean mVisible;
47afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
48afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /** {@link Runnable} to be run when the page is rendered in hardware layer. */
49afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private final Runnable mNotifyPageRenderedInHardwareLayer = new Runnable() {
50afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        @Override
51afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        public void run() {
52afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            // Switch to hardware layer.
53afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            mUseSoftwareLayer = false;
54afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            destroyBitmap();
55afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            invalidate();
56afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
57afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    };
58afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
59afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    @Override
60afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void onDraw(Canvas canvas) {
61afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        // Always render in hardware layer to avoid flicker when switch.
62afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        super.onDraw(canvas);
63afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
6430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        // Render in software layer on top if needed, and we're visible (i.e. it's worthwhile to
6530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        // do all this)
6630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        if (mUseSoftwareLayer && mVisible && getWidth() > 0 && getHeight() > 0) {
6730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            if (mBitmap == null) {
6830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                try {
6930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    // Create an offscreen bitmap.
7030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
7130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mCanvas = new Canvas(mBitmap);
7230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                } catch (OutOfMemoryError e) {
7330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    // just give up
7430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mBitmap = null;
7530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mCanvas = null;
7630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                    mUseSoftwareLayer = false;
7730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                }
7830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            }
7930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
8030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            if (mBitmap != null) {
8130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                final int x = getScrollX();
8230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                final int y = getScrollY();
8330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
8430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.save();
8530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.translate(-x, -y);
8630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                super.onDraw(mCanvas);
8730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                mCanvas.restore();
8830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
8930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang                canvas.drawBitmap(mBitmap, x, y, null /* paint */);
9030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            }
91afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
92afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
93afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
94afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    @Override
95afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void destroy() {
96afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        destroyBitmap();
9730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        removeCallbacks(mNotifyPageRenderedInHardwareLayer);
98afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
99afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        super.destroy();
100afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
101afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
102afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
103afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * Destroys the {@link Bitmap} used for software layer.
104afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
105afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    private void destroyBitmap() {
106afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        if (mBitmap != null) {
107afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            mBitmap = null;
10830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            mCanvas = null;
109afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
110afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
111afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
112afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
113afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * Enable this WebView to also draw to an internal software canvas until
11430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * {@link #onRenderComplete()} is called. The software draw will happen every time
115afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * a normal {@link #onDraw(Canvas)} happens, and will overwrite whatever is normally drawn
116afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * (i.e. drawn in hardware) with the results of software rendering.
117afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * <p>
118afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * This is useful when you know that the WebView draws sooner to a software layer than it does
119afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     * to its normal hardware layer.
120afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
121afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    public void setUseSoftwareLayer(boolean useSoftware) {
122afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        mUseSoftwareLayer = useSoftware;
123afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
124afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp
125afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    /**
12630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * Notifies the {@link ConversationWebView} that it has become visible. It can use this signal
12730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang     * to switch between software and hardware layer.
128afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp     */
12930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    public void onRenderComplete() {
13030bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        if (mUseSoftwareLayer) {
131afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp            // Schedule to switch from software layer to hardware layer in 1s.
13230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang            postDelayed(mNotifyPageRenderedInHardwareLayer, mWebviewInitialDelay);
133afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp        }
134afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp    }
135f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
13630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    public void onUserVisibilityChanged(boolean visible) {
13730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mVisible = visible;
13830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang    }
13930bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang
14023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final int mViewportWidth;
14123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final float mDensity;
14223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
143f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    private final Set<ScrollListener> mScrollListeners =
144f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang            new CopyOnWriteArraySet<ScrollListener>();
145f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
146632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
147632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * True when WebView is handling a touch-- in between POINTER_DOWN and
148632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * POINTER_UP/POINTER_CANCEL.
149632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
150632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mHandlingTouch;
151ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang    private boolean mIgnoringTouch;
152632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
153b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
154632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
155f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c) {
156f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        this(c, null);
157f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
158f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
159f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c, AttributeSet attrs) {
160f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super(c, attrs);
16123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
16230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        final Resources r = getResources();
16330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mViewportWidth = r.getInteger(R.integer.conversation_webview_viewport_px);
16430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mWebviewInitialDelay = r.getInteger(R.integer.webview_initial_delay);
16530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang        mDensity = r.getDisplayMetrics().density;
166f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
167f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
168f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
169f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void addScrollListener(ScrollListener l) {
170f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.add(l);
171f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
172f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
173f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
174f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void removeScrollListener(ScrollListener l) {
175f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.remove(l);
176f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
177f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
17856d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei    @Override
179f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
180f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super.onScrollChanged(l, t, oldl, oldt);
181f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
182f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        for (ScrollListener listener : mScrollListeners) {
183bb6f0504c1607c89d9a3dd3e6023f36d61837016Andrew Sapperstein            listener.onNotifierScroll(t);
184f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        }
185f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
186f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
187bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    @Override
188bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    public boolean onTouchEvent(MotionEvent ev) {
189632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final int action = ev.getActionMasked();
190bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
191632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        switch (action) {
192632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_DOWN:
193632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = true;
194632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
195632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_POINTER_DOWN:
196632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.d(LOG_TAG, "WebView disabling intercepts: POINTER_DOWN");
197632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                requestDisallowInterceptTouchEvent(true);
198632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
199632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_CANCEL:
200632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_UP:
201632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = false;
202ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang                mIgnoringTouch = false;
203632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
204bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang        }
205bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
206ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang        final boolean handled = mIgnoringTouch || super.onTouchEvent(ev);
20702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
20802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang        return handled;
209632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
210632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
211632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public boolean isHandlingTouch() {
212632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return mHandlingTouch;
213bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    }
214f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
21523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int getViewportWidth() {
21623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return mViewportWidth;
21723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
21823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
2192160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang    /**
2202160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     * Returns the effective width available for HTML content in DP units. This width takes into
2212160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     * account the given margin (in screen px) by excluding it. This is not the same as DOM width,
2222160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     * since the document is rendered at CSS px={@link #mViewportWidth}.
2232160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     *
2242160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     * @param sideMarginPx HTML body margin, if any (in screen px)
2252160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     * @return width available for HTML content (in dp)
2262160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang     */
2272160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang    public int getWidthInDp(int sideMarginPx) {
2282160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang        return (int) ((getWidth() - sideMarginPx * 2) / mDensity);
2294dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang    }
2304dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang
23123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    /**
23223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * Similar to {@link #getScale()}, except that it returns the initially expected scale, as
23323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * determined by the ratio of actual screen pixels to logical HTML pixels.
23423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * <p>This assumes that we are able to control the logical HTML viewport with a meta-viewport
23523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * tag.
23623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     */
23723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public float getInitialScale() {
2384dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang        final float scale;
2394dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang        if (getSettings().getLoadWithOverviewMode()) {
2404dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang            // in overview mode (aka auto-fit mode), the base ratio is screen px : viewport px
2414dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang            scale = (float) getWidth() / getViewportWidth();
2424dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang        } else {
2434dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang            // in no-zoom mode, the base ratio is just screen px : mdpi css px (i.e. density)
2444dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang            scale = mDensity;
2454dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang        }
2464dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang        return scale;
24723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
24823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
24923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int screenPxToWebPx(int screenPx) {
25023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (screenPx / getInitialScale());
25123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
25223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
25323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int webPxToScreenPx(int webPx) {
25423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (webPx * getInitialScale());
25523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
25623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
2572b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    public float screenPxToWebPxError(int screenPx) {
2582b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei        return screenPx / getInitialScale() - screenPxToWebPx(screenPx);
2592b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    }
2602b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei
2612b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    public float webPxToScreenPxError(int webPx) {
2622b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei        return webPx * getInitialScale() - webPxToScreenPx(webPx);
2632b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei    }
26402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang
265f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang}
266