• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.webkit;
18 
19 import android.view.SurfaceView;
20 import android.view.View;
21 import android.view.ViewGroup;
22 import android.widget.AbsoluteLayout;
23 
24 import java.util.ArrayList;
25 
26 class ViewManager {
27     private final WebView mWebView;
28     private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
29     private boolean mHidden;
30     private boolean mReadyToDraw;
31     private boolean mZoomInProgress = false;
32 
33     // Threshold at which a surface is prevented from further increasing in size
34     private final int MAX_SURFACE_AREA;
35     // GPU Limit (hard coded for now)
36     private static final int MAX_SURFACE_DIMENSION = 2048;
37 
38     class ChildView {
39         int x;
40         int y;
41         int width;
42         int height;
43         View mView; // generic view to show
44 
ChildView()45         ChildView() {
46         }
47 
setBounds(int x, int y, int width, int height)48         void setBounds(int x, int y, int width, int height) {
49             this.x = x;
50             this.y = y;
51             this.width = width;
52             this.height = height;
53         }
54 
attachView(int x, int y, int width, int height)55         void attachView(int x, int y, int width, int height) {
56             if (mView == null) {
57                 return;
58             }
59             setBounds(x, y, width, height);
60 
61             mWebView.mPrivateHandler.post(new Runnable() {
62                 public void run() {
63                     // This method may be called multiple times. If the view is
64                     // already attached, just set the new LayoutParams,
65                     // otherwise attach the view and add it to the list of
66                     // children.
67                     requestLayout(ChildView.this);
68 
69                     if (mView.getParent() == null) {
70                         attachViewOnUIThread();
71                     }
72                 }
73             });
74         }
75 
attachViewOnUIThread()76         private void attachViewOnUIThread() {
77             mWebView.addView(mView);
78             mChildren.add(this);
79             if (!mReadyToDraw) {
80                 mView.setVisibility(View.GONE);
81             }
82         }
83 
removeView()84         void removeView() {
85             if (mView == null) {
86                 return;
87             }
88             mWebView.mPrivateHandler.post(new Runnable() {
89                 public void run() {
90                     removeViewOnUIThread();
91                 }
92             });
93         }
94 
removeViewOnUIThread()95         private void removeViewOnUIThread() {
96             mWebView.removeView(mView);
97             mChildren.remove(this);
98         }
99     }
100 
ViewManager(WebView w)101     ViewManager(WebView w) {
102         mWebView = w;
103 
104         int pixelArea = w.getResources().getDisplayMetrics().widthPixels *
105                         w.getResources().getDisplayMetrics().heightPixels;
106         /* set the threshold to be 275% larger than the screen size. The
107            percentage is simply an estimation and is not based on anything but
108            basic trial-and-error tests run on multiple devices.
109          */
110         MAX_SURFACE_AREA = (int)(pixelArea * 2.75);
111     }
112 
createView()113     ChildView createView() {
114         return new ChildView();
115     }
116 
117     /**
118      * This should only be called from the UI thread.
119      */
requestLayout(ChildView v)120     private void requestLayout(ChildView v) {
121 
122         int width = mWebView.contentToViewDimension(v.width);
123         int height = mWebView.contentToViewDimension(v.height);
124         int x = mWebView.contentToViewX(v.x);
125         int y = mWebView.contentToViewY(v.y);
126 
127         AbsoluteLayout.LayoutParams lp;
128         ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams();
129 
130         if (layoutParams instanceof AbsoluteLayout.LayoutParams) {
131             lp = (AbsoluteLayout.LayoutParams) layoutParams;
132             lp.width = width;
133             lp.height = height;
134             lp.x = x;
135             lp.y = y;
136         } else {
137             lp = new AbsoluteLayout.LayoutParams(width, height, x, y);
138         }
139 
140         // apply the layout to the view
141         v.mView.setLayoutParams(lp);
142 
143         if(v.mView instanceof SurfaceView) {
144 
145             final SurfaceView sView = (SurfaceView) v.mView;
146 
147             if (sView.isFixedSize() && mZoomInProgress) {
148                 /* If we're already fixed, and we're in a zoom, then do nothing
149                    about the size. Just wait until we get called at the end of
150                    the zoom session (with mZoomInProgress false) and we'll
151                    fixup our size then.
152                  */
153                 return;
154             }
155 
156             /* Compute proportional fixed width/height if necessary.
157              *
158              * NOTE: plugins (e.g. Flash) must not explicitly fix the size of
159              * their surface. The logic below will result in unexpected behavior
160              * for the plugin if they attempt to fix the size of the surface.
161              */
162             int fixedW = width;
163             int fixedH = height;
164             if (fixedW > MAX_SURFACE_DIMENSION || fixedH > MAX_SURFACE_DIMENSION) {
165                 if (v.width > v.height) {
166                     fixedW = MAX_SURFACE_DIMENSION;
167                     fixedH = v.height * MAX_SURFACE_DIMENSION / v.width;
168                 } else {
169                     fixedH = MAX_SURFACE_DIMENSION;
170                     fixedW = v.width * MAX_SURFACE_DIMENSION / v.height;
171                 }
172             }
173             if (fixedW * fixedH > MAX_SURFACE_AREA) {
174                 float area = MAX_SURFACE_AREA;
175                 if (v.width > v.height) {
176                     fixedW = (int)Math.sqrt(area * v.width / v.height);
177                     fixedH = v.height * fixedW / v.width;
178                 } else {
179                     fixedH = (int)Math.sqrt(area * v.height / v.width);
180                     fixedW = v.width * fixedH / v.height;
181                 }
182             }
183 
184             if (fixedW != width || fixedH != height) {
185                 // if we get here, either our dimensions or area (or both)
186                 // exeeded our max, so we had to compute fixedW and fixedH
187                 sView.getHolder().setFixedSize(fixedW, fixedH);
188             } else if (!sView.isFixedSize() && mZoomInProgress) {
189                 // just freeze where we were (view size) until we're done with
190                 // the zoom progress
191                 sView.getHolder().setFixedSize(sView.getWidth(),
192                                                sView.getHeight());
193             } else if (sView.isFixedSize() && !mZoomInProgress) {
194                 /* The changing of visibility is a hack to get around a bug in
195                  * the framework that causes the surface to revert to the size
196                  * it was prior to being fixed before it redraws using the
197                  * values currently in its layout.
198                  *
199                  * The surface is destroyed when it is set to invisible and then
200                  * recreated at the new dimensions when it is made visible. The
201                  * same destroy/create step occurs without the change in
202                  * visibility, but then exhibits the behavior described in the
203                  * previous paragraph.
204                  */
205                 if (sView.getVisibility() == View.VISIBLE) {
206                     sView.setVisibility(View.INVISIBLE);
207                     sView.getHolder().setSizeFromLayout();
208                     // setLayoutParams() only requests the layout. If we set it
209                     // to VISIBLE now, it will use the old dimension to set the
210                     // size. Post a message to ensure that it shows the new size.
211                     mWebView.mPrivateHandler.post(new Runnable() {
212                         public void run() {
213                             sView.setVisibility(View.VISIBLE);
214                         }
215                     });
216                 } else {
217                     sView.getHolder().setSizeFromLayout();
218                 }
219             }
220         }
221     }
222 
startZoom()223     void startZoom() {
224         mZoomInProgress = true;
225         for (ChildView v : mChildren) {
226             requestLayout(v);
227         }
228     }
229 
endZoom()230     void endZoom() {
231         mZoomInProgress = false;
232         for (ChildView v : mChildren) {
233             requestLayout(v);
234         }
235     }
236 
scaleAll()237     void scaleAll() {
238         for (ChildView v : mChildren) {
239             requestLayout(v);
240         }
241     }
242 
hideAll()243     void hideAll() {
244         if (mHidden) {
245             return;
246         }
247         for (ChildView v : mChildren) {
248             v.mView.setVisibility(View.GONE);
249         }
250         mHidden = true;
251     }
252 
showAll()253     void showAll() {
254         if (!mHidden) {
255             return;
256         }
257         for (ChildView v : mChildren) {
258             v.mView.setVisibility(View.VISIBLE);
259         }
260         mHidden = false;
261     }
262 
postResetStateAll()263     void postResetStateAll() {
264         mWebView.mPrivateHandler.post(new Runnable() {
265             public void run() {
266                 mReadyToDraw = false;
267             }
268         });
269     }
270 
postReadyToDrawAll()271     void postReadyToDrawAll() {
272         mWebView.mPrivateHandler.post(new Runnable() {
273             public void run() {
274                 mReadyToDraw = true;
275                 for (ChildView v : mChildren) {
276                     v.mView.setVisibility(View.VISIBLE);
277                 }
278             }
279         });
280     }
281 
hitTest(int contentX, int contentY)282     ChildView hitTest(int contentX, int contentY) {
283         if (mHidden) {
284             return null;
285         }
286         for (ChildView v : mChildren) {
287             if (v.mView.getVisibility() == View.VISIBLE) {
288                 if (contentX >= v.x && contentX < (v.x + v.width)
289                         && contentY >= v.y && contentY < (v.y + v.height)) {
290                     return v;
291                 }
292             }
293         }
294         return null;
295     }
296 }
297