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