From cee385972b69bf064639638f42f57ad4f00826a9 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 7 Jun 2024 11:28:31 -0700 Subject: [PATCH 01/12] Add a callback-based notification for surfaceRecreated. --- shell/platform/android/build.gradle | 4 ++ .../engine/renderer/FlutterRenderer.java | 30 ++++++++++++++ .../io/flutter/view/TextureRegistry.java | 40 ++++++++++++++++++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/build.gradle b/shell/platform/android/build.gradle index cd813bf8883b9..b495b8632c74c 100644 --- a/shell/platform/android/build.gradle +++ b/shell/platform/android/build.gradle @@ -63,3 +63,7 @@ android { } } +dependencies { + implementation 'androidx.lifecycle:lifecycle-process:2.8.1' +} + 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 cbab9e99ef593..3a4fd7f4b71d8 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -24,6 +24,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; + import io.flutter.Log; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.view.TextureRegistry; @@ -78,6 +82,9 @@ public class FlutterRenderer implements TextureRegistry { private final Set> onTrimMemoryListeners = new HashSet<>(); + @NonNull + private final List producers = new ArrayList<>(); + @NonNull private final FlutterUiDisplayListener flutterUiDisplayListener = new FlutterUiDisplayListener() { @@ -95,6 +102,15 @@ public void onFlutterUiNoLongerDisplayed() { public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { this.flutterJNI = flutterJNI; this.flutterJNI.addIsDisplayingFlutterUiListener(flutterUiDisplayListener); + ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { + @Override + public void onResume(@NonNull LifecycleOwner owner) { + Log.v(TAG, "onResume called; notifying SurfaceProducers"); + for (SurfaceProducer producer : producers) { + producer.surfaceRecreated(); + } + } + }); } /** @@ -211,6 +227,7 @@ public SurfaceProducer createSurfaceProducer() { Log.v(TAG, "New SurfaceTextureSurfaceProducer ID: " + texture.id()); entry = producer; } + producers.add(entry); return entry; } @@ -453,6 +470,7 @@ final class ImageReaderSurfaceProducer new HashMap(); private PerImage lastDequeuedImage = null; private PerImageReader lastReaderDequeuedFrom = null; + private SurfaceProducer.OnSurfaceRecreatedCallback callback = null; /** Internal class: state held per Image produced by ImageReaders. */ private class PerImage { @@ -732,6 +750,18 @@ private void maybeWaitOnFence(Image image) { this.id = id; } + @Override + public void surfaceRecreated() { + if (callback != null) { + callback.onSurfaceRecreated(); + } + } + + @Override + public void setOnSurfaceRecreatedCallback(SurfaceProducer.OnSurfaceRecreatedCallback callback) { + this.callback = callback; + } + @Override public long id() { return id; diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index a8b5a52d0e6c5..161bbc5568af9 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -7,6 +7,8 @@ import android.graphics.SurfaceTexture; import android.media.Image; import android.view.Surface; +import android.view.SurfaceHolder; + import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -79,7 +81,12 @@ interface SurfaceProducer extends TextureEntry { int getHeight(); /** - * Get a Surface that can be used to update the texture contents. + * Direct access to the surface object. + * + *

When using this API, you will usually need to implement {@link SurfaceHolder.Callback} + * and provide it to {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)} in order + * to be notified when a new surface has been created, such as after a backgrounded app has been\ + * resumed. * *

NOTE: You should not cache the returned surface but instead invoke getSurface each time * you need to draw. The surface may change when the texture is resized or has its format @@ -89,6 +96,37 @@ interface SurfaceProducer extends TextureEntry { */ Surface getSurface(); + /** + * Sets a callback that is notified when + * + * @param callback The callback to notify, or null to remove the callback. + */ + void setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback callback); + + /** + * Callback invoked by {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)}. + */ + interface OnSurfaceRecreatedCallback { + /** + * Invoked when a new surface was forcibly recreated by {@link SurfaceProducer}. + * + *

Typically plugins will use this callback as a signal to redraw. + */ + void onSurfaceRecreated(); + } + + /** + * @deprecated This method is not officially part of the public API surface and will be removed. + */ + @Deprecated + @SuppressWarnings("DeprecatedIsStillUsed") + void surfaceRecreated(); + + /** + * @deprecated This method is not officially part of the public API surface and will be removed. + */ + @Deprecated + @SuppressWarnings("DeprecatedIsStillUsed") void scheduleFrame(); }; From 2521e40236e86483a73c86a95423a54a6bd0b27a Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 7 Jun 2024 11:28:51 -0700 Subject: [PATCH 02/12] ++ --- .../engine/renderer/FlutterRenderer.java | 25 ++++++++++--------- .../io/flutter/view/TextureRegistry.java | 11 +++----- 2 files changed, 17 insertions(+), 19 deletions(-) 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 3a4fd7f4b71d8..6521337d8c753 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -27,7 +27,6 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; - import io.flutter.Log; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.view.TextureRegistry; @@ -82,8 +81,7 @@ public class FlutterRenderer implements TextureRegistry { private final Set> onTrimMemoryListeners = new HashSet<>(); - @NonNull - private final List producers = new ArrayList<>(); + @NonNull private final List producers = new ArrayList<>(); @NonNull private final FlutterUiDisplayListener flutterUiDisplayListener = @@ -102,15 +100,18 @@ public void onFlutterUiNoLongerDisplayed() { public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { this.flutterJNI = flutterJNI; this.flutterJNI.addIsDisplayingFlutterUiListener(flutterUiDisplayListener); - ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { - @Override - public void onResume(@NonNull LifecycleOwner owner) { - Log.v(TAG, "onResume called; notifying SurfaceProducers"); - for (SurfaceProducer producer : producers) { - producer.surfaceRecreated(); - } - } - }); + ProcessLifecycleOwner.get() + .getLifecycle() + .addObserver( + new DefaultLifecycleObserver() { + @Override + public void onResume(@NonNull LifecycleOwner owner) { + Log.v(TAG, "onResume called; notifying SurfaceProducers"); + for (SurfaceProducer producer : producers) { + producer.surfaceRecreated(); + } + } + }); } /** diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 161bbc5568af9..7d3f58636c9d1 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -8,7 +8,6 @@ import android.media.Image; import android.view.Surface; import android.view.SurfaceHolder; - import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -83,9 +82,9 @@ interface SurfaceProducer extends TextureEntry { /** * Direct access to the surface object. * - *

When using this API, you will usually need to implement {@link SurfaceHolder.Callback} - * and provide it to {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)} in order - * to be notified when a new surface has been created, such as after a backgrounded app has been\ + *

When using this API, you will usually need to implement {@link SurfaceHolder.Callback} and + * provide it to {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)} in order to + * be notified when a new surface has been created, such as after a backgrounded app has been\ * resumed. * *

NOTE: You should not cache the returned surface but instead invoke getSurface each time @@ -103,9 +102,7 @@ interface SurfaceProducer extends TextureEntry { */ void setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback callback); - /** - * Callback invoked by {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)}. - */ + /** Callback invoked by {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)}. */ interface OnSurfaceRecreatedCallback { /** * Invoked when a new surface was forcibly recreated by {@link SurfaceProducer}. From 39350b009c57de5ffab257532c85eef66567c6f5 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 7 Jun 2024 11:50:48 -0700 Subject: [PATCH 03/12] ++ --- .../engine/renderer/SurfaceTextureSurfaceProducer.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 0592a0bab1299..62a23987796aa 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java @@ -55,6 +55,16 @@ public void release() { released = true; } + @Override + public void surfaceRecreated() { + // Intentionally blank: SurfaceTextures don't need to be recreated. + } + + @Override + public void setOnSurfaceRecreatedCallback(TextureRegistry.SurfaceProducer.OnSurfaceRecreatedCallback callback) { + // Intentionally blank: SurfaceTextures don't need to be recreated. + } + @Override @NonNull public SurfaceTexture getSurfaceTexture() { From cdd9bdfb3115832e545df73aae1dff7b1a6fbaa6 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 7 Jun 2024 12:01:17 -0700 Subject: [PATCH 04/12] ++ --- .../engine/renderer/SurfaceTextureSurfaceProducer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 62a23987796aa..491825a1ebb15 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java @@ -61,7 +61,8 @@ public void surfaceRecreated() { } @Override - public void setOnSurfaceRecreatedCallback(TextureRegistry.SurfaceProducer.OnSurfaceRecreatedCallback callback) { + public void setOnSurfaceRecreatedCallback( + TextureRegistry.SurfaceProducer.OnSurfaceRecreatedCallback callback) { // Intentionally blank: SurfaceTextures don't need to be recreated. } From a0ce64dcafa62b27c120235dbfb070a9156d0ecf Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 7 Jun 2024 15:12:55 -0700 Subject: [PATCH 05/12] Added androidx.lifecycle thingy. --- shell/platform/android/BUILD.gn | 1 + shell/platform/android/build.gradle | 2 +- .../scenario_app/android/app/gradle.lockfile | 1 + tools/androidx/files.json | 22 +++++++++---------- .../android_embedding_bundle/build.gradle | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 0829a94c73052..40e6912ec335f 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -374,6 +374,7 @@ embedding_dependencies_jars = [ "//third_party/android_embedding_dependencies/lib/lifecycle-common-java8-2.2.0.jar", "//third_party/android_embedding_dependencies/lib/lifecycle-livedata-2.0.0.jar", "//third_party/android_embedding_dependencies/lib/lifecycle-livedata-core-2.0.0.jar", + "//third_party/android_embedding_dependencies/lib/lifecycle-process-2.2.0.jar", "//third_party/android_embedding_dependencies/lib/lifecycle-runtime-2.2.0.jar", "//third_party/android_embedding_dependencies/lib/lifecycle-viewmodel-2.1.0.jar", "//third_party/android_embedding_dependencies/lib/loader-1.0.0.jar", diff --git a/shell/platform/android/build.gradle b/shell/platform/android/build.gradle index b495b8632c74c..a63ffd6f7354c 100644 --- a/shell/platform/android/build.gradle +++ b/shell/platform/android/build.gradle @@ -64,6 +64,6 @@ android { } dependencies { - implementation 'androidx.lifecycle:lifecycle-process:2.8.1' + implementation 'androidx.lifecycle:lifecycle-process:2.2.0' } diff --git a/testing/scenario_app/android/app/gradle.lockfile b/testing/scenario_app/android/app/gradle.lockfile index fedf60cf04806..f79689a036e99 100644 --- a/testing/scenario_app/android/app/gradle.lockfile +++ b/testing/scenario_app/android/app/gradle.lockfile @@ -28,6 +28,7 @@ androidx.lifecycle:lifecycle-common-java8:2.2.0=debugAndroidTestCompileClasspath androidx.lifecycle:lifecycle-common:2.3.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-livedata-core:2.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-livedata:2.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-process:2.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-runtime:2.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-viewmodel:2.1.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugImplementationDependenciesMetadata,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseImplementationDependenciesMetadata,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath diff --git a/tools/androidx/files.json b/tools/androidx/files.json index 79ee484d6e737..3e03b721f82a5 100644 --- a/tools/androidx/files.json +++ b/tools/androidx/files.json @@ -13,17 +13,19 @@ "url": "https://maven.google.com/androidx/lifecycle/lifecycle-common-java8/2.2.0/lifecycle-common-java8-2.2.0.jar", "out_file_name": "androidx_lifecycle_common_java8.jar", "maven_dependency": "androidx.lifecycle:lifecycle-common-java8:2.2.0", - "provides": [ - "androidx.lifecycle.DefaultLifecycleObserver" - ] + "provides": ["androidx.lifecycle.DefaultLifecycleObserver"] + }, + { + "url": "https://maven.google.com/androidx/lifecycle/lifecycle-process/2.2.0/lifecycle-process-2.2.0.aar", + "out_file_name": "androidx_lifecycle_process.aar", + "maven_dependency": "androidx.lifecycle:lifecycle-process:2.2.0", + "provides": ["androidx.lifecycle.ProcessLifecycleOwner"] }, { "url": "https://maven.google.com/androidx/lifecycle/lifecycle-runtime/2.2.0/lifecycle-runtime-2.2.0.aar", "out_file_name": "androidx_lifecycle_runtime.aar", "maven_dependency": "androidx.lifecycle:lifecycle-runtime:2.2.0", - "provides": [ - "androidx.lifecycle.LifecycleRegistry" - ] + "provides": ["androidx.lifecycle.LifecycleRegistry"] }, { "url": "https://maven.google.com/androidx/fragment/fragment/1.1.0/fragment-1.1.0.aar", @@ -54,17 +56,13 @@ "url": "https://maven.google.com/androidx/tracing/tracing/1.0.0/tracing-1.0.0.aar", "out_file_name": "androidx_tracing.aar", "maven_dependency": "androidx.tracing:tracing:1.0.0", - "provides": [ - "androidx.tracing.Trace" - ] + "provides": ["androidx.tracing.Trace"] }, { "url": "https://dl.google.com/android/maven2/androidx/core/core/1.6.0/core-1.6.0.aar", "out_file_name": "androidx_core.aar", "maven_dependency": "androidx.core:core:1.6.0", - "provides": [ - "androidx.core.view.WindowInsetsControllerCompat" - ] + "provides": ["androidx.core.view.WindowInsetsControllerCompat"] }, { "url": "https://maven.google.com/androidx/window/window-java/1.0.0-beta04/window-java-1.0.0-beta04.aar", diff --git a/tools/cipd/android_embedding_bundle/build.gradle b/tools/cipd/android_embedding_bundle/build.gradle index 9b93d0a0afbdf..c10b3c9ec33b0 100644 --- a/tools/cipd/android_embedding_bundle/build.gradle +++ b/tools/cipd/android_embedding_bundle/build.gradle @@ -13,7 +13,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:3.5.0" + classpath "com.android.tools.build:gradle:7.0.2" } } From d4281ceb6f8be4c649240c24c0dc93720f033ea5 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 08:55:06 -0700 Subject: [PATCH 06/12] Address feedback, cleanup a bit. --- .../engine/renderer/FlutterRenderer.java | 27 +++++----- .../SurfaceTextureSurfaceProducer.java | 10 +--- .../android/io/flutter/view/FlutterView.java | 1 + .../io/flutter/view/TextureRegistry.java | 54 ++++++++++--------- .../platform/PlatformViewsControllerTest.java | 9 +++- 5 files changed, 53 insertions(+), 48 deletions(-) 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 6521337d8c753..46f1d5abd69e5 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -81,7 +81,7 @@ public class FlutterRenderer implements TextureRegistry { private final Set> onTrimMemoryListeners = new HashSet<>(); - @NonNull private final List producers = new ArrayList<>(); + @NonNull private final List imageReaderProducers = new ArrayList<>(); @NonNull private final FlutterUiDisplayListener flutterUiDisplayListener = @@ -106,9 +106,11 @@ public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { new DefaultLifecycleObserver() { @Override public void onResume(@NonNull LifecycleOwner owner) { - Log.v(TAG, "onResume called; notifying SurfaceProducers"); - for (SurfaceProducer producer : producers) { - producer.surfaceRecreated(); + Log.w(TAG, "onResume called; notifying SurfaceProducers"); + for (ImageReaderSurfaceProducer producer : imageReaderProducers) { + if (producer.callback != null) { + producer.callback.onSurfaceChanged(); + } } } }); @@ -214,6 +216,7 @@ public SurfaceProducer createSurfaceProducer() { final ImageReaderSurfaceProducer producer = new ImageReaderSurfaceProducer(id); registerImageTexture(id, producer); addOnTrimMemoryListener(producer); + imageReaderProducers.add(producer); Log.v(TAG, "New ImageReaderSurfaceProducer ID: " + id); entry = producer; } else { @@ -228,7 +231,6 @@ public SurfaceProducer createSurfaceProducer() { Log.v(TAG, "New SurfaceTextureSurfaceProducer ID: " + texture.id()); entry = producer; } - producers.add(entry); return entry; } @@ -471,7 +473,7 @@ final class ImageReaderSurfaceProducer new HashMap(); private PerImage lastDequeuedImage = null; private PerImageReader lastReaderDequeuedFrom = null; - private SurfaceProducer.OnSurfaceRecreatedCallback callback = null; + private Callback callback = null; /** Internal class: state held per Image produced by ImageReaders. */ private class PerImage { @@ -697,6 +699,7 @@ public void onTrimMemory(int level) { private void releaseInternal() { cleanup(); released = true; + imageReaderProducers.remove(this); } private void cleanup() { @@ -718,6 +721,9 @@ private void cleanup() { } imageReaderQueue.clear(); } + if (this.callback != null) { + this.callback.onSurfaceDestroyed(); + } } @TargetApi(API_LEVELS.API_33) @@ -752,14 +758,7 @@ private void maybeWaitOnFence(Image image) { } @Override - public void surfaceRecreated() { - if (callback != null) { - callback.onSurfaceRecreated(); - } - } - - @Override - public void setOnSurfaceRecreatedCallback(SurfaceProducer.OnSurfaceRecreatedCallback callback) { + public void setCallback(Callback callback) { this.callback = callback; } 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 491825a1ebb15..cefd5774a117e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java @@ -56,14 +56,8 @@ public void release() { } @Override - public void surfaceRecreated() { - // Intentionally blank: SurfaceTextures don't need to be recreated. - } - - @Override - public void setOnSurfaceRecreatedCallback( - TextureRegistry.SurfaceProducer.OnSurfaceRecreatedCallback callback) { - // Intentionally blank: SurfaceTextures don't need to be recreated. + public void setCallback(Callback callback) { + // Intentionally blank: SurfaceTextures don't get platform notifications or cleanup. } @Override diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 62f2a17505174..8dbdae146ef83 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -902,6 +902,7 @@ public ImageTextureEntry createImageTexture() { throw new UnsupportedOperationException("Image textures are not supported in this mode."); } + @NonNull @Override public SurfaceProducer createSurfaceProducer() { throw new UnsupportedOperationException( diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 7d3f58636c9d1..e76a8a53bbe9f 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -7,7 +7,7 @@ import android.graphics.SurfaceTexture; import android.media.Image; import android.view.Surface; -import android.view.SurfaceHolder; + import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -63,7 +63,7 @@ interface TextureEntry { /** @return The identity of this texture. */ long id(); - /** Deregisters and releases all resources . */ + /** De-registers and releases all resources . */ void release(); } @@ -82,13 +82,13 @@ interface SurfaceProducer extends TextureEntry { /** * Direct access to the surface object. * - *

When using this API, you will usually need to implement {@link SurfaceHolder.Callback} and - * provide it to {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)} in order to - * be notified when a new surface has been created, such as after a backgrounded app has been\ - * resumed. + *

When using this API, you will usually need to implement {@link SurfaceProducer.Callback} + * and provide it to {@link #setCallback(Callback)} in order to be notified when an existing + * surface has been destroyed (such as when the application goes to the background) or a new + * surface has been created (such as when the application is resumed back to the foreground). * - *

NOTE: You should not cache the returned surface but instead invoke getSurface each time - * you need to draw. The surface may change when the texture is resized or has its format + *

NOTE: You should not cache the returned surface but instead invoke {@code getSurface} each + * time you need to draw. The surface may change when the texture is resized or has its format * changed. * * @return a Surface to use for a drawing target for various APIs. @@ -96,28 +96,32 @@ interface SurfaceProducer extends TextureEntry { Surface getSurface(); /** - * Sets a callback that is notified when + * Sets a callback that is notified when a previously created {@link Surface} returned by + * {@link SurfaceProducer#getSurface()} is no longer valid, either due to being destroyed or + * being changed. * * @param callback The callback to notify, or null to remove the callback. */ - void setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback callback); + void setCallback(Callback callback); - /** Callback invoked by {@link #setOnSurfaceRecreatedCallback(OnSurfaceRecreatedCallback)}. */ - interface OnSurfaceRecreatedCallback { + /** Callback invoked by {@link #setCallback(Callback)}. */ + interface Callback { /** - * Invoked when a new surface was forcibly recreated by {@link SurfaceProducer}. + * Invoked when a previous surface is now invalid and a new surface is now available. * - *

Typically plugins will use this callback as a signal to redraw. + *

Typically plugins will use this callback as a signal to redraw, such as due to the + * texture being resized, the format being changed, or the application being resumed after + * being suspended in the background. */ - void onSurfaceRecreated(); - } + void onSurfaceChanged(); - /** - * @deprecated This method is not officially part of the public API surface and will be removed. - */ - @Deprecated - @SuppressWarnings("DeprecatedIsStillUsed") - void surfaceRecreated(); + /** + * Invoked when a previous surface is now invalid. + * + *

Typically plugins will use this callback as a signal to release resources. + */ + void onSurfaceDestroyed(); + } /** * @deprecated This method is not officially part of the public API surface and will be removed. @@ -125,7 +129,7 @@ interface OnSurfaceRecreatedCallback { @Deprecated @SuppressWarnings("DeprecatedIsStillUsed") void scheduleFrame(); - }; + } /** A registry entry for a managed SurfaceTexture. */ @Keep @@ -179,7 +183,7 @@ interface ImageConsumer { * @return Image or null. */ @Nullable - public Image acquireLatestImage(); + Image acquireLatestImage(); } @Keep @@ -190,6 +194,6 @@ interface GLTextureConsumer { * @return SurfaceTexture. */ @NonNull - public SurfaceTexture getSurfaceTexture(); + SurfaceTexture getSurfaceTexture(); } } 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 0ecc09838bc33..afee9b58afaed 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -1582,13 +1582,15 @@ private static void attachToFlutterView( new TextureRegistry() { public void TextureRegistry() {} + @NonNull @Override public SurfaceTextureEntry createSurfaceTexture() { return registerSurfaceTexture(mock(SurfaceTexture.class)); } + @NonNull @Override - public SurfaceTextureEntry registerSurfaceTexture(SurfaceTexture surfaceTexture) { + public SurfaceTextureEntry registerSurfaceTexture(@NonNull SurfaceTexture surfaceTexture) { return new SurfaceTextureEntry() { @NonNull @Override @@ -1606,6 +1608,7 @@ public void release() {} }; } + @NonNull @Override public ImageTextureEntry createImageTexture() { return new ImageTextureEntry() { @@ -1622,9 +1625,13 @@ public void pushImage(Image image) {} }; } + @NonNull @Override public SurfaceProducer createSurfaceProducer() { return new SurfaceProducer() { + @Override + public void setCallback(SurfaceProducer.Callback cb) {} + @Override public long id() { return 0; From 6254178618c4f275adb1d6a5e4359c906e9c7c34 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 08:55:44 -0700 Subject: [PATCH 07/12] ++ --- .../platform/android/io/flutter/view/TextureRegistry.java | 7 +++---- .../plugin/platform/PlatformViewsControllerTest.java | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index e76a8a53bbe9f..824b304e53e6d 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -7,7 +7,6 @@ import android.graphics.SurfaceTexture; import android.media.Image; import android.view.Surface; - import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -96,9 +95,9 @@ interface SurfaceProducer extends TextureEntry { Surface getSurface(); /** - * Sets a callback that is notified when a previously created {@link Surface} returned by - * {@link SurfaceProducer#getSurface()} is no longer valid, either due to being destroyed or - * being changed. + * Sets a callback that is notified when a previously created {@link Surface} returned by {@link + * SurfaceProducer#getSurface()} is no longer valid, either due to being destroyed or being + * changed. * * @param callback The callback to notify, or null to remove the callback. */ 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 afee9b58afaed..af3a79e7adc22 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -1590,7 +1590,8 @@ public SurfaceTextureEntry createSurfaceTexture() { @NonNull @Override - public SurfaceTextureEntry registerSurfaceTexture(@NonNull SurfaceTexture surfaceTexture) { + public SurfaceTextureEntry registerSurfaceTexture( + @NonNull SurfaceTexture surfaceTexture) { return new SurfaceTextureEntry() { @NonNull @Override From f0829b7be6ac8711c8f4294ad7eb3d23d7300d9a Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 09:06:09 -0700 Subject: [PATCH 08/12] ++ --- .../io/flutter/embedding/engine/renderer/FlutterRenderer.java | 2 +- shell/platform/android/io/flutter/view/TextureRegistry.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 46f1d5abd69e5..e64a7f95954c4 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -109,7 +109,7 @@ public void onResume(@NonNull LifecycleOwner owner) { Log.w(TAG, "onResume called; notifying SurfaceProducers"); for (ImageReaderSurfaceProducer producer : imageReaderProducers) { if (producer.callback != null) { - producer.callback.onSurfaceChanged(); + producer.callback.onSurfaceCreated(); } } } diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 824b304e53e6d..63b22db199800 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -112,7 +112,7 @@ interface Callback { * texture being resized, the format being changed, or the application being resumed after * being suspended in the background. */ - void onSurfaceChanged(); + void onSurfaceCreated(); /** * Invoked when a previous surface is now invalid. From 56a8e687937f96bd9295b1103c125d89184de68f Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 10:57:20 -0700 Subject: [PATCH 09/12] ++ --- .../embedding/engine/renderer/FlutterRenderer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e64a7f95954c4..13b57c7d98e75 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -106,7 +106,7 @@ public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { new DefaultLifecycleObserver() { @Override public void onResume(@NonNull LifecycleOwner owner) { - Log.w(TAG, "onResume called; notifying SurfaceProducers"); + Log.v(TAG, "onResume called; notifying SurfaceProducers"); for (ImageReaderSurfaceProducer producer : imageReaderProducers) { if (producer.callback != null) { producer.callback.onSurfaceCreated(); @@ -694,6 +694,9 @@ public void onTrimMemory(int level) { } cleanup(); createNewReader = true; + if (this.callback != null) { + this.callback.onSurfaceDestroyed(); + } } private void releaseInternal() { @@ -721,9 +724,6 @@ private void cleanup() { } imageReaderQueue.clear(); } - if (this.callback != null) { - this.callback.onSurfaceDestroyed(); - } } @TargetApi(API_LEVELS.API_33) From 21382e2c8d8253b3da30455a87381858b814c70b Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 14:18:15 -0700 Subject: [PATCH 10/12] Update DEPS. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 20fcdd3ff1e04..4a60a9c990993 100644 --- a/DEPS +++ b/DEPS @@ -785,7 +785,7 @@ deps = { 'packages': [ { 'package': 'flutter/android/embedding_bundle', - 'version': 'last_updated:2023-08-11T11:35:44-0700' + 'version': 'last_updated:2024-06-12T14:15:49-0700' } ], 'condition': 'download_android_deps', From 26b832b49cf8caa7187ce655030b205a0fc5582c Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 12 Jun 2024 15:27:30 -0700 Subject: [PATCH 11/12] ++ --- shell/platform/android/build.gradle | 5 ----- 1 file changed, 5 deletions(-) diff --git a/shell/platform/android/build.gradle b/shell/platform/android/build.gradle index a63ffd6f7354c..5d8bded77f378 100644 --- a/shell/platform/android/build.gradle +++ b/shell/platform/android/build.gradle @@ -62,8 +62,3 @@ android { implementation "org.mockito:mockito-android:$mockitoVersion" } } - -dependencies { - implementation 'androidx.lifecycle:lifecycle-process:2.2.0' -} - From c8b4446439af586e29354b307ad46ccda79c35f9 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 13 Jun 2024 16:54:08 -0700 Subject: [PATCH 12/12] ++ --- .../io/flutter/view/TextureRegistry.java | 6 +-- .../engine/renderer/FlutterRendererTest.java | 46 +++++++++++++++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 63b22db199800..caeb799d7598a 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -122,11 +122,7 @@ interface Callback { void onSurfaceDestroyed(); } - /** - * @deprecated This method is not officially part of the public API surface and will be removed. - */ - @Deprecated - @SuppressWarnings("DeprecatedIsStillUsed") + /** This method is not officially part of the public API surface and will be deprecated. */ void scheduleFrame(); } 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 ed79d7b6a08f0..1779d6fc56077 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 @@ -23,6 +23,8 @@ import android.os.Looper; import android.view.Surface; import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleRegistry; +import androidx.lifecycle.ProcessLifecycleOwner; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import io.flutter.embedding.android.FlutterActivity; @@ -728,8 +730,46 @@ public void SurfaceTextureSurfaceProducerCreatesAConnectedTexture() { } @Test - public void CanLaunchActivityUsingFlutterEngine() { - // This is a placeholder test that will be used to test lifecycle events w/ SurfaceProducer. - scenarioRule.getScenario().moveToState(Lifecycle.State.RESUMED); + public void ImageReaderSurfaceProducerIsDestroyedOnTrimMemory() { + FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); + TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + + // Create and set a mock callback. + TextureRegistry.SurfaceProducer.Callback callback = + mock(TextureRegistry.SurfaceProducer.Callback.class); + producer.setCallback(callback); + + // Trim memory. + ((FlutterRenderer.ImageReaderSurfaceProducer) producer).onTrimMemory(40); + + // Verify. + verify(callback).onSurfaceDestroyed(); + } + + @Test + public void ImageReaderSurfaceProducerIsCreatedOnLifecycleResume() throws Exception { + FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); + TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + + // Create a callback. + CountDownLatch latch = new CountDownLatch(1); + TextureRegistry.SurfaceProducer.Callback callback = + new TextureRegistry.SurfaceProducer.Callback() { + @Override + public void onSurfaceCreated() { + latch.countDown(); + } + + @Override + public void onSurfaceDestroyed() {} + }; + producer.setCallback(callback); + + // Trigger a resume. + ((LifecycleRegistry) ProcessLifecycleOwner.get().getLifecycle()) + .setCurrentState(Lifecycle.State.RESUMED); + + // Verify. + latch.await(); } }