Home | History | Annotate | Download | only in webkit
      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