Merge "[SB] In StatusBarViewModel, keep icons hidden during camera launch." into main
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index d4d2b48..d91c6bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -30,11 +30,12 @@
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.R;
 
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.function.Predicate;
 
 /**
@@ -82,38 +83,55 @@
     private final DisplayManager mDisplayManager;
 
     /**
-     * The text description of the density values of the default display.
+     * The text description of the density values.
      */
-    private String[] mDefaultDisplayDensityEntries;
+    @Nullable
+    private final String[] mEntries;
 
     /**
-     * The density values of the default display.
+     * The density values.
      */
-    private int[] mDefaultDisplayDensityValues;
+    @Nullable
+    private final int[] mValues;
 
-    /**
-     * The density values, indexed by display unique ID.
-     */
-    private final Map<String, int[]> mValuesPerDisplay = new HashMap();
+    private final int mDefaultDensity;
+    private final int mCurrentIndex;
 
-    private int mDefaultDensityForDefaultDisplay;
-    private int mCurrentIndex = -1;
-
-    public DisplayDensityUtils(Context context) {
+    public DisplayDensityUtils(@NonNull Context context) {
         this(context, INTERNAL_ONLY);
     }
 
     /**
-     * Creates an instance that stores the density values for the displays that satisfy
-     * the predicate.
+     * Creates an instance that stores the density values for the smallest display that satisfies
+     * the predicate. It is enough to store the values for one display because the same density
+     * should be set to all the displays that satisfy the predicate.
      * @param context The context
      * @param predicate Determines what displays the density should be set for. The default display
      *                  must satisfy this predicate.
      */
-    public DisplayDensityUtils(Context context, Predicate predicate) {
+    public DisplayDensityUtils(@NonNull Context context,
+            @NonNull Predicate<DisplayInfo> predicate) {
         mPredicate = predicate;
         mDisplayManager = context.getSystemService(DisplayManager.class);
 
+        Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        DisplayInfo defaultDisplayInfo = new DisplayInfo();
+        if (!defaultDisplay.getDisplayInfo(defaultDisplayInfo)) {
+            Log.w(LOG_TAG, "Cannot fetch display info for the default display");
+            mEntries = null;
+            mValues = null;
+            mDefaultDensity = 0;
+            mCurrentIndex = -1;
+            return;
+        }
+        if (!mPredicate.test(defaultDisplayInfo)) {
+            throw new IllegalArgumentException(
+                    "Predicate must not filter out the default display.");
+        }
+
+        int idOfSmallestDisplay = Display.DEFAULT_DISPLAY;
+        int minDimensionPx = Math.min(defaultDisplayInfo.logicalWidth,
+                defaultDisplayInfo.logicalHeight);
         for (Display display : mDisplayManager.getDisplays(
                 DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
             DisplayInfo info = new DisplayInfo();
@@ -122,121 +140,123 @@
                 continue;
             }
             if (!mPredicate.test(info)) {
-                if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    throw new IllegalArgumentException("Predicate must not filter out the default "
-                            + "display.");
-                }
                 continue;
             }
-
-            final int defaultDensity = DisplayDensityUtils.getDefaultDensityForDisplay(
-                    display.getDisplayId());
-            if (defaultDensity <= 0) {
-                Log.w(LOG_TAG, "Cannot fetch default density for display "
-                        + display.getDisplayId());
-                continue;
+            int minDimension = Math.min(info.logicalWidth, info.logicalHeight);
+            if (minDimension < minDimensionPx) {
+                minDimensionPx = minDimension;
+                idOfSmallestDisplay = display.getDisplayId();
             }
-
-            final Resources res = context.getResources();
-
-            final int currentDensity = info.logicalDensityDpi;
-            int currentDensityIndex = -1;
-
-            // Compute number of "larger" and "smaller" scales for this display.
-            final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight);
-            final int maxDensity =
-                    DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
-            final float maxScaleDimen = context.getResources().getFraction(
-                    R.fraction.display_density_max_scale, 1, 1);
-            final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
-            final float minScale = context.getResources().getFraction(
-                    R.fraction.display_density_min_scale, 1, 1);
-            final float minScaleInterval = context.getResources().getFraction(
-                    R.fraction.display_density_min_scale_interval, 1, 1);
-            final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
-                    0, SUMMARIES_LARGER.length);
-            final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
-                    0, SUMMARIES_SMALLER.length);
-
-            String[] entries = new String[1 + numSmaller + numLarger];
-            int[] values = new int[entries.length];
-            int curIndex = 0;
-
-            if (numSmaller > 0) {
-                final float interval = (1 - minScale) / numSmaller;
-                for (int i = numSmaller - 1; i >= 0; i--) {
-                    // Round down to a multiple of 2 by truncating the low bit.
-                    final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
-                    if (currentDensity == density) {
-                        currentDensityIndex = curIndex;
-                    }
-                    entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
-                    values[curIndex] = density;
-                    curIndex++;
-                }
-            }
-
-            if (currentDensity == defaultDensity) {
-                currentDensityIndex = curIndex;
-            }
-            values[curIndex] = defaultDensity;
-            entries[curIndex] = res.getString(SUMMARY_DEFAULT);
-            curIndex++;
-
-            if (numLarger > 0) {
-                final float interval = (maxScale - 1) / numLarger;
-                for (int i = 0; i < numLarger; i++) {
-                    // Round down to a multiple of 2 by truncating the low bit.
-                    final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
-                    if (currentDensity == density) {
-                        currentDensityIndex = curIndex;
-                    }
-                    values[curIndex] = density;
-                    entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
-                    curIndex++;
-                }
-            }
-
-            final int displayIndex;
-            if (currentDensityIndex >= 0) {
-                displayIndex = currentDensityIndex;
-            } else {
-                // We don't understand the current density. Must have been set by
-                // someone else. Make room for another entry...
-                int newLength = values.length + 1;
-                values = Arrays.copyOf(values, newLength);
-                values[curIndex] = currentDensity;
-
-                entries = Arrays.copyOf(entries, newLength);
-                entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
-
-                displayIndex = curIndex;
-            }
-
-            if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                mDefaultDensityForDefaultDisplay = defaultDensity;
-                mCurrentIndex = displayIndex;
-                mDefaultDisplayDensityEntries = entries;
-                mDefaultDisplayDensityValues = values;
-            }
-            mValuesPerDisplay.put(info.uniqueId, values);
         }
+
+        final int defaultDensity =
+                DisplayDensityUtils.getDefaultDensityForDisplay(idOfSmallestDisplay);
+        if (defaultDensity <= 0) {
+            Log.w(LOG_TAG, "Cannot fetch default density for display " + idOfSmallestDisplay);
+            mEntries = null;
+            mValues = null;
+            mDefaultDensity = 0;
+            mCurrentIndex = -1;
+            return;
+        }
+
+        final Resources res = context.getResources();
+
+        final int currentDensity = defaultDisplayInfo.logicalDensityDpi;
+        int currentDensityIndex = -1;
+
+        // Compute number of "larger" and "smaller" scales for this display.
+        final int maxDensity =
+                DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+        final float maxScaleDimen = context.getResources().getFraction(
+                R.fraction.display_density_max_scale, 1, 1);
+        final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
+        final float minScale = context.getResources().getFraction(
+                R.fraction.display_density_min_scale, 1, 1);
+        final float minScaleInterval = context.getResources().getFraction(
+                R.fraction.display_density_min_scale_interval, 1, 1);
+        final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
+                0, SUMMARIES_LARGER.length);
+        final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
+                0, SUMMARIES_SMALLER.length);
+
+        String[] entries = new String[1 + numSmaller + numLarger];
+        int[] values = new int[entries.length];
+        int curIndex = 0;
+
+        if (numSmaller > 0) {
+            final float interval = (1 - minScale) / numSmaller;
+            for (int i = numSmaller - 1; i >= 0; i--) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+                values[curIndex] = density;
+                curIndex++;
+            }
+        }
+
+        if (currentDensity == defaultDensity) {
+            currentDensityIndex = curIndex;
+        }
+        values[curIndex] = defaultDensity;
+        entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+        curIndex++;
+
+        if (numLarger > 0) {
+            final float interval = (maxScale - 1) / numLarger;
+            for (int i = 0; i < numLarger; i++) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                values[curIndex] = density;
+                entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+                curIndex++;
+            }
+        }
+
+        final int displayIndex;
+        if (currentDensityIndex >= 0) {
+            displayIndex = currentDensityIndex;
+        } else {
+            // We don't understand the current density. Must have been set by
+            // someone else. Make room for another entry...
+            int newLength = values.length + 1;
+            values = Arrays.copyOf(values, newLength);
+            values[curIndex] = currentDensity;
+
+            entries = Arrays.copyOf(entries, newLength);
+            entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+            displayIndex = curIndex;
+        }
+
+        mDefaultDensity = defaultDensity;
+        mCurrentIndex = displayIndex;
+        mEntries = entries;
+        mValues = values;
     }
 
-    public String[] getDefaultDisplayDensityEntries() {
-        return mDefaultDisplayDensityEntries;
+    @Nullable
+    public String[] getEntries() {
+        return mEntries;
     }
 
-    public int[] getDefaultDisplayDensityValues() {
-        return mDefaultDisplayDensityValues;
+    @Nullable
+    public int[] getValues() {
+        return mValues;
     }
 
-    public int getCurrentIndexForDefaultDisplay() {
+    public int getCurrentIndex() {
         return mCurrentIndex;
     }
 
-    public int getDefaultDensityForDefaultDisplay() {
-        return mDefaultDensityForDefaultDisplay;
+    public int getDefaultDensity() {
+        return mDefaultDensity;
     }
 
     /**
@@ -311,15 +331,9 @@
                     if (!mPredicate.test(info)) {
                         continue;
                     }
-                    if (!mValuesPerDisplay.containsKey(info.uniqueId)) {
-                        Log.w(LOG_TAG, "Unable to save forced display density setting "
-                                + "for display " + info.uniqueId);
-                        continue;
-                    }
 
                     final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-                    wm.setForcedDisplayDensityForUser(displayId,
-                            mValuesPerDisplay.get(info.uniqueId)[index], userId);
+                    wm.setForcedDisplayDensityForUser(displayId, mValues[index], userId);
                 }
             } catch (RemoteException exc) {
                 Log.w(LOG_TAG, "Unable to save forced display density setting");
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
index d001ef96..031fbab 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
@@ -68,7 +68,10 @@
         seedColor = null,
         overrideChroma = null,
         typefaceCache =
-            TypefaceCache(messageBuffer) { Typeface.createFromAsset(pluginCtx.assets, it) },
+            TypefaceCache(messageBuffer) {
+                // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
+                return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
+            },
         getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor,
         messageBuffer = messageBuffer,
     )
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 3903dba..900971b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -17,6 +17,8 @@
 import android.content.res.Resources
 import android.view.LayoutInflater
 import com.android.systemui.customization.R
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
@@ -53,7 +55,9 @@
         }
 
         return if (clockReactiveVariants) {
-            val assets = AssetLoader(ctx, ctx, "clocks/", messageBuffers!!.infraMessageBuffer)
+            val buffer =
+                messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO)
+            val assets = AssetLoader(ctx, ctx, "clocks/", buffer)
             FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers)
         } else {
             DefaultClockController(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index ef24d2a..9067fb0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -71,7 +71,7 @@
         val layer = face.layers[0]
 
         layerController =
-            if (isLargeClock)
+            if (isLargeClock) {
                 ComposedDigitalLayerController(
                     ctx,
                     resources,
@@ -79,7 +79,7 @@
                     layer as ComposedDigitalHandLayer,
                     messageBuffer,
                 )
-            else {
+            } else {
                 val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
                 SimpleDigitalHandLayerController(
                     ctx,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
index fd1f52b..ec0773f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.panels.data.repository
 
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -25,7 +24,7 @@
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -59,15 +58,22 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun withDualShade_returnsCorrectValue() =
         with(kosmos) {
             testScope.runTest {
-                val latest by collectLastValue(underTest.columns)
-                assertThat(latest).isEqualTo(4)
+                val latest by collectLastValue(underTest.dualShadeColumns)
 
-                setColumnsInConfig(8, id = R.integer.quick_settings_dual_shade_num_columns)
-                // Asserts config changes are ignored
+                assertThat(latest).isEqualTo(4)
+            }
+        }
+
+    @Test
+    fun withSplitShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.splitShadeColumns)
+                fakeShadeRepository.setShadeLayoutWide(true)
+
                 assertThat(latest).isEqualTo(4)
             }
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
new file mode 100644
index 0000000..35f7504
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 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 com.android.systemui.qs.panels.domain.interactor
+
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSColumnsInteractorTest : SysuiTestCase() {
+    private val kosmos =
+        testKosmos().apply {
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_infinite_grid_num_columns,
+                1,
+            )
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_dual_shade_num_columns,
+                2,
+            )
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_split_shade_num_columns,
+                3,
+            )
+            qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository)
+        }
+    private lateinit var underTest: QSColumnsInteractor
+
+    @Before
+    fun setUp() {
+        underTest = with(kosmos) { qsColumnsInteractor }
+    }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun withSingleShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                assertThat(latest).isEqualTo(1)
+            }
+        }
+
+    @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun withDualShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                assertThat(latest).isEqualTo(2)
+            }
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun withSplitShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                fakeShadeRepository.setShadeLayoutWide(true)
+
+                assertThat(latest).isEqualTo(3)
+            }
+        }
+}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 7d840cf..f96a0b9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -82,6 +82,9 @@
     <!-- The number of columns in the Dual Shade QuickSettings -->
     <integer name="quick_settings_dual_shade_num_columns">4</integer>
 
+    <!-- The number of columns in the Split Shade QuickSettings -->
+    <integer name="quick_settings_split_shade_num_columns">4</integer>
+
     <!-- Override column number for quick settings.
     For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
     TODO (b/293252410) - change this comment/resource when flag is enabled -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
index 082f622..a9205c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
@@ -19,40 +19,31 @@
 import android.content.res.Resources
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class QSColumnsRepository
 @Inject
 constructor(
-    @Application scope: CoroutineScope,
     @Main private val resources: Resources,
     configurationRepository: ConfigurationRepository,
 ) {
-    val columns: StateFlow<Int> =
-        if (DualShade.isEnabled) {
-                flowOf(resources.getInteger(R.integer.quick_settings_dual_shade_num_columns))
-            } else {
-                configurationRepository.onConfigurationChange.emitOnStart().mapLatest {
-                    resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns)
-                }
-            }
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns),
-            )
+    val splitShadeColumns: Flow<Int> =
+        flowOf(resources.getInteger(R.integer.quick_settings_split_shade_num_columns))
+    val dualShadeColumns: Flow<Int> =
+        flowOf(resources.getInteger(R.integer.quick_settings_dual_shade_num_columns))
+    val columns: Flow<Int> =
+        configurationRepository.onConfigurationChange.emitOnStart().mapLatest {
+            resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns)
+        }
+    val defaultColumns: Int =
+        resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
index 9b45c56..11ea60e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
@@ -17,11 +17,35 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.stateIn
 
 @SysUISingleton
-class QSColumnsInteractor @Inject constructor(repo: QSColumnsRepository) {
-    val columns: StateFlow<Int> = repo.columns
+class QSColumnsInteractor
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    repo: QSColumnsRepository,
+    shadeInteractor: ShadeInteractor,
+) {
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val columns: StateFlow<Int> =
+        shadeInteractor.shadeMode
+            .flatMapLatest {
+                when (it) {
+                    ShadeMode.Dual -> repo.dualShadeColumns
+                    ShadeMode.Split -> repo.splitShadeColumns
+                    ShadeMode.Single -> repo.columns
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), repo.defaultColumns)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
index 0ca025f..742b79c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
@@ -19,9 +19,6 @@
 import android.content.res.mainResources
 import com.android.systemui.common.ui.data.repository.configurationRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 
-val Kosmos.qsColumnsRepository by
-    Kosmos.Fixture {
-        QSColumnsRepository(applicationCoroutineScope, mainResources, configurationRepository)
-    }
+var Kosmos.qsColumnsRepository by
+    Kosmos.Fixture { QSColumnsRepository(mainResources, configurationRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
index 02ed264..47615f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
@@ -17,6 +17,11 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 
-val Kosmos.qsColumnsInteractor by Kosmos.Fixture { QSColumnsInteractor(qsColumnsRepository) }
+val Kosmos.qsColumnsInteractor by
+    Kosmos.Fixture {
+        QSColumnsInteractor(applicationCoroutineScope, qsColumnsRepository, shadeInteractor)
+    }