From c8d5f0f1aa6f8e408a1cf14fa619e38cb5172233 Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 10 Apr 2024 07:45:57 -0700 Subject: [PATCH] //xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance:appearanceAndroid (#44006) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44006 Changelog: [Internal] Reviewed By: cortinico Differential Revision: D55725334 fbshipit-source-id: 4d7ad2bb6395674d47fd5e3cd01a8515627f6cd4 --- .../ReactAndroid/api/ReactAndroid.api | 12 +- .../modules/appearance/AppearanceModule.java | 125 ------------------ .../modules/appearance/AppearanceModule.kt | 100 ++++++++++++++ 3 files changed, 109 insertions(+), 128 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 366c59121cf482..8672bd2d639340 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2965,17 +2965,23 @@ public class com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModul public fun setAccessibilityFocus (D)V } -public class com/facebook/react/modules/appearance/AppearanceModule : com/facebook/fbreact/specs/NativeAppearanceSpec { +public final class com/facebook/react/modules/appearance/AppearanceModule : com/facebook/fbreact/specs/NativeAppearanceSpec { + public static final field Companion Lcom/facebook/react/modules/appearance/AppearanceModule$Companion; + public static final field NAME Ljava/lang/String; public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme;)V + public synthetic fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun addListener (Ljava/lang/String;)V - public fun emitAppearanceChanged (Ljava/lang/String;)V + public final fun emitAppearanceChanged (Ljava/lang/String;)V public fun getColorScheme ()Ljava/lang/String; - public fun onConfigurationChanged (Landroid/content/Context;)V + public final fun onConfigurationChanged (Landroid/content/Context;)V public fun removeListeners (D)V public fun setColorScheme (Ljava/lang/String;)V } +public final class com/facebook/react/modules/appearance/AppearanceModule$Companion { +} + public abstract interface class com/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme { public abstract fun getScheme ()Ljava/lang/String; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java deleted file mode 100644 index 4dece29d17fc52..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * 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.app.Activity; -import android.content.Context; -import android.content.res.Configuration; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatDelegate; -import com.facebook.fbreact.specs.NativeAppearanceSpec; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.module.annotations.ReactModule; - -/** Module that exposes the user's preferred color scheme. */ -@ReactModule(name = NativeAppearanceSpec.NAME) -public class AppearanceModule extends NativeAppearanceSpec { - - private static final String APPEARANCE_CHANGED_EVENT_NAME = "appearanceChanged"; - - private String mColorScheme = "light"; - - private final @Nullable OverrideColorScheme mOverrideColorScheme; - - /** Optional override to the current color scheme */ - public interface OverrideColorScheme { - - /** - * Color scheme will use the return value instead of the current system configuration. Available - * scheme: {light, dark} - */ - public String getScheme(); - } - - public AppearanceModule(ReactApplicationContext reactContext) { - this(reactContext, null); - } - - public AppearanceModule( - ReactApplicationContext reactContext, @Nullable OverrideColorScheme overrideColorScheme) { - super(reactContext); - - mOverrideColorScheme = overrideColorScheme; - mColorScheme = colorSchemeForCurrentConfiguration(reactContext); - } - - private String colorSchemeForCurrentConfiguration(Context context) { - if (mOverrideColorScheme != null) { - return mOverrideColorScheme.getScheme(); - } - 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 void setColorScheme(String style) { - if (style.equals("dark")) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - } else if (style.equals("light")) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - } else if (style.equals("unspecified")) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - } - } - - @Override - public String getColorScheme() { - // Attempt to use the Activity context first in order to get the most up to date - // scheme. This covers the scenario when AppCompatDelegate.setDefaultNightMode() - // is called directly (which can occur in Brownfield apps for example). - Activity activity = getCurrentActivity(); - - mColorScheme = - colorSchemeForCurrentConfiguration( - activity != null ? activity : getReactApplicationContext()); - - return mColorScheme; - } - - /** Stub */ - @Override - public void addListener(String eventName) {} - - /** Stub */ - @Override - 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(Context currentContext) { - String newColorScheme = colorSchemeForCurrentConfiguration(currentContext); - 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); - - ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - - if (reactApplicationContext != null) { - reactApplicationContext.emitDeviceEvent(APPEARANCE_CHANGED_EVENT_NAME, appearancePreferences); - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt new file mode 100644 index 00000000000000..cf1690a96fd94d --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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 androidx.appcompat.app.AppCompatDelegate +import com.facebook.fbreact.specs.NativeAppearanceSpec +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.annotations.ReactModule + +/** Module that exposes the user's preferred color scheme. */ +@ReactModule(name = NativeAppearanceSpec.NAME) +public class AppearanceModule +@JvmOverloads +constructor( + reactContext: ReactApplicationContext, + private val overrideColorScheme: OverrideColorScheme? = null +) : NativeAppearanceSpec(reactContext) { + + private var colorScheme = colorSchemeForCurrentConfiguration(reactContext) + + /** Optional override to the current color scheme */ + public interface OverrideColorScheme { + /** + * Color scheme will use the return value instead of the current system configuration. Available + * scheme: {light, dark} + */ + public fun getScheme(): String + } + + private fun colorSchemeForCurrentConfiguration(context: Context): String { + if (overrideColorScheme != null) { + return overrideColorScheme.getScheme() + } + + val currentNightMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + when (currentNightMode) { + Configuration.UI_MODE_NIGHT_NO -> return "light" + Configuration.UI_MODE_NIGHT_YES -> return "dark" + else -> return "light" + } + } + + public override fun getColorScheme(): String { + // Attempt to use the Activity context first in order to get the most up to date + // scheme. This covers the scenario when AppCompatDelegate.setDefaultNightMode() + // is called directly (which can occur in Brownfield apps for example). + val activity = getCurrentActivity() + colorScheme = colorSchemeForCurrentConfiguration(activity ?: getReactApplicationContext()) + return colorScheme + } + + public override fun setColorScheme(style: String) { + when { + style == "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + style == "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + style == "unspecified" -> + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + } + } + + /** Stub */ + public override fun addListener(eventName: String): Unit = Unit + + /** Stub */ + public override fun removeListeners(count: Double): Unit = Unit + + /* + * Call this from your root activity whenever configuration changes. If the + * color scheme has changed, an event will emitted. + */ + public fun onConfigurationChanged(currentContext: Context) { + val newColorScheme = colorSchemeForCurrentConfiguration(currentContext) + if (colorScheme != newColorScheme) { + colorScheme = newColorScheme + emitAppearanceChanged(colorScheme) + } + } + + /** Sends an event to the JS instance that the preferred color scheme has changed. */ + public fun emitAppearanceChanged(colorScheme: String) { + val appearancePreferences = Arguments.createMap() + appearancePreferences.putString("colorScheme", colorScheme) + val reactApplicationContext = getReactApplicationContextIfActiveOrWarn() + reactApplicationContext?.emitDeviceEvent(APPEARANCE_CHANGED_EVENT_NAME, appearancePreferences) + } + + public companion object { + public const val NAME: String = NativeAppearanceSpec.NAME + private const val APPEARANCE_CHANGED_EVENT_NAME = "appearanceChanged" + } +}