diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index da32f3205b23c..f63e899b4e8c9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -1210,7 +1210,6 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { false, systemSettingsObserver); - localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration()); sendViewportMetricsToFlutter(); flutterEngine.getPlatformViewsController().attachToView(this); diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 518e18c733d26..708e3f4a8c241 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -349,6 +349,8 @@ public FlutterEngine( this.pluginRegistry = new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader); + localizationPlugin.sendLocalesToFlutter(context.getResources().getConfiguration()); + // Only automatically register plugins if both constructor parameter and // loaded AndroidManifest config turn this feature on. if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) { diff --git a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java index 98a8e37757bdb..308ab1d07cf56 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java @@ -7,6 +7,8 @@ import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -15,6 +17,9 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.LocaleList; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import io.flutter.FlutterInjector; @@ -41,12 +46,20 @@ @RunWith(AndroidJUnit4.class) public class FlutterEngineTest { private final Context ctx = ApplicationProvider.getApplicationContext(); + private final Context mockContext = mock(Context.class); @Mock FlutterJNI flutterJNI; boolean jniAttached; @Before public void setUp() { MockitoAnnotations.openMocks(this); + + Resources mockResources = mock(Resources.class); + Configuration mockConfiguration = mock(Configuration.class); + doReturn(mockResources).when(mockContext).getResources(); + doReturn(mockConfiguration).when(mockResources).getConfiguration(); + doReturn(LocaleList.getEmptyLocaleList()).when(mockConfiguration).getLocales(); + jniAttached = false; when(flutterJNI.isAttached()).thenAnswer(invocation -> jniAttached); doAnswer( @@ -82,6 +95,20 @@ public void itAutomaticallyRegistersPluginsByDefault() { assertEquals(flutterEngine, registeredEngines.get(0)); } + @Test + public void itSendLocalesOnEngineInit() { + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + when(mockFlutterJNI.isAttached()).thenReturn(true); + + assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty()); + FlutterLoader mockFlutterLoader = mock(FlutterLoader.class); + when(mockFlutterLoader.automaticallyRegisterPlugins()).thenReturn(true); + FlutterEngine flutterEngine = new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJNI); + + verify(mockFlutterJNI, times(1)) + .dispatchPlatformMessage(eq("flutter/localization"), any(), anyInt(), anyInt()); + } + // Helps show the root cause of MissingPluginException type errors like // https://github.com/flutter/flutter/issues/78625. @Test @@ -194,36 +221,34 @@ public void itNotifiesPlatformViewsControllerAboutJNILifecycle() { @Test public void itUsesApplicationContext() throws NameNotFoundException { - Context context = mock(Context.class); Context packageContext = mock(Context.class); - when(context.createPackageContext(any(), anyInt())).thenReturn(packageContext); + when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); new FlutterEngine( - context, + mockContext, mock(FlutterLoader.class), flutterJNI, /*dartVmArgs=*/ new String[] {}, /*automaticallyRegisterPlugins=*/ false); - verify(context, atLeast(1)).getApplicationContext(); + verify(mockContext, atLeast(1)).getApplicationContext(); } @Test public void itUsesPackageContextForAssetManager() throws NameNotFoundException { - Context context = mock(Context.class); Context packageContext = mock(Context.class); - when(context.createPackageContext(any(), anyInt())).thenReturn(packageContext); + when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); new FlutterEngine( - context, + mockContext, mock(FlutterLoader.class), flutterJNI, /*dartVmArgs=*/ new String[] {}, /*automaticallyRegisterPlugins=*/ false); verify(packageContext, atLeast(1)).getAssets(); - verify(context, times(0)).getAssets(); + verify(mockContext, times(0)).getAssets(); } @Test @@ -232,7 +257,6 @@ public void itCanUseFlutterLoaderInjectionViaFlutterInjector() throws NameNotFou FlutterLoader mockFlutterLoader = mock(FlutterLoader.class); FlutterInjector.setInstance( new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build()); - Context mockContext = mock(Context.class); Context packageContext = mock(Context.class); when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); @@ -246,14 +270,13 @@ public void itCanUseFlutterLoaderInjectionViaFlutterInjector() throws NameNotFou @Test public void itNotifiesListenersForDestruction() throws NameNotFoundException { - Context context = mock(Context.class); Context packageContext = mock(Context.class); - when(context.createPackageContext(any(), anyInt())).thenReturn(packageContext); + when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); FlutterEngine engineUnderTest = new FlutterEngine( - context, + mockContext, mock(FlutterLoader.class), flutterJNI, /*dartVmArgs=*/ new String[] {}, @@ -267,15 +290,14 @@ public void itNotifiesListenersForDestruction() throws NameNotFoundException { @Test public void itDoesNotAttachAgainWhenBuiltWithAnAttachedJNI() throws NameNotFoundException { - Context context = mock(Context.class); Context packageContext = mock(Context.class); - when(context.createPackageContext(any(), anyInt())).thenReturn(packageContext); + when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); when(flutterJNI.isAttached()).thenReturn(true); FlutterEngine engineUnderTest = new FlutterEngine( - context, + mockContext, mock(FlutterLoader.class), flutterJNI, /*dartVmArgs=*/ new String[] {}, @@ -286,15 +308,14 @@ public void itDoesNotAttachAgainWhenBuiltWithAnAttachedJNI() throws NameNotFound @Test public void itComesWithARunningDartExecutorIfJNIIsAlreadyAttached() throws NameNotFoundException { - Context context = mock(Context.class); Context packageContext = mock(Context.class); - when(context.createPackageContext(any(), anyInt())).thenReturn(packageContext); + when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext); when(flutterJNI.isAttached()).thenReturn(true); FlutterEngine engineUnderTest = new FlutterEngine( - context, + mockContext, mock(FlutterLoader.class), flutterJNI, /*dartVmArgs=*/ new String[] {},