Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 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.view;
     18 
     19 import android.util.DisplayMetrics;
     20 import com.android.internal.view.BaseIWindow;
     21 
     22 import android.content.Context;
     23 import android.content.res.Configuration;
     24 import android.content.res.Resources;
     25 import android.content.res.CompatibilityInfo.Translator;
     26 import android.graphics.Canvas;
     27 import android.graphics.PixelFormat;
     28 import android.graphics.PorterDuff;
     29 import android.graphics.Rect;
     30 import android.graphics.Region;
     31 import android.os.Handler;
     32 import android.os.Message;
     33 import android.os.RemoteException;
     34 import android.os.SystemClock;
     35 import android.os.ParcelFileDescriptor;
     36 import android.util.AttributeSet;
     37 import android.util.Config;
     38 import android.util.Log;
     39 
     40 import java.lang.ref.WeakReference;
     41 import java.util.ArrayList;
     42 import java.util.concurrent.locks.ReentrantLock;
     43 
     44 /**
     45  * Provides a dedicated drawing surface embedded inside of a view hierarchy.
     46  * You can control the format of this surface and, if you like, its size; the
     47  * SurfaceView takes care of placing the surface at the correct location on the
     48  * screen
     49  *
     50  * <p>The surface is Z ordered so that it is behind the window holding its
     51  * SurfaceView; the SurfaceView punches a hole in its window to allow its
     52  * surface to be displayed.  The view hierarchy will take care of correctly
     53  * compositing with the Surface any siblings of the SurfaceView that would
     54  * normally appear on top of it.  This can be used to place overlays such as
     55  * buttons on top of the Surface, though note however that it can have an
     56  * impact on performance since a full alpha-blended composite will be performed
     57  * each time the Surface changes.
     58  *
     59  * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
     60  * which can be retrieved by calling {@link #getHolder}.
     61  *
     62  * <p>The Surface will be created for you while the SurfaceView's window is
     63  * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
     64  * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
     65  * Surface is created and destroyed as the window is shown and hidden.
     66  *
     67  * <p>One of the purposes of this class is to provide a surface in which a
     68  * secondary thread can render in to the screen.  If you are going to use it
     69  * this way, you need to be aware of some threading semantics:
     70  *
     71  * <ul>
     72  * <li> All SurfaceView and
     73  * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
     74  * from the thread running the SurfaceView's window (typically the main thread
     75  * of the application).  They thus need to correctly synchronize with any
     76  * state that is also touched by the drawing thread.
     77  * <li> You must ensure that the drawing thread only touches the underlying
     78  * Surface while it is valid -- between
     79  * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
     80  * and
     81  * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
     82  * </ul>
     83  */
     84 public class SurfaceView extends View {
     85     static private final String TAG = "SurfaceView";
     86     static private final boolean DEBUG = false;
     87     static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
     88 
     89     final ArrayList<SurfaceHolder.Callback> mCallbacks
     90             = new ArrayList<SurfaceHolder.Callback>();
     91 
     92     final int[] mLocation = new int[2];
     93 
     94     final ReentrantLock mSurfaceLock = new ReentrantLock();
     95     final Surface mSurface = new Surface();
     96     boolean mDrawingStopped = true;
     97 
     98     final WindowManager.LayoutParams mLayout
     99             = new WindowManager.LayoutParams();
    100     IWindowSession mSession;
    101     MyWindow mWindow;
    102     final Rect mVisibleInsets = new Rect();
    103     final Rect mWinFrame = new Rect();
    104     final Rect mContentInsets = new Rect();
    105     final Configuration mConfiguration = new Configuration();
    106 
    107     static final int KEEP_SCREEN_ON_MSG = 1;
    108     static final int GET_NEW_SURFACE_MSG = 2;
    109     static final int UPDATE_WINDOW_MSG = 3;
    110 
    111     int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
    112 
    113     boolean mIsCreating = false;
    114 
    115     final Handler mHandler = new Handler() {
    116         @Override
    117         public void handleMessage(Message msg) {
    118             switch (msg.what) {
    119                 case KEEP_SCREEN_ON_MSG: {
    120                     setKeepScreenOn(msg.arg1 != 0);
    121                 } break;
    122                 case GET_NEW_SURFACE_MSG: {
    123                     handleGetNewSurface();
    124                 } break;
    125                 case UPDATE_WINDOW_MSG: {
    126                     updateWindow(false, false);
    127                 } break;
    128             }
    129         }
    130     };
    131 
    132     final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
    133             = new ViewTreeObserver.OnScrollChangedListener() {
    134                     public void onScrollChanged() {
    135                         updateWindow(false, false);
    136                     }
    137             };
    138 
    139     boolean mRequestedVisible = false;
    140     boolean mWindowVisibility = false;
    141     boolean mViewVisibility = false;
    142     int mRequestedWidth = -1;
    143     int mRequestedHeight = -1;
    144     /* Set SurfaceView's format to 565 by default to maintain backward
    145      * compatibility with applications assuming this format.
    146      */
    147     int mRequestedFormat = PixelFormat.RGB_565;
    148     int mRequestedType = -1;
    149 
    150     boolean mHaveFrame = false;
    151     boolean mDestroyReportNeeded = false;
    152     boolean mNewSurfaceNeeded = false;
    153     long mLastLockTime = 0;
    154 
    155     boolean mVisible = false;
    156     int mLeft = -1;
    157     int mTop = -1;
    158     int mWidth = -1;
    159     int mHeight = -1;
    160     int mFormat = -1;
    161     int mType = -1;
    162     final Rect mSurfaceFrame = new Rect();
    163     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
    164     boolean mUpdateWindowNeeded;
    165     boolean mReportDrawNeeded;
    166     private Translator mTranslator;
    167 
    168     public SurfaceView(Context context) {
    169         super(context);
    170         init();
    171     }
    172 
    173     public SurfaceView(Context context, AttributeSet attrs) {
    174         super(context, attrs);
    175         init();
    176     }
    177 
    178     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
    179         super(context, attrs, defStyle);
    180         init();
    181     }
    182 
    183     private void init() {
    184         setWillNotDraw(true);
    185     }
    186 
    187     /**
    188      * Return the SurfaceHolder providing access and control over this
    189      * SurfaceView's underlying surface.
    190      *
    191      * @return SurfaceHolder The holder of the surface.
    192      */
    193     public SurfaceHolder getHolder() {
    194         return mSurfaceHolder;
    195     }
    196 
    197     @Override
    198     protected void onAttachedToWindow() {
    199         super.onAttachedToWindow();
    200         mParent.requestTransparentRegion(this);
    201         mSession = getWindowSession();
    202         mLayout.token = getWindowToken();
    203         mLayout.setTitle("SurfaceView");
    204         mViewVisibility = getVisibility() == VISIBLE;
    205         getViewTreeObserver().addOnScrollChangedListener(mScrollChangedListener);
    206     }
    207 
    208     @Override
    209     protected void onWindowVisibilityChanged(int visibility) {
    210         super.onWindowVisibilityChanged(visibility);
    211         mWindowVisibility = visibility == VISIBLE;
    212         mRequestedVisible = mWindowVisibility && mViewVisibility;
    213         updateWindow(false, false);
    214     }
    215 
    216     @Override
    217     public void setVisibility(int visibility) {
    218         super.setVisibility(visibility);
    219         mViewVisibility = visibility == VISIBLE;
    220         mRequestedVisible = mWindowVisibility && mViewVisibility;
    221         updateWindow(false, false);
    222     }
    223 
    224     /**
    225      * This method is not intended for general use. It was created
    226      * temporarily to improve performance of 3D layers in Launcher
    227      * and should be removed and fixed properly.
    228      *
    229      * Do not call this method. Ever.
    230      *
    231      * @hide
    232      */
    233     protected void showSurface() {
    234         if (mSession != null) {
    235             updateWindow(true, false);
    236         }
    237     }
    238 
    239     /**
    240      * This method is not intended for general use. It was created
    241      * temporarily to improve performance of 3D layers in Launcher
    242      * and should be removed and fixed properly.
    243      *
    244      * Do not call this method. Ever.
    245      *
    246      * @hide
    247      */
    248     protected void hideSurface() {
    249         if (mSession != null && mWindow != null) {
    250             mSurfaceLock.lock();
    251             try {
    252                 DisplayMetrics metrics = getResources().getDisplayMetrics();
    253                 mLayout.x = metrics.widthPixels * 3;
    254                 mSession.relayout(mWindow, mLayout, mWidth, mHeight, VISIBLE, false,
    255                         mWinFrame, mContentInsets, mVisibleInsets, mConfiguration, mSurface);
    256             } catch (RemoteException e) {
    257                 // Ignore
    258             } finally {
    259                 mSurfaceLock.unlock();
    260             }
    261         }
    262     }
    263 
    264     @Override
    265     protected void onDetachedFromWindow() {
    266         getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener);
    267         mRequestedVisible = false;
    268         updateWindow(false, false);
    269         mHaveFrame = false;
    270         if (mWindow != null) {
    271             try {
    272                 mSession.remove(mWindow);
    273             } catch (RemoteException ex) {
    274             }
    275             mWindow = null;
    276         }
    277         mSession = null;
    278         mLayout.token = null;
    279 
    280         super.onDetachedFromWindow();
    281     }
    282 
    283     @Override
    284     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    285         int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
    286         int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
    287         setMeasuredDimension(width, height);
    288     }
    289 
    290     /** @hide */
    291     @Override
    292     protected boolean setFrame(int left, int top, int right, int bottom) {
    293         boolean result = super.setFrame(left, top, right, bottom);
    294         updateWindow(false, false);
    295         return result;
    296     }
    297 
    298     @Override
    299     public boolean gatherTransparentRegion(Region region) {
    300         if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
    301             return super.gatherTransparentRegion(region);
    302         }
    303 
    304         boolean opaque = true;
    305         if ((mPrivateFlags & SKIP_DRAW) == 0) {
    306             // this view draws, remove it from the transparent region
    307             opaque = super.gatherTransparentRegion(region);
    308         } else if (region != null) {
    309             int w = getWidth();
    310             int h = getHeight();
    311             if (w>0 && h>0) {
    312                 getLocationInWindow(mLocation);
    313                 // otherwise, punch a hole in the whole hierarchy
    314                 int l = mLocation[0];
    315                 int t = mLocation[1];
    316                 region.op(l, t, l+w, t+h, Region.Op.UNION);
    317             }
    318         }
    319         if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
    320             opaque = false;
    321         }
    322         return opaque;
    323     }
    324 
    325     @Override
    326     public void draw(Canvas canvas) {
    327         if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
    328             // draw() is not called when SKIP_DRAW is set
    329             if ((mPrivateFlags & SKIP_DRAW) == 0) {
    330                 // punch a whole in the view-hierarchy below us
    331                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
    332             }
    333         }
    334         super.draw(canvas);
    335     }
    336 
    337     @Override
    338     protected void dispatchDraw(Canvas canvas) {
    339         if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
    340             // if SKIP_DRAW is cleared, draw() has already punched a hole
    341             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
    342                 // punch a whole in the view-hierarchy below us
    343                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
    344             }
    345         }
    346         // reposition ourselves where the surface is
    347         mHaveFrame = true;
    348         updateWindow(false, false);
    349         super.dispatchDraw(canvas);
    350     }
    351 
    352     /**
    353      * Control whether the surface view's surface is placed on top of another
    354      * regular surface view in the window (but still behind the window itself).
    355      * This is typically used to place overlays on top of an underlying media
    356      * surface view.
    357      *
    358      * <p>Note that this must be set before the surface view's containing
    359      * window is attached to the window manager.
    360      *
    361      * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
    362      */
    363     public void setZOrderMediaOverlay(boolean isMediaOverlay) {
    364         mWindowType = isMediaOverlay
    365                 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
    366                 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
    367     }
    368 
    369     /**
    370      * Control whether the surface view's surface is placed on top of its
    371      * window.  Normally it is placed behind the window, to allow it to
    372      * (for the most part) appear to composite with the views in the
    373      * hierarchy.  By setting this, you cause it to be placed above the
    374      * window.  This means that none of the contents of the window this
    375      * SurfaceView is in will be visible on top of its surface.
    376      *
    377      * <p>Note that this must be set before the surface view's containing
    378      * window is attached to the window manager.
    379      *
    380      * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
    381      */
    382     public void setZOrderOnTop(boolean onTop) {
    383         if (onTop) {
    384             mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
    385             // ensures the surface is placed below the IME
    386             mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
    387         } else {
    388             mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
    389             mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
    390         }
    391     }
    392 
    393     /**
    394      * Hack to allow special layering of windows.  The type is one of the
    395      * types in WindowManager.LayoutParams.  This is a hack so:
    396      * @hide
    397      */
    398     public void setWindowType(int type) {
    399         mWindowType = type;
    400     }
    401 
    402     private void updateWindow(boolean force, boolean redrawNeeded) {
    403         if (!mHaveFrame) {
    404             return;
    405         }
    406         ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
    407         if (viewRoot != null) {
    408             mTranslator = viewRoot.mTranslator;
    409         }
    410 
    411         Resources res = getContext().getResources();
    412         if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) {
    413             mSurface.setCompatibleDisplayMetrics(res.getDisplayMetrics(), mTranslator);
    414         }
    415 
    416         int myWidth = mRequestedWidth;
    417         if (myWidth <= 0) myWidth = getWidth();
    418         int myHeight = mRequestedHeight;
    419         if (myHeight <= 0) myHeight = getHeight();
    420 
    421         getLocationInWindow(mLocation);
    422         final boolean creating = mWindow == null;
    423         final boolean formatChanged = mFormat != mRequestedFormat;
    424         final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
    425         final boolean visibleChanged = mVisible != mRequestedVisible
    426                 || mNewSurfaceNeeded;
    427         final boolean typeChanged = mType != mRequestedType;
    428         if (force || creating || formatChanged || sizeChanged || visibleChanged
    429             || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]
    430             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
    431 
    432             if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
    433                     + " format=" + formatChanged + " size=" + sizeChanged
    434                     + " visible=" + visibleChanged
    435                     + " left=" + (mLeft != mLocation[0])
    436                     + " top=" + (mTop != mLocation[1]));
    437 
    438             try {
    439                 final boolean visible = mVisible = mRequestedVisible;
    440                 mLeft = mLocation[0];
    441                 mTop = mLocation[1];
    442                 mWidth = myWidth;
    443                 mHeight = myHeight;
    444                 mFormat = mRequestedFormat;
    445                 mType = mRequestedType;
    446 
    447                 // Scaling/Translate window's layout here because mLayout is not used elsewhere.
    448 
    449                 // Places the window relative
    450                 mLayout.x = mLeft;
    451                 mLayout.y = mTop;
    452                 mLayout.width = getWidth();
    453                 mLayout.height = getHeight();
    454                 if (mTranslator != null) {
    455                     mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
    456                 }
    457 
    458                 mLayout.format = mRequestedFormat;
    459                 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    460                               | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    461                               | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
    462                               | WindowManager.LayoutParams.FLAG_SCALED
    463                               | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    464                               | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    465                               ;
    466                 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
    467                     mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    468                 }
    469 
    470                 mLayout.memoryType = mRequestedType;
    471 
    472                 if (mWindow == null) {
    473                     mWindow = new MyWindow(this);
    474                     mLayout.type = mWindowType;
    475                     mLayout.gravity = Gravity.LEFT|Gravity.TOP;
    476                     mSession.addWithoutInputChannel(mWindow, mLayout,
    477                             mVisible ? VISIBLE : GONE, mContentInsets);
    478                 }
    479 
    480                 if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
    481                     reportSurfaceDestroyed();
    482                 }
    483 
    484                 mNewSurfaceNeeded = false;
    485 
    486                 boolean realSizeChanged;
    487                 boolean reportDrawNeeded;
    488 
    489                 mSurfaceLock.lock();
    490                 try {
    491                     mUpdateWindowNeeded = false;
    492                     reportDrawNeeded = mReportDrawNeeded;
    493                     mReportDrawNeeded = false;
    494                     mDrawingStopped = !visible;
    495 
    496                     final int relayoutResult = mSession.relayout(
    497                         mWindow, mLayout, mWidth, mHeight,
    498                             visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
    499                             mVisibleInsets, mConfiguration, mSurface);
    500                     if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
    501                         mReportDrawNeeded = true;
    502                     }
    503 
    504                     if (localLOGV) Log.i(TAG, "New surface: " + mSurface
    505                             + ", vis=" + visible + ", frame=" + mWinFrame);
    506 
    507                     mSurfaceFrame.left = 0;
    508                     mSurfaceFrame.top = 0;
    509                     if (mTranslator == null) {
    510                         mSurfaceFrame.right = mWinFrame.width();
    511                         mSurfaceFrame.bottom = mWinFrame.height();
    512                     } else {
    513                         float appInvertedScale = mTranslator.applicationInvertedScale;
    514                         mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
    515                         mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
    516                     }
    517 
    518                     final int surfaceWidth = mSurfaceFrame.right;
    519                     final int surfaceHeight = mSurfaceFrame.bottom;
    520                     realSizeChanged = mLastSurfaceWidth != surfaceWidth
    521                             || mLastSurfaceHeight != surfaceHeight;
    522                     mLastSurfaceWidth = surfaceWidth;
    523                     mLastSurfaceHeight = surfaceHeight;
    524                 } finally {
    525                     mSurfaceLock.unlock();
    526                 }
    527 
    528                 try {
    529                     redrawNeeded |= creating | reportDrawNeeded;
    530 
    531                     if (visible) {
    532                         mDestroyReportNeeded = true;
    533 
    534                         SurfaceHolder.Callback callbacks[];
    535                         synchronized (mCallbacks) {
    536                             callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
    537                             mCallbacks.toArray(callbacks);
    538                         }
    539 
    540                         if (visibleChanged) {
    541                             mIsCreating = true;
    542                             for (SurfaceHolder.Callback c : callbacks) {
    543                                 c.surfaceCreated(mSurfaceHolder);
    544                             }
    545                         }
    546                         if (creating || formatChanged || sizeChanged
    547                                 || visibleChanged || realSizeChanged) {
    548                             for (SurfaceHolder.Callback c : callbacks) {
    549                                 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
    550                             }
    551                         }
    552                         if (redrawNeeded) {
    553                             for (SurfaceHolder.Callback c : callbacks) {
    554                                 if (c instanceof SurfaceHolder.Callback2) {
    555                                     ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
    556                                             mSurfaceHolder);
    557                                 }
    558                             }
    559                         }
    560                     } else {
    561                         mSurface.release();
    562                     }
    563                 } finally {
    564                     mIsCreating = false;
    565                     if (redrawNeeded) {
    566                         mSession.finishDrawing(mWindow);
    567                     }
    568                 }
    569             } catch (RemoteException ex) {
    570             }
    571             if (localLOGV) Log.v(
    572                 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
    573                 " w=" + mLayout.width + " h=" + mLayout.height +
    574                 ", frame=" + mSurfaceFrame);
    575         }
    576     }
    577 
    578     private void reportSurfaceDestroyed() {
    579         if (mDestroyReportNeeded) {
    580             mDestroyReportNeeded = false;
    581             SurfaceHolder.Callback callbacks[];
    582             synchronized (mCallbacks) {
    583                 callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
    584                 mCallbacks.toArray(callbacks);
    585             }
    586             for (SurfaceHolder.Callback c : callbacks) {
    587                 c.surfaceDestroyed(mSurfaceHolder);
    588             }
    589         }
    590         super.onDetachedFromWindow();
    591     }
    592 
    593     void handleGetNewSurface() {
    594         mNewSurfaceNeeded = true;
    595         updateWindow(false, false);
    596     }
    597 
    598     /**
    599      * Check to see if the surface has fixed size dimensions or if the surface's
    600      * dimensions are dimensions are dependent on its current layout.
    601      *
    602      * @return true if the surface has dimensions that are fixed in size
    603      * @hide
    604      */
    605     public boolean isFixedSize() {
    606         return (mRequestedWidth != -1 || mRequestedHeight != -1);
    607     }
    608 
    609     private static class MyWindow extends BaseIWindow {
    610         private final WeakReference<SurfaceView> mSurfaceView;
    611 
    612         public MyWindow(SurfaceView surfaceView) {
    613             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
    614         }
    615 
    616         public void resized(int w, int h, Rect coveredInsets,
    617                 Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
    618             SurfaceView surfaceView = mSurfaceView.get();
    619             if (surfaceView != null) {
    620                 if (localLOGV) Log.v(
    621                         "SurfaceView", surfaceView + " got resized: w=" +
    622                                 w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
    623                 surfaceView.mSurfaceLock.lock();
    624                 try {
    625                     if (reportDraw) {
    626                         surfaceView.mUpdateWindowNeeded = true;
    627                         surfaceView.mReportDrawNeeded = true;
    628                         surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
    629                     } else if (surfaceView.mWinFrame.width() != w
    630                             || surfaceView.mWinFrame.height() != h) {
    631                         surfaceView.mUpdateWindowNeeded = true;
    632                         surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
    633                     }
    634                 } finally {
    635                     surfaceView.mSurfaceLock.unlock();
    636                 }
    637             }
    638         }
    639 
    640         public void dispatchAppVisibility(boolean visible) {
    641             // The point of SurfaceView is to let the app control the surface.
    642         }
    643 
    644         public void dispatchGetNewSurface() {
    645             SurfaceView surfaceView = mSurfaceView.get();
    646             if (surfaceView != null) {
    647                 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
    648                 surfaceView.mHandler.sendMessage(msg);
    649             }
    650         }
    651 
    652         public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
    653             Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
    654         }
    655 
    656         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
    657         }
    658 
    659         int mCurWidth = -1;
    660         int mCurHeight = -1;
    661     }
    662 
    663     private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
    664 
    665         private static final String LOG_TAG = "SurfaceHolder";
    666 
    667         public boolean isCreating() {
    668             return mIsCreating;
    669         }
    670 
    671         public void addCallback(Callback callback) {
    672             synchronized (mCallbacks) {
    673                 // This is a linear search, but in practice we'll
    674                 // have only a couple callbacks, so it doesn't matter.
    675                 if (mCallbacks.contains(callback) == false) {
    676                     mCallbacks.add(callback);
    677                 }
    678             }
    679         }
    680 
    681         public void removeCallback(Callback callback) {
    682             synchronized (mCallbacks) {
    683                 mCallbacks.remove(callback);
    684             }
    685         }
    686 
    687         public void setFixedSize(int width, int height) {
    688             if (mRequestedWidth != width || mRequestedHeight != height) {
    689                 mRequestedWidth = width;
    690                 mRequestedHeight = height;
    691                 requestLayout();
    692             }
    693         }
    694 
    695         public void setSizeFromLayout() {
    696             if (mRequestedWidth != -1 || mRequestedHeight != -1) {
    697                 mRequestedWidth = mRequestedHeight = -1;
    698                 requestLayout();
    699             }
    700         }
    701 
    702         public void setFormat(int format) {
    703 
    704             // for backward compatibility reason, OPAQUE always
    705             // means 565 for SurfaceView
    706             if (format == PixelFormat.OPAQUE)
    707                 format = PixelFormat.RGB_565;
    708 
    709             mRequestedFormat = format;
    710             if (mWindow != null) {
    711                 updateWindow(false, false);
    712             }
    713         }
    714 
    715         public void setType(int type) {
    716             switch (type) {
    717             case SURFACE_TYPE_HARDWARE:
    718             case SURFACE_TYPE_GPU:
    719                 // these are deprecated, treat as "NORMAL"
    720                 type = SURFACE_TYPE_NORMAL;
    721                 break;
    722             }
    723             switch (type) {
    724             case SURFACE_TYPE_NORMAL:
    725             case SURFACE_TYPE_PUSH_BUFFERS:
    726                 mRequestedType = type;
    727                 if (mWindow != null) {
    728                     updateWindow(false, false);
    729                 }
    730                 break;
    731             }
    732         }
    733 
    734         public void setKeepScreenOn(boolean screenOn) {
    735             Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
    736             msg.arg1 = screenOn ? 1 : 0;
    737             mHandler.sendMessage(msg);
    738         }
    739 
    740         public Canvas lockCanvas() {
    741             return internalLockCanvas(null);
    742         }
    743 
    744         public Canvas lockCanvas(Rect dirty) {
    745             return internalLockCanvas(dirty);
    746         }
    747 
    748         private final Canvas internalLockCanvas(Rect dirty) {
    749             if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
    750                 throw new BadSurfaceTypeException(
    751                         "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
    752             }
    753             mSurfaceLock.lock();
    754 
    755             if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
    756                     + mDrawingStopped + ", win=" + mWindow);
    757 
    758             Canvas c = null;
    759             if (!mDrawingStopped && mWindow != null) {
    760                 Rect frame = dirty != null ? dirty : mSurfaceFrame;
    761                 try {
    762                     c = mSurface.lockCanvas(frame);
    763                 } catch (Exception e) {
    764                     Log.e(LOG_TAG, "Exception locking surface", e);
    765                 }
    766             }
    767 
    768             if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
    769             if (c != null) {
    770                 mLastLockTime = SystemClock.uptimeMillis();
    771                 return c;
    772             }
    773 
    774             // If the Surface is not ready to be drawn, then return null,
    775             // but throttle calls to this function so it isn't called more
    776             // than every 100ms.
    777             long now = SystemClock.uptimeMillis();
    778             long nextTime = mLastLockTime + 100;
    779             if (nextTime > now) {
    780                 try {
    781                     Thread.sleep(nextTime-now);
    782                 } catch (InterruptedException e) {
    783                 }
    784                 now = SystemClock.uptimeMillis();
    785             }
    786             mLastLockTime = now;
    787             mSurfaceLock.unlock();
    788 
    789             return null;
    790         }
    791 
    792         public void unlockCanvasAndPost(Canvas canvas) {
    793             mSurface.unlockCanvasAndPost(canvas);
    794             mSurfaceLock.unlock();
    795         }
    796 
    797         public Surface getSurface() {
    798             return mSurface;
    799         }
    800 
    801         public Rect getSurfaceFrame() {
    802             return mSurfaceFrame;
    803         }
    804     };
    805 }
    806