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