// adapted from expo-router (MIT license) - https://github.com/expo/expo package dev.onejs.one import android.content.res.Configuration import android.graphics.Color import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import androidx.appcompat.view.ContextThemeWrapper import com.google.android.material.color.MaterialColors class OneModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = NAME @ReactMethod(isBlockingSynchronousMethod = true) fun Material3Color(name: String, scheme: String): String? { return materialColor(name, scheme) } @ReactMethod(isBlockingSynchronousMethod = true) fun Material3DynamicColor(name: String, scheme: String): String? { return dynamicColor(name, scheme) } private fun materialColor(name: String, scheme: String): String? { val resolvedScheme = if (scheme != "dark" && scheme != "light") getSystemScheme() else scheme val theme = if (resolvedScheme == "dark") { com.google.android.material.R.style.Theme_Material3_Dark } else { com.google.android.material.R.style.Theme_Material3_Light } return getColorFromTheme(name, theme) } private fun dynamicColor(name: String, scheme: String): String? { val resolvedScheme = if (scheme != "dark" && scheme != "light") getSystemScheme() else scheme val theme = if (resolvedScheme == "dark") { com.google.android.material.R.style.Theme_Material3_DynamicColors_Dark } else { com.google.android.material.R.style.Theme_Material3_DynamicColors_Light } return getColorFromTheme(name, theme) } private fun getColorFromTheme(name: String, themeResId: Int): String? { val context = reactApplicationContext val wrappedContext = ContextThemeWrapper(context, themeResId) val attr = attrMap[name.lowercase()] ?: return null return MaterialColors.getColorOrNull(wrappedContext, attr)?.let { colorToHex(it) } } private fun getSystemScheme(): String { val context = reactApplicationContext val currentNightMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK return when (currentNightMode) { Configuration.UI_MODE_NIGHT_YES -> "dark" Configuration.UI_MODE_NIGHT_NO -> "light" else -> "light" } } companion object { const val NAME = "One" fun colorToHex(color: Int): String { return String.format("#%02x%02x%02x", Color.red(color), Color.green(color), Color.blue(color)) } val attrMap = mapOf( "primary" to androidx.appcompat.R.attr.colorPrimary, "onprimary" to com.google.android.material.R.attr.colorOnPrimary, "primarycontainer" to com.google.android.material.R.attr.colorPrimaryContainer, "onprimarycontainer" to com.google.android.material.R.attr.colorOnPrimaryContainer, "primaryinverse" to com.google.android.material.R.attr.colorPrimaryInverse, "primaryfixed" to com.google.android.material.R.attr.colorPrimaryFixed, "primaryfixeddim" to com.google.android.material.R.attr.colorPrimaryFixedDim, "onprimaryfixed" to com.google.android.material.R.attr.colorOnPrimaryFixed, "onprimaryfixedvariant" to com.google.android.material.R.attr.colorOnPrimaryFixedVariant, "secondary" to com.google.android.material.R.attr.colorSecondary, "onsecondary" to com.google.android.material.R.attr.colorOnSecondary, "secondarycontainer" to com.google.android.material.R.attr.colorSecondaryContainer, "onsecondarycontainer" to com.google.android.material.R.attr.colorOnSecondaryContainer, "secondaryfixed" to com.google.android.material.R.attr.colorSecondaryFixed, "secondaryfixeddim" to com.google.android.material.R.attr.colorSecondaryFixedDim, "onsecondaryfixed" to com.google.android.material.R.attr.colorOnSecondaryFixed, "onsecondaryfixedvariant" to com.google.android.material.R.attr.colorOnSecondaryFixedVariant, "tertiary" to com.google.android.material.R.attr.colorTertiary, "ontertiary" to com.google.android.material.R.attr.colorOnTertiary, "tertiarycontainer" to com.google.android.material.R.attr.colorTertiaryContainer, "ontertiarycontainer" to com.google.android.material.R.attr.colorOnTertiaryContainer, "tertiaryfixed" to com.google.android.material.R.attr.colorTertiaryFixed, "tertiaryfixeddim" to com.google.android.material.R.attr.colorTertiaryFixedDim, "ontertiaryfixed" to com.google.android.material.R.attr.colorOnTertiaryFixed, "ontertiaryfixedvariant" to com.google.android.material.R.attr.colorOnTertiaryFixedVariant, "error" to androidx.appcompat.R.attr.colorError, "onerror" to com.google.android.material.R.attr.colorOnError, "errorcontainer" to com.google.android.material.R.attr.colorErrorContainer, "onerrorcontainer" to com.google.android.material.R.attr.colorOnErrorContainer, "outline" to com.google.android.material.R.attr.colorOutline, "outlinevariant" to com.google.android.material.R.attr.colorOutlineVariant, "background" to android.R.attr.colorBackground, "onbackground" to com.google.android.material.R.attr.colorOnBackground, "surface" to com.google.android.material.R.attr.colorSurface, "onsurface" to com.google.android.material.R.attr.colorOnSurface, "surfacevariant" to com.google.android.material.R.attr.colorSurfaceVariant, "onsurfacevariant" to com.google.android.material.R.attr.colorOnSurfaceVariant, "surfaceinverse" to com.google.android.material.R.attr.colorSurfaceInverse, "onsurfaceinverse" to com.google.android.material.R.attr.colorOnSurfaceInverse, "surfacebright" to com.google.android.material.R.attr.colorSurfaceBright, "surfacedim" to com.google.android.material.R.attr.colorSurfaceDim, "surfacecontainer" to com.google.android.material.R.attr.colorSurfaceContainer, "surfacecontainerlow" to com.google.android.material.R.attr.colorSurfaceContainerLow, "surfacecontainerlowest" to com.google.android.material.R.attr.colorSurfaceContainerLowest, "surfacecontainerhigh" to com.google.android.material.R.attr.colorSurfaceContainerHigh, "surfacecontainerhighest" to com.google.android.material.R.attr.colorSurfaceContainerHighest ) } }