Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2011 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.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Canvas;
     22 import android.graphics.Matrix;
     23 import android.graphics.Paint;
     24 import android.graphics.Rect;
     25 import android.graphics.SurfaceTexture;
     26 import android.os.Looper;
     27 import android.util.AttributeSet;
     28 import android.util.Log;
     29 
     30 /**
     31  * <p>A TextureView can be used to display a content stream. Such a content
     32  * stream can for instance be a video or an OpenGL scene. The content stream
     33  * can come from the application's process as well as a remote process.</p>
     34  *
     35  * <p>TextureView can only be used in a hardware accelerated window. When
     36  * rendered in software, TextureView will draw nothing.</p>
     37  *
     38  * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
     39  * window but behaves as a regular View. This key difference allows a
     40  * TextureView to be moved, transformed, animated, etc. For instance, you
     41  * can make a TextureView semi-translucent by calling
     42  * <code>myView.setAlpha(0.5f)</code>.</p>
     43  *
     44  * <p>Using a TextureView is simple: all you need to do is get its
     45  * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
     46  * render content. The following example demonstrates how to render the
     47  * camera preview into a TextureView:</p>
     48  *
     49  * <pre>
     50  *  public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
     51  *      private Camera mCamera;
     52  *      private TextureView mTextureView;
     53  *
     54  *      protected void onCreate(Bundle savedInstanceState) {
     55  *          super.onCreate(savedInstanceState);
     56  *
     57  *          mTextureView = new TextureView(this);
     58  *          mTextureView.setSurfaceTextureListener(this);
     59  *
     60  *          setContentView(mTextureView);
     61  *      }
     62  *
     63  *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
     64  *          mCamera = Camera.open();
     65  *
     66  *          try {
     67  *              mCamera.setPreviewTexture(surface);
     68  *              mCamera.startPreview();
     69  *          } catch (IOException ioe) {
     70  *              // Something bad happened
     71  *          }
     72  *      }
     73  *
     74  *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
     75  *          // Ignored, Camera does all the work for us
     76  *      }
     77  *
     78  *      public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
     79  *          mCamera.stopPreview();
     80  *          mCamera.release();
     81  *          return true;
     82  *      }
     83  *
     84  *      public void onSurfaceTextureUpdated(SurfaceTexture surface) {
     85  *          // Invoked every time there's a new Camera preview frame
     86  *      }
     87  *  }
     88  * </pre>
     89  *
     90  * <p>A TextureView's SurfaceTexture can be obtained either by invoking
     91  * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
     92  * It is important to know that a SurfaceTexture is available only after the
     93  * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
     94  * been invoked.) It is therefore highly recommended you use a listener to
     95  * be notified when the SurfaceTexture becomes available.</p>
     96  *
     97  * <p>It is important to note that only one producer can use the TextureView.
     98  * For instance, if you use a TextureView to display the camera preview, you
     99  * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same
    100  * time.</p>
    101  *
    102  * @see SurfaceView
    103  * @see SurfaceTexture
    104  */
    105 public class TextureView extends View {
    106     private static final String LOG_TAG = "TextureView";
    107 
    108     private HardwareLayer mLayer;
    109     private SurfaceTexture mSurface;
    110     private SurfaceTextureListener mListener;
    111 
    112     private boolean mOpaque = true;
    113 
    114     private final Matrix mMatrix = new Matrix();
    115     private boolean mMatrixChanged;
    116 
    117     private final Object[] mLock = new Object[0];
    118     private boolean mUpdateLayer;
    119     private boolean mUpdateSurface;
    120 
    121     private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
    122 
    123     private Canvas mCanvas;
    124     private int mSaveCount;
    125 
    126     private final Object[] mNativeWindowLock = new Object[0];
    127     // Used from native code, do not write!
    128     @SuppressWarnings({"UnusedDeclaration"})
    129     private int mNativeWindow;
    130 
    131     /**
    132      * Creates a new TextureView.
    133      *
    134      * @param context The context to associate this view with.
    135      */
    136     public TextureView(Context context) {
    137         super(context);
    138         init();
    139     }
    140 
    141     /**
    142      * Creates a new TextureView.
    143      *
    144      * @param context The context to associate this view with.
    145      * @param attrs The attributes of the XML tag that is inflating the view.
    146      */
    147     @SuppressWarnings({"UnusedDeclaration"})
    148     public TextureView(Context context, AttributeSet attrs) {
    149         super(context, attrs);
    150         init();
    151     }
    152 
    153     /**
    154      * Creates a new TextureView.
    155      *
    156      * @param context The context to associate this view with.
    157      * @param attrs The attributes of the XML tag that is inflating the view.
    158      * @param defStyle The default style to apply to this view. If 0, no style
    159      *        will be applied (beyond what is included in the theme). This may
    160      *        either be an attribute resource, whose value will be retrieved
    161      *        from the current theme, or an explicit style resource.
    162      */
    163     @SuppressWarnings({"UnusedDeclaration"})
    164     public TextureView(Context context, AttributeSet attrs, int defStyle) {
    165         super(context, attrs, defStyle);
    166         init();
    167     }
    168 
    169     private void init() {
    170         mLayerPaint = new Paint();
    171     }
    172 
    173     /**
    174      * {@inheritDoc}
    175      */
    176     @Override
    177     public boolean isOpaque() {
    178         return mOpaque;
    179     }
    180 
    181     /**
    182      * Indicates whether the content of this TextureView is opaque. The
    183      * content is assumed to be opaque by default.
    184      *
    185      * @param opaque True if the content of this TextureView is opaque,
    186      *               false otherwise
    187      */
    188     public void setOpaque(boolean opaque) {
    189         if (opaque != mOpaque) {
    190             mOpaque = opaque;
    191             if (mLayer != null) {
    192                 updateLayerAndInvalidate();
    193             }
    194         }
    195     }
    196 
    197     @Override
    198     protected void onAttachedToWindow() {
    199         super.onAttachedToWindow();
    200 
    201         if (!isHardwareAccelerated()) {
    202             Log.w(LOG_TAG, "A TextureView or a subclass can only be "
    203                     + "used with hardware acceleration enabled.");
    204         }
    205     }
    206 
    207     @Override
    208     protected void onDetachedFromWindow() {
    209         super.onDetachedFromWindow();
    210         if (mLayer != null && mAttachInfo != null && mAttachInfo.mHardwareRenderer != null) {
    211             boolean success = mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
    212                 @Override
    213                 public void run() {
    214                     destroySurface();
    215                 }
    216             });
    217 
    218             if (!success) {
    219                 Log.w(LOG_TAG, "TextureView was not able to destroy its surface: " + this);
    220             }
    221         }
    222     }
    223 
    224     private void destroySurface() {
    225         if (mLayer != null) {
    226             mSurface.detachFromGLContext();
    227             // SurfaceTexture owns the texture name and detachFromGLContext
    228             // should have deleted it
    229             mLayer.clearStorage();
    230 
    231             boolean shouldRelease = true;
    232             if (mListener != null) {
    233                 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
    234             }
    235 
    236             synchronized (mNativeWindowLock) {
    237                 nDestroyNativeWindow();
    238             }
    239 
    240             mLayer.destroy();
    241             if (shouldRelease) mSurface.release();
    242             mSurface = null;
    243             mLayer = null;
    244         }
    245     }
    246 
    247     /**
    248      * The layer type of a TextureView is ignored since a TextureView is always
    249      * considered to act as a hardware layer. The optional paint supplied to this
    250      * method will however be taken into account when rendering the content of
    251      * this TextureView.
    252      *
    253      * @param layerType The ype of layer to use with this view, must be one of
    254      *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
    255      *        {@link #LAYER_TYPE_HARDWARE}
    256      * @param paint The paint used to compose the layer. This argument is optional
    257      *        and can be null. It is ignored when the layer type is
    258      *        {@link #LAYER_TYPE_NONE}
    259      */
    260     @Override
    261     public void setLayerType(int layerType, Paint paint) {
    262         if (paint != mLayerPaint) {
    263             mLayerPaint = paint;
    264             invalidate();
    265         }
    266     }
    267 
    268     /**
    269      * Always returns {@link #LAYER_TYPE_HARDWARE}.
    270      */
    271     @Override
    272     public int getLayerType() {
    273         return LAYER_TYPE_HARDWARE;
    274     }
    275 
    276     @Override
    277     boolean hasStaticLayer() {
    278         return true;
    279     }
    280 
    281     /**
    282      * Calling this method has no effect.
    283      */
    284     @Override
    285     public void buildLayer() {
    286     }
    287 
    288     /**
    289      * Subclasses of TextureView cannot do their own rendering
    290      * with the {@link Canvas} object.
    291      *
    292      * @param canvas The Canvas to which the View is rendered.
    293      */
    294     @Override
    295     public final void draw(Canvas canvas) {
    296         // NOTE: Maintain this carefully (see View.java)
    297         mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
    298 
    299         applyUpdate();
    300         applyTransformMatrix();
    301     }
    302 
    303     /**
    304      * Subclasses of TextureView cannot do their own rendering
    305      * with the {@link Canvas} object.
    306      *
    307      * @param canvas The Canvas to which the View is rendered.
    308      */
    309     @Override
    310     protected final void onDraw(Canvas canvas) {
    311     }
    312 
    313     @Override
    314     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    315         super.onSizeChanged(w, h, oldw, oldh);
    316         if (mSurface != null) {
    317             nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
    318             updateLayer();
    319             if (mListener != null) {
    320                 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
    321             }
    322         }
    323     }
    324 
    325     @Override
    326     boolean destroyLayer(boolean valid) {
    327         return false;
    328     }
    329 
    330     /**
    331      * @hide
    332      */
    333     @Override
    334     protected void destroyHardwareResources() {
    335         super.destroyHardwareResources();
    336         destroySurface();
    337         invalidateParentCaches();
    338         invalidate(true);
    339     }
    340 
    341     @Override
    342     HardwareLayer getHardwareLayer() {
    343         // NOTE: Maintain these two lines very carefully (see View.java)
    344         mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
    345         mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    346 
    347         if (mLayer == null) {
    348             if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
    349                 return null;
    350             }
    351 
    352             mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
    353             if (!mUpdateSurface) {
    354                 // Create a new SurfaceTexture for the layer.
    355                 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
    356             }
    357             nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
    358             nCreateNativeWindow(mSurface);
    359 
    360             mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
    361                 @Override
    362                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
    363                     // Per SurfaceTexture's documentation, the callback may be invoked
    364                     // from an arbitrary thread
    365                     updateLayer();
    366 
    367                     if (Looper.myLooper() == Looper.getMainLooper()) {
    368                         invalidate();
    369                     } else {
    370                         postInvalidate();
    371                     }
    372                 }
    373             };
    374             mSurface.setOnFrameAvailableListener(mUpdateListener);
    375 
    376             if (mListener != null && !mUpdateSurface) {
    377                 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
    378             }
    379             mLayer.setLayerPaint(mLayerPaint);
    380         }
    381 
    382         if (mUpdateSurface) {
    383             // Someone has requested that we use a specific SurfaceTexture, so
    384             // tell mLayer about it and set the SurfaceTexture to use the
    385             // current view size.
    386             mUpdateSurface = false;
    387 
    388             // Since we are updating the layer, force an update to ensure its
    389             // parameters are correct (width, height, transform, etc.)
    390             updateLayer();
    391             mMatrixChanged = true;
    392 
    393             mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
    394             nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
    395         }
    396 
    397         applyUpdate();
    398         applyTransformMatrix();
    399 
    400         return mLayer;
    401     }
    402 
    403     @Override
    404     protected void onVisibilityChanged(View changedView, int visibility) {
    405         super.onVisibilityChanged(changedView, visibility);
    406 
    407         if (mSurface != null) {
    408             // When the view becomes invisible, stop updating it, it's a waste of CPU
    409             // To cancel updates, the easiest thing to do is simply to remove the
    410             // updates listener
    411             if (visibility == VISIBLE) {
    412                 mSurface.setOnFrameAvailableListener(mUpdateListener);
    413                 updateLayerAndInvalidate();
    414             } else {
    415                 mSurface.setOnFrameAvailableListener(null);
    416             }
    417         }
    418     }
    419 
    420     private void updateLayer() {
    421         synchronized (mLock) {
    422             mUpdateLayer = true;
    423         }
    424     }
    425 
    426     private void updateLayerAndInvalidate() {
    427         synchronized (mLock) {
    428             mUpdateLayer = true;
    429         }
    430         invalidate();
    431     }
    432 
    433     private void applyUpdate() {
    434         if (mLayer == null) {
    435             return;
    436         }
    437 
    438         synchronized (mLock) {
    439             if (mUpdateLayer) {
    440                 mUpdateLayer = false;
    441             } else {
    442                 return;
    443             }
    444         }
    445 
    446         mLayer.update(getWidth(), getHeight(), mOpaque);
    447 
    448         if (mListener != null) {
    449             mListener.onSurfaceTextureUpdated(mSurface);
    450         }
    451     }
    452 
    453     /**
    454      * <p>Sets the transform to associate with this texture view.
    455      * The specified transform applies to the underlying surface
    456      * texture and does not affect the size or position of the view
    457      * itself, only of its content.</p>
    458      *
    459      * <p>Some transforms might prevent the content from drawing
    460      * all the pixels contained within this view's bounds. In such
    461      * situations, make sure this texture view is not marked opaque.</p>
    462      *
    463      * @param transform The transform to apply to the content of
    464      *        this view.
    465      *
    466      * @see #getTransform(android.graphics.Matrix)
    467      * @see #isOpaque()
    468      * @see #setOpaque(boolean)
    469      */
    470     public void setTransform(Matrix transform) {
    471         mMatrix.set(transform);
    472         mMatrixChanged = true;
    473         invalidateParentIfNeeded();
    474     }
    475 
    476     /**
    477      * Returns the transform associated with this texture view.
    478      *
    479      * @param transform The {@link Matrix} in which to copy the current
    480      *        transform. Can be null.
    481      *
    482      * @return The specified matrix if not null or a new {@link Matrix}
    483      *         instance otherwise.
    484      *
    485      * @see #setTransform(android.graphics.Matrix)
    486      */
    487     public Matrix getTransform(Matrix transform) {
    488         if (transform == null) {
    489             transform = new Matrix();
    490         }
    491 
    492         transform.set(mMatrix);
    493 
    494         return transform;
    495     }
    496 
    497     private void applyTransformMatrix() {
    498         if (mMatrixChanged && mLayer != null) {
    499             mLayer.setTransform(mMatrix);
    500             mMatrixChanged = false;
    501         }
    502     }
    503 
    504     /**
    505      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
    506      * of the associated surface texture. If the surface texture is not available,
    507      * this method returns null.</p>
    508      *
    509      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
    510      * pixel format and its dimensions are the same as this view's.</p>
    511      *
    512      * <p><strong>Do not</strong> invoke this method from a drawing method
    513      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
    514      *
    515      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
    516      *
    517      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
    518      *         texture is not available or the width &lt;= 0 or the height &lt;= 0
    519      *
    520      * @see #isAvailable()
    521      * @see #getBitmap(android.graphics.Bitmap)
    522      * @see #getBitmap(int, int)
    523      */
    524     public Bitmap getBitmap() {
    525         return getBitmap(getWidth(), getHeight());
    526     }
    527 
    528     /**
    529      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
    530      * of the associated surface texture. If the surface texture is not available,
    531      * this method returns null.</p>
    532      *
    533      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
    534      * pixel format.</p>
    535      *
    536      * <p><strong>Do not</strong> invoke this method from a drawing method
    537      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
    538      *
    539      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
    540      *
    541      * @param width The width of the bitmap to create
    542      * @param height The height of the bitmap to create
    543      *
    544      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
    545      *         texture is not available or width is &lt;= 0 or height is &lt;= 0
    546      *
    547      * @see #isAvailable()
    548      * @see #getBitmap(android.graphics.Bitmap)
    549      * @see #getBitmap()
    550      */
    551     public Bitmap getBitmap(int width, int height) {
    552         if (isAvailable() && width > 0 && height > 0) {
    553             return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
    554                     width, height, Bitmap.Config.ARGB_8888));
    555         }
    556         return null;
    557     }
    558 
    559     /**
    560      * <p>Copies the content of this view's surface texture into the specified
    561      * bitmap. If the surface texture is not available, the copy is not executed.
    562      * The content of the surface texture will be scaled to fit exactly inside
    563      * the specified bitmap.</p>
    564      *
    565      * <p><strong>Do not</strong> invoke this method from a drawing method
    566      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
    567      *
    568      * <p>If an error occurs, the bitmap is left unchanged.</p>
    569      *
    570      * @param bitmap The bitmap to copy the content of the surface texture into,
    571      *               cannot be null, all configurations are supported
    572      *
    573      * @return The bitmap specified as a parameter
    574      *
    575      * @see #isAvailable()
    576      * @see #getBitmap(int, int)
    577      * @see #getBitmap()
    578      *
    579      * @throws IllegalStateException if the hardware rendering context cannot be
    580      *         acquired to capture the bitmap
    581      */
    582     public Bitmap getBitmap(Bitmap bitmap) {
    583         if (bitmap != null && isAvailable()) {
    584             AttachInfo info = mAttachInfo;
    585             if (info != null && info.mHardwareRenderer != null &&
    586                     info.mHardwareRenderer.isEnabled()) {
    587                 if (!info.mHardwareRenderer.validate()) {
    588                     throw new IllegalStateException("Could not acquire hardware rendering context");
    589                 }
    590             }
    591 
    592             applyUpdate();
    593             applyTransformMatrix();
    594 
    595             // This case can happen if the app invokes setSurfaceTexture() before
    596             // we are able to create the hardware layer. We can safely initialize
    597             // the layer here thanks to the validate() call at the beginning of
    598             // this method
    599             if (mLayer == null && mUpdateSurface) {
    600                 getHardwareLayer();
    601             }
    602 
    603             if (mLayer != null) {
    604                 mLayer.copyInto(bitmap);
    605             }
    606         }
    607         return bitmap;
    608     }
    609 
    610     /**
    611      * Returns true if the {@link SurfaceTexture} associated with this
    612      * TextureView is available for rendering. When this method returns
    613      * true, {@link #getSurfaceTexture()} returns a valid surface texture.
    614      */
    615     public boolean isAvailable() {
    616         return mSurface != null;
    617     }
    618 
    619     /**
    620      * <p>Start editing the pixels in the surface.  The returned Canvas can be used
    621      * to draw into the surface's bitmap.  A null is returned if the surface has
    622      * not been created or otherwise cannot be edited. You will usually need
    623      * to implement
    624      * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
    625      * to find out when the Surface is available for use.</p>
    626      *
    627      * <p>The content of the Surface is never preserved between unlockCanvas()
    628      * and lockCanvas(), for this reason, every pixel within the Surface area
    629      * must be written. The only exception to this rule is when a dirty
    630      * rectangle is specified, in which case, non-dirty pixels will be
    631      * preserved.</p>
    632      *
    633      * <p>This method can only be used if the underlying surface is not already
    634      * owned by another producer. For instance, if the TextureView is being used
    635      * to render the camera's preview you cannot invoke this method.</p>
    636      *
    637      * @return A Canvas used to draw into the surface.
    638      *
    639      * @see #lockCanvas(android.graphics.Rect)
    640      * @see #unlockCanvasAndPost(android.graphics.Canvas)
    641      */
    642     public Canvas lockCanvas() {
    643         return lockCanvas(null);
    644     }
    645 
    646     /**
    647      * Just like {@link #lockCanvas()} but allows specification of a dirty
    648      * rectangle. Every pixel within that rectangle must be written; however
    649      * pixels outside the dirty rectangle will be preserved by the next call
    650      * to lockCanvas().
    651      *
    652      * @param dirty Area of the surface that will be modified.
    653 
    654      * @return A Canvas used to draw into the surface.
    655      *
    656      * @see #lockCanvas()
    657      * @see #unlockCanvasAndPost(android.graphics.Canvas)
    658      */
    659     public Canvas lockCanvas(Rect dirty) {
    660         if (!isAvailable()) return null;
    661 
    662         if (mCanvas == null) {
    663             mCanvas = new Canvas();
    664         }
    665 
    666         synchronized (mNativeWindowLock) {
    667             nLockCanvas(mNativeWindow, mCanvas, dirty);
    668         }
    669         mSaveCount = mCanvas.save();
    670 
    671         return mCanvas;
    672     }
    673 
    674     /**
    675      * Finish editing pixels in the surface. After this call, the surface's
    676      * current pixels will be shown on the screen, but its content is lost,
    677      * in particular there is no guarantee that the content of the Surface
    678      * will remain unchanged when lockCanvas() is called again.
    679      *
    680      * @param canvas The Canvas previously returned by lockCanvas()
    681      *
    682      * @see #lockCanvas()
    683      * @see #lockCanvas(android.graphics.Rect)
    684      */
    685     public void unlockCanvasAndPost(Canvas canvas) {
    686         if (mCanvas != null && canvas == mCanvas) {
    687             canvas.restoreToCount(mSaveCount);
    688             mSaveCount = 0;
    689 
    690             synchronized (mNativeWindowLock) {
    691                 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
    692             }
    693         }
    694     }
    695 
    696     /**
    697      * Returns the {@link SurfaceTexture} used by this view. This method
    698      * may return null if the view is not attached to a window or if the surface
    699      * texture has not been initialized yet.
    700      *
    701      * @see #isAvailable()
    702      */
    703     public SurfaceTexture getSurfaceTexture() {
    704         return mSurface;
    705     }
    706 
    707     /**
    708      * Set the {@link SurfaceTexture} for this view to use. If a {@link
    709      * SurfaceTexture} is already being used by this view, it is immediately
    710      * released and not be usable any more.  The {@link
    711      * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
    712      * called for the previous {@link SurfaceTexture}.  Similarly, the {@link
    713      * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
    714      * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
    715      *
    716      * The {@link SurfaceTexture} object must be detached from all OpenGL ES
    717      * contexts prior to calling this method.
    718      *
    719      * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
    720      * @see SurfaceTexture#detachFromGLContext()
    721      */
    722     public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
    723         if (surfaceTexture == null) {
    724             throw new NullPointerException("surfaceTexture must not be null");
    725         }
    726         if (mSurface != null) {
    727             mSurface.release();
    728         }
    729         mSurface = surfaceTexture;
    730         mUpdateSurface = true;
    731         invalidateParentIfNeeded();
    732     }
    733 
    734     /**
    735      * Returns the {@link SurfaceTextureListener} currently associated with this
    736      * texture view.
    737      *
    738      * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
    739      * @see SurfaceTextureListener
    740      */
    741     public SurfaceTextureListener getSurfaceTextureListener() {
    742         return mListener;
    743     }
    744 
    745     /**
    746      * Sets the {@link SurfaceTextureListener} used to listen to surface
    747      * texture events.
    748      *
    749      * @see #getSurfaceTextureListener()
    750      * @see SurfaceTextureListener
    751      */
    752     public void setSurfaceTextureListener(SurfaceTextureListener listener) {
    753         mListener = listener;
    754     }
    755 
    756     /**
    757      * This listener can be used to be notified when the surface texture
    758      * associated with this texture view is available.
    759      */
    760     public static interface SurfaceTextureListener {
    761         /**
    762          * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
    763          *
    764          * @param surface The surface returned by
    765          *                {@link android.view.TextureView#getSurfaceTexture()}
    766          * @param width The width of the surface
    767          * @param height The height of the surface
    768          */
    769         public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
    770 
    771         /**
    772          * Invoked when the {@link SurfaceTexture}'s buffers size changed.
    773          *
    774          * @param surface The surface returned by
    775          *                {@link android.view.TextureView#getSurfaceTexture()}
    776          * @param width The new width of the surface
    777          * @param height The new height of the surface
    778          */
    779         public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
    780 
    781         /**
    782          * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
    783          * If returns true, no rendering should happen inside the surface texture after this method
    784          * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
    785          * Most applications should return true.
    786          *
    787          * @param surface The surface about to be destroyed
    788          */
    789         public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
    790 
    791         /**
    792          * Invoked when the specified {@link SurfaceTexture} is updated through
    793          * {@link SurfaceTexture#updateTexImage()}.
    794          *
    795          * @param surface The surface just updated
    796          */
    797         public void onSurfaceTextureUpdated(SurfaceTexture surface);
    798     }
    799 
    800     private native void nCreateNativeWindow(SurfaceTexture surface);
    801     private native void nDestroyNativeWindow();
    802 
    803     private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
    804             int width, int height);
    805 
    806     private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
    807     private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
    808 }
    809