diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 532e692358981..7edaf4932fbb4 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -756,6 +756,11 @@ public void setCallback(Callback callback) { this.callback = callback; } + @Override + public boolean handlesCropAndRotation() { + return false; + } + @Override public long id() { return id; diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java index cefd5774a117e..6f522a65c375e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java @@ -60,6 +60,11 @@ public void setCallback(Callback callback) { // Intentionally blank: SurfaceTextures don't get platform notifications or cleanup. } + @Override + public boolean handlesCropAndRotation() { + return true; + } + @Override @NonNull public SurfaceTexture getSurfaceTexture() { diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index caeb799d7598a..c9c9836bf20d6 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -124,6 +124,28 @@ interface Callback { /** This method is not officially part of the public API surface and will be deprecated. */ void scheduleFrame(); + + /** + * Returns whether the current rendering path handles crop and rotation metadata. + * + *
On most newer Android devices (API 29+), a {@link android.media.ImageReader} backend is + * used, which has more features, works in new graphic backends directly (such as Impeller's + * Vulkan backend), and is the Android recommended solution. However, crop and rotation metadata + * are not handled automatically, and require plugin authors to make + * appropriate changes ({@see https://github.com/flutter/flutter/issues/144407}). + * + *
{@code
+ * void example(SurfaceProducer producer) {
+ * bool supported = producer.handlesCropAndRotation();
+ * if (!supported) {
+ * // Manually rotate/crop, either in the Android plugin or in the Dart framework layer.
+ * }
+ * }
+ * }
+ *
+ * @return {@code true} if crop and rotation is handled automatically, {@code false} otherwise.
+ */
+ boolean handlesCropAndRotation();
}
/** A registry entry for a managed SurfaceTexture. */
diff --git a/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java b/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java
index 0ed6a4754a589..7ab861ab40854 100644
--- a/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java
@@ -7,8 +7,10 @@
import static android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -735,6 +737,27 @@ public void SurfaceTextureSurfaceProducerCreatesAConnectedTexture() {
}
}
+ @Test
+ public void SurfaceTextureSurfaceProducerDoesNotCropOrRotate() {
+ try {
+ FlutterRenderer.debugForceSurfaceProducerGlTextures = true;
+ FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer();
+ TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer();
+
+ assertTrue(producer.handlesCropAndRotation());
+ } finally {
+ FlutterRenderer.debugForceSurfaceProducerGlTextures = false;
+ }
+ }
+
+ @Test
+ public void ImageReaderSurfaceProducerDoesNotCropOrRotate() {
+ FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer();
+ TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer();
+
+ assertFalse(producer.handlesCropAndRotation());
+ }
+
@Test
public void ImageReaderSurfaceProducerIsDestroyedOnTrimMemory() {
FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer();
diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java
index 310f5fe051f6f..f9ebf55087fd4 100644
--- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java
+++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java
@@ -1651,6 +1651,11 @@ public Surface getSurface() {
return null;
}
+ @Override
+ public boolean handlesCropAndRotation() {
+ return false;
+ }
+
public void scheduleFrame() {}
};
}