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         mRenderNode.setIsTextureView();
210     }
211 
212     /**
213      * Creates a new TextureView.
214      *
215      * @param context The context to associate this view with.
216      * @param attrs The attributes of the XML tag that is inflating the view.
217      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs)218     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs) {
219         super(context, attrs);
220         mRenderNode.setIsTextureView();
221     }
222 
223     /**
224      * Creates a new TextureView.
225      *
226      * @param context The context to associate this view with.
227      * @param attrs The attributes of the XML tag that is inflating the view.
228      * @param defStyleAttr An attribute in the current theme that contains a
229      *        reference to a style resource that supplies default values for
230      *        the view. Can be 0 to not look for defaults.
231      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)232     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
233         super(context, attrs, defStyleAttr);
234         mRenderNode.setIsTextureView();
235     }
236 
237     /**
238      * Creates a new TextureView.
239      *
240      * @param context The context to associate this view with.
241      * @param attrs The attributes of the XML tag that is inflating the view.
242      * @param defStyleAttr An attribute in the current theme that contains a
243      *        reference to a style resource that supplies default values for
244      *        the view. Can be 0 to not look for defaults.
245      * @param defStyleRes A resource identifier of a style resource that
246      *        supplies default values for the view, used only if
247      *        defStyleAttr is 0 or can not be found in the theme. Can be 0
248      *        to not look for defaults.
249      */
TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)250     public TextureView(@NonNull Context context, @Nullable AttributeSet attrs,
251             int defStyleAttr, int defStyleRes) {
252         super(context, attrs, defStyleAttr, defStyleRes);
253         mRenderNode.setIsTextureView();
254     }
255 
256     /**
257      * {@inheritDoc}
258      */
259     @Override
isOpaque()260     public boolean isOpaque() {
261         return mOpaque;
262     }
263 
264     /**
265      * Indicates whether the content of this TextureView is opaque. The
266      * content is assumed to be opaque by default.
267      *
268      * @param opaque True if the content of this TextureView is opaque,
269      *               false otherwise
270      */
setOpaque(boolean opaque)271     public void setOpaque(boolean opaque) {
272         if (opaque != mOpaque) {
273             mOpaque = opaque;
274             if (mLayer != null) {
275                 updateLayerAndInvalidate();
276             }
277         }
278     }
279 
280     @Override
onAttachedToWindow()281     protected void onAttachedToWindow() {
282         super.onAttachedToWindow();
283 
284         if (!isHardwareAccelerated()) {
285             Log.w(LOG_TAG, "A TextureView or a subclass can only be "
286                     + "used with hardware acceleration enabled.");
287         }
288 
289         if (mHadSurface) {
290             invalidate(true);
291             mHadSurface = false;
292         }
293     }
294 
295     /** @hide */
296     @Override
297     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onDetachedFromWindowInternal()298     protected void onDetachedFromWindowInternal() {
299         destroyHardwareLayer();
300         releaseSurfaceTexture();
301         super.onDetachedFromWindowInternal();
302     }
303 
304     /**
305      * @hide
306      */
307     @Override
308     @UnsupportedAppUsage
destroyHardwareResources()309     protected void destroyHardwareResources() {
310         super.destroyHardwareResources();
311         destroyHardwareLayer();
312     }
313 
314     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
destroyHardwareLayer()315     private void destroyHardwareLayer() {
316         if (mLayer != null) {
317             mLayer.detachSurfaceTexture();
318             mLayer.close();
319             mLayer = null;
320             mMatrixChanged = true;
321         }
322     }
323 
releaseSurfaceTexture()324     private void releaseSurfaceTexture() {
325         if (mSurface != null) {
326             boolean shouldRelease = true;
327 
328             if (mListener != null) {
329                 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
330             }
331 
332             synchronized (mNativeWindowLock) {
333                 nDestroyNativeWindow();
334             }
335 
336             if (shouldRelease) {
337                 mSurface.release();
338             }
339             mSurface = null;
340             mHadSurface = true;
341         }
342     }
343 
344     /**
345      * The layer type of a TextureView is ignored since a TextureView is always
346      * considered to act as a hardware layer. The optional paint supplied to this
347      * method will however be taken into account when rendering the content of
348      * this TextureView.
349      *
350      * @param layerType The type of layer to use with this view, must be one of
351      *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
352      *        {@link #LAYER_TYPE_HARDWARE}
353      * @param paint The paint used to compose the layer. This argument is optional
354      *        and can be null. It is ignored when the layer type is
355      *        {@link #LAYER_TYPE_NONE}
356      */
357     @Override
setLayerType(int layerType, @Nullable Paint paint)358     public void setLayerType(int layerType, @Nullable Paint paint) {
359         setLayerPaint(paint);
360     }
361 
362     @Override
setLayerPaint(@ullable Paint paint)363     public void setLayerPaint(@Nullable Paint paint) {
364         if (paint != mLayerPaint) {
365             mLayerPaint = paint;
366             invalidate();
367         }
368     }
369 
370     /**
371      * Always returns {@link #LAYER_TYPE_HARDWARE}.
372      */
373     @Override
getLayerType()374     public int getLayerType() {
375         return LAYER_TYPE_HARDWARE;
376     }
377 
378     /**
379      * Calling this method has no effect.
380      */
381     @Override
buildLayer()382     public void buildLayer() {
383     }
384 
385     @Override
setForeground(Drawable foreground)386     public void setForeground(Drawable foreground) {
387         if (foreground != null && !sTextureViewIgnoresDrawableSetters) {
388             throw new UnsupportedOperationException(
389                     "TextureView doesn't support displaying a foreground drawable");
390         }
391     }
392 
393     @Override
setBackgroundDrawable(Drawable background)394     public void setBackgroundDrawable(Drawable background) {
395         if (background != null && !sTextureViewIgnoresDrawableSetters) {
396             throw new UnsupportedOperationException(
397                     "TextureView doesn't support displaying a background drawable");
398         }
399     }
400 
401     /**
402      * Subclasses of TextureView cannot do their own rendering
403      * with the {@link Canvas} object.
404      *
405      * @param canvas The Canvas to which the View is rendered.
406      */
407     @Override
draw(Canvas canvas)408     public final void draw(Canvas canvas) {
409         // NOTE: Maintain this carefully (see View#draw)
410         mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
411 
412         /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background,
413         scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing
414         properties (alpha, layer paint) affect all of the content of a TextureView. */
415 
416         if (canvas.isHardwareAccelerated()) {
417             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
418 
419             TextureLayer layer = getTextureLayer();
420             if (layer != null) {
421                 applyUpdate();
422                 applyTransformMatrix();
423 
424                 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
425                 recordingCanvas.drawTextureLayer(layer);
426             }
427         }
428     }
429 
430     /**
431      * Subclasses of TextureView cannot do their own rendering
432      * with the {@link Canvas} object.
433      *
434      * @param canvas The Canvas to which the View is rendered.
435      */
436     @Override
onDraw(Canvas canvas)437     protected final void onDraw(Canvas canvas) {
438     }
439 
440     @Override
onSizeChanged(int w, int h, int oldw, int oldh)441     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
442         super.onSizeChanged(w, h, oldw, oldh);
443         if (mSurface != null) {
444             mSurface.setDefaultBufferSize(getWidth(), getHeight());
445             updateLayer();
446             if (mListener != null) {
447                 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
448             }
449         }
450     }
451 
getTextureLayer()452     TextureLayer getTextureLayer() {
453         if (mLayer == null) {
454             if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) {
455                 return null;
456             }
457 
458             mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer();
459             boolean createNewSurface = (mSurface == null);
460             if (createNewSurface) {
461                 // Create a new SurfaceTexture for the layer.
462                 mSurface = new SurfaceTexture(false);
463                 nCreateNativeWindow(mSurface);
464             }
465             mLayer.setSurfaceTexture(mSurface);
466             mSurface.setDefaultBufferSize(getWidth(), getHeight());
467             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
468 
469             if (mListener != null && createNewSurface) {
470                 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
471             }
472             mLayer.setLayerPaint(mLayerPaint);
473         }
474 
475         if (mUpdateSurface) {
476             // Someone has requested that we use a specific SurfaceTexture, so
477             // tell mLayer about it and set the SurfaceTexture to use the
478             // current view size.
479             mUpdateSurface = false;
480 
481             // Since we are updating the layer, force an update to ensure its
482             // parameters are correct (width, height, transform, etc.)
483             updateLayer();
484             mMatrixChanged = true;
485 
486             mLayer.setSurfaceTexture(mSurface);
487             mSurface.setDefaultBufferSize(getWidth(), getHeight());
488         }
489 
490         return mLayer;
491     }
492 
493     @Override
onVisibilityChanged(View changedView, int visibility)494     protected void onVisibilityChanged(View changedView, int visibility) {
495         super.onVisibilityChanged(changedView, visibility);
496 
497         if (mSurface != null) {
498             // When the view becomes invisible, stop updating it, it's a waste of CPU
499             // To cancel updates, the easiest thing to do is simply to remove the
500             // updates listener
501             if (visibility == VISIBLE) {
502                 if (mLayer != null) {
503                     mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
504                 }
505                 updateLayerAndInvalidate();
506             } else {
507                 mSurface.setOnFrameAvailableListener(null);
508             }
509         }
510     }
511 
updateLayer()512     private void updateLayer() {
513         synchronized (mLock) {
514             mUpdateLayer = true;
515         }
516     }
517 
updateLayerAndInvalidate()518     private void updateLayerAndInvalidate() {
519         synchronized (mLock) {
520             mUpdateLayer = true;
521         }
522         invalidate();
523     }
524 
applyUpdate()525     private void applyUpdate() {
526         if (mLayer == null) {
527             return;
528         }
529 
530         synchronized (mLock) {
531             if (mUpdateLayer) {
532                 mUpdateLayer = false;
533             } else {
534                 return;
535             }
536         }
537 
538         mLayer.prepare(getWidth(), getHeight(), mOpaque);
539         mLayer.updateSurfaceTexture();
540 
541         if (mListener != null) {
542             mListener.onSurfaceTextureUpdated(mSurface);
543         }
544     }
545 
546     /**
547      * <p>Sets the transform to associate with this texture view.
548      * The specified transform applies to the underlying surface
549      * texture and does not affect the size or position of the view
550      * itself, only of its content.</p>
551      *
552      * <p>Some transforms might prevent the content from drawing
553      * all the pixels contained within this view's bounds. In such
554      * situations, make sure this texture view is not marked opaque.</p>
555      *
556      * @param transform The transform to apply to the content of
557      *        this view. If null the transform will be set to identity.
558      *
559      * @see #getTransform(android.graphics.Matrix)
560      * @see #isOpaque()
561      * @see #setOpaque(boolean)
562      */
setTransform(@ullable Matrix transform)563     public void setTransform(@Nullable Matrix transform) {
564         mMatrix.set(transform);
565         mMatrixChanged = true;
566         invalidateParentIfNeeded();
567     }
568 
569     /**
570      * Returns the transform associated with this texture view.
571      *
572      * @param transform The {@link Matrix} in which to copy the current
573      *        transform. Can be null.
574      *
575      * @return The specified matrix if not null or a new {@link Matrix}
576      *         instance otherwise.
577      *
578      * @see #setTransform(android.graphics.Matrix)
579      */
getTransform(@ullable Matrix transform)580     public @NonNull Matrix getTransform(@Nullable Matrix transform) {
581         if (transform == null) {
582             transform = new Matrix();
583         }
584 
585         transform.set(mMatrix);
586 
587         return transform;
588     }
589 
applyTransformMatrix()590     private void applyTransformMatrix() {
591         if (mMatrixChanged && mLayer != null) {
592             mLayer.setTransform(mMatrix);
593             mMatrixChanged = false;
594         }
595     }
596 
597     /**
598      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
599      * of the associated surface texture. If the surface texture is not available,
600      * this method returns null.</p>
601      *
602      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
603      * pixel format and its dimensions are the same as this view's.</p>
604      *
605      * <p><strong>Do not</strong> invoke this method from a drawing method
606      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
607      *
608      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
609      *
610      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
611      *         texture is not available or the width &lt;= 0 or the height &lt;= 0
612      *
613      * @see #isAvailable()
614      * @see #getBitmap(android.graphics.Bitmap)
615      * @see #getBitmap(int, int)
616      */
getBitmap()617     public @Nullable Bitmap getBitmap() {
618         return getBitmap(getWidth(), getHeight());
619     }
620 
621     /**
622      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
623      * of the associated surface texture. If the surface texture is not available,
624      * this method returns null.</p>
625      *
626      * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
627      * pixel format.</p>
628      *
629      * <p><strong>Do not</strong> invoke this method from a drawing method
630      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
631      *
632      * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
633      *
634      * @param width The width of the bitmap to create
635      * @param height The height of the bitmap to create
636      *
637      * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
638      *         texture is not available or width is &lt;= 0 or height is &lt;= 0
639      *
640      * @see #isAvailable()
641      * @see #getBitmap(android.graphics.Bitmap)
642      * @see #getBitmap()
643      */
getBitmap(int width, int height)644     public @Nullable Bitmap getBitmap(int width, int height) {
645         if (isAvailable() && width > 0 && height > 0) {
646             return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
647                     width, height, Bitmap.Config.ARGB_8888));
648         }
649         return null;
650     }
651 
652     /**
653      * <p>Copies the content of this view's surface texture into the specified
654      * bitmap. If the surface texture is not available, the copy is not executed.
655      * The content of the surface texture will be scaled to fit exactly inside
656      * the specified bitmap.</p>
657      *
658      * <p><strong>Do not</strong> invoke this method from a drawing method
659      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
660      *
661      * <p>If an error occurs, the bitmap is left unchanged.</p>
662      *
663      * @param bitmap The bitmap to copy the content of the surface texture into,
664      *               cannot be null, all configurations are supported
665      *
666      * @return The bitmap specified as a parameter
667      *
668      * @see #isAvailable()
669      * @see #getBitmap(int, int)
670      * @see #getBitmap()
671      *
672      * @throws IllegalStateException if the hardware rendering context cannot be
673      *         acquired to capture the bitmap
674      */
getBitmap(@onNull Bitmap bitmap)675     public @NonNull Bitmap getBitmap(@NonNull Bitmap bitmap) {
676         if (bitmap != null && isAvailable()) {
677             applyUpdate();
678             applyTransformMatrix();
679 
680             // This case can happen if the app invokes setSurfaceTexture() before
681             // we are able to create the hardware layer. We can safely initialize
682             // the layer here thanks to the validate() call at the beginning of
683             // this method
684             if (mLayer == null && mUpdateSurface) {
685                 getTextureLayer();
686             }
687 
688             if (mLayer != null) {
689                 mLayer.copyInto(bitmap);
690             }
691         }
692         return bitmap;
693     }
694 
695     /**
696      * Returns true if the {@link SurfaceTexture} associated with this
697      * TextureView is available for rendering. When this method returns
698      * true, {@link #getSurfaceTexture()} returns a valid surface texture.
699      */
isAvailable()700     public boolean isAvailable() {
701         return mSurface != null;
702     }
703 
704     /**
705      * <p>Start editing the pixels in the surface.  The returned Canvas can be used
706      * to draw into the surface's bitmap.  A null is returned if the surface has
707      * not been created or otherwise cannot be edited. You will usually need
708      * to implement
709      * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
710      * to find out when the Surface is available for use.</p>
711      *
712      * <p>The content of the Surface is never preserved between unlockCanvas()
713      * and lockCanvas(), for this reason, every pixel within the Surface area
714      * must be written. The only exception to this rule is when a dirty
715      * rectangle is specified, in which case, non-dirty pixels will be
716      * preserved.</p>
717      *
718      * <p>This method can only be used if the underlying surface is not already
719      * owned by another producer. For instance, if the TextureView is being used
720      * to render the camera's preview you cannot invoke this method.</p>
721      *
722      * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for
723      * drawing (see {@link #isAvailable()}).
724      *
725      * @see #lockCanvas(android.graphics.Rect)
726      * @see #unlockCanvasAndPost(android.graphics.Canvas)
727      */
lockCanvas()728     public @Nullable Canvas lockCanvas() {
729         return lockCanvas(null);
730     }
731 
732     /**
733      * Just like {@link #lockCanvas()} but allows specification of a dirty
734      * rectangle. Every pixel within that rectangle must be written; however
735      * pixels outside the dirty rectangle will be preserved by the next call
736      * to lockCanvas().
737      *
738      * This method can return null if the underlying surface texture is not
739      * available (see {@link #isAvailable()} or if the surface texture is
740      * already connected to an image producer (for instance: the camera,
741      * OpenGL, a media player, etc.)
742      *
743      * @param dirty Area of the surface that will be modified. If null the area of the entire
744      *              surface is used.
745 
746      * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for
747      * drawing (see {@link #isAvailable()}).
748      *
749      * @see #lockCanvas()
750      * @see #unlockCanvasAndPost(android.graphics.Canvas)
751      * @see #isAvailable()
752      */
lockCanvas(@ullable Rect dirty)753     public @Nullable Canvas lockCanvas(@Nullable Rect dirty) {
754         if (!isAvailable()) return null;
755 
756         if (mCanvas == null) {
757             mCanvas = new Canvas();
758         }
759 
760         synchronized (mNativeWindowLock) {
761             if (!nLockCanvas(mNativeWindow, mCanvas, dirty)) {
762                 return null;
763             }
764         }
765         mSaveCount = mCanvas.save();
766 
767         return mCanvas;
768     }
769 
770     /**
771      * Finish editing pixels in the surface. After this call, the surface's
772      * current pixels will be shown on the screen, but its content is lost,
773      * in particular there is no guarantee that the content of the Surface
774      * will remain unchanged when lockCanvas() is called again.
775      *
776      * @param canvas The Canvas previously returned by lockCanvas()
777      *
778      * @see #lockCanvas()
779      * @see #lockCanvas(android.graphics.Rect)
780      */
unlockCanvasAndPost(@onNull Canvas canvas)781     public void unlockCanvasAndPost(@NonNull Canvas canvas) {
782         if (mCanvas != null && canvas == mCanvas) {
783             canvas.restoreToCount(mSaveCount);
784             mSaveCount = 0;
785 
786             synchronized (mNativeWindowLock) {
787                 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
788             }
789         }
790     }
791 
792     /**
793      * Returns the {@link SurfaceTexture} used by this view. This method
794      * may return null if the view is not attached to a window or if the surface
795      * texture has not been initialized yet.
796      *
797      * @see #isAvailable()
798      */
getSurfaceTexture()799     public @Nullable SurfaceTexture getSurfaceTexture() {
800         return mSurface;
801     }
802 
803     /**
804      * Set the {@link SurfaceTexture} for this view to use. If a {@link
805      * SurfaceTexture} is already being used by this view, it is immediately
806      * released and not usable any more.  The {@link
807      * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
808      * called for the previous {@link SurfaceTexture}.  Similarly, the {@link
809      * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
810      * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
811      *
812      * The {@link SurfaceTexture} object must be detached from all OpenGL ES
813      * contexts prior to calling this method.
814      *
815      * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
816      * @see SurfaceTexture#detachFromGLContext()
817      */
setSurfaceTexture(@onNull SurfaceTexture surfaceTexture)818     public void setSurfaceTexture(@NonNull SurfaceTexture surfaceTexture) {
819         if (surfaceTexture == null) {
820             throw new NullPointerException("surfaceTexture must not be null");
821         }
822         if (surfaceTexture == mSurface) {
823             throw new IllegalArgumentException("Trying to setSurfaceTexture to " +
824                     "the same SurfaceTexture that's already set.");
825         }
826         if (surfaceTexture.isReleased()) {
827             throw new IllegalArgumentException("Cannot setSurfaceTexture to a " +
828                     "released SurfaceTexture");
829         }
830         if (mSurface != null) {
831             nDestroyNativeWindow();
832             mSurface.release();
833         }
834         mSurface = surfaceTexture;
835         nCreateNativeWindow(mSurface);
836 
837         /*
838          * If the view is visible and we already made a layer, update the
839          * listener in the new surface to use the existing listener in the view.
840          * Otherwise this will be called when the view becomes visible or the
841          * layer is created
842          */
843         if (((mViewFlags & VISIBILITY_MASK) == VISIBLE) && mLayer != null) {
844             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
845         }
846         mUpdateSurface = true;
847         invalidateParentIfNeeded();
848     }
849 
850     /**
851      * Returns the {@link SurfaceTextureListener} currently associated with this
852      * texture view.
853      *
854      * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
855      * @see SurfaceTextureListener
856      */
getSurfaceTextureListener()857     public @Nullable SurfaceTextureListener getSurfaceTextureListener() {
858         return mListener;
859     }
860 
861     /**
862      * Sets the {@link SurfaceTextureListener} used to listen to surface
863      * texture events.
864      *
865      * @see #getSurfaceTextureListener()
866      * @see SurfaceTextureListener
867      */
setSurfaceTextureListener(@ullable SurfaceTextureListener listener)868     public void setSurfaceTextureListener(@Nullable SurfaceTextureListener listener) {
869         mListener = listener;
870     }
871 
872     @UnsupportedAppUsage
873     private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
874             surfaceTexture -> {
875                 updateLayer();
876                 invalidate();
877             };
878 
879     /**
880      * This listener can be used to be notified when the surface texture
881      * associated with this texture view is available.
882      */
883     public interface SurfaceTextureListener {
884         /**
885          * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
886          *
887          * @param surface The surface returned by
888          *                {@link android.view.TextureView#getSurfaceTexture()}
889          * @param width The width of the surface
890          * @param height The height of the surface
891          */
onSurfaceTextureAvailable(@onNull SurfaceTexture surface, int width, int height)892         void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height);
893 
894         /**
895          * Invoked when the {@link SurfaceTexture}'s buffers size changed.
896          *
897          * @param surface The surface returned by
898          *                {@link android.view.TextureView#getSurfaceTexture()}
899          * @param width The new width of the surface
900          * @param height The new height of the surface
901          */
onSurfaceTextureSizeChanged(@onNull SurfaceTexture surface, int width, int height)902         void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height);
903 
904         /**
905          * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
906          * If returns true, no rendering should happen inside the surface texture after this method
907          * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
908          * Most applications should return true.
909          *
910          * @param surface The surface about to be destroyed
911          */
onSurfaceTextureDestroyed(@onNull SurfaceTexture surface)912         boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface);
913 
914         /**
915          * Invoked when the specified {@link SurfaceTexture} is updated through
916          * {@link SurfaceTexture#updateTexImage()}.
917          *
918          * @param surface The surface just updated
919          */
onSurfaceTextureUpdated(@onNull SurfaceTexture surface)920         void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface);
921     }
922 
923     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
nCreateNativeWindow(SurfaceTexture surface)924     private native void nCreateNativeWindow(SurfaceTexture surface);
925     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
nDestroyNativeWindow()926     private native void nDestroyNativeWindow();
927 
nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty)928     private static native boolean nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty);
nUnlockCanvasAndPost(long nativeWindow, Canvas canvas)929     private static native void nUnlockCanvasAndPost(long nativeWindow, Canvas canvas);
930 }
931