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