• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.Context;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.graphics.Matrix;
26 import android.graphics.Paint;
27 import android.graphics.RecordingCanvas;
28 import android.graphics.Rect;
29 import android.graphics.SurfaceTexture;
30 import android.graphics.TextureLayer;
31 import android.graphics.drawable.Drawable;
32 import android.os.Build;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 
36 /**
37  * <p>A TextureView can be used to display a content stream, such as that
38  * coming from a camera preview, a video, or an OpenGL scene. The content stream
39  * can come from the application's process as well as a remote process.</p>
40  *
41  * <p>TextureView can only be used in a hardware accelerated window. When
42  * rendered in software, TextureView will draw nothing.</p>
43  *
44  * <p><b>TextureView vs. SurfaceView Capabilities</b></p>
45 
46  * <p>
47  *   <table>
48  *     <tr>
49  *       <th>&nbsp;</th>
50  *       <th style="text-align: center;">TextureView</th>
51  *       <th style="text-align: center;">SurfaceView</th>
52  *     </tr>
53  *     <tr>
54  *       <td>Supports alpha</td>
55  *       <td style="text-align: center;">X</td>
56  *       <td style="text-align: center;">&nbsp;</td>
57  *     </tr>
58  *     <tr>
59  *       <td>Supports rotations</td>
60  *       <td style="text-align: center;">X</td>
61  *       <td style="text-align: center;">&nbsp;</td>
62  *     </tr>
63  *     <tr>
64  *       <td>Supports clipping</td>
65  *       <td style="text-align: center;">X</td>
66  *       <td style="text-align: center;">&nbsp;</td>
67  *     </tr>
68  *     <tr>
69  *       <td>HDR support</td>
70  *       <td style="text-align: center;">Limited (on Android T+)</td>
71  *       <td style="text-align: center;">Full</td>
72  *     </tr>
73  *     <tr>
74  *       <td>Renders DRM content</td>
75  *       <td style="text-align: center;">&nbsp;</td>
76  *       <td style="text-align: center;">X</td>
77  *     </tr>
78  *   </table>
79  * </p>
80  *
81  * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
82  * window but behaves as a regular View. This key difference allows a
83  * TextureView to have translucency, arbitrary rotations, and complex
84  * clipping. For example, you can make a TextureView semi-translucent by
85  * calling <code>myView.setAlpha(0.5f)</code>.</p>
86  *
87  * <p>One implication of this integration of TextureView into the view
88  * hierarchy is that it may have slower performance than
89  * SurfaceView. TextureView contents must be copied, internally, from the
90  * underlying surface into the view displaying those contents. For
91  * that reason, <b>SurfaceView is recommended as a more general solution
92  * to problems requiring rendering to surfaces.</b></p>
93  *
94  * <p>Using a TextureView is simple: all you need to do is get its
95  * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
96  * render content. The following example demonstrates how to render a video
97  * into a TextureView:</p>
98  *
99  * <pre>
100  *  public class MyActivity extends Activity implements TextureView.SurfaceTextureListener {
101  *      private MediaPlayer mMediaPlayer;
102  *      private TextureView mTextureView;
103  *
104  *      protected void onCreate(Bundle savedInstanceState) {
105  *          super.onCreate(savedInstanceState);
106  *
107  *          mMediaPlayer = new MediaPlayer();
108  *
109  *          mTextureView = new TextureView(this);
110  *          mTextureView.setSurfaceTextureListener(this);
111  *          setContentView(mTextureView);
112  *      }
113  *
114  *      public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture,
115  *                                            int width, int height) {
116  *          AssetFileDescriptor fileDescriptor = // get file descriptor
117  *          mMediaPlayer.setDataSource(fileDescriptor);
118  *          mMediaPlayer.setSurface(new Surface(surfaceTexture));
119  *          mMediaPlayer.prepareAsync();
120  *          mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
121  *              &#64;Override
122  *              public void onPrepared(MediaPlayer mp) {
123  *                  mMediaPlayer.start();
124  *              }
125  *          });
126  *         } catch (IOException e) {
127  *             e.printStackTrace();
128  *         }
129  *      }
130  *
131  *     &#64;Override
132  *     public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture,
133  *                                             int width, int height) {
134  *         // Handle size change depending on media needs
135  *     }
136  *
137  *     &#64;Override
138  *     public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
139  *         // Release unneeded resources
140  *         mMediaPlayer.stop();
141  *         mMediaPlayer.release();
142  *         return true;
143  *     }
144  *
145  *     &#64;Override
146  *     public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
147  *          // Invoked every time there's a new video frame
148  *     }
149  *
150  *  }
151  * </pre>
152  *
153  * <p>Similarly, TextureView can supply the surface needed for GL rendering or
154  * camera previews. Camera2 APIs require the surface created by TextureView,
155  * although developers are recommended to use the CameraX APIs instead, for which
156  * PreviewView creates its own TextureView or SurfaceView internally.</p>
157  *
158  * <p>A TextureView's SurfaceTexture can be obtained either by invoking
159  * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
160  * It is important to know that a SurfaceTexture is available only after the
161  * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
162  * been invoked.) It is therefore highly recommended you use a listener to
163  * be notified when the SurfaceTexture becomes available.</p>
164  *
165  * <p>It is important to note that only one producer can use the TextureView.
166  * For instance, if you use a TextureView to display the camera preview, you
167  * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same
168  * time.</p>
169  *
170  * @see SurfaceView
171  * @see SurfaceTexture
172  */
173 public class TextureView extends View {
174     private static final String LOG_TAG = "TextureView";
175 
176     @UnsupportedAppUsage
177     private TextureLayer mLayer;
178     @UnsupportedAppUsage
179     private SurfaceTexture mSurface;
180     private SurfaceTextureListener mListener;
181     private boolean mHadSurface;
182 
183     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
184     private boolean mOpaque = true;
185 
186     private final Matrix mMatrix = new Matrix();
187     private boolean mMatrixChanged;
188 
189     private final Object[] mLock = new Object[0];
190     private boolean mUpdateLayer;
191     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
192     private boolean mUpdateSurface;
193 
194     private Canvas mCanvas;
195     private int mSaveCount;
196 
197     private final Object[] mNativeWindowLock = new Object[0];
198     // Set by native code, do not write!
199     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
200     private long mNativeWindow;
201 
202     /**
203      * Creates a new TextureView.
204      *
205      * @param context The context to associate this view with.
206      */
TextureView(@onNull Context context)207     public TextureView(@NonNull Context context) {
208         super(context);
209     }
210 
211     /**
212      * Creates a new TextureView.
213      *
214      * @param context The context to associate this view with.
215      * @param attrs The attributes of the XML tag that is inflating the view.
216      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs)217     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs) {
218         super(context, attrs);
219     }
220 
221     /**
222      * Creates a new TextureView.
223      *
224      * @param context The context to associate this view with.
225      * @param attrs The attributes of the XML tag that is inflating the view.
226      * @param defStyleAttr An attribute in the current theme that contains a
227      *        reference to a style resource that supplies default values for
228      *        the view. Can be 0 to not look for defaults.
229      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)230     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
231         super(context, attrs, defStyleAttr);
232     }
233 
234     /**
235      * Creates a new TextureView.
236      *
237      * @param context The context to associate this view with.
238      * @param attrs The attributes of the XML tag that is inflating the view.
239      * @param defStyleAttr An attribute in the current theme that contains a
240      *        reference to a style resource that supplies default values for
241      *        the view. Can be 0 to not look for defaults.
242      * @param defStyleRes A resource identifier of a style resource that
243      *        supplies default values for the view, used only if
244      *        defStyleAttr is 0 or can not be found in the theme. Can be 0
245      *        to not look for defaults.
246      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)247     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs,
248             int defStyleAttr, int defStyleRes) {
249         super(context, attrs, defStyleAttr, defStyleRes);
250     }
251 
252     /**
253      * {@inheritDoc}
254      */
255     @Override
isOpaque()256     public boolean isOpaque() {
257         return mOpaque;
258     }
259 
260     /**
261      * Indicates whether the content of this TextureView is opaque. The
262      * content is assumed to be opaque by default.
263      *
264      * @param opaque True if the content of this TextureView is opaque,
265      *               false otherwise
266      */
setOpaque(boolean opaque)267     public void setOpaque(boolean opaque) {
268         if (opaque != mOpaque) {
269             mOpaque = opaque;
270             if (mLayer != null) {
271                 updateLayerAndInvalidate();
272             }
273         }
274     }
275 
276     @Override
onAttachedToWindow()277     protected void onAttachedToWindow() {
278         super.onAttachedToWindow();
279 
280         if (!isHardwareAccelerated()) {
281             Log.w(LOG_TAG, "A TextureView or a subclass can only be "
282                     + "used with hardware acceleration enabled.");
283         }
284 
285         if (mHadSurface) {
286             invalidate(true);
287             mHadSurface = false;
288         }
289     }
290 
291     /** @hide */
292     @Override
293     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onDetachedFromWindowInternal()294     protected void onDetachedFromWindowInternal() {
295         destroyHardwareLayer();
296         releaseSurfaceTexture();
297         super.onDetachedFromWindowInternal();
298     }
299 
300     /**
301      * @hide
302      */
303     @Override
304     @UnsupportedAppUsage
destroyHardwareResources()305     protected void destroyHardwareResources() {
306         super.destroyHardwareResources();
307         destroyHardwareLayer();
308     }
309 
310     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
destroyHardwareLayer()311     private void destroyHardwareLayer() {
312         if (mLayer != null) {
313             mLayer.detachSurfaceTexture();
314             mLayer.close();
315             mLayer = null;
316             mMatrixChanged = true;
317         }
318     }
319 
releaseSurfaceTexture()320     private void releaseSurfaceTexture() {
321         if (mSurface != null) {
322             boolean shouldRelease = true;
323 
324             if (mListener != null) {
325                 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
326             }
327 
328             synchronized (mNativeWindowLock) {
329                 nDestroyNativeWindow();
330             }
331 
332             if (shouldRelease) {
333                 mSurface.release();
334             }
335             mSurface = null;
336             mHadSurface = true;
337         }
338     }
339 
340     /**
341      * The layer type of a TextureView is ignored since a TextureView is always
342      * considered to act as a hardware layer. The optional paint supplied to this
343      * method will however be taken into account when rendering the content of
344      * this TextureView.
345      *
346      * @param layerType The type of layer to use with this view, must be one of
347      *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
348      *        {@link #LAYER_TYPE_HARDWARE}
349      * @param paint The paint used to compose the layer. This argument is optional
350      *        and can be null. It is ignored when the layer type is
351      *        {@link #LAYER_TYPE_NONE}
352      */
353     @Override
setLayerType(int layerType, @Nullable Paint paint)354     public void setLayerType(int layerType, @Nullable Paint paint) {
355         setLayerPaint(paint);
356     }
357 
358     @Override
setLayerPaint(@ullable Paint paint)359     public void setLayerPaint(@Nullable Paint paint) {
360         if (paint != mLayerPaint) {
361             mLayerPaint = paint;
362             invalidate();
363         }
364     }
365 
366     /**
367      * Always returns {@link #LAYER_TYPE_HARDWARE}.
368      */
369     @Override
getLayerType()370     public int getLayerType() {
371         return LAYER_TYPE_HARDWARE;
372     }
373 
374     /**
375      * Calling this method has no effect.
376      */
377     @Override
buildLayer()378     public void buildLayer() {
379     }
380 
381     @Override
setForeground(Drawable foreground)382     public void setForeground(Drawable foreground) {
383         if (foreground != null && !sTextureViewIgnoresDrawableSetters) {
384             throw new UnsupportedOperationException(
385                     "TextureView doesn't support displaying a foreground drawable");
386         }
387     }
388 
389     @Override
setBackgroundDrawable(Drawable background)390     public void setBackgroundDrawable(Drawable background) {
391         if (background != null && !sTextureViewIgnoresDrawableSetters) {
392             throw new UnsupportedOperationException(
393                     "TextureView doesn't support displaying a background drawable");
394         }
395     }
396 
397     /**
398      * Subclasses of TextureView cannot do their own rendering
399      * with the {@link Canvas} object.
400      *
401      * @param canvas The Canvas to which the View is rendered.
402      */
403     @Override
draw(Canvas canvas)404     public final void draw(Canvas canvas) {
405         // NOTE: Maintain this carefully (see View#draw)
406         mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
407 
408         /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background,
409         scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing
410         properties (alpha, layer paint) affect all of the content of a TextureView. */
411 
412         if (canvas.isHardwareAccelerated()) {
413             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
414 
415             TextureLayer layer = getTextureLayer();
416             if (layer != null) {
417                 applyUpdate();
418                 applyTransformMatrix();
419 
420                 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
421                 recordingCanvas.drawTextureLayer(layer);
422             }
423         }
424     }
425 
426     /**
427      * Subclasses of TextureView cannot do their own rendering
428      * with the {@link Canvas} object.
429      *
430      * @param canvas The Canvas to which the View is rendered.
431      */
432     @Override
onDraw(Canvas canvas)433     protected final void onDraw(Canvas canvas) {
434     }
435 
436     @Override
onSizeChanged(int w, int h, int oldw, int oldh)437     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
438         super.onSizeChanged(w, h, oldw, oldh);
439         if (mSurface != null) {
440             mSurface.setDefaultBufferSize(getWidth(), getHeight());
441             updateLayer();
442             if (mListener != null) {
443                 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
444             }
445         }
446     }
447 
getTextureLayer()448     TextureLayer getTextureLayer() {
449         if (mLayer == null) {
450             if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) {
451                 return null;
452             }
453 
454             mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer();
455             boolean createNewSurface = (mSurface == null);
456             if (createNewSurface) {
457                 // Create a new SurfaceTexture for the layer.
458                 mSurface = new SurfaceTexture(false);
459                 nCreateNativeWindow(mSurface);
460             }
461             mLayer.setSurfaceTexture(mSurface);
462             mSurface.setDefaultBufferSize(getWidth(), getHeight());
463             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
464 
465             if (mListener != null && createNewSurface) {
466                 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
467             }
468             mLayer.setLayerPaint(mLayerPaint);
469         }
470 
471         if (mUpdateSurface) {
472             // Someone has requested that we use a specific SurfaceTexture, so
473             // tell mLayer about it and set the SurfaceTexture to use the
474             // current view size.
475             mUpdateSurface = false;
476 
477             // Since we are updating the layer, force an update to ensure its
478             // parameters are correct (width, height, transform, etc.)
479             updateLayer();
480             mMatrixChanged = true;
481 
482             mLayer.setSurfaceTexture(mSurface);
483             mSurface.setDefaultBufferSize(getWidth(), getHeight());
484         }
485 
486         return mLayer;
487     }
488 
489     @Override
onVisibilityChanged(View changedView, int visibility)490     protected void onVisibilityChanged(View changedView, int visibility) {
491         super.onVisibilityChanged(changedView, visibility);
492 
493         if (mSurface != null) {
494             // When the view becomes invisible, stop updating it, it's a waste of CPU
495             // To cancel updates, the easiest thing to do is simply to remove the
496             // updates listener
497             if (visibility == VISIBLE) {
498                 if (mLayer != null) {
499                     mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
500                 }
501                 updateLayerAndInvalidate();
502             } else {
503                 mSurface.setOnFrameAvailableListener(null);
504             }
505         }
506     }
507 
updateLayer()508     private void updateLayer() {
509         synchronized (mLock) {
510             mUpdateLayer = true;
511         }
512     }
513 
updateLayerAndInvalidate()514     private void updateLayerAndInvalidate() {
515         synchronized (mLock) {
516             mUpdateLayer = true;
517         }
518         invalidate();
519     }
520 
applyUpdate()521     private void applyUpdate() {
522         if (mLayer == null) {
523             return;
524         }
525 
526         synchronized (mLock) {
527             if (mUpdateLayer) {
528                 mUpdateLayer = false;
529             } else {
530                 return;
531             }
532         }
533 
534         mLayer.prepare(getWidth(), getHeight(), mOpaque);
535         mLayer.updateSurfaceTexture();
536 
537         if (mListener != null) {
538             mListener.onSurfaceTextureUpdated(mSurface);
539         }
540     }
541 
542     /**
543      * <p>Sets the transform to associate with this texture view.
544      * The specified transform applies to the underlying surface
545      * texture and does not affect the size or position of the view
546      * itself, only of its content.</p>
547      *
548      * <p>Some transforms might prevent the content from drawing
549      * all the pixels contained within this view's bounds. In such
550      * situations, make sure this texture view is not marked opaque.</p>
551      *
552      * @param transform The transform to apply to the content of
553      *        this view. If null the transform will be set to identity.
554      *
555      * @see #getTransform(android.graphics.Matrix)
556      * @see #isOpaque()
557      * @see #setOpaque(boolean)
558      */
setTransform(@ullable Matrix transform)559     public void setTransform(@Nullable Matrix transform) {
560         mMatrix.set(transform);
561         mMatrixChanged = true;
562         invalidateParentIfNeeded();
563     }
564 
565     /**
566      * Returns the transform associated with this texture view.
567      *
568      * @param transform The {@link Matrix} in which to copy the current
569      *        transform. Can be null.
570      *
571      * @return The specified matrix if not null or a new {@link Matrix}
572      *         instance otherwise.
573      *
574      * @see #setTransform(android.graphics.Matrix)
575      */
getTransform(@ullable Matrix transform)576     public @NonNull Matrix getTransform(@Nullable Matrix transform) {
577         if (transform == null) {
578             transform = new Matrix();
579         }
580 
581         transform.set(mMatrix);
582 
583         return transform;
584     }
585 
applyTransformMatrix()586     private void applyTransformMatrix() {
587         if (mMatrixChanged && mLayer != null) {
588             mLayer.setTransform(mMatrix);
589             mMatrixChanged = false;
590         }
591     }
592 
593     /**
594      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
595      * of the associated surface texture. If the surface texture is not available,
596      * this method returns null.</p>
597      *
598      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
599      * pixel format and its dimensions are the same as this view's.</p>
600      *
601      * <p><strong>Do not</strong> invoke this method from a drawing method
602      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
603      *
604      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
605      *
606      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
607      *         texture is not available or the width &lt;= 0 or the height &lt;= 0
608      *
609      * @see #isAvailable()
610      * @see #getBitmap(android.graphics.Bitmap)
611      * @see #getBitmap(int, int)
612      */
getBitmap()613     public @Nullable Bitmap getBitmap() {
614         return getBitmap(getWidth(), getHeight());
615     }
616 
617     /**
618      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
619      * of the associated surface texture. If the surface texture is not available,
620      * this method returns null.</p>
621      *
622      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
623      * pixel format.</p>
624      *
625      * <p><strong>Do not</strong> invoke this method from a drawing method
626      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
627      *
628      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
629      *
630      * @param width The width of the bitmap to create
631      * @param height The height of the bitmap to create
632      *
633      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
634      *         texture is not available or width is &lt;= 0 or height is &lt;= 0
635      *
636      * @see #isAvailable()
637      * @see #getBitmap(android.graphics.Bitmap)
638      * @see #getBitmap()
639      */
getBitmap(int width, int height)640     public @Nullable Bitmap getBitmap(int width, int height) {
641         if (isAvailable() && width > 0 && height > 0) {
642             return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
643                     width, height, Bitmap.Config.ARGB_8888));
644         }
645         return null;
646     }
647 
648     /**
649      * <p>Copies the content of this view's surface texture into the specified
650      * bitmap. If the surface texture is not available, the copy is not executed.
651      * The content of the surface texture will be scaled to fit exactly inside
652      * the specified bitmap.</p>
653      *
654      * <p><strong>Do not</strong> invoke this method from a drawing method
655      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
656      *
657      * <p>If an error occurs, the bitmap is left unchanged.</p>
658      *
659      * @param bitmap The bitmap to copy the content of the surface texture into,
660      *               cannot be null, all configurations are supported
661      *
662      * @return The bitmap specified as a parameter
663      *
664      * @see #isAvailable()
665      * @see #getBitmap(int, int)
666      * @see #getBitmap()
667      *
668      * @throws IllegalStateException if the hardware rendering context cannot be
669      *         acquired to capture the bitmap
670      */
getBitmap(@onNull Bitmap bitmap)671     public @NonNull Bitmap getBitmap(@NonNull Bitmap bitmap) {
672         if (bitmap != null && isAvailable()) {
673             applyUpdate();
674             applyTransformMatrix();
675 
676             // This case can happen if the app invokes setSurfaceTexture() before
677             // we are able to create the hardware layer. We can safely initialize
678             // the layer here thanks to the validate() call at the beginning of
679             // this method
680             if (mLayer == null && mUpdateSurface) {
681                 getTextureLayer();
682             }
683 
684             if (mLayer != null) {
685                 mLayer.copyInto(bitmap);
686             }
687         }
688         return bitmap;
689     }
690 
691     /**
692      * Returns true if the {@link SurfaceTexture} associated with this
693      * TextureView is available for rendering. When this method returns
694      * true, {@link #getSurfaceTexture()} returns a valid surface texture.
695      */
isAvailable()696     public boolean isAvailable() {
697         return mSurface != null;
698     }
699 
700     /**
701      * <p>Start editing the pixels in the surface.  The returned Canvas can be used
702      * to draw into the surface's bitmap.  A null is returned if the surface has
703      * not been created or otherwise cannot be edited. You will usually need
704      * to implement
705      * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
706      * to find out when the Surface is available for use.</p>
707      *
708      * <p>The content of the Surface is never preserved between unlockCanvas()
709      * and lockCanvas(), for this reason, every pixel within the Surface area
710      * must be written. The only exception to this rule is when a dirty
711      * rectangle is specified, in which case, non-dirty pixels will be
712      * preserved.</p>
713      *
714      * <p>This method can only be used if the underlying surface is not already
715      * owned by another producer. For instance, if the TextureView is being used
716      * to render the camera's preview you cannot invoke this method.</p>
717      *
718      * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for
719      * drawing (see {@link #isAvailable()}).
720      *
721      * @see #lockCanvas(android.graphics.Rect)
722      * @see #unlockCanvasAndPost(android.graphics.Canvas)
723      */
lockCanvas()724     public @Nullable Canvas lockCanvas() {
725         return lockCanvas(null);
726     }
727 
728     /**
729      * Just like {@link #lockCanvas()} but allows specification of a dirty
730      * rectangle. Every pixel within that rectangle must be written; however
731      * pixels outside the dirty rectangle will be preserved by the next call
732      * to lockCanvas().
733      *
734      * This method can return null if the underlying surface texture is not
735      * available (see {@link #isAvailable()} or if the surface texture is
736      * already connected to an image producer (for instance: the camera,
737      * OpenGL, a media player, etc.)
738      *
739      * @param dirty Area of the surface that will be modified. If null the area of the entire
740      *              surface is used.
741 
742      * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for
743      * drawing (see {@link #isAvailable()}).
744      *
745      * @see #lockCanvas()
746      * @see #unlockCanvasAndPost(android.graphics.Canvas)
747      * @see #isAvailable()
748      */
lockCanvas(@ullable Rect dirty)749     public @Nullable Canvas lockCanvas(@Nullable Rect dirty) {
750         if (!isAvailable()) return null;
751 
752         if (mCanvas == null) {
753             mCanvas = new Canvas();
754         }
755 
756         synchronized (mNativeWindowLock) {
757             if (!nLockCanvas(mNativeWindow, mCanvas, dirty)) {
758                 return null;
759             }
760         }
761         mSaveCount = mCanvas.save();
762 
763         return mCanvas;
764     }
765 
766     /**
767      * Finish editing pixels in the surface. After this call, the surface's
768      * current pixels will be shown on the screen, but its content is lost,
769      * in particular there is no guarantee that the content of the Surface
770      * will remain unchanged when lockCanvas() is called again.
771      *
772      * @param canvas The Canvas previously returned by lockCanvas()
773      *
774      * @see #lockCanvas()
775      * @see #lockCanvas(android.graphics.Rect)
776      */
unlockCanvasAndPost(@onNull Canvas canvas)777     public void unlockCanvasAndPost(@NonNull Canvas canvas) {
778         if (mCanvas != null && canvas == mCanvas) {
779             canvas.restoreToCount(mSaveCount);
780             mSaveCount = 0;
781 
782             synchronized (mNativeWindowLock) {
783                 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
784             }
785         }
786     }
787 
788     /**
789      * Returns the {@link SurfaceTexture} used by this view. This method
790      * may return null if the view is not attached to a window or if the surface
791      * texture has not been initialized yet.
792      *
793      * @see #isAvailable()
794      */
getSurfaceTexture()795     public @Nullable SurfaceTexture getSurfaceTexture() {
796         return mSurface;
797     }
798 
799     /**
800      * Set the {@link SurfaceTexture} for this view to use. If a {@link
801      * SurfaceTexture} is already being used by this view, it is immediately
802      * released and not usable any more.  The {@link
803      * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
804      * called for the previous {@link SurfaceTexture}.  Similarly, the {@link
805      * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
806      * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
807      *
808      * The {@link SurfaceTexture} object must be detached from all OpenGL ES
809      * contexts prior to calling this method.
810      *
811      * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
812      * @see SurfaceTexture#detachFromGLContext()
813      */
setSurfaceTexture(@onNull SurfaceTexture surfaceTexture)814     public void setSurfaceTexture(@NonNull SurfaceTexture surfaceTexture) {
815         if (surfaceTexture == null) {
816             throw new NullPointerException("surfaceTexture must not be null");
817         }
818         if (surfaceTexture == mSurface) {
819             throw new IllegalArgumentException("Trying to setSurfaceTexture to " +
820                     "the same SurfaceTexture that's already set.");
821         }
822         if (surfaceTexture.isReleased()) {
823             throw new IllegalArgumentException("Cannot setSurfaceTexture to a " +
824                     "released SurfaceTexture");
825         }
826         if (mSurface != null) {
827             nDestroyNativeWindow();
828             mSurface.release();
829         }
830         mSurface = surfaceTexture;
831         nCreateNativeWindow(mSurface);
832 
833         /*
834          * If the view is visible and we already made a layer, update the
835          * listener in the new surface to use the existing listener in the view.
836          * Otherwise this will be called when the view becomes visible or the
837          * layer is created
838          */
839         if (((mViewFlags & VISIBILITY_MASK) == VISIBLE) && mLayer != null) {
840             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
841         }
842         mUpdateSurface = true;
843         invalidateParentIfNeeded();
844     }
845 
846     /**
847      * Returns the {@link SurfaceTextureListener} currently associated with this
848      * texture view.
849      *
850      * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
851      * @see SurfaceTextureListener
852      */
getSurfaceTextureListener()853     public @Nullable SurfaceTextureListener getSurfaceTextureListener() {
854         return mListener;
855     }
856 
857     /**
858      * Sets the {@link SurfaceTextureListener} used to listen to surface
859      * texture events.
860      *
861      * @see #getSurfaceTextureListener()
862      * @see SurfaceTextureListener
863      */
setSurfaceTextureListener(@ullable SurfaceTextureListener listener)864     public void setSurfaceTextureListener(@Nullable SurfaceTextureListener listener) {
865         mListener = listener;
866     }
867 
868     @UnsupportedAppUsage
869     private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
870             surfaceTexture -> {
871                 updateLayer();
872                 invalidate();
873             };
874 
875     /**
876      * This listener can be used to be notified when the surface texture
877      * associated with this texture view is available.
878      */
879     public interface SurfaceTextureListener {
880         /**
881          * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
882          *
883          * @param surface The surface returned by
884          *                {@link android.view.TextureView#getSurfaceTexture()}
885          * @param width The width of the surface
886          * @param height The height of the surface
887          */
onSurfaceTextureAvailable(@onNull SurfaceTexture surface, int width, int height)888         void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height);
889 
890         /**
891          * Invoked when the {@link SurfaceTexture}'s buffers size changed.
892          *
893          * @param surface The surface returned by
894          *                {@link android.view.TextureView#getSurfaceTexture()}
895          * @param width The new width of the surface
896          * @param height The new height of the surface
897          */
onSurfaceTextureSizeChanged(@onNull SurfaceTexture surface, int width, int height)898         void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height);
899 
900         /**
901          * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
902          * If returns true, no rendering should happen inside the surface texture after this method
903          * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
904          * Most applications should return true.
905          *
906          * @param surface The surface about to be destroyed
907          */
onSurfaceTextureDestroyed(@onNull SurfaceTexture surface)908         boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface);
909 
910         /**
911          * Invoked when the specified {@link SurfaceTexture} is updated through
912          * {@link SurfaceTexture#updateTexImage()}.
913          *
914          * @param surface The surface just updated
915          */
onSurfaceTextureUpdated(@onNull SurfaceTexture surface)916         void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface);
917     }
918 
919     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
nCreateNativeWindow(SurfaceTexture surface)920     private native void nCreateNativeWindow(SurfaceTexture surface);
921     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
nDestroyNativeWindow()922     private native void nDestroyNativeWindow();
923 
nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty)924     private static native boolean nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty);
nUnlockCanvasAndPost(long nativeWindow, Canvas canvas)925     private static native void nUnlockCanvasAndPost(long nativeWindow, Canvas canvas);
926 }
927