Skip to content

Commit

Permalink
Add Appearance module
Browse files Browse the repository at this point in the history
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
  • Loading branch information
hramos authored and facebook-github-bot committed Aug 31, 2019
1 parent 51681e8 commit 17862a7
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 48 deletions.
4 changes: 2 additions & 2 deletions RNTester/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def enableProguardInReleaseBuilds = true
def useIntlJsc = false

android {
compileSdkVersion 28
compileSdkVersion 29

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
Expand All @@ -121,7 +121,7 @@ android {
defaultConfig {
applicationId "com.facebook.react.uiapp"
minSdkVersion 16
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
Expand Down
2 changes: 1 addition & 1 deletion RNTester/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
android:name=".RNTesterActivity"
android:label="@string/app_name"
android:screenOrientation="fullSensor"
android:configChanges="orientation|screenSize" >
android:configChanges="orientation|screenSize|uiMode" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
*/
package com.facebook.react.uiapp;

import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactInstanceManager;

public class RNTesterActivity extends ReactActivity {
public static class RNTesterActivityDelegate extends ReactActivityDelegate {
Expand Down Expand Up @@ -53,4 +55,14 @@ protected ReactActivityDelegate createReactActivityDelegate() {
protected String getMainComponentName() {
return "RNTesterApp";
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ReactInstanceManager instanceManager = getReactInstanceManager();

if (instanceManager != null) {
instanceManager.onConfigurationChanged(newConfig);
}
}
}
180 changes: 135 additions & 45 deletions RNTester/js/RNTesterApp.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ const {
Text,
TouchableWithoutFeedback,
UIManager,
useColorScheme,
View,
} = require('react-native');

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

UIManager.setLayoutAnimationEnabledExperimental(true);

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

const Header = ({title, onPressDrawer}) => {
const Header = ({
onPressDrawer,
title,
}: {
onPressDrawer?: () => mixed,
title: string,
}) => (
<RNTesterThemeContext.Consumer>
{theme => {
return (
<View style={[styles.toolbar, {backgroundColor: theme.ToolbarColor}]}>
<View style={styles.toolbarCenter}>
<Text style={[styles.title, {color: theme.LabelColor}]}>
{title}
</Text>
</View>
<View style={styles.toolbarLeft}>
<TouchableWithoutFeedback onPress={onPressDrawer}>
<Image source={HEADER_NAV_ICON} />
</TouchableWithoutFeedback>
</View>
</View>
);
}}
</RNTesterThemeContext.Consumer>
);

const RNTesterExampleContainerViaHook = ({
onPressDrawer,
title,
module,
exampleRef,
}: {
onPressDrawer?: () => mixed,
title: string,
module: RNTesterExample,
exampleRef: () => void,
}) => {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
return (
<View style={styles.toolbar}>
<View style={styles.toolbarCenter}>
<Text style={styles.title}>{title}</Text>
<RNTesterThemeContext.Provider value={theme}>
<View style={styles.container}>
<Header
title={title}
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
* was found when making Flow check .android.js files. */
onPressDrawer={onPressDrawer}
/>
<RNTesterExampleContainer module={module} ref={exampleRef} />
</View>
<View style={styles.toolbarLeft}>
<TouchableWithoutFeedback onPress={onPressDrawer}>
<Image source={HEADER_NAV_ICON} />
</TouchableWithoutFeedback>
</RNTesterThemeContext.Provider>
);
};

const RNTesterDrawerContentViaHook = ({
onNavigate,
list,
}: {
onNavigate?: () => mixed,
list: {
ComponentExamples: Array<RNTesterExample>,
APIExamples: Array<RNTesterExample>,
},
}) => {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
return (
<RNTesterThemeContext.Provider value={theme}>
<View
style={[
styles.drawerContentWrapper,
{backgroundColor: theme.SystemBackgroundColor},
]}>
<RNTesterExampleList
list={list}
displayTitleRow={true}
disableSearch={true}
onNavigate={onNavigate}
/>
</View>
</RNTesterThemeContext.Provider>
);
};

const RNTesterExampleListViaHook = ({
title,
onPressDrawer,
onNavigate,
list,
}: {
title: string,
onPressDrawer?: () => mixed,
onNavigate?: () => mixed,
list: {
ComponentExamples: Array<RNTesterExample>,
APIExamples: Array<RNTesterExample>,
},
}) => {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? themes.dark : themes.light;
return (
<RNTesterThemeContext.Provider value={theme}>
<View style={styles.container}>
<Header
title={title}
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
* was found when making Flow check .android.js files. */
onPressDrawer={onPressDrawer}
/>
<RNTesterExampleList onNavigate={onNavigate} list={list} />
</View>
</View>
</RNTesterThemeContext.Provider>
);
};

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

_renderDrawerContent = () => {
return (
<View style={styles.drawerContentWrapper}>
<RNTesterExampleList
list={RNTesterList}
displayTitleRow={true}
disableSearch={true}
onNavigate={this._handleAction}
/>
</View>
<RNTesterDrawerContentViaHook
onNavigate={this._handleAction}
list={RNTesterList}
/>
);
};

Expand All @@ -164,39 +264,31 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
);
} else if (ExampleModule) {
return (
<View style={styles.container}>
<Header
title={ExampleModule.title}
<RNTesterExampleContainerViaHook
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
* when making Flow check .android.js files. */
onPressDrawer={() => this.drawer.openDrawer()}
title={ExampleModule.title}
module={ExampleModule}
exampleRef={example => {
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
* was found when making Flow check .android.js files. */
onPressDrawer={() => this.drawer.openDrawer()}
/>
<RNTesterExampleContainer
module={ExampleModule}
ref={example => {
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
* was found when making Flow check .android.js files. */
this._exampleRef = example;
}}
/>
</View>
this._exampleRef = example;
}}
/>
);
}
}

return (
<View style={styles.container}>
<Header
title="RNTester"
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue
* was found when making Flow check .android.js files. */
onPressDrawer={() => this.drawer.openDrawer()}
/>
<RNTesterExampleList
onNavigate={this._handleAction}
list={RNTesterList}
/>
</View>
<RNTesterExampleListViaHook
title={'RNTester'}
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
* when making Flow check .android.js files. */
onPressDrawer={() => this.drawer.openDrawer()}
onNavigate={this._handleAction}
list={RNTesterList}
/>
);
}

Expand Down Expand Up @@ -246,7 +338,6 @@ const styles = StyleSheet.create({
flex: 1,
},
toolbar: {
backgroundColor: '#E9EAED',
height: 56,
},
toolbarLeft: {
Expand All @@ -268,7 +359,6 @@ const styles = StyleSheet.create({
drawerContentWrapper: {
flex: 1,
paddingTop: StatusBar.currentHeight,
backgroundColor: 'white',
},
});

Expand Down
4 changes: 4 additions & 0 deletions RNTester/js/utils/RNTesterList.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ const APIExamples: Array<RNTesterExample> = [
key: 'AnimatedExample',
module: require('../examples/Animated/AnimatedExample'),
},
{
key: 'AppearanceExample',
module: require('../examples/Appearance/AppearanceExample'),
},
{
key: 'AppStateExample',
module: require('../examples/AppState/AppStateExample'),
Expand Down
1 change: 1 addition & 0 deletions ReactAndroid/src/main/java/com/facebook/react/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rn_android_library(
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/module/model:model"),
react_native_target("java/com/facebook/react/modules/appregistry:appregistry"),
react_native_target("java/com/facebook/react/modules/appearance:appearance"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/debug:debug"),
react_native_target("java/com/facebook/react/modules/fabric:fabric"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
Expand Down Expand Up @@ -78,6 +79,7 @@
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.modules.appearance.AppearanceModule;
import com.facebook.react.modules.appregistry.AppRegistry;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.DeviceEventManagerModule;
Expand Down Expand Up @@ -712,6 +714,17 @@ public void onWindowFocusChange(boolean hasFocus) {
}
}

/** Call this from {@link Activity#onConfigurationChanged()}. */
@ThreadConfined(UI)
public void onConfigurationChanged(Configuration newConfig) {
UiThreadUtil.assertOnUiThread();

ReactContext currentContext = getCurrentReactContext();
if (currentContext != null) {
currentContext.getNativeModule(AppearanceModule.class).onConfigurationChanged();
}
}

@ThreadConfined(UI)
public void showDevOptionsDialog() {
UiThreadUtil.assertOnUiThread();
Expand Down
Loading

0 comments on commit 17862a7

Please sign in to comment.