Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ee91881

Browse files
Adds Dark Mode support to new Android embedding (this was accidentally missed previously). (#13215)
1 parent 98e6d15 commit ee91881

File tree

2 files changed

+117
-12
lines changed

2 files changed

+117
-12
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterView.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
package io.flutter.embedding.android;
66

7-
import android.annotation.TargetApi;
87
import android.annotation.SuppressLint;
8+
import android.annotation.TargetApi;
99
import android.content.Context;
1010
import android.content.res.Configuration;
1111
import android.graphics.Insets;
@@ -39,6 +39,7 @@
3939
import io.flutter.embedding.engine.renderer.FlutterRenderer;
4040
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
4141
import io.flutter.embedding.engine.renderer.RenderSurface;
42+
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
4243
import io.flutter.plugin.editing.TextInputPlugin;
4344
import io.flutter.plugin.platform.PlatformViewsController;
4445
import io.flutter.view.AccessibilityBridge;
@@ -761,10 +762,19 @@ private void sendLocalesToFlutter(@NonNull Configuration config) {
761762
*
762763
* FlutterEngine must be non-null when this method is invoked.
763764
*/
764-
private void sendUserSettingsToFlutter() {
765-
flutterEngine.getSettingsChannel().startMessage()
765+
@VisibleForTesting
766+
/* package */ void sendUserSettingsToFlutter() {
767+
// Lookup the current brightness of the Android OS.
768+
boolean isNightModeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
769+
SettingsChannel.PlatformBrightness brightness = isNightModeOn
770+
? SettingsChannel.PlatformBrightness.dark
771+
: SettingsChannel.PlatformBrightness.light;
772+
773+
flutterEngine.getSettingsChannel()
774+
.startMessage()
766775
.setTextScaleFactor(getResources().getConfiguration().fontScale)
767776
.setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
777+
.setPlatformBrightness(brightness)
768778
.send();
769779
}
770780

shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,45 @@
11
package io.flutter.embedding.android;
22

3+
import android.content.Context;
4+
import android.content.res.Configuration;
5+
import android.content.res.Resources;
6+
import android.support.annotation.NonNull;
7+
8+
import static junit.framework.TestCase.assertEquals;
9+
import static org.mockito.Matchers.any;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.reset;
312
import static org.mockito.Mockito.spy;
413
import static org.mockito.Mockito.times;
514
import static org.mockito.Mockito.verify;
615
import static org.mockito.Mockito.when;
716

817
import io.flutter.embedding.engine.FlutterJNI;
18+
import io.flutter.embedding.engine.dart.DartExecutor;
919
import io.flutter.embedding.engine.loader.FlutterLoader;
20+
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
21+
import io.flutter.embedding.engine.renderer.FlutterRenderer;
22+
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
23+
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
24+
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
25+
import io.flutter.embedding.engine.systemchannels.NavigationChannel;
26+
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
27+
import io.flutter.embedding.engine.systemchannels.SystemChannel;
1028
import io.flutter.plugin.platform.PlatformViewsController;
1129
import org.junit.Before;
1230
import org.junit.Test;
1331
import org.junit.runner.RunWith;
1432
import org.mockito.Mock;
1533
import org.mockito.MockitoAnnotations;
1634
import org.mockito.Spy;
35+
import org.mockito.invocation.InvocationOnMock;
36+
import org.mockito.stubbing.Answer;
1737
import org.robolectric.RobolectricTestRunner;
1838
import org.robolectric.RuntimeEnvironment;
1939
import org.robolectric.annotation.Config;
2040

41+
import java.util.concurrent.atomic.AtomicReference;
42+
2143
import io.flutter.embedding.engine.FlutterEngine;
2244

2345
@Config(manifest = Config.NONE)
@@ -44,15 +66,88 @@ public void attachToFlutterEngine_alertsPlatformViews() {
4466
verify(platformViewsController, times(1)).attachToView(flutterView);
4567
}
4668

47-
@Test
48-
public void detachFromFlutterEngine_alertsPlatformViews() {
49-
FlutterView flutterView = new FlutterView(RuntimeEnvironment.application);
50-
FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
51-
when(flutterEngine.getPlatformViewsController()).thenReturn(platformViewsController);
69+
@Test
70+
public void detachFromFlutterEngine_alertsPlatformViews() {
71+
FlutterView flutterView = new FlutterView(RuntimeEnvironment.application);
72+
FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
73+
when(flutterEngine.getPlatformViewsController()).thenReturn(platformViewsController);
74+
75+
flutterView.attachToFlutterEngine(flutterEngine);
76+
flutterView.detachFromFlutterEngine();
77+
78+
verify(platformViewsController, times(1)).detachFromView();
79+
}
80+
81+
// TODO(mattcarroll): turn this into an e2e test. GitHub #42990
82+
@Test
83+
public void itSendsLightPlatformBrightnessToFlutter() {
84+
// Setup test.
85+
AtomicReference<SettingsChannel.PlatformBrightness> reportedBrightness = new AtomicReference<>();
86+
87+
// FYI - The default brightness is LIGHT, which is why we don't need to configure it.
88+
FlutterView flutterView = new FlutterView(RuntimeEnvironment.application);
89+
FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
5290

53-
flutterView.attachToFlutterEngine(flutterEngine);
54-
flutterView.detachFromFlutterEngine();
91+
SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
92+
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
93+
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
94+
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
95+
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class))).thenAnswer(new Answer<SettingsChannel.MessageBuilder>() {
96+
@Override
97+
public SettingsChannel.MessageBuilder answer(InvocationOnMock invocation) throws Throwable {
98+
reportedBrightness.set((SettingsChannel.PlatformBrightness) invocation.getArguments()[0]);
99+
return fakeMessageBuilder;
100+
}
101+
});
102+
when(fakeSettingsChannel.startMessage()).thenReturn(fakeMessageBuilder);
103+
when(flutterEngine.getSettingsChannel()).thenReturn(fakeSettingsChannel);
55104

56-
verify(platformViewsController, times(1)).detachFromView();
57-
}
105+
flutterView.attachToFlutterEngine(flutterEngine);
106+
107+
// Execute behavior under test.
108+
flutterView.sendUserSettingsToFlutter();
109+
110+
// Verify results.
111+
assertEquals(SettingsChannel.PlatformBrightness.light, reportedBrightness.get());
112+
}
113+
114+
// TODO(mattcarroll): turn this into an e2e test. GitHub #42990
115+
@Test
116+
public void itSendsDarkPlatformBrightnessToFlutter() {
117+
// Setup test.
118+
AtomicReference<SettingsChannel.PlatformBrightness> reportedBrightness = new AtomicReference<>();
119+
120+
Context spiedContext = spy(RuntimeEnvironment.application);
121+
122+
Resources spiedResources = spy(spiedContext.getResources());
123+
when(spiedContext.getResources()).thenReturn(spiedResources);
124+
125+
Configuration spiedConfiguration = spy(spiedResources.getConfiguration());
126+
spiedConfiguration.uiMode = (spiedResources.getConfiguration().uiMode | Configuration.UI_MODE_NIGHT_YES) & ~Configuration.UI_MODE_NIGHT_NO;
127+
when(spiedResources.getConfiguration()).thenReturn(spiedConfiguration);
128+
129+
FlutterView flutterView = new FlutterView(spiedContext);
130+
FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
131+
132+
SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
133+
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
134+
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
135+
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
136+
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class))).thenAnswer(new Answer<SettingsChannel.MessageBuilder>() {
137+
@Override
138+
public SettingsChannel.MessageBuilder answer(InvocationOnMock invocation) throws Throwable {
139+
reportedBrightness.set((SettingsChannel.PlatformBrightness) invocation.getArguments()[0]);
140+
return fakeMessageBuilder;
141+
}
142+
});
143+
when(fakeSettingsChannel.startMessage()).thenReturn(fakeMessageBuilder);
144+
when(flutterEngine.getSettingsChannel()).thenReturn(fakeSettingsChannel);
145+
146+
// Execute behavior under test.
147+
flutterView.attachToFlutterEngine(flutterEngine);
148+
flutterView.sendUserSettingsToFlutter();
149+
150+
// Verify results.
151+
assertEquals(SettingsChannel.PlatformBrightness.dark, reportedBrightness.get());
152+
}
58153
}

0 commit comments

Comments
 (0)