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