Skip to content

Commit

Permalink
Observe light / dark theme switch (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
zoontek authored Oct 21, 2024
1 parent bc69331 commit 3a40e80
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.zoontek.rnedgetoedge

import android.app.Activity
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.util.TypedValue
import android.os.Handler
import android.os.Looper
import android.view.WindowManager

import androidx.core.content.ContextCompat
Expand All @@ -13,75 +13,60 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat

import com.facebook.common.logging.FLog
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.common.ReactConstants

object RNEdgeToEdgeModuleImpl {
const val NAME = "RNEdgeToEdge"
private var isInitialHostResume = true

fun onHostResume(activity: Activity?) {
if (activity == null) {
return FLog.w(ReactConstants.TAG, "$NAME: Ignored, current activity is null.")
}
private fun applyEdgeToEdge(reactContext: ReactApplicationContext) {
val activity = reactContext.currentActivity
?: return FLog.w(ReactConstants.TAG, "$NAME: Ignored, current activity is null.")

activity.runOnUiThread {
val window = activity.window
val view = window.decorView
val context = view.context

val isDarkMode =
view.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES

WindowCompat.setDecorFitsSystemWindows(window, false)

window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = ContextCompat.getColor(context, R.color.navigationBarColor)

window.navigationBarColor = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDarkMode ->
ContextCompat.getColor(context, R.color.systemBarLightScrim)
else -> ContextCompat.getColor(context, R.color.systemBarDarkScrim)
val isDarkMode =
view.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES

WindowInsetsControllerCompat(window, view).run {
isAppearanceLightNavigationBars = !isDarkMode
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isStatusBarContrastEnforced = false
window.isNavigationBarContrastEnforced = true
}

WindowInsetsControllerCompat(window, view).run {
if (isInitialHostResume) {
val typedValue = TypedValue()

isAppearanceLightStatusBars = activity
.theme
.resolveAttribute(android.R.attr.windowLightStatusBar, typedValue, true) &&
typedValue.data != 0
}

isInitialHostResume = false
isAppearanceLightNavigationBars = !isDarkMode
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
}
}

fun onHostDestroy() {
isInitialHostResume = true
fun onHostResume(reactContext: ReactApplicationContext) {
applyEdgeToEdge(reactContext)
}

fun setSystemBarsConfig(activity: Activity?, config: ReadableMap) {
if (activity == null) {
return FLog.w(ReactConstants.TAG, "$NAME: Ignored, current activity is null.")
}
fun onConfigChange(reactContext: ReactApplicationContext) {
Handler(Looper.getMainLooper()).postDelayed({ applyEdgeToEdge(reactContext) }, 100)
}

fun setSystemBarsConfig(reactContext: ReactApplicationContext, config: ReadableMap) {
val activity = reactContext.currentActivity
?: return FLog.w(ReactConstants.TAG, "$NAME: Ignored, current activity is null.")

val statusBarHidden =
config.takeIf { it.hasKey("statusBarHidden") }?.getBoolean("statusBarHidden")
Expand Down
7 changes: 7 additions & 0 deletions android/src/main/res/values-night-v27/styles.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.EdgeToEdge.Base" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
6 changes: 0 additions & 6 deletions android/src/main/res/values-v26/styles.xml

This file was deleted.

5 changes: 5 additions & 0 deletions android/src/main/res/values-v27/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/policy/DecorView.java;l=142 -->
<color name="navigationBarColor">#e6ffffff</color>
</resources>
7 changes: 5 additions & 2 deletions android/src/main/res/values-v27/styles.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.EdgeToEdge.Base" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar">true</item>
</style>

<style name="Theme.EdgeToEdge" parent="Theme.EdgeToEdge.Common">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:navigationBarColor">@color/systemBarLightScrim</item>
<item name="android:windowLightNavigationBar">true</item>
</style>
</resources>
4 changes: 4 additions & 0 deletions android/src/main/res/values-v29/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="navigationBarColor">@android:color/transparent</color>
</resources>
1 change: 0 additions & 1 deletion android/src/main/res/values-v29/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:enforceStatusBarContrast">false</item>
<item name="android:enforceNavigationBarContrast">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>
1 change: 0 additions & 1 deletion android/src/main/res/values-v30/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
<item name="android:windowLayoutInDisplayCutoutMode">always</item>
<item name="android:enforceStatusBarContrast">false</item>
<item name="android:enforceNavigationBarContrast">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>
4 changes: 1 addition & 3 deletions android/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@
<resources>
<!-- https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/remote_color_resources_res/values/colors.xml;l=67 -->
<!-- https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/color/system_bar_background_semi_transparent.xml -->
<color name="systemBarDarkScrim">#801b1b1b</color>
<!-- https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/policy/DecorView.java;l=142 -->
<color name="systemBarLightScrim">#e6ffffff</color>
<color name="navigationBarColor">#801b1b1b</color>
</resources>
2 changes: 1 addition & 1 deletion android/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
</style>

<style name="Theme.EdgeToEdge" parent="Theme.EdgeToEdge.Common">
<item name="android:navigationBarColor">@color/systemBarDarkScrim</item>
</style>
</resources>
23 changes: 17 additions & 6 deletions android/src/newarch/com/zoontek/rnedgetoedge/RNEdgeToEdgeModule.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package com.zoontek.rnedgetoedge

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter

import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule

@ReactModule(name = RNEdgeToEdgeModuleImpl.NAME)
class RNEdgeToEdgeModule(reactContext: ReactApplicationContext?) :
class RNEdgeToEdgeModule(reactContext: ReactApplicationContext) :
NativeRNEdgeToEdgeSpec(reactContext), LifecycleEventListener {

private val configChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
RNEdgeToEdgeModuleImpl.onConfigChange(reactContext)
}
}

init {
reactApplicationContext.registerReceiver(configChangeReceiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
reactApplicationContext.addLifecycleEventListener(this)
}

override fun invalidate() {
reactApplicationContext.unregisterReceiver(configChangeReceiver)
reactApplicationContext.removeLifecycleEventListener(this)
}

Expand All @@ -22,16 +35,14 @@ class RNEdgeToEdgeModule(reactContext: ReactApplicationContext?) :
}

override fun onHostResume() {
RNEdgeToEdgeModuleImpl.onHostResume(currentActivity)
RNEdgeToEdgeModuleImpl.onHostResume(reactApplicationContext)
}

override fun onHostPause() {}

override fun onHostDestroy() {
RNEdgeToEdgeModuleImpl.onHostDestroy()
}
override fun onHostDestroy() {}

override fun setSystemBarsConfig(config: ReadableMap) {
RNEdgeToEdgeModuleImpl.setSystemBarsConfig(currentActivity, config)
RNEdgeToEdgeModuleImpl.setSystemBarsConfig(reactApplicationContext, config)
}
}
23 changes: 17 additions & 6 deletions android/src/oldarch/com/zoontek/rnedgetoedge/RNBarsModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.zoontek.rnedgetoedge

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter

import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
Expand All @@ -8,14 +13,22 @@ import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule

@ReactModule(name = RNEdgeToEdgeModuleImpl.NAME)
class RNEdgeToEdgeModule(reactContext: ReactApplicationContext?) :
class RNEdgeToEdgeModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext), LifecycleEventListener {

private val configChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
RNEdgeToEdgeModuleImpl.onConfigChange(reactContext)
}
}

init {
reactApplicationContext.registerReceiver(configChangeReceiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
reactApplicationContext.addLifecycleEventListener(this)
}

override fun invalidate() {
reactApplicationContext.unregisterReceiver(configChangeReceiver)
reactApplicationContext.removeLifecycleEventListener(this)
}

Expand All @@ -24,17 +37,15 @@ class RNEdgeToEdgeModule(reactContext: ReactApplicationContext?) :
}

override fun onHostResume() {
RNEdgeToEdgeModuleImpl.onHostResume(currentActivity)
RNEdgeToEdgeModuleImpl.onHostResume(reactApplicationContext)
}

override fun onHostPause() {}

override fun onHostDestroy() {
RNEdgeToEdgeModuleImpl.onHostDestroy()
}
override fun onHostDestroy() {}

@ReactMethod
fun setSystemBarsConfig(config: ReadableMap) {
RNEdgeToEdgeModuleImpl.setSystemBarsConfig(currentActivity, config)
RNEdgeToEdgeModuleImpl.setSystemBarsConfig(reactApplicationContext, config)
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-edge-to-edge",
"version": "0.1.2",
"version": "0.2.0",
"license": "MIT",
"description": "Effortlessly enable edge-to-edge display in React Native",
"author": "Mathieu Acthernoene <[email protected]>",
Expand Down

0 comments on commit 3a40e80

Please sign in to comment.