-
Notifications
You must be signed in to change notification settings - Fork 6k
Fix compatibility issues with SurfaceTexture on Android Q. #31698
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,8 @@ | |
| import io.flutter.Log; | ||
| import io.flutter.embedding.android.AndroidTouchProcessor; | ||
| import io.flutter.util.ViewUtils; | ||
| import io.flutter.view.TextureRegistry; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
|
|
||
| /** | ||
| * Wraps a platform view to intercept gestures and project this view onto a {@link SurfaceTexture}. | ||
|
|
@@ -52,12 +54,45 @@ class PlatformViewWrapper extends FrameLayout { | |
| private AndroidTouchProcessor touchProcessor; | ||
|
|
||
| @Nullable @VisibleForTesting ViewTreeObserver.OnGlobalFocusChangeListener activeFocusListener; | ||
| @Nullable private TextureRegistry.SurfaceTextureEntry textureEntry; | ||
| private final AtomicLong pendingFramesCount = new AtomicLong(0L); | ||
|
|
||
| private final TextureRegistry.OnFrameConsumedListener listener = | ||
| new TextureRegistry.OnFrameConsumedListener() { | ||
| @Override | ||
| public void onFrameConsumed() { | ||
| if (Build.VERSION.SDK_INT == 29) { | ||
| pendingFramesCount.decrementAndGet(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| private void onFrameProduced() { | ||
| if (Build.VERSION.SDK_INT == 29) { | ||
| pendingFramesCount.incrementAndGet(); | ||
| } | ||
| } | ||
|
|
||
| private boolean shouldDrawToSurfaceNow() { | ||
| if (Build.VERSION.SDK_INT == 29) { | ||
| return pendingFramesCount.get() <= 0L; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| public PlatformViewWrapper(@NonNull Context context) { | ||
| super(context); | ||
| setWillNotDraw(false); | ||
| } | ||
|
|
||
| public PlatformViewWrapper( | ||
| @NonNull Context context, @NonNull TextureRegistry.SurfaceTextureEntry textureEntry) { | ||
| this(context); | ||
| this.textureEntry = textureEntry; | ||
| textureEntry.setOnFrameConsumedListener(listener); | ||
| setTexture(textureEntry.surfaceTexture()); | ||
| } | ||
|
|
||
| /** | ||
| * Sets the touch processor that allows to intercept gestures. | ||
| * | ||
|
|
@@ -109,6 +144,7 @@ public void setTexture(@Nullable SurfaceTexture newTx) { | |
| } else { | ||
| canvas.drawColor(Color.TRANSPARENT); | ||
| } | ||
| onFrameProduced(); | ||
| } finally { | ||
| surface.unlockCanvasAndPost(canvas); | ||
| } | ||
|
|
@@ -202,19 +238,29 @@ public void draw(Canvas canvas) { | |
| Log.e(TAG, "Invalid texture. The platform view cannot be displayed."); | ||
| return; | ||
| } | ||
| // Override the canvas that this subtree of views will use to draw. | ||
| final Canvas surfaceCanvas = surface.lockHardwareCanvas(); | ||
| try { | ||
| // Clear the current pixels in the canvas. | ||
| // This helps when a WebView renders an HTML document with transparent background. | ||
| if (Build.VERSION.SDK_INT >= 29) { | ||
| surfaceCanvas.drawColor(Color.TRANSPARENT, BlendMode.CLEAR); | ||
| } else { | ||
| surfaceCanvas.drawColor(Color.TRANSPARENT); | ||
| // We've observed on Android Q that we have to wait for the consumer of {@link SurfaceTexture} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you test with versions below Q? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @jreck
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, I did. They work fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to clarify, which versions did you test on? I'm getting reports that it may be several versions. See googleads/googleads-mobile-flutter#269 (comment)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have tested on real devices from Android9 to Android12. |
||
| // to consume the last image before continuing to draw, otherwise subsequent calls to | ||
| // {@code dequeueBuffer} to request a free buffer from the {@link BufferQueue} will fail. | ||
| // See https://github.com/flutter/flutter/issues/98722 | ||
| if (!shouldDrawToSurfaceNow()) { | ||
| // If there are still frames that are not consumed, we will draw them next time. | ||
| invalidate(); | ||
| } else { | ||
| // Override the canvas that this subtree of views will use to draw. | ||
| final Canvas surfaceCanvas = surface.lockHardwareCanvas(); | ||
| try { | ||
| // Clear the current pixels in the canvas. | ||
| // This helps when a WebView renders an HTML document with transparent background. | ||
| if (Build.VERSION.SDK_INT >= 29) { | ||
| surfaceCanvas.drawColor(Color.TRANSPARENT, BlendMode.CLEAR); | ||
| } else { | ||
| surfaceCanvas.drawColor(Color.TRANSPARENT); | ||
| } | ||
| super.draw(surfaceCanvas); | ||
| onFrameProduced(); | ||
| } finally { | ||
| surface.unlockCanvasAndPost(surfaceCanvas); | ||
| } | ||
| super.draw(surfaceCanvas); | ||
| } finally { | ||
| surface.unlockCanvasAndPost(surfaceCanvas); | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
java doc for this constructor as well as the previous one to document when to use which
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.