Fix retrieving initial system bar appearance on API 30+
Fixes: 219993701
Test: New tests
Change-Id: I1859688c4d89fa7c365eb420fb1646af0b7cd7db
diff --git a/core/core/src/androidTest/AndroidManifest.xml b/core/core/src/androidTest/AndroidManifest.xml
index 30756c9..284af21 100644
--- a/core/core/src/androidTest/AndroidManifest.xml
+++ b/core/core/src/androidTest/AndroidManifest.xml
@@ -118,6 +118,16 @@
android:theme="@android:style/Theme.Light.NoTitleBar" />
<activity
+ android:name="androidx.core.view.LightSystemBarsActivity"
+ android:exported="true"
+ android:theme="@style/LightSystemBarsTheme" />
+
+ <activity
+ android:name="androidx.core.view.DarkSystemBarsActivity"
+ android:exported="true"
+ android:theme="@style/DarkSystemBarsTheme" />
+
+ <activity
android:name="androidx.core.view.WindowInsetsCompatActivity"
android:exported="true"
android:theme="@android:style/Theme.Light.NoTitleBar" />
diff --git a/core/core/src/androidTest/java/androidx/core/view/DarkSystemBarsActivity.java b/core/core/src/androidTest/java/androidx/core/view/DarkSystemBarsActivity.java
new file mode 100644
index 0000000..f635cc5
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/DarkSystemBarsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view;
+
+import android.support.v4.BaseTestActivity;
+
+import androidx.core.test.R;
+
+public class DarkSystemBarsActivity extends BaseTestActivity {
+ @Override
+ protected int getContentViewLayoutResId() {
+ return R.layout.insets_compat_activity;
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/LightSystemBarsActivity.java b/core/core/src/androidTest/java/androidx/core/view/LightSystemBarsActivity.java
new file mode 100644
index 0000000..6aff709
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/LightSystemBarsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view;
+
+import android.support.v4.BaseTestActivity;
+
+import androidx.core.test.R;
+
+public class LightSystemBarsActivity extends BaseTestActivity {
+ @Override
+ protected int getContentViewLayoutResId() {
+ return R.layout.insets_compat_activity;
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/ThemeSystemBarsTest.kt b/core/core/src/androidTest/java/androidx/core/view/ThemeSystemBarsTest.kt
new file mode 100644
index 0000000..8f5b9c0
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/ThemeSystemBarsTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ThemeSystemBarsTest {
+
+ @SdkSuppress(maxSdkVersion = 22)
+ @Test
+ fun statusBar_dark_before_supported() {
+ val scenario = ActivityScenario.launch(LightSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightStatusBars).isFalse()
+
+ scenario.close()
+ }
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
+ fun statusBar_light() {
+ val scenario = ActivityScenario.launch(LightSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightStatusBars).isTrue()
+
+ scenario.close()
+ }
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
+ fun statusBar_dark() {
+ val scenario = ActivityScenario.launch(DarkSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightStatusBars).isFalse()
+
+ scenario.close()
+ }
+
+ @SdkSuppress(maxSdkVersion = 26)
+ @Test
+ fun navigationBar_dark_before_supported() {
+ val scenario = ActivityScenario.launch(LightSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightNavigationBars).isFalse()
+
+ scenario.close()
+ }
+
+ @SdkSuppress(minSdkVersion = 27)
+ @Test
+ fun navigationBar_light() {
+ val scenario = ActivityScenario.launch(LightSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightNavigationBars).isTrue()
+
+ scenario.close()
+ }
+
+ @SdkSuppress(minSdkVersion = 27)
+ @Test
+ fun navigationBar_dark() {
+ val scenario = ActivityScenario.launch(DarkSystemBarsActivity::class.java)
+
+ val insetsController = scenario.withActivity {
+ WindowCompat.getInsetsController(window, window.decorView)
+ }
+
+ assertThat(insetsController.isAppearanceLightNavigationBars).isFalse()
+
+ scenario.close()
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
index ddf7d18..a48a1d0 100644
--- a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
+++ b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
@@ -19,7 +19,6 @@
import android.app.Dialog
import android.os.Build
import android.view.View
-import android.view.WindowInsetsController
import android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
import android.widget.EditText
import android.widget.TextView
@@ -27,6 +26,7 @@
import androidx.core.graphics.Insets
import androidx.core.test.R
import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
@@ -76,7 +76,7 @@
scenario.withActivity {
WindowCompat.getInsetsController(window, container).systemBarsBehavior =
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Needed on API 23 to report the nav bar insets
this.window.addFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
@@ -234,6 +234,33 @@
@SdkSuppress(minSdkVersion = 23)
@Test
+ public fun initial_statusBar_light() {
+ // Set the flag with the systemUiVisibility flags even on newer APIs to emulate this value
+ // being set in the theme
+ scenario.onActivity {
+ it.window.decorView.systemUiVisibility =
+ it.window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ }
+ Espresso.onIdle()
+ assertThat(windowInsetsController.isAppearanceLightStatusBars(), `is`(true))
+ }
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
+ public fun initial_statusBar_dark() {
+ // Set the flag with the systemUiVisibility flags even on newer APIs to emulate this value
+ // being set in the theme
+ scenario.onActivity {
+ it.window.decorView.systemUiVisibility =
+ it.window.decorView.systemUiVisibility and
+ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
+ }
+ Espresso.onIdle()
+ assertThat(windowInsetsController.isAppearanceLightStatusBars(), `is`(false))
+ }
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
public fun statusBar_light() {
scenario.onActivity { windowInsetsController.setAppearanceLightStatusBars(true) }
if (Build.VERSION.SDK_INT < 31) {
@@ -269,6 +296,33 @@
@SdkSuppress(minSdkVersion = 26)
@Test
+ public fun initial_navigationBar_light() {
+ // Set the flag with the systemUiVisibility flags even on newer APIs to emulate this value
+ // being set in the theme
+ scenario.onActivity {
+ it.window.decorView.systemUiVisibility =
+ it.window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+ }
+ Espresso.onIdle()
+ assertThat(windowInsetsController.isAppearanceLightNavigationBars(), `is`(true))
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test
+ public fun initial_navigationBar_dark() {
+ // Set the flag with the systemUiVisibility flags even on newer APIs to emulate this value
+ // being set in the theme
+ scenario.onActivity {
+ it.window.decorView.systemUiVisibility =
+ it.window.decorView.systemUiVisibility and
+ View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
+ }
+ Espresso.onIdle()
+ assertThat(windowInsetsController.isAppearanceLightNavigationBars(), `is`(false))
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test
public fun navigationBar_light() {
scenario.onActivity { windowInsetsController.setAppearanceLightNavigationBars(true) }
val systemUiVisibility = scenario.withActivity { window.decorView }.systemUiVisibility
diff --git a/core/core/src/androidTest/res/values/styles.xml b/core/core/src/androidTest/res/values/styles.xml
index 8f5f021..c8ad0f0 100644
--- a/core/core/src/androidTest/res/values/styles.xml
+++ b/core/core/src/androidTest/res/values/styles.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
<style name="TestActivityTheme" parent="@android:style/Theme.Holo">
<item name="android:windowAnimationStyle">@null</item>
</style>
@@ -38,4 +38,14 @@
<style name="ThemeOverlay.Core.ColorStateListInflaterCompat" parent="">
<item name="android:textColorPrimary">@color/text_color</item>
</style>
+
+ <style name="LightSystemBarsTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowLightStatusBar" tools:targetApi="23">true</item>
+ <item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
+ </style>
+
+ <style name="DarkSystemBarsTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowLightStatusBar" tools:targetApi="23">false</item>
+ <item name="android:windowLightNavigationBar" tools:targetApi="27">false</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
index 3f1564f..ef3d133 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
@@ -194,6 +194,12 @@
* Checks if the foreground of the status bar is set to light.
* <p>
* This method always returns false on API < 23.
+ * <p>
+ * If this value is being set in the theme (via {@link android.R.attr#windowLightStatusBar}),
+ * then the correct value will only be returned once attached to the window.
+ * <p>
+ * Once this method is called, modifying `systemUiVisibility` directly to change the
+ * appearance is undefined behavior.
*
* @return true if the foreground is light
* @see #setAppearanceLightStatusBars(boolean)
@@ -207,6 +213,9 @@
* bar can be read clearly. If false, reverts to the default appearance.
* <p>
* This method has no effect on API < 23.
+ * <p>
+ * Once this method is called, modifying `systemUiVisibility` directly to change the
+ * appearance is undefined behavior.
*
* @see #isAppearanceLightStatusBars()
*/
@@ -218,6 +227,13 @@
* Checks if the foreground of the navigation bar is set to light.
* <p>
* This method always returns false on API < 26.
+ * <p>
+ * If this value is being set in the theme (via
+ * {@link android.R.attr#windowLightNavigationBar}),
+ * then the correct value will only be returned once attached to the window.
+ * <p>
+ * Once this method is called, modifying `systemUiVisibility` directly to change the
+ * appearance is undefined behavior.
*
* @return true if the foreground is light
* @see #setAppearanceLightNavigationBars(boolean)
@@ -231,6 +247,9 @@
* the bar can be read clearly. If false, reverts to the default appearance.
* <p>
* This method has no effect on API < 26.
+ * <p>
+ * Once this method is called, modifying `systemUiVisibility` directly to change the
+ * appearance is undefined behavior.
*
* @see #isAppearanceLightNavigationBars()
*/
@@ -637,6 +656,13 @@
@Override
public boolean isAppearanceLightStatusBars() {
+ // This is a side-effectful workaround
+ // Because the mask is zero, this won't change the system bar appearance
+ // However, it "unlocks" reading the effective system bar appearance in the following
+ // call. Without this being "unlocked," the system bar appearance will always return
+ // nothing, even if it has been set in the theme or by the system ui flags before
+ // querying for it.
+ mInsetsController.setSystemBarsAppearance(0, 0);
return (mInsetsController.getSystemBarsAppearance()
& WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS) != 0;
}
@@ -664,6 +690,13 @@
@Override
public boolean isAppearanceLightNavigationBars() {
+ // This is a side-effectful workaround
+ // Because the mask is zero, this won't change the system bar appearance
+ // However, it "unlocks" reading the effective system bar appearance in the following
+ // call. Without this being "unlocked," the system bar appearance will always return
+ // nothing, even if it has been set in the theme or by the system ui flags before
+ // querying for it.
+ mInsetsController.setSystemBarsAppearance(0, 0);
return (mInsetsController.getSystemBarsAppearance()
& WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
}