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