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 45 ChildView() { 46 } 47 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 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 76 private void attachViewOnUIThread() { 77 mWebView.addView(mView); 78 mChildren.add(this); 79 if (!mReadyToDraw) { 80 mView.setVisibility(View.GONE); 81 } 82 } 83 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 95 private void removeViewOnUIThread() { 96 mWebView.removeView(mView); 97 mChildren.remove(this); 98 } 99 } 100 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 113 ChildView createView() { 114 return new ChildView(); 115 } 116 117 /** 118 * This should only be called from the UI thread. 119 */ 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 223 void startZoom() { 224 mZoomInProgress = true; 225 for (ChildView v : mChildren) { 226 requestLayout(v); 227 } 228 } 229 230 void endZoom() { 231 mZoomInProgress = false; 232 for (ChildView v : mChildren) { 233 requestLayout(v); 234 } 235 } 236 237 void scaleAll() { 238 for (ChildView v : mChildren) { 239 requestLayout(v); 240 } 241 } 242 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 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 263 void postResetStateAll() { 264 mWebView.mPrivateHandler.post(new Runnable() { 265 public void run() { 266 mReadyToDraw = false; 267 } 268 }); 269 } 270 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 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