mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Ensure PlatformView engine life cycle callbacks are invoked (flutter/engine#42491)
- Move some code off of the message handler onto the parent class. - Call the engine life cycle callbacks on PlatformView regardless of which mode is used. - Re-enable and fix test that these callbacks are invoked. Fixes [#120329](https://github.com/flutter/flutter/issues/120329) *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I signed the [CLA]. - [X] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
This commit is contained in:
parent
8bcecafd93
commit
d72aace3ef
@ -157,7 +157,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
public void createForPlatformViewLayer(
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
// API level 19 is required for `android.graphics.ImageReader`.
|
||||
ensureValidAndroidVersion(19);
|
||||
enforceMinimumAndroidApiVersion(19);
|
||||
ensureValidRequest(request);
|
||||
|
||||
final PlatformView platformView = createPlatformView(request, false);
|
||||
@ -456,205 +456,203 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
embeddedView.clearFocus();
|
||||
}
|
||||
|
||||
private void ensureValidAndroidVersion(int minSdkVersion) {
|
||||
if (Build.VERSION.SDK_INT < minSdkVersion) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to use platform views with API "
|
||||
+ Build.VERSION.SDK_INT
|
||||
+ ", required API level is: "
|
||||
+ minSdkVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureValidRequest(
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
if (!validateDirection(request.direction)) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to create a view with unknown direction value: "
|
||||
+ request.direction
|
||||
+ "(view id: "
|
||||
+ request.viewId
|
||||
+ ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a platform view based on `request`, performs configuration that's common to
|
||||
// all display modes, and adds it to `platformViews`.
|
||||
@TargetApi(19)
|
||||
private PlatformView createPlatformView(
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request,
|
||||
boolean wrapContext) {
|
||||
final PlatformViewFactory viewFactory = registry.getFactory(request.viewType);
|
||||
if (viewFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to create a platform view of unregistered type: " + request.viewType);
|
||||
}
|
||||
|
||||
Object createParams = null;
|
||||
if (request.params != null) {
|
||||
createParams = viewFactory.getCreateArgsCodec().decodeMessage(request.params);
|
||||
}
|
||||
|
||||
// In some display modes, the context needs to be modified during display.
|
||||
// TODO(stuartmorgan): Make this wrapping unconditional if possible; for context see
|
||||
// https://github.com/flutter/flutter/issues/113449
|
||||
final Context mutableContext = wrapContext ? new MutableContextWrapper(context) : context;
|
||||
final PlatformView platformView =
|
||||
viewFactory.create(mutableContext, request.viewId, createParams);
|
||||
|
||||
// Configure the view to match the requested layout direction.
|
||||
final View embeddedView = platformView.getView();
|
||||
if (embeddedView == null) {
|
||||
throw new IllegalStateException(
|
||||
"PlatformView#getView() returned null, but an Android view reference was expected.");
|
||||
}
|
||||
embeddedView.setLayoutDirection(request.direction);
|
||||
|
||||
platformViews.put(request.viewId, platformView);
|
||||
return platformView;
|
||||
}
|
||||
|
||||
// Configures the view for Hybrid Composition mode.
|
||||
private void configureForHybridComposition(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
Log.i(TAG, "Using hybrid composition for platform view: " + request.viewId);
|
||||
}
|
||||
|
||||
// Configures the view for Virtual Display mode, returning the associated texture ID.
|
||||
private long configureForVirtualDisplay(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
// This mode adds the view to a virtual display, which is wired up to a GL texture that
|
||||
// is composed by the Flutter engine.
|
||||
|
||||
// API level 20 is required to use VirtualDisplay#setSurface.
|
||||
ensureValidAndroidVersion(20);
|
||||
|
||||
Log.i(TAG, "Hosting view in a virtual display for platform view: " + request.viewId);
|
||||
|
||||
final TextureRegistry.SurfaceTextureEntry textureEntry =
|
||||
textureRegistry.createSurfaceTexture();
|
||||
final int physicalWidth = toPhysicalPixels(request.logicalWidth);
|
||||
final int physicalHeight = toPhysicalPixels(request.logicalHeight);
|
||||
final VirtualDisplayController vdController =
|
||||
VirtualDisplayController.create(
|
||||
context,
|
||||
accessibilityEventsDelegate,
|
||||
platformView,
|
||||
textureEntry,
|
||||
physicalWidth,
|
||||
physicalHeight,
|
||||
request.viewId,
|
||||
null,
|
||||
(view, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
platformViewsChannel.invokeViewFocused(request.viewId);
|
||||
}
|
||||
});
|
||||
|
||||
if (vdController == null) {
|
||||
throw new IllegalStateException(
|
||||
"Failed creating virtual display for a "
|
||||
+ request.viewType
|
||||
+ " with id: "
|
||||
+ request.viewId);
|
||||
}
|
||||
|
||||
// If our FlutterEngine is already attached to a Flutter UI, provide that Android
|
||||
// View to this new platform view.
|
||||
if (flutterView != null) {
|
||||
vdController.onFlutterViewAttached(flutterView);
|
||||
}
|
||||
|
||||
// The embedded view doesn't need to be sized in Virtual Display mode because the
|
||||
// virtual display itself is sized.
|
||||
|
||||
vdControllers.put(request.viewId, vdController);
|
||||
final View embeddedView = platformView.getView();
|
||||
contextToEmbeddedView.put(embeddedView.getContext(), embeddedView);
|
||||
|
||||
return textureEntry.id();
|
||||
}
|
||||
|
||||
// Configures the view for Texture Layer Hybrid Composition mode, returning the associated
|
||||
// texture ID.
|
||||
@TargetApi(23)
|
||||
private long configureForTextureLayerComposition(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
// This mode attaches the view to the Android view hierarchy and record its drawing
|
||||
// operations, so they can be forwarded to a GL texture that is composed by the
|
||||
// Flutter engine.
|
||||
|
||||
// API level 23 is required to use Surface#lockHardwareCanvas().
|
||||
ensureValidAndroidVersion(23);
|
||||
Log.i(TAG, "Hosting view in view hierarchy for platform view: " + request.viewId);
|
||||
|
||||
final int physicalWidth = toPhysicalPixels(request.logicalWidth);
|
||||
final int physicalHeight = toPhysicalPixels(request.logicalHeight);
|
||||
PlatformViewWrapper viewWrapper;
|
||||
long textureId;
|
||||
if (usesSoftwareRendering) {
|
||||
viewWrapper = new PlatformViewWrapper(context);
|
||||
textureId = -1;
|
||||
} else {
|
||||
final TextureRegistry.SurfaceTextureEntry textureEntry =
|
||||
textureRegistry.createSurfaceTexture();
|
||||
viewWrapper = new PlatformViewWrapper(context, textureEntry);
|
||||
textureId = textureEntry.id();
|
||||
}
|
||||
viewWrapper.setTouchProcessor(androidTouchProcessor);
|
||||
viewWrapper.setBufferSize(physicalWidth, physicalHeight);
|
||||
|
||||
final FrameLayout.LayoutParams viewWrapperLayoutParams =
|
||||
new FrameLayout.LayoutParams(physicalWidth, physicalHeight);
|
||||
|
||||
// Size and position the view wrapper.
|
||||
final int physicalTop = toPhysicalPixels(request.logicalTop);
|
||||
final int physicalLeft = toPhysicalPixels(request.logicalLeft);
|
||||
viewWrapperLayoutParams.topMargin = physicalTop;
|
||||
viewWrapperLayoutParams.leftMargin = physicalLeft;
|
||||
viewWrapper.setLayoutParams(viewWrapperLayoutParams);
|
||||
|
||||
// Size the embedded view.
|
||||
final View embeddedView = platformView.getView();
|
||||
embeddedView.setLayoutParams(new FrameLayout.LayoutParams(physicalWidth, physicalHeight));
|
||||
|
||||
// Accessibility in the embedded view is initially disabled because if a Flutter app
|
||||
// disabled accessibility in the first frame, the embedding won't receive an update to
|
||||
// disable accessibility since the embedding never received an update to enable it.
|
||||
// The AccessibilityBridge keeps track of the accessibility nodes, and handles the deltas
|
||||
// when the framework sends a new a11y tree to the embedding.
|
||||
// To prevent races, the framework populate the SemanticsNode after the platform view has
|
||||
// been created.
|
||||
embeddedView.setImportantForAccessibility(
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
// Add the embedded view to the wrapper.
|
||||
viewWrapper.addView(embeddedView);
|
||||
|
||||
// Listen for focus changed in any subview, so the framework is notified when the platform
|
||||
// view is focused.
|
||||
viewWrapper.setOnDescendantFocusChangeListener(
|
||||
(v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
platformViewsChannel.invokeViewFocused(request.viewId);
|
||||
} else if (textInputPlugin != null) {
|
||||
textInputPlugin.clearPlatformViewClient(request.viewId);
|
||||
}
|
||||
});
|
||||
flutterView.addView(viewWrapper);
|
||||
viewWrappers.append(request.viewId, viewWrapper);
|
||||
return textureId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeToNativeViewHierarchy(boolean yes) {
|
||||
synchronizeToNativeViewHierarchy = yes;
|
||||
}
|
||||
};
|
||||
|
||||
/// Throws an exception if the SDK version is below minSdkVersion.
|
||||
private void enforceMinimumAndroidApiVersion(int minSdkVersion) {
|
||||
if (Build.VERSION.SDK_INT < minSdkVersion) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to use platform views with API "
|
||||
+ Build.VERSION.SDK_INT
|
||||
+ ", required API level is: "
|
||||
+ minSdkVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureValidRequest(
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
if (!validateDirection(request.direction)) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to create a view with unknown direction value: "
|
||||
+ request.direction
|
||||
+ "(view id: "
|
||||
+ request.viewId
|
||||
+ ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a platform view based on `request`, performs configuration that's common to
|
||||
// all display modes, and adds it to `platformViews`.
|
||||
@TargetApi(19)
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public PlatformView createPlatformView(
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request, boolean wrapContext) {
|
||||
final PlatformViewFactory viewFactory = registry.getFactory(request.viewType);
|
||||
if (viewFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Trying to create a platform view of unregistered type: " + request.viewType);
|
||||
}
|
||||
|
||||
Object createParams = null;
|
||||
if (request.params != null) {
|
||||
createParams = viewFactory.getCreateArgsCodec().decodeMessage(request.params);
|
||||
}
|
||||
|
||||
// In some display modes, the context needs to be modified during display.
|
||||
// TODO(stuartmorgan): Make this wrapping unconditional if possible; for context see
|
||||
// https://github.com/flutter/flutter/issues/113449
|
||||
final Context mutableContext = wrapContext ? new MutableContextWrapper(context) : context;
|
||||
final PlatformView platformView =
|
||||
viewFactory.create(mutableContext, request.viewId, createParams);
|
||||
|
||||
// Configure the view to match the requested layout direction.
|
||||
final View embeddedView = platformView.getView();
|
||||
if (embeddedView == null) {
|
||||
throw new IllegalStateException(
|
||||
"PlatformView#getView() returned null, but an Android view reference was expected.");
|
||||
}
|
||||
embeddedView.setLayoutDirection(request.direction);
|
||||
platformViews.put(request.viewId, platformView);
|
||||
maybeInvokeOnFlutterViewAttached(platformView);
|
||||
return platformView;
|
||||
}
|
||||
|
||||
// Configures the view for Hybrid Composition mode.
|
||||
private void configureForHybridComposition(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
enforceMinimumAndroidApiVersion(19);
|
||||
Log.i(TAG, "Using hybrid composition for platform view: " + request.viewId);
|
||||
}
|
||||
|
||||
// Configures the view for Virtual Display mode, returning the associated texture ID.
|
||||
private long configureForVirtualDisplay(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
// This mode adds the view to a virtual display, which is wired up to a GL texture that
|
||||
// is composed by the Flutter engine.
|
||||
|
||||
// API level 20 is required to use VirtualDisplay#setSurface.
|
||||
enforceMinimumAndroidApiVersion(20);
|
||||
|
||||
Log.i(TAG, "Hosting view in a virtual display for platform view: " + request.viewId);
|
||||
|
||||
final TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture();
|
||||
final int physicalWidth = toPhysicalPixels(request.logicalWidth);
|
||||
final int physicalHeight = toPhysicalPixels(request.logicalHeight);
|
||||
final VirtualDisplayController vdController =
|
||||
VirtualDisplayController.create(
|
||||
context,
|
||||
accessibilityEventsDelegate,
|
||||
platformView,
|
||||
textureEntry,
|
||||
physicalWidth,
|
||||
physicalHeight,
|
||||
request.viewId,
|
||||
null,
|
||||
(view, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
platformViewsChannel.invokeViewFocused(request.viewId);
|
||||
}
|
||||
});
|
||||
|
||||
if (vdController == null) {
|
||||
throw new IllegalStateException(
|
||||
"Failed creating virtual display for a "
|
||||
+ request.viewType
|
||||
+ " with id: "
|
||||
+ request.viewId);
|
||||
}
|
||||
|
||||
// The embedded view doesn't need to be sized in Virtual Display mode because the
|
||||
// virtual display itself is sized.
|
||||
|
||||
vdControllers.put(request.viewId, vdController);
|
||||
final View embeddedView = platformView.getView();
|
||||
contextToEmbeddedView.put(embeddedView.getContext(), embeddedView);
|
||||
|
||||
return textureEntry.id();
|
||||
}
|
||||
|
||||
// Configures the view for Texture Layer Hybrid Composition mode, returning the associated
|
||||
// texture ID.
|
||||
@TargetApi(23)
|
||||
private long configureForTextureLayerComposition(
|
||||
@NonNull PlatformView platformView,
|
||||
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
|
||||
// This mode attaches the view to the Android view hierarchy and record its drawing
|
||||
// operations, so they can be forwarded to a GL texture that is composed by the
|
||||
// Flutter engine.
|
||||
|
||||
// API level 23 is required to use Surface#lockHardwareCanvas().
|
||||
enforceMinimumAndroidApiVersion(23);
|
||||
Log.i(TAG, "Hosting view in view hierarchy for platform view: " + request.viewId);
|
||||
|
||||
final int physicalWidth = toPhysicalPixels(request.logicalWidth);
|
||||
final int physicalHeight = toPhysicalPixels(request.logicalHeight);
|
||||
PlatformViewWrapper viewWrapper;
|
||||
long textureId;
|
||||
if (usesSoftwareRendering) {
|
||||
viewWrapper = new PlatformViewWrapper(context);
|
||||
textureId = -1;
|
||||
} else {
|
||||
final TextureRegistry.SurfaceTextureEntry textureEntry =
|
||||
textureRegistry.createSurfaceTexture();
|
||||
viewWrapper = new PlatformViewWrapper(context, textureEntry);
|
||||
textureId = textureEntry.id();
|
||||
}
|
||||
viewWrapper.setTouchProcessor(androidTouchProcessor);
|
||||
viewWrapper.setBufferSize(physicalWidth, physicalHeight);
|
||||
|
||||
final FrameLayout.LayoutParams viewWrapperLayoutParams =
|
||||
new FrameLayout.LayoutParams(physicalWidth, physicalHeight);
|
||||
|
||||
// Size and position the view wrapper.
|
||||
final int physicalTop = toPhysicalPixels(request.logicalTop);
|
||||
final int physicalLeft = toPhysicalPixels(request.logicalLeft);
|
||||
viewWrapperLayoutParams.topMargin = physicalTop;
|
||||
viewWrapperLayoutParams.leftMargin = physicalLeft;
|
||||
viewWrapper.setLayoutParams(viewWrapperLayoutParams);
|
||||
|
||||
// Size the embedded view.
|
||||
final View embeddedView = platformView.getView();
|
||||
embeddedView.setLayoutParams(new FrameLayout.LayoutParams(physicalWidth, physicalHeight));
|
||||
|
||||
// Accessibility in the embedded view is initially disabled because if a Flutter app
|
||||
// disabled accessibility in the first frame, the embedding won't receive an update to
|
||||
// disable accessibility since the embedding never received an update to enable it.
|
||||
// The AccessibilityBridge keeps track of the accessibility nodes, and handles the deltas
|
||||
// when the framework sends a new a11y tree to the embedding.
|
||||
// To prevent races, the framework populate the SemanticsNode after the platform view has
|
||||
// been created.
|
||||
embeddedView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
// Add the embedded view to the wrapper.
|
||||
viewWrapper.addView(embeddedView);
|
||||
|
||||
// Listen for focus changed in any subview, so the framework is notified when the platform
|
||||
// view is focused.
|
||||
viewWrapper.setOnDescendantFocusChangeListener(
|
||||
(v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
platformViewsChannel.invokeViewFocused(request.viewId);
|
||||
} else if (textInputPlugin != null) {
|
||||
textInputPlugin.clearPlatformViewClient(request.viewId);
|
||||
}
|
||||
});
|
||||
|
||||
flutterView.addView(viewWrapper);
|
||||
viewWrappers.append(request.viewId, viewWrapper);
|
||||
|
||||
maybeInvokeOnFlutterViewAttached(platformView);
|
||||
|
||||
return textureId;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public MotionEvent toMotionEvent(
|
||||
float density, PlatformViewsChannel.PlatformViewTouch touch, boolean usingVirtualDiplay) {
|
||||
@ -840,6 +838,15 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeInvokeOnFlutterViewAttached(PlatformView view) {
|
||||
if (flutterView == null) {
|
||||
Log.i(TAG, "null flutterView");
|
||||
// There is currently no FlutterView that we are attached to.
|
||||
return;
|
||||
}
|
||||
view.onFlutterViewAttached(flutterView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAccessibilityBridge(@NonNull AccessibilityBridge accessibilityBridge) {
|
||||
accessibilityEventsDelegate.setAccessibilityBridge(accessibilityBridge);
|
||||
@ -1024,6 +1031,16 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes a single
|
||||
*
|
||||
* @param viewId the PlatformView ID.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void disposePlatformView(int viewId) {
|
||||
channelHandler.dispose(viewId);
|
||||
}
|
||||
|
||||
private void initializeRootImageViewIfNeeded() {
|
||||
if (synchronizeToNativeViewHierarchy && !flutterViewConvertedToImageView) {
|
||||
flutterView.convertToImageView();
|
||||
|
||||
@ -218,8 +218,8 @@ class SingleViewPresentation extends Presentation {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PlatformView getView() {
|
||||
if (state.platformView == null) return null;
|
||||
return state.platformView;
|
||||
}
|
||||
|
||||
|
||||
@ -38,9 +38,11 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer;
|
||||
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.KeyboardChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.StandardMessageCodec;
|
||||
import io.flutter.plugin.common.StandardMethodCodec;
|
||||
import io.flutter.plugin.localization.LocalizationPlugin;
|
||||
import io.flutter.view.TextureRegistry;
|
||||
@ -61,47 +63,82 @@ import org.robolectric.shadows.ShadowSurfaceView;
|
||||
@Config(manifest = Config.NONE)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PlatformViewsControllerTest {
|
||||
// An implementation of PlatformView that counts invocations of its lifecycle callbacks.
|
||||
class CountingPlatformView implements PlatformView {
|
||||
static final String VIEW_TYPE_ID = "CountingPlatformView";
|
||||
private View view;
|
||||
|
||||
public CountingPlatformView(Context context) {
|
||||
view = new SurfaceView(context);
|
||||
}
|
||||
|
||||
public int disposeCalls = 0;
|
||||
public int attachCalls = 0;
|
||||
public int detachCalls = 0;
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disposeCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlutterViewAttached(View flutterView) {
|
||||
attachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlutterViewDetached() {
|
||||
detachCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void itNotifiesVirtualDisplayControllersOfViewAttachmentAndDetachment() {
|
||||
// Setup test structure.
|
||||
FlutterView fakeFlutterView = new FlutterView(ApplicationProvider.getApplicationContext());
|
||||
|
||||
// Create fake VirtualDisplayControllers. This requires internal knowledge of
|
||||
// PlatformViewsController. We know that all PlatformViewsController does is
|
||||
// forward view attachment/detachment calls to it's VirtualDisplayControllers.
|
||||
//
|
||||
// TODO(mattcarroll): once PlatformViewsController is refactored into testable
|
||||
// pieces, remove this test and avoid verifying private behavior.
|
||||
VirtualDisplayController fakeVdController1 = mock(VirtualDisplayController.class);
|
||||
VirtualDisplayController fakeVdController2 = mock(VirtualDisplayController.class);
|
||||
|
||||
// Create the PlatformViewsController that is under test.
|
||||
@Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
|
||||
public void itNotifiesPlatformViewsOfEngineAttachmentAndDetachment() {
|
||||
PlatformViewsController platformViewsController = new PlatformViewsController();
|
||||
FlutterJNI jni = new FlutterJNI();
|
||||
attach(jni, platformViewsController);
|
||||
// Get the platform view registry.
|
||||
PlatformViewRegistry registry = platformViewsController.getRegistry();
|
||||
|
||||
// Manually inject fake VirtualDisplayControllers into the PlatformViewsController.
|
||||
platformViewsController.vdControllers.put(0, fakeVdController1);
|
||||
platformViewsController.vdControllers.put(1, fakeVdController1);
|
||||
// Register a factory for our platform view.
|
||||
registry.registerViewFactory(
|
||||
CountingPlatformView.VIEW_TYPE_ID,
|
||||
new PlatformViewFactory(StandardMessageCodec.INSTANCE) {
|
||||
@Override
|
||||
public PlatformView create(Context context, int viewId, Object args) {
|
||||
return new CountingPlatformView(context);
|
||||
}
|
||||
});
|
||||
|
||||
// Execute test & verify results.
|
||||
// Attach PlatformViewsController to the fake Flutter View.
|
||||
platformViewsController.attachToView(fakeFlutterView);
|
||||
|
||||
// Verify that all virtual display controllers were notified of View attachment.
|
||||
verify(fakeVdController1, times(1)).onFlutterViewAttached(eq(fakeFlutterView));
|
||||
verify(fakeVdController1, never()).onFlutterViewDetached();
|
||||
verify(fakeVdController2, times(1)).onFlutterViewAttached(eq(fakeFlutterView));
|
||||
verify(fakeVdController2, never()).onFlutterViewDetached();
|
||||
|
||||
// Detach PlatformViewsController from the fake Flutter View.
|
||||
// Create the platform view.
|
||||
int viewId = 0;
|
||||
final PlatformViewsChannel.PlatformViewCreationRequest request =
|
||||
new PlatformViewsChannel.PlatformViewCreationRequest(
|
||||
viewId++,
|
||||
CountingPlatformView.VIEW_TYPE_ID,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
128,
|
||||
View.LAYOUT_DIRECTION_LTR,
|
||||
null);
|
||||
PlatformView pView = platformViewsController.createPlatformView(request, true);
|
||||
assertTrue(pView instanceof CountingPlatformView);
|
||||
CountingPlatformView cpv = (CountingPlatformView) pView;
|
||||
assertEquals(1, cpv.attachCalls);
|
||||
assertEquals(0, cpv.detachCalls);
|
||||
assertEquals(0, cpv.disposeCalls);
|
||||
platformViewsController.detachFromView();
|
||||
|
||||
// Verify that all virtual display controllers were notified of the View detachment.
|
||||
verify(fakeVdController1, times(1)).onFlutterViewAttached(eq(fakeFlutterView));
|
||||
verify(fakeVdController1, times(1)).onFlutterViewDetached();
|
||||
verify(fakeVdController2, times(1)).onFlutterViewAttached(eq(fakeFlutterView));
|
||||
verify(fakeVdController2, times(1)).onFlutterViewDetached();
|
||||
assertEquals(1, cpv.attachCalls);
|
||||
assertEquals(1, cpv.detachCalls);
|
||||
assertEquals(0, cpv.disposeCalls);
|
||||
platformViewsController.disposePlatformView(viewId);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user