Skip to content

Commit

Permalink
Sync Modal system bars visibility with current activity (facebook#48516)
Browse files Browse the repository at this point in the history
Summary:
Fixes facebook#37801

This PR fixes the system bars visibility not being in sync as with the activity it is displayed on.

Here I am also taking into account the feedback given in [this PR](facebook#36854) which was also aiming to address this issue. Unfortunately, I tried in multiple ways to get this to work by simply extending the whole system bars behaviour in the dialog with the activity system bars behaviour but found out that that solution won't work as we [currently clear the flag "FLAG_NOT_FOCUSABLE"](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt#L314) and unless we remove that line, extending the behaviour won't work. Removing that line is not an option as it would cause other side effects in the dialog itself.

With the above said, I ended up doing this in a more explicit way, by checking whether the status or navigation bars are hidden in the activity and then hiding then as well in the dialog or otherwise, similar as we are currently doing with the status bars appearance.

## Changelog:

[ANDROID] [FIXED] - Sync Modal system bars visibility with current activity

Pull Request resolved: facebook#48516

Test Plan:
In order to test this, you need to trigger any method that hides or shows the system bars using `window.insetsController`. Here a very small example of it:

```kt
fun toggleSystemBarsVisibility(shouldHide: Boolean) {
  val window = currentActivity?.window
  val controller = window?.insetsController
  if (shouldHide) {
    controller?.hide(WindowInsets.Type.systemBars())
  } else {
    controller?.show(WindowInsets.Type.systemBars())
  }
}
```

You can do this optionally from JS to make testing different cases easier. Below is a screen recording of how the solutions looks like:

<details>
<summary>Screen recording showcasing the solution in the test plan</summary>

https://github.com/user-attachments/assets/c497c1cb-5e65-4f31-98cc-aefd2d7b0339
</details>

Reviewed By: mdvacca, Abbondanzo

Differential Revision: D67906071

Pulled By: alanleedev

fbshipit-source-id: cbb2d15520d7729a9e9eafb5f5efb8d20d796c60
  • Loading branch information
mateoguzmana authored and facebook-github-bot committed Jan 28, 2025
1 parent 409047b commit 7016225
Showing 1 changed file with 36 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewStructure
import android.view.Window
import android.view.WindowInsetsController
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
import androidx.annotation.UiThread
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.facebook.common.logging.FLog
import com.facebook.react.R
import com.facebook.react.bridge.GuardedRunnable
Expand Down Expand Up @@ -380,6 +381,10 @@ public class ReactModalHostView(context: ThemedReactContext) :
}
}

/**
* Updates the system appearance of the dialog to match the activity that it is being displayed
* on.
*/
private fun updateSystemAppearance() {
val currentActivity = getCurrentActivity() ?: return
val dialog = checkNotNull(dialog) { "dialog must exist when we call updateProperties" }
Expand All @@ -388,19 +393,43 @@ public class ReactModalHostView(context: ThemedReactContext) :
val activityWindow = currentActivity.window
// Modeled after the version check in StatusBarModule.setStyle
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
val insetsController: WindowInsetsController = checkNotNull(activityWindow.insetsController)
val activityAppearance: Int = insetsController.systemBarsAppearance
val activityWindowInsetsController =
WindowInsetsControllerCompat(activityWindow, activityWindow.decorView)
val dialogWindowInsetsController =
WindowInsetsControllerCompat(dialogWindow, dialogWindow.decorView)

dialogWindowInsetsController.isAppearanceLightStatusBars =
activityWindowInsetsController.isAppearanceLightStatusBars

val activityLightStatusBars =
activityAppearance and WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
val activityRootWindowInsets =
WindowInsetsCompat.toWindowInsetsCompat(activityWindow.decorView.rootWindowInsets)

dialogWindow.insetsController?.setSystemBarsAppearance(
activityLightStatusBars, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
syncSystemBarsVisibility(activityRootWindowInsets, dialogWindowInsetsController)
} else {
dialogWindow.decorView.systemUiVisibility = activityWindow.decorView.systemUiVisibility
}
}

/**
* Syncs the visibility of the system bars based on their visibility in the root window insets.
* This ensures consistency between the system bars visibility in the activity and the dialog.
*/
private fun syncSystemBarsVisibility(
activityRootWindowInsets: WindowInsetsCompat,
dialogWindowInsetsController: WindowInsetsControllerCompat?,
types: List<Int> =
listOf(WindowInsetsCompat.Type.statusBars(), WindowInsetsCompat.Type.navigationBars()),
) {
types.forEach { type ->
val isVisible = activityRootWindowInsets.isVisible(type)
if (isVisible) {
dialogWindowInsetsController?.show(type)
} else {
dialogWindowInsetsController?.hide(type)
}
}
}

/**
* Sets the testID on the DialogRootViewGroup. Since the accessibility events are not triggered on
* the on the ReactModalHostView, the testID is forwarded to the DialogRootViewGroup to set the
Expand Down

0 comments on commit 7016225

Please sign in to comment.