Add registration calls for ComponentCallbacks2 (flutter/engine#34206)

This commit is contained in:
Gary Qian 2022-06-28 00:29:04 -07:00 committed by GitHub
parent 3d93160b36
commit 2a7cb615dd
3 changed files with 92 additions and 15 deletions

View File

@ -71,6 +71,11 @@ import java.util.List;
private static final String PLUGINS_RESTORATION_BUNDLE_KEY = "plugins";
private static final int FLUTTER_SPLASH_VIEW_FALLBACK_ID = 486947586;
/** Factory to obtain a FlutterActivityAndFragmentDelegate instance. */
public interface DelegateFactory {
FlutterActivityAndFragmentDelegate createDelegate(FlutterActivityAndFragmentDelegate.Host host);
}
// The FlutterActivity or FlutterFragment that is delegating most of its calls
// to this FlutterActivityAndFragmentDelegate.
@NonNull private Host host;

View File

@ -94,7 +94,9 @@ import java.util.List;
* Activity}, as well as forwarding lifecycle calls from an {@code Activity} or a {@code Fragment}.
*/
public class FlutterFragment extends Fragment
implements FlutterActivityAndFragmentDelegate.Host, ComponentCallbacks2 {
implements FlutterActivityAndFragmentDelegate.Host,
ComponentCallbacks2,
FlutterActivityAndFragmentDelegate.DelegateFactory {
/**
* The ID of the {@code FlutterView} created by this activity.
*
@ -732,6 +734,14 @@ public class FlutterFragment extends Fragment
// implementation for details about why it exists.
@VisibleForTesting @Nullable /* package */ FlutterActivityAndFragmentDelegate delegate;
@NonNull private FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory = this;
/** Default delegate factory that creates a simple FlutterActivityAndFragmentDelegate instance. */
public FlutterActivityAndFragmentDelegate createDelegate(
FlutterActivityAndFragmentDelegate.Host host) {
return new FlutterActivityAndFragmentDelegate(host);
}
private final OnBackPressedCallback onBackPressedCallback =
new OnBackPressedCallback(true) {
@Override
@ -757,8 +767,10 @@ public class FlutterFragment extends Fragment
// TODO(mattcarroll): remove this when tests allow for it
// (https://github.com/flutter/flutter/issues/43798)
@VisibleForTesting
/* package */ void setDelegate(@NonNull FlutterActivityAndFragmentDelegate delegate) {
this.delegate = delegate;
/* package */ void setDelegateFactory(
@NonNull FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory) {
this.delegateFactory = delegateFactory;
delegate = delegateFactory.createDelegate(this);
}
/**
@ -773,11 +785,12 @@ public class FlutterFragment extends Fragment
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate = delegateFactory.createDelegate(this);
delegate.onAttach(context);
if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
}
context.registerComponentCallbacks(this);
}
@Override
@ -873,6 +886,7 @@ public class FlutterFragment extends Fragment
@Override
public void onDetach() {
getContext().unregisterComponentCallbacks(this);
super.onDetach();
if (delegate != null) {
delegate.onDetach();

View File

@ -6,9 +6,11 @@ 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.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -36,10 +38,25 @@ public class FlutterFragmentTest {
private final Context ctx = ApplicationProvider.getApplicationContext();
boolean isDelegateAttached;
class TestDelegateFactory implements FlutterActivityAndFragmentDelegate.DelegateFactory {
FlutterActivityAndFragmentDelegate delegate;
TestDelegateFactory(FlutterActivityAndFragmentDelegate delegate) {
this.delegate = delegate;
}
public FlutterActivityAndFragmentDelegate createDelegate(
FlutterActivityAndFragmentDelegate.Host host) {
return delegate;
}
}
@Test
public void itCreatesDefaultFragmentWithExpectedDefaults() {
FlutterFragment fragment = FlutterFragment.createDefault();
fragment.setDelegate(new FlutterActivityAndFragmentDelegate(fragment));
TestDelegateFactory delegateFactory =
new TestDelegateFactory(new FlutterActivityAndFragmentDelegate(fragment));
fragment.setDelegateFactory(delegateFactory);
assertEquals("main", fragment.getDartEntrypointFunctionName());
assertNull(fragment.getDartEntrypointLibraryUri());
@ -68,7 +85,9 @@ public class FlutterFragmentTest {
.renderMode(RenderMode.texture)
.transparencyMode(TransparencyMode.opaque)
.build();
fragment.setDelegate(new FlutterActivityAndFragmentDelegate(fragment));
TestDelegateFactory delegateFactory =
new TestDelegateFactory(new FlutterActivityAndFragmentDelegate(fragment));
fragment.setDelegateFactory(delegateFactory);
assertEquals("custom_entrypoint", fragment.getDartEntrypointFunctionName());
assertEquals("package:foo/bar.dart", fragment.getDartEntrypointLibraryUri());
@ -127,6 +146,7 @@ public class FlutterFragmentTest {
public void itCanBeDetachedFromTheEngineAndStopSendingFurtherEvents() {
FlutterActivityAndFragmentDelegate mockDelegate =
mock(FlutterActivityAndFragmentDelegate.class);
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
FlutterFragment fragment =
FlutterFragment.withCachedEngine("my_cached_engine")
.destroyEngineWithFragment(true)
@ -136,7 +156,7 @@ public class FlutterFragmentTest {
when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
fragment.setDelegate(mockDelegate);
fragment.setDelegateFactory(delegateFactory);
fragment.onStart();
fragment.onResume();
fragment.onPostResume();
@ -175,13 +195,14 @@ public class FlutterFragmentTest {
isDelegateAttached = true;
when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
FlutterFragment fragment =
FlutterFragment.withCachedEngine("my_cached_engine")
.destroyEngineWithFragment(true)
.build();
fragment.setDelegate(mockDelegate);
fragment.setDelegateFactory(delegateFactory);
fragment.onStart();
fragment.onResume();
fragment.onPostResume();
@ -201,13 +222,16 @@ public class FlutterFragmentTest {
isDelegateAttached = true;
when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
FlutterFragment fragment =
FlutterFragment.withCachedEngine("my_cached_engine")
.destroyEngineWithFragment(true)
.build();
spy(
FlutterFragment.withCachedEngine("my_cached_engine")
.destroyEngineWithFragment(true)
.build());
when(fragment.getContext()).thenReturn(mock(Context.class));
fragment.setDelegate(mockDelegate);
fragment.setDelegateFactory(delegateFactory);
fragment.onStart();
fragment.onResume();
fragment.onPostResume();
@ -224,7 +248,8 @@ public class FlutterFragmentTest {
public void itReturnsExclusiveAppComponent() {
FlutterFragment fragment = FlutterFragment.createDefault();
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(fragment);
fragment.setDelegate(delegate);
TestDelegateFactory delegateFactory = new TestDelegateFactory(delegate);
fragment.setDelegateFactory(delegateFactory);
assertEquals(fragment.getExclusiveAppComponent(), delegate);
}
@ -255,7 +280,8 @@ public class FlutterFragmentTest {
isDelegateAttached = true;
when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
fragment.setDelegate(mockDelegate);
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
fragment.setDelegateFactory(delegateFactory);
activity.onBackPressed();
@ -294,11 +320,43 @@ public class FlutterFragmentTest {
FlutterActivityAndFragmentDelegate mockDelegate =
mock(FlutterActivityAndFragmentDelegate.class);
fragment.setDelegate(mockDelegate);
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
fragment.setDelegateFactory(delegateFactory);
assertTrue(fragment.popSystemNavigator());
verify(mockDelegate, never()).onBackPressed();
assertTrue(onBackPressedCalled.get());
}
@Test
public void itRegistersComponentCallbacks() {
FlutterActivityAndFragmentDelegate mockDelegate =
mock(FlutterActivityAndFragmentDelegate.class);
isDelegateAttached = true;
when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach();
TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate);
Context spyCtx = spy(ctx);
// We need to mock FlutterJNI to avoid triggering native code.
FlutterJNI flutterJNI = mock(FlutterJNI.class);
when(flutterJNI.isAttached()).thenReturn(true);
FlutterEngine flutterEngine =
new FlutterEngine(spyCtx, new FlutterLoader(), flutterJNI, null, false);
FlutterEngineCache.getInstance().put("my_cached_engine", flutterEngine);
FlutterFragment fragment = spy(FlutterFragment.withCachedEngine("my_cached_engine").build());
when(fragment.getContext()).thenReturn(spyCtx);
fragment.setDelegateFactory(delegateFactory);
fragment.onAttach(spyCtx);
verify(spyCtx, times(1)).registerComponentCallbacks(any());
verify(spyCtx, never()).unregisterComponentCallbacks(any());
fragment.onDetach();
verify(spyCtx, times(1)).registerComponentCallbacks(any());
verify(spyCtx, times(1)).unregisterComponentCallbacks(any());
}
}