Skip to content

Commit 17862a7

Browse files
hramosfacebook-github-bot
authored andcommitted
Add Appearance module
Summary: Android implementation of the Appearance native module. Exposes the user's preferred color scheme: "dark" for Night theme ON, "light" for Night theme OFF. Emits a `appearanceChanged` event when the current uiMode configuration changes. To make your app handle Night mode changes, make sure to do the following: * Declare your Activity can handle uiMode configuration changes (https://developer.android.com/preview/features/darktheme#java): ``` android:configChanges="uiMode" ``` * Make sure to pass the configuration changed activity lifecycle callback from your ReactActivity: ``` Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(); if (mReactInstanceManager != null) { mReactInstanceManager.onConfigurationChanged(newConfig); } } ``` ### RNTester Adds the AppearanceExample to RNTester on Android. Changelog: [Android][Added] - New Appearance module exposes the user's current Night theme preference Reviewed By: makovkastar Differential Revision: D16942161 fbshipit-source-id: d24a8ff800a1c5f70f4efdec6891396c2078067e
1 parent 51681e8 commit 17862a7

File tree

11 files changed

+285
-48
lines changed

11 files changed

+285
-48
lines changed

RNTester/android/app/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def enableProguardInReleaseBuilds = true
101101
def useIntlJsc = false
102102

103103
android {
104-
compileSdkVersion 28
104+
compileSdkVersion 29
105105

106106
compileOptions {
107107
sourceCompatibility JavaVersion.VERSION_1_8
@@ -121,7 +121,7 @@ android {
121121
defaultConfig {
122122
applicationId "com.facebook.react.uiapp"
123123
minSdkVersion 16
124-
targetSdkVersion 28
124+
targetSdkVersion 29
125125
versionCode 1
126126
versionName "1.0"
127127
}

RNTester/android/app/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
android:name=".RNTesterActivity"
2929
android:label="@string/app_name"
3030
android:screenOrientation="fullSensor"
31-
android:configChanges="orientation|screenSize" >
31+
android:configChanges="orientation|screenSize|uiMode" >
3232
<intent-filter>
3333
<action android:name="android.intent.action.MAIN" />
3434
<category android:name="android.intent.category.LAUNCHER" />

RNTester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.java

+12
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
*/
77
package com.facebook.react.uiapp;
88

9+
import android.content.res.Configuration;
910
import android.os.Bundle;
1011
import androidx.annotation.Nullable;
1112
import com.facebook.react.ReactActivity;
1213
import com.facebook.react.ReactActivityDelegate;
14+
import com.facebook.react.ReactInstanceManager;
1315

1416
public class RNTesterActivity extends ReactActivity {
1517
public static class RNTesterActivityDelegate extends ReactActivityDelegate {
@@ -53,4 +55,14 @@ protected ReactActivityDelegate createReactActivityDelegate() {
5355
protected String getMainComponentName() {
5456
return "RNTesterApp";
5557
}
58+
59+
@Override
60+
public void onConfigurationChanged(Configuration newConfig) {
61+
super.onConfigurationChanged(newConfig);
62+
ReactInstanceManager instanceManager = getReactInstanceManager();
63+
64+
if (instanceManager != null) {
65+
instanceManager.onConfigurationChanged(newConfig);
66+
}
67+
}
5668
}

RNTester/js/RNTesterApp.android.js

+135-45
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@ const {
3333
Text,
3434
TouchableWithoutFeedback,
3535
UIManager,
36+
useColorScheme,
3637
View,
3738
} = require('react-native');
3839

40+
import type {RNTesterExample} from './types/RNTesterTypes';
3941
import type {RNTesterNavigationState} from './utils/RNTesterNavigationReducer';
42+
import {RNTesterThemeContext, themes} from './components/RNTesterTheme';
4043

4144
UIManager.setLayoutAnimationEnabledExperimental(true);
4245

@@ -54,18 +57,119 @@ const HEADER_NAV_ICON = nativeImageSource({
5457
height: 48,
5558
});
5659

57-
const Header = ({title, onPressDrawer}) => {
60+
const Header = ({
61+
onPressDrawer,
62+
title,
63+
}: {
64+
onPressDrawer?: () => mixed,
65+
title: string,
66+
}) => (
67+
<RNTesterThemeContext.Consumer>
68+
{theme => {
69+
return (
70+
<View style={[styles.toolbar, {backgroundColor: theme.ToolbarColor}]}>
71+
<View style={styles.toolbarCenter}>
72+
<Text style={[styles.title, {color: theme.LabelColor}]}>
73+
{title}
74+
</Text>
75+
</View>
76+
<View style={styles.toolbarLeft}>
77+
<TouchableWithoutFeedback onPress={onPressDrawer}>
78+
<Image source={HEADER_NAV_ICON} />
79+
</TouchableWithoutFeedback>
80+
</View>
81+
</View>
82+
);
83+
}}
84+
</RNTesterThemeContext.Consumer>
85+
);
86+
87+
const RNTesterExampleContainerViaHook = ({
88+
onPressDrawer,
89+
title,
90+
module,
91+
exampleRef,
92+
}: {
93+
onPressDrawer?: () => mixed,
94+
title: string,
95+
module: RNTesterExample,
96+
exampleRef: () => void,
97+
}) => {
98+
const colorScheme = useColorScheme();
99+
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
58100
return (
59-
<View style={styles.toolbar}>
60-
<View style={styles.toolbarCenter}>
61-
<Text style={styles.title}>{title}</Text>
101+
<RNTesterThemeContext.Provider value={theme}>
102+
<View style={styles.container}>
103+
<Header
104+
title={title}
105+
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
106+
* was found when making Flow check .android.js files. */
107+
onPressDrawer={onPressDrawer}
108+
/>
109+
<RNTesterExampleContainer module={module} ref={exampleRef} />
62110
</View>
63-
<View style={styles.toolbarLeft}>
64-
<TouchableWithoutFeedback onPress={onPressDrawer}>
65-
<Image source={HEADER_NAV_ICON} />
66-
</TouchableWithoutFeedback>
111+
</RNTesterThemeContext.Provider>
112+
);
113+
};
114+
115+
const RNTesterDrawerContentViaHook = ({
116+
onNavigate,
117+
list,
118+
}: {
119+
onNavigate?: () => mixed,
120+
list: {
121+
ComponentExamples: Array<RNTesterExample>,
122+
APIExamples: Array<RNTesterExample>,
123+
},
124+
}) => {
125+
const colorScheme = useColorScheme();
126+
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
127+
return (
128+
<RNTesterThemeContext.Provider value={theme}>
129+
<View
130+
style={[
131+
styles.drawerContentWrapper,
132+
{backgroundColor: theme.SystemBackgroundColor},
133+
]}>
134+
<RNTesterExampleList
135+
list={list}
136+
displayTitleRow={true}
137+
disableSearch={true}
138+
onNavigate={onNavigate}
139+
/>
140+
</View>
141+
</RNTesterThemeContext.Provider>
142+
);
143+
};
144+
145+
const RNTesterExampleListViaHook = ({
146+
title,
147+
onPressDrawer,
148+
onNavigate,
149+
list,
150+
}: {
151+
title: string,
152+
onPressDrawer?: () => mixed,
153+
onNavigate?: () => mixed,
154+
list: {
155+
ComponentExamples: Array<RNTesterExample>,
156+
APIExamples: Array<RNTesterExample>,
157+
},
158+
}) => {
159+
const colorScheme = useColorScheme();
160+
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
161+
return (
162+
<RNTesterThemeContext.Provider value={theme}>
163+
<View style={styles.container}>
164+
<Header
165+
title={title}
166+
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
167+
* was found when making Flow check .android.js files. */
168+
onPressDrawer={onPressDrawer}
169+
/>
170+
<RNTesterExampleList onNavigate={onNavigate} list={list} />
67171
</View>
68-
</View>
172+
</RNTesterThemeContext.Provider>
69173
);
70174
};
71175

@@ -133,14 +237,10 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
133237

134238
_renderDrawerContent = () => {
135239
return (
136-
<View style={styles.drawerContentWrapper}>
137-
<RNTesterExampleList
138-
list={RNTesterList}
139-
displayTitleRow={true}
140-
disableSearch={true}
141-
onNavigate={this._handleAction}
142-
/>
143-
</View>
240+
<RNTesterDrawerContentViaHook
241+
onNavigate={this._handleAction}
242+
list={RNTesterList}
243+
/>
144244
);
145245
};
146246

@@ -164,39 +264,31 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
164264
);
165265
} else if (ExampleModule) {
166266
return (
167-
<View style={styles.container}>
168-
<Header
169-
title={ExampleModule.title}
267+
<RNTesterExampleContainerViaHook
268+
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
269+
* when making Flow check .android.js files. */
270+
onPressDrawer={() => this.drawer.openDrawer()}
271+
title={ExampleModule.title}
272+
module={ExampleModule}
273+
exampleRef={example => {
170274
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
171275
* was found when making Flow check .android.js files. */
172-
onPressDrawer={() => this.drawer.openDrawer()}
173-
/>
174-
<RNTesterExampleContainer
175-
module={ExampleModule}
176-
ref={example => {
177-
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
178-
* was found when making Flow check .android.js files. */
179-
this._exampleRef = example;
180-
}}
181-
/>
182-
</View>
276+
this._exampleRef = example;
277+
}}
278+
/>
183279
);
184280
}
185281
}
186282

187283
return (
188-
<View style={styles.container}>
189-
<Header
190-
title="RNTester"
191-
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
192-
* was found when making Flow check .android.js files. */
193-
onPressDrawer={() => this.drawer.openDrawer()}
194-
/>
195-
<RNTesterExampleList
196-
onNavigate={this._handleAction}
197-
list={RNTesterList}
198-
/>
199-
</View>
284+
<RNTesterExampleListViaHook
285+
title={'RNTester'}
286+
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
287+
* when making Flow check .android.js files. */
288+
onPressDrawer={() => this.drawer.openDrawer()}
289+
onNavigate={this._handleAction}
290+
list={RNTesterList}
291+
/>
200292
);
201293
}
202294

@@ -246,7 +338,6 @@ const styles = StyleSheet.create({
246338
flex: 1,
247339
},
248340
toolbar: {
249-
backgroundColor: '#E9EAED',
250341
height: 56,
251342
},
252343
toolbarLeft: {
@@ -268,7 +359,6 @@ const styles = StyleSheet.create({
268359
drawerContentWrapper: {
269360
flex: 1,
270361
paddingTop: StatusBar.currentHeight,
271-
backgroundColor: 'white',
272362
},
273363
});
274364

RNTester/js/utils/RNTesterList.android.js

+4
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ const APIExamples: Array<RNTesterExample> = [
128128
key: 'AnimatedExample',
129129
module: require('../examples/Animated/AnimatedExample'),
130130
},
131+
{
132+
key: 'AppearanceExample',
133+
module: require('../examples/Appearance/AppearanceExample'),
134+
},
131135
{
132136
key: 'AppStateExample',
133137
module: require('../examples/AppState/AppStateExample'),

ReactAndroid/src/main/java/com/facebook/react/BUCK

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ rn_android_library(
3333
react_native_target("java/com/facebook/react/module/annotations:annotations"),
3434
react_native_target("java/com/facebook/react/module/model:model"),
3535
react_native_target("java/com/facebook/react/modules/appregistry:appregistry"),
36+
react_native_target("java/com/facebook/react/modules/appearance:appearance"),
3637
react_native_target("java/com/facebook/react/modules/core:core"),
3738
react_native_target("java/com/facebook/react/modules/debug:debug"),
3839
react_native_target("java/com/facebook/react/modules/fabric:fabric"),

ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import android.app.Activity;
3535
import android.content.Context;
3636
import android.content.Intent;
37+
import android.content.res.Configuration;
3738
import android.net.Uri;
3839
import android.os.Bundle;
3940
import android.os.Process;
@@ -78,6 +79,7 @@
7879
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
7980
import com.facebook.react.devsupport.interfaces.DevSupportManager;
8081
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
82+
import com.facebook.react.modules.appearance.AppearanceModule;
8183
import com.facebook.react.modules.appregistry.AppRegistry;
8284
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
8385
import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -712,6 +714,17 @@ public void onWindowFocusChange(boolean hasFocus) {
712714
}
713715
}
714716

717+
/** Call this from {@link Activity#onConfigurationChanged()}. */
718+
@ThreadConfined(UI)
719+
public void onConfigurationChanged(Configuration newConfig) {
720+
UiThreadUtil.assertOnUiThread();
721+
722+
ReactContext currentContext = getCurrentReactContext();
723+
if (currentContext != null) {
724+
currentContext.getNativeModule(AppearanceModule.class).onConfigurationChanged();
725+
}
726+
}
727+
715728
@ThreadConfined(UI)
716729
public void showDevOptionsDialog() {
717730
UiThreadUtil.assertOnUiThread();

0 commit comments

Comments
 (0)