Skip to content

Commit

Permalink
For mozilla-mobile#11527 - Use deprecated APIs for immersive mode sin…
Browse files Browse the repository at this point in the history
…ce they work better

Opened https://issuetracker.google.com/u/2/issues/214012501 for what seems a
bug with the new insets based APIs.
In the meantime we are using the old OnSystemUiVisibilityChangeListener to
know when to restore immersive mode.

Removed the onWindowFocusChangeListener extension property since by having to
offer it through a getter the current implementation would always leak the old
one.
Fenix wasn't using it when this APIs allowed Fenix to pass such a listener and
there was no issue observed so there should be no observable negative impact.
  • Loading branch information
Mugurell committed Jan 12, 2022
1 parent 4891abb commit 746c6f1
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
package mozilla.components.support.ktx.android.view

import android.app.Activity
import android.os.Build
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.core.view.WindowInsetsCompat
Expand Down Expand Up @@ -47,23 +45,23 @@ internal fun Activity.setAsImmersive() {
*/
@VisibleForTesting
internal fun Activity.enableImmersiveModeRestore() {
window.decorView.viewTreeObserver?.addOnWindowFocusChangeListener(onWindowFocusChangeListener)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.setOnApplyWindowInsetsListener { _, insets ->
if (insets.isVisible(statusBars())) {
setAsImmersive()
}
insets
}
} else {
@Suppress("DEPRECATION") // insets.isVisible(int) is available only starting with API 30
window.decorView.setOnSystemUiVisibilityChangeListener { newFlags ->
if (newFlags and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
setAsImmersive()
}
// Still using the now deprecated approach until a resolution of https://issuetracker.google.com/issues/214012501
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// window.decorView.setOnApplyWindowInsetsListener { _, insets ->
// if (insets.isVisible(statusBars())) {
// setAsImmersive()
// }
// insets
// }
// } else {
@Suppress("DEPRECATION") // insets.isVisible(int) is available only starting with API 30
window.decorView.setOnSystemUiVisibilityChangeListener { newFlags ->
if (newFlags and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
setAsImmersive()
}
}
// }
}

/**
Expand All @@ -75,32 +73,21 @@ fun Activity.exitImmersiveModeIfNeeded() {
return
}

window.decorView.viewTreeObserver?.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.setOnApplyWindowInsetsListener(null)
} else {
@Suppress("DEPRECATION")
window.decorView.setOnSystemUiVisibilityChangeListener(null)
}
// Still using the now deprecated approach until a resolution of https://issuetracker.google.com/issues/214012501
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// window.decorView.setOnApplyWindowInsetsListener(null)
// } else {
@Suppress("DEPRECATION")
window.decorView.setOnSystemUiVisibilityChangeListener(null)
// }

window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.getWindowInsetsController().apply {
show(WindowInsetsCompat.Type.systemBars())
}
}

/**
* OnWindowFocusChangeListener used to ensure immersive mode is not broken by other views interactions.
*/
@VisibleForTesting
internal val Activity.onWindowFocusChangeListener: ViewTreeObserver.OnWindowFocusChangeListener
get() = ViewTreeObserver.OnWindowFocusChangeListener { hasFocus ->
if (hasFocus) {
setAsImmersive()
}
}

/**
* Calls [Activity.reportFullyDrawn] while also preventing crashes under some circumstances.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class ActivityTest {
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
// verify that the immersive mode restoration is set as expected
verify(window.decorView.viewTreeObserver).addOnWindowFocusChangeListener(any())
verify(window.decorView).setOnSystemUiVisibilityChangeListener(any())
}

Expand All @@ -65,15 +64,13 @@ class ActivityTest {
verify(window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
verify(window.decorView.viewTreeObserver, never()).addOnWindowFocusChangeListener(any())
verify(window.decorView, never()).setOnSystemUiVisibilityChangeListener(any())
}

@Test
fun `check enableImmersiveModeRestore sets focus and insets listeners`() {
activity.enableImmersiveModeRestore()

verify(window.decorView.viewTreeObserver).addOnWindowFocusChangeListener(any())
verify(window.decorView).setOnSystemUiVisibilityChangeListener(any())
}

Expand All @@ -97,26 +94,6 @@ class ActivityTest {
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}

@Test
fun `check enableImmersiveModeRestore set focus listener has the correct behavior`() {
val focusListenerCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()

activity.enableImmersiveModeRestore()
verify(window.decorView.viewTreeObserver).addOnWindowFocusChangeListener(focusListenerCaptor.capture())

focusListenerCaptor.value.onWindowFocusChanged(false)
// If the activity is not focused restoration is needed.
// Cannot test if "setAsImmersive()" was called it being an extension function but we can check the effect of that call.
verify(window, never()).addFlags(anyInt())
verify(decorView, never()).systemUiVisibility = anyInt()
verify(decorView, never()).systemUiVisibility = anyInt()

focusListenerCaptor.value.onWindowFocusChanged(true)
verify(window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
verify(decorView).systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}

@Test
fun `check exitImmersiveModeIfNeeded sets the correct flags`() {
val attributes = mock(WindowManager.LayoutParams::class.java)
Expand All @@ -129,17 +106,14 @@ class ActivityTest {
verify(decorView, never()).systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN.inv()
verify(decorView, never()).systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION.inv()
verify(decorView, never()).setOnSystemUiVisibilityChangeListener(null)
verify(window.decorView.viewTreeObserver, never()).removeOnWindowFocusChangeListener(any())

attributes.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON

activity.exitImmersiveModeIfNeeded()

verify(window).clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
verify(decorView, times(2)).systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
verify(window.decorView.viewTreeObserver).removeOnWindowFocusChangeListener(any())
verify(decorView).setOnSystemUiVisibilityChangeListener(null)
verify(window.decorView.viewTreeObserver).removeOnWindowFocusChangeListener(any())
}

@Test
Expand All @@ -151,12 +125,10 @@ class ActivityTest {
activity.exitImmersiveModeIfNeeded()

verify(decorView, never()).setOnSystemUiVisibilityChangeListener(null)
verify(window.decorView.viewTreeObserver, never()).removeOnWindowFocusChangeListener(any())

attributes.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
activity.exitImmersiveModeIfNeeded()

verify(decorView).setOnSystemUiVisibilityChangeListener(null)
verify(window.decorView.viewTreeObserver).removeOnWindowFocusChangeListener(any())
}
}
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ permalink: /changelog/
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/main/buildSrc/src/main/java/Gecko.kt)
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/main/.config.yml)

* **support-ktx**
* 🚒 Bug fixed [issue #11527](https://github.com/mozilla-mobile/android-components/issues/11527) - Using now deprecated APIs to fix immersive mode not being applied all the time. https://issuetracker.google.com/u/2/issues/214012501 can be followed for what seems a framework issue.

# 97.0.0
* [Commits](https://github.com/mozilla-mobile/android-components/compare/v96.0.0...v97.0.0)
Expand Down

0 comments on commit 746c6f1

Please sign in to comment.