mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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
This commit is contained in:
committed by
Facebook Github Bot
parent
51681e80ab
commit
17862a78db
@@ -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"),
|
||||
|
||||
@@ -34,6 +34,7 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JS_VM_CALLS;
|
||||
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;
|
||||
@@ -78,6 +79,7 @@ import com.facebook.react.devsupport.RedBoxHandler;
|
||||
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;
|
||||
@@ -712,6 +714,17 @@ public class ReactInstanceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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();
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.facebook.react.modules.appearance;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
/** Module that exposes the user's preferred color scheme. For API >= 29. */
|
||||
@ReactModule(name = AppearanceModule.NAME)
|
||||
public class AppearanceModule extends ReactContextBaseJavaModule {
|
||||
public static final String NAME = "Appearance";
|
||||
|
||||
private static final String APPEARANCE_CHANGED_EVENT_NAME = "appearanceChanged";
|
||||
private static final int ANDROID_TEN = 29;
|
||||
|
||||
private String mColorScheme = "light";
|
||||
|
||||
public AppearanceModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
mColorScheme = colorSchemeForCurrentConfiguration(reactContext);
|
||||
}
|
||||
|
||||
private static String colorSchemeForCurrentConfiguration(Context context) {
|
||||
// TODO: (hramos) T52929922: Switch to Build.VERSION_CODES.ANDROID_TEN or equivalent
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_TEN) {
|
||||
int currentNightMode =
|
||||
context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
switch (currentNightMode) {
|
||||
case Configuration.UI_MODE_NIGHT_NO:
|
||||
return "light";
|
||||
case Configuration.UI_MODE_NIGHT_YES:
|
||||
return "dark";
|
||||
}
|
||||
}
|
||||
|
||||
return "light";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String getColorScheme() {
|
||||
mColorScheme = colorSchemeForCurrentConfiguration(getReactApplicationContext());
|
||||
return mColorScheme;
|
||||
}
|
||||
|
||||
/** Stub */
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {}
|
||||
|
||||
/** Stub */
|
||||
@ReactMethod
|
||||
public void removeListeners(double count) {}
|
||||
|
||||
/*
|
||||
* Call this from your root activity whenever configuration changes. If the
|
||||
* color scheme has changed, an event will emitted.
|
||||
*/
|
||||
public void onConfigurationChanged() {
|
||||
String newColorScheme = colorSchemeForCurrentConfiguration(getReactApplicationContext());
|
||||
if (!mColorScheme.equals(newColorScheme)) {
|
||||
mColorScheme = newColorScheme;
|
||||
emitAppearanceChanged(mColorScheme);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends an event to the JS instance that the preferred color scheme has changed. */
|
||||
public void emitAppearanceChanged(String colorScheme) {
|
||||
WritableMap appearancePreferences = Arguments.createMap();
|
||||
appearancePreferences.putString("colorScheme", colorScheme);
|
||||
|
||||
getReactApplicationContext()
|
||||
.getJSModule(RCTDeviceEventEmitter.class)
|
||||
.emit(APPEARANCE_CHANGED_EVENT_NAME, appearancePreferences);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library")
|
||||
|
||||
rn_android_library(
|
||||
name = "appearance",
|
||||
srcs = glob(["**/*.java"]),
|
||||
is_androidx = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
react_native_dep("third-party/java/jsr-305:jsr-305"),
|
||||
react_native_dep("third-party/android/support-annotations:android-support-annotations"),
|
||||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
react_native_target("java/com/facebook/react/bridge:bridge"),
|
||||
react_native_target("java/com/facebook/react/common:common"),
|
||||
react_native_target("java/com/facebook/react/module/annotations:annotations"),
|
||||
react_native_target("java/com/facebook/react/modules/core:core"),
|
||||
],
|
||||
)
|
||||
@@ -26,6 +26,7 @@ rn_android_library(
|
||||
react_native_target("java/com/facebook/react/devsupport:devsupport"),
|
||||
react_native_target("java/com/facebook/react/module/model:model"),
|
||||
react_native_target("java/com/facebook/react/modules/accessibilityinfo:accessibilityinfo"),
|
||||
react_native_target("java/com/facebook/react/modules/appearance:appearance"),
|
||||
react_native_target("java/com/facebook/react/modules/appstate:appstate"),
|
||||
react_native_target("java/com/facebook/react/modules/blob:blob"),
|
||||
react_native_target("java/com/facebook/react/modules/camera:camera"),
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.facebook.react.module.annotations.ReactModuleList;
|
||||
import com.facebook.react.module.model.ReactModuleInfo;
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
||||
import com.facebook.react.modules.accessibilityinfo.AccessibilityInfoModule;
|
||||
import com.facebook.react.modules.appearance.AppearanceModule;
|
||||
import com.facebook.react.modules.appstate.AppStateModule;
|
||||
import com.facebook.react.modules.blob.BlobModule;
|
||||
import com.facebook.react.modules.blob.FileReaderModule;
|
||||
@@ -71,6 +72,7 @@ import java.util.Map;
|
||||
@ReactModuleList(
|
||||
nativeModules = {
|
||||
AccessibilityInfoModule.class,
|
||||
AppearanceModule.class,
|
||||
AppStateModule.class,
|
||||
BlobModule.class,
|
||||
FileReaderModule.class,
|
||||
@@ -112,6 +114,8 @@ public class MainReactPackage extends TurboReactPackage {
|
||||
switch (name) {
|
||||
case AccessibilityInfoModule.NAME:
|
||||
return new AccessibilityInfoModule(context);
|
||||
case AppearanceModule.NAME:
|
||||
return new AppearanceModule(context);
|
||||
case AppStateModule.NAME:
|
||||
return new AppStateModule(context);
|
||||
case BlobModule.NAME:
|
||||
@@ -210,6 +214,7 @@ public class MainReactPackage extends TurboReactPackage {
|
||||
Class<? extends NativeModule>[] moduleList =
|
||||
new Class[] {
|
||||
AccessibilityInfoModule.class,
|
||||
AppearanceModule.class,
|
||||
AppStateModule.class,
|
||||
BlobModule.class,
|
||||
FileReaderModule.class,
|
||||
|
||||
Reference in New Issue
Block a user