1 /* 2 * Copyright (C) 2013 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mail.browse; 19 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.Looper; 23 import android.util.AttributeSet; 24 import android.view.MotionEvent; 25 import android.webkit.WebView; 26 27 import com.android.mail.utils.Clock; 28 import com.android.mail.utils.LogTag; 29 import com.android.mail.utils.LogUtils; 30 import com.android.mail.utils.Throttle; 31 32 /** 33 * A WebView designed to live within a {@link MessageScrollView}. 34 */ 35 public class MessageWebView extends WebView implements MessageScrollView.Touchable { 36 37 private static final String LOG_TAG = LogTag.getLogTag(); 38 39 private static Handler sMainThreadHandler; 40 41 private boolean mTouched; 42 43 private static final int MIN_RESIZE_INTERVAL = 200; 44 private static final int MAX_RESIZE_INTERVAL = 300; 45 private final Clock mClock = Clock.INSTANCE; 46 47 private final Throttle mThrottle = new Throttle("MessageWebView", 48 new Runnable() { 49 @Override public void run() { 50 performSizeChangeDelayed(); 51 } 52 }, getMainThreadHandler(), 53 MIN_RESIZE_INTERVAL, MAX_RESIZE_INTERVAL); 54 55 private int mRealWidth; 56 private int mRealHeight; 57 private boolean mIgnoreNext; 58 private long mLastSizeChangeTime = -1; 59 MessageWebView(Context c)60 public MessageWebView(Context c) { 61 this(c, null); 62 } 63 MessageWebView(Context c, AttributeSet attrs)64 public MessageWebView(Context c, AttributeSet attrs) { 65 super(c, attrs); 66 } 67 68 @Override wasTouched()69 public boolean wasTouched() { 70 return mTouched; 71 } 72 73 @Override clearTouched()74 public void clearTouched() { 75 mTouched = false; 76 } 77 78 @Override onTouchEvent(MotionEvent event)79 public boolean onTouchEvent(MotionEvent event) { 80 mTouched = true; 81 final boolean handled = super.onTouchEvent(event); 82 LogUtils.d(MessageScrollView.LOG_TAG,"OUT WebView.onTouch, returning handled=%s ev=%s", 83 handled, event); 84 return handled; 85 } 86 87 @Override onSizeChanged(int w, int h, int ow, int oh)88 protected void onSizeChanged(int w, int h, int ow, int oh) { 89 mRealWidth = w; 90 mRealHeight = h; 91 final long now = mClock.getTime(); 92 boolean recentlySized = (now - mLastSizeChangeTime < MIN_RESIZE_INTERVAL); 93 94 // It's known that the previous resize event may cause a resize event immediately. If 95 // this happens sufficiently close to the last resize event, drop it on the floor. 96 if (mIgnoreNext) { 97 mIgnoreNext = false; 98 if (recentlySized) { 99 LogUtils.w(LOG_TAG, "Suppressing size change in MessageWebView"); 100 return; 101 } 102 } 103 104 if (recentlySized) { 105 mThrottle.onEvent(); 106 } else { 107 // It's been a sufficiently long time - just perform the resize as normal. This should 108 // be the normal code path. 109 performSizeChange(ow, oh); 110 } 111 } 112 performSizeChange(int ow, int oh)113 private void performSizeChange(int ow, int oh) { 114 super.onSizeChanged(mRealWidth, mRealHeight, ow, oh); 115 mLastSizeChangeTime = mClock.getTime(); 116 } 117 performSizeChangeDelayed()118 private void performSizeChangeDelayed() { 119 mIgnoreNext = true; 120 performSizeChange(getWidth(), getHeight()); 121 } 122 123 /** 124 * @return a {@link Handler} tied to the main thread. 125 */ getMainThreadHandler()126 public static Handler getMainThreadHandler() { 127 if (sMainThreadHandler == null) { 128 // No need to synchronize -- it's okay to create an extra Handler, which will be used 129 // only once and then thrown away. 130 sMainThreadHandler = new Handler(Looper.getMainLooper()); 131 } 132 return sMainThreadHandler; 133 } 134 } 135