Merge "Set Window#setDecorFitsSystemWindows on Android R+ and View#setFitsSystemWindows on Android Q- instead of using WindowCompat#setDecorFitsSystemWindows" into androidx-main
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
index 2ccd6f3..fd2dde3 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 import android.view.PixelCopy;
 import android.view.View;
+import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.ImageView;
@@ -43,6 +44,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
@@ -62,7 +64,6 @@
 import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.utils.ThreadUtils;
 import androidx.core.view.DisplayCutoutCompat;
-import androidx.core.view.WindowCompat;
 import androidx.core.view.WindowInsetsCompat;
 import androidx.fragment.app.FragmentActivity;
 import androidx.lifecycle.Lifecycle;
@@ -86,6 +87,8 @@
     View mActivityContainerView;
     View mLocalContentContainerView;
 
+    boolean mDecorFitsSystemWindows = false;
+
     /**
      * Displays the snapshot of the surface view to avoid a visual glitch when app comes
      * to foreground. This view sits behind the surface view and will be visible only when surface
@@ -251,6 +254,14 @@
             return new WindowInsets.Builder(insets).setInsets(
                     WindowInsets.Type.displayCutout(), Insets.NONE).build();
         }
+
+        @DoNotInline
+        static void setDecorFitsSystemWindows(BaseCarAppActivity activity, Window window,
+                boolean decorFitsSystemWindows) {
+            // Set mDecorFitsSystemWindows so we can retrieve its value for testing.
+            activity.mDecorFitsSystemWindows = decorFitsSystemWindows;
+            window.setDecorFitsSystemWindows(decorFitsSystemWindows);
+        }
     }
 
     @Override
@@ -325,20 +336,30 @@
 
         // Remove display cut-out insets on DecorView
         getWindow().getDecorView().setOnApplyWindowInsetsListener((view, insets) -> {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            if (Build.VERSION.SDK_INT >= 30) {
                 insets = Api30Impl.getDecorViewInsets(insets);
             }
             return view.onApplyWindowInsets(insets);
         });
 
-        // IMPORTANT: The SystemUiVisibility applied here must match the insets provided to the
-        // host in OnApplyWindowInsetsListener above. Failing to do so would cause a mismatch
-        // between the insets applied to the content on the hosts side vs. the actual visible
-        // window available on the client side.
-        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+        if (Build.VERSION.SDK_INT >= 30) {
+            Api30Impl.setDecorFitsSystemWindows(this, getWindow(), false);
+        } else {
+            getWindow().getDecorView().setFitsSystemWindows(false);
+        }
         mActivityContainerView.requestApplyInsets();
     }
 
+    /**
+     * TODO(b/283985939): Workaround for testing {@code setDecorFitsSystemWindows} for older
+     * versions of Roboelectric that don't support {@code getDecorFitsSystemWindows}. Remove this
+     * once Roboelectric version is upgraded to v4.10.3.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public boolean getDecorFitsSystemWindows() {
+        return mDecorFitsSystemWindows;
+    }
+
     /** Takes a snapshot of the surface view and puts it in the surfaceSnapshotView if succeeded. */
     private void takeSurfaceSnapshot() {
         // Nothing to do if the surface is not ready yet.
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
index 02804af..f371f31 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
@@ -42,6 +42,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -74,6 +75,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.shadows.ShadowPackageManager;
@@ -396,6 +398,39 @@
 
     }
 
+    @Test
+    @Config(maxSdk = Build.VERSION_CODES.Q)
+    public void testFitsSystemWindows_whenQAndBelow_shouldSetFitsSystemWindowsToFalse() {
+        Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+        try (ActivityScenario<CarAppActivity> scenario = ActivityScenario.launch(newIntent)) {
+            scenario.onActivity(activity -> {
+                try {
+                    assertThat(
+                            activity.getWindow().getDecorView().getFitsSystemWindows()).isFalse();
+                } catch (Exception e) {
+                    fail(Log.getStackTraceString(e));
+                }
+            });
+
+        }
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.R)
+    public void testDecorFitsSystemWindows_whenRAndAbove_shouldSetDecorFitsSystemWindowsToFalse() {
+        Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+        try (ActivityScenario<CarAppActivity> scenario = ActivityScenario.launch(newIntent)) {
+            scenario.onActivity(activity -> {
+                try {
+                    assertThat(activity.getDecorFitsSystemWindows()).isFalse();
+                } catch (Exception e) {
+                    fail(Log.getStackTraceString(e));
+                }
+            });
+
+        }
+    }
+
     interface CarActivityAction {
         void accept(ActivityScenario<CarAppActivity> scenario, CarAppActivity activity)
                 throws Exception;