ConversationWebView.java revision 23014705ca9872cd5004a1aa76e83ae260165eca
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;
21f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.util.AttributeSet;
22bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huangimport android.view.MotionEvent;
23f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.WebView;
24f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
2523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huangimport com.android.mail.R;
26b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
27632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport com.android.mail.utils.LogUtils;
28632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
29f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.Set;
30f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.concurrent.CopyOnWriteArraySet;
31f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
32f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangpublic class ConversationWebView extends WebView implements ScrollNotifier {
33f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
3423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    // NARROW_COLUMNS reflow can trigger the document to change size, so notify interested parties.
3523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public interface ContentSizeChangeListener {
3623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        void onHeightChange(int h);
3723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
3823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
3923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private ContentSizeChangeListener mSizeChangeListener;
4023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
4123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private int mCachedContentHeight;
4223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
4323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final int mViewportWidth;
4423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    private final float mDensity;
4523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
46f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    private final Set<ScrollListener> mScrollListeners =
47f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang            new CopyOnWriteArraySet<ScrollListener>();
48f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
49632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    /**
50632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * True when WebView is handling a touch-- in between POINTER_DOWN and
51632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     * POINTER_UP/POINTER_CANCEL.
52632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang     */
53632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private boolean mHandlingTouch;
54632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
55b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
56632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
57f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c) {
58f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        this(c, null);
59f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
60f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
61f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public ConversationWebView(Context c, AttributeSet attrs) {
62f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super(c, attrs);
6323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
6423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        mViewportWidth = getResources().getInteger(R.integer.conversation_webview_viewport_px);
6523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        mDensity = getResources().getDisplayMetrics().density;
66f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
67f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
68f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
69f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void addScrollListener(ScrollListener l) {
70f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.add(l);
71f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
72f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
73f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
74f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    public void removeScrollListener(ScrollListener l) {
75f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        mScrollListeners.remove(l);
76f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
77f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
7823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public void setContentSizeChangeListener(ContentSizeChangeListener l) {
7923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        mSizeChangeListener = l;
8023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
8123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
82f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    @Override
83f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
84f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        super.onScrollChanged(l, t, oldl, oldt);
85f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
86f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        for (ScrollListener listener : mScrollListeners) {
87f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang            listener.onNotifierScroll(l, t);
88f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang        }
89f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang    }
90f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
91bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    @Override
9223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public void invalidate() {
9323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        super.invalidate();
9423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
9523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        if (mSizeChangeListener != null) {
9623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang            final int contentHeight = getContentHeight();
9723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang            if (contentHeight != mCachedContentHeight) {
9823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang                mCachedContentHeight = contentHeight;
9923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang                mSizeChangeListener.onHeightChange(contentHeight);
10023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang            }
10123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        }
10223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
10323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
10423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    @Override
105bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    public boolean onTouchEvent(MotionEvent ev) {
106632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final int action = ev.getActionMasked();
107bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
108632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        switch (action) {
109632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_DOWN:
110632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = true;
111632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
112632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_POINTER_DOWN:
113632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.d(LOG_TAG, "WebView disabling intercepts: POINTER_DOWN");
114632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                requestDisallowInterceptTouchEvent(true);
115632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
116632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_CANCEL:
117632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            case MotionEvent.ACTION_UP:
118632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mHandlingTouch = false;
119632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                break;
120bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang        }
121bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang
122632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return super.onTouchEvent(ev);
123632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
124632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
125632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public boolean isHandlingTouch() {
126632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return mHandlingTouch;
127bb56a1512559a5f024ba213c4bdcfe3d9d9387deAndy Huang    }
128f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang
12923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int getViewportWidth() {
13023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return mViewportWidth;
13123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
13223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
13323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    /**
13423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * Similar to {@link #getScale()}, except that it returns the initially expected scale, as
13523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * determined by the ratio of actual screen pixels to logical HTML pixels.
13623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * <p>This assumes that we are able to control the logical HTML viewport with a meta-viewport
13723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     * tag.
13823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang     */
13923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public float getInitialScale() {
14023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        // an HTML meta-viewport width of "device-width" and unspecified (medium) density means
14123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        // that the default scale is effectively the screen density.
14223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return mDensity;
14323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
14423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
14523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int screenPxToWebPx(int screenPx) {
14623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (screenPx / getInitialScale());
14723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
14823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
14923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    public int webPxToScreenPx(int webPx) {
15023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang        return (int) (webPx * getInitialScale());
15123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang    }
15223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang
153f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang}
154