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;
         }