/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webkit; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.widget.AbsoluteLayout; import java.util.ArrayList; class ViewManager { private final WebView mWebView; private final ArrayList mChildren = new ArrayList(); private boolean mHidden; private boolean mReadyToDraw; private boolean mZoomInProgress = false; // Threshold at which a surface is prevented from further increasing in size private final int MAX_SURFACE_THRESHOLD; class ChildView { int x; int y; int width; int height; View mView; // generic view to show ChildView() { } void setBounds(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } void attachView(int x, int y, int width, int height) { if (mView == null) { return; } setBounds(x, y, width, height); mWebView.mPrivateHandler.post(new Runnable() { public void run() { // This method may be called multiple times. If the view is // already attached, just set the new LayoutParams, // otherwise attach the view and add it to the list of // children. requestLayout(ChildView.this); if (mView.getParent() == null) { attachViewOnUIThread(); } } }); } private void attachViewOnUIThread() { mWebView.addView(mView); mChildren.add(this); if (!mReadyToDraw) { mView.setVisibility(View.GONE); } } void removeView() { if (mView == null) { return; } mWebView.mPrivateHandler.post(new Runnable() { public void run() { removeViewOnUIThread(); } }); } private void removeViewOnUIThread() { mWebView.removeView(mView); mChildren.remove(this); } } ViewManager(WebView w) { mWebView = w; int pixelArea = w.getResources().getDisplayMetrics().widthPixels * w.getResources().getDisplayMetrics().heightPixels; /* set the threshold to be 275% larger than the screen size. The percentage is simply an estimation and is not based on anything but basic trial-and-error tests run on multiple devices. */ MAX_SURFACE_THRESHOLD = (int)(pixelArea * 2.75); } ChildView createView() { return new ChildView(); } /** * Shorthand for calling mWebView.contentToViewDimension. Used when * obtaining a view dimension from a content dimension, whether it be in x * or y. */ private int ctvD(int val) { return mWebView.contentToViewDimension(val); } /** * Shorthand for calling mWebView.contentToViewX. Used when obtaining a * view x coordinate from a content x coordinate. */ private int ctvX(int val) { return mWebView.contentToViewX(val); } /** * Shorthand for calling mWebView.contentToViewY. Used when obtaining a * view y coordinate from a content y coordinate. */ private int ctvY(int val) { return mWebView.contentToViewY(val); } /** * This should only be called from the UI thread. */ private void requestLayout(ChildView v) { int width = ctvD(v.width); int height = ctvD(v.height); int x = ctvX(v.x); int y = ctvY(v.y); AbsoluteLayout.LayoutParams lp; ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams(); if (layoutParams instanceof AbsoluteLayout.LayoutParams) { lp = (AbsoluteLayout.LayoutParams) layoutParams; lp.width = width; lp.height = height; lp.x = x; lp.y = y; } else { lp = new AbsoluteLayout.LayoutParams(width, height, x, y); } // apply the layout to the view v.mView.setLayoutParams(lp); if(v.mView instanceof SurfaceView) { final SurfaceView sView = (SurfaceView) v.mView; boolean exceedThreshold = (width * height) > MAX_SURFACE_THRESHOLD; /* If the surface has exceeded a predefined threshold or the webview * is currently zoom then fix the size of the surface. * * NOTE: plugins (e.g. Flash) must not explicitly fix the size of * their surface. The logic below will result in unexpected behavior * for the plugin if they attempt to fix the size of the surface. */ if (!sView.isFixedSize() && (exceedThreshold || mZoomInProgress)) { sView.getHolder().setFixedSize(width, height); } else if (sView.isFixedSize() && !exceedThreshold && !mZoomInProgress) { /* The changing of visibility is a hack to get around a bug in * the framework that causes the surface to revert to the size * it was prior to being fixed before it redraws using the * values currently in its layout. * * The surface is destroyed when it is set to invisible and then * recreated at the new dimensions when it is made visible. The * same destroy/create step occurs without the change in * visibility, but then exhibits the behavior described in the * previous paragraph. */ if (sView.getVisibility() == View.VISIBLE) { sView.setVisibility(View.INVISIBLE); sView.getHolder().setSizeFromLayout(); sView.setVisibility(View.VISIBLE); } else { sView.getHolder().setSizeFromLayout(); } } else if (sView.isFixedSize() && exceedThreshold) { sView.requestLayout(); } } } void startZoom() { mZoomInProgress = true; for (ChildView v : mChildren) { requestLayout(v); } } void endZoom() { mZoomInProgress = false; for (ChildView v : mChildren) { requestLayout(v); } } void scaleAll() { for (ChildView v : mChildren) { requestLayout(v); } } void hideAll() { if (mHidden) { return; } for (ChildView v : mChildren) { v.mView.setVisibility(View.GONE); } mHidden = true; } void showAll() { if (!mHidden) { return; } for (ChildView v : mChildren) { v.mView.setVisibility(View.VISIBLE); } mHidden = false; } void postResetStateAll() { mWebView.mPrivateHandler.post(new Runnable() { public void run() { mReadyToDraw = false; } }); } void postReadyToDrawAll() { mWebView.mPrivateHandler.post(new Runnable() { public void run() { mReadyToDraw = true; for (ChildView v : mChildren) { v.mView.setVisibility(View.VISIBLE); } } }); } ChildView hitTest(int contentX, int contentY) { if (mHidden) { return null; } for (ChildView v : mChildren) { if (v.mView.getVisibility() == View.VISIBLE) { if (contentX >= v.x && contentX < (v.x + v.width) && contentY >= v.y && contentY < (v.y + v.height)) { return v; } } } return null; } }