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