Incorporate ANC slice into the Volume Panel button

Flag: aconfig new_volume_panel TEAMFOOD
Test: manual on the phone with a supported headset
Test: atest AncSliceInteractorTest
Fixes: 328369400
Change-Id: I5d4ef3067e94b32eea2cd28581a6bfdfb5f58ac5
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
index ccb5d36..fa052e8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -17,15 +17,12 @@
 package com.android.systemui.volume.panel.component.anc
 
 import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
-import com.android.systemui.volume.panel.component.anc.ui.composable.AncPopup
-import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
-import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.anc.ui.composable.AncButtonComponent
 import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 import dagger.multibindings.IntoMap
 import dagger.multibindings.StringKey
 
@@ -40,14 +37,8 @@
         criteria: AncAvailabilityCriteria
     ): ComponentAvailabilityCriteria
 
-    companion object {
-
-        @Provides
-        @IntoMap
-        @StringKey(VolumePanelComponents.ANC)
-        fun provideVolumePanelUiComponent(
-            viewModel: AncViewModel,
-            popup: AncPopup,
-        ): VolumePanelUiComponent = ButtonComponent(viewModel.button, popup::show)
-    }
+    @Binds
+    @IntoMap
+    @StringKey(VolumePanelComponents.ANC)
+    fun bindVolumePanelUiComponent(component: AncButtonComponent): VolumePanelUiComponent
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
new file mode 100644
index 0000000..00225fc
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.volume.panel.component.anc.ui.composable
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+class AncButtonComponent
+@Inject
+constructor(
+    private val viewModel: AncViewModel,
+    private val ancPopup: AncPopup,
+) : ComposeVolumePanelUiComponent {
+
+    @Composable
+    override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+        val slice by viewModel.buttonSlice.collectAsState()
+        val label = stringResource(R.string.volume_panel_noise_control_title)
+        Column(
+            modifier = modifier,
+            verticalArrangement = Arrangement.spacedBy(12.dp),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            SliceAndroidView(
+                modifier =
+                    Modifier.height(64.dp)
+                        .fillMaxWidth()
+                        .semantics {
+                            role = Role.Button
+                            contentDescription = label
+                        }
+                        .clip(RoundedCornerShape(28.dp)),
+                slice = slice,
+                onWidthChanged = viewModel::onButtonSliceWidthChanged,
+                onClick = { ancPopup.show(null) }
+            )
+            Text(
+                modifier = Modifier.clearAndSetSemantics {},
+                text = label,
+                style = MaterialTheme.typography.labelMedium,
+                maxLines = 1,
+                overflow = TextOverflow.Ellipsis,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index 9f0da00..2af042a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -16,9 +16,6 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.composable
 
-import android.content.Context
-import android.view.ContextThemeWrapper
-import android.view.View
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material3.MaterialTheme
@@ -30,9 +27,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.viewinterop.AndroidView
 import androidx.slice.Slice
-import androidx.slice.widget.SliceView
 import com.android.systemui.animation.Expandable
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -49,7 +44,7 @@
 ) {
 
     /** Shows a popup with the [expandable] animation. */
-    fun show(expandable: Expandable) {
+    fun show(expandable: Expandable?) {
         volumePanelPopup.show(expandable, { Title() }, { Content(it) })
     }
 
@@ -66,49 +61,18 @@
 
     @Composable
     private fun Content(dialog: SystemUIDialog) {
-        val slice: Slice? by viewModel.slice.collectAsState()
+        val isAvailable by viewModel.isAvailable.collectAsState(true)
 
-        if (slice == null) {
+        if (!isAvailable) {
             SideEffect { dialog.dismiss() }
             return
         }
 
-        AndroidView<SliceView>(
+        val slice by viewModel.popupSlice.collectAsState()
+        SliceAndroidView(
             modifier = Modifier.fillMaxWidth(),
-            factory = { context: Context ->
-                SliceView(ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel))
-                    .apply {
-                        mode = SliceView.MODE_LARGE
-                        isScrollable = false
-                        importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
-                        setShowTitleItems(true)
-                        addOnLayoutChangeListener(
-                            OnWidthChangedLayoutListener(viewModel::changeSliceWidth)
-                        )
-                    }
-            },
-            update = { sliceView: SliceView -> sliceView.slice = slice }
+            slice = slice,
+            onWidthChanged = viewModel::onPopupSliceWidthChanged
         )
     }
-
-    private class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
-        View.OnLayoutChangeListener {
-        override fun onLayoutChange(
-            v: View?,
-            left: Int,
-            top: Int,
-            right: Int,
-            bottom: Int,
-            oldLeft: Int,
-            oldTop: Int,
-            oldRight: Int,
-            oldBottom: Int
-        ) {
-            val newWidth = right - left
-            val oldWidth = oldRight - oldLeft
-            if (oldWidth != newWidth) {
-                widthChanged(newWidth)
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
new file mode 100644
index 0000000..f354b80
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.volume.panel.component.anc.ui.composable
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.ContextThemeWrapper
+import android.view.MotionEvent
+import android.view.View
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.slice.Slice
+import androidx.slice.widget.SliceView
+import com.android.systemui.res.R
+
+@Composable
+fun SliceAndroidView(
+    slice: Slice?,
+    modifier: Modifier = Modifier,
+    onWidthChanged: ((Int) -> Unit)? = null,
+    onClick: (() -> Unit)? = null,
+) {
+    AndroidView(
+        modifier = modifier,
+        factory = { context: Context ->
+            ClickableSliceView(
+                    ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
+                    onClick,
+                )
+                .apply {
+                    mode = SliceView.MODE_LARGE
+                    isScrollable = false
+                    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+                    setShowTitleItems(true)
+                    if (onWidthChanged != null) {
+                        addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
+                    }
+                    if (onClick != null) {
+                        setOnClickListener { onClick() }
+                    }
+                }
+        },
+        update = { sliceView: SliceView -> sliceView.slice = slice }
+    )
+}
+
+class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
+    View.OnLayoutChangeListener {
+
+    override fun onLayoutChange(
+        v: View?,
+        left: Int,
+        top: Int,
+        right: Int,
+        bottom: Int,
+        oldLeft: Int,
+        oldTop: Int,
+        oldRight: Int,
+        oldBottom: Int
+    ) {
+        val newWidth = right - left
+        val oldWidth = oldRight - oldLeft
+        if (oldWidth != newWidth) {
+            widthChanged(newWidth)
+        }
+    }
+}
+
+/**
+ * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
+ * first.
+ */
+@SuppressLint("ViewConstructor") // only used in this class
+private class ClickableSliceView(
+    context: Context,
+    private val onClick: (() -> Unit)?,
+) : SliceView(context) {
+
+    init {
+        if (onClick != null) {
+            setOnClickListener {}
+        }
+    }
+
+    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
+        return onClick != null || super.onInterceptTouchEvent(ev)
+    }
+
+    override fun onClick(v: View?) {
+        onClick?.let { it() } ?: super.onClick(v)
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
index 9f9bc62..b489dfc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -59,7 +59,7 @@
      * @param content is the popup body
      */
     fun show(
-        expandable: Expandable,
+        expandable: Expandable?,
         title: @Composable (SystemUIDialog) -> Unit,
         content: @Composable (SystemUIDialog) -> Unit,
     ) {
@@ -70,7 +70,7 @@
             ) {
                 PopupComposable(it, title, content)
             }
-        val controller = expandable.dialogTransitionController()
+        val controller = expandable?.dialogTransitionController()
         if (controller == null) {
             dialog.show()
         } else {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
index e31cdcd..dc96139 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -72,7 +72,7 @@
             testScope.runTest {
                 localMediaRepository.updateCurrentConnectedDevice(null)
 
-                val slice by collectLastValue(underTest.ancSlice(1))
+                val slice by collectLastValue(underTest.ancSlice(1, false, false))
                 runCurrent()
 
                 assertThat(slice).isNull()
@@ -86,7 +86,7 @@
             testScope.runTest {
                 localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
 
-                val slice by collectLastValue(underTest.ancSlice(1))
+                val slice by collectLastValue(underTest.ancSlice(1, false, false))
                 runCurrent()
 
                 assertThat(slice).isNotNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
index 53f0bc9..81e6ac4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
 import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -57,10 +58,10 @@
                     FakeSliceFactory.createSlice(hasError = true, hasSliceItem = true)
                 )
 
-                val slice by collectLastValue(underTest.ancSlice)
+                val slice by collectLastValue(underTest.ancSlices)
                 runCurrent()
 
-                assertThat(slice).isNull()
+                assertThat(slice).isInstanceOf(AncSlices.Unavailable::class.java)
             }
         }
     }
@@ -74,10 +75,10 @@
                     FakeSliceFactory.createSlice(hasError = false, hasSliceItem = false)
                 )
 
-                val slice by collectLastValue(underTest.ancSlice)
+                val slice by collectLastValue(underTest.ancSlices)
                 runCurrent()
 
-                assertThat(slice).isNull()
+                assertThat(slice).isInstanceOf(AncSlices.Unavailable::class.java)
             }
         }
     }
@@ -91,10 +92,10 @@
                     FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
                 )
 
-                val slice by collectLastValue(underTest.ancSlice)
+                val slice by collectLastValue(underTest.ancSlices)
                 runCurrent()
 
-                assertThat(slice).isNotNull()
+                assertThat(slice).isInstanceOf(AncSlices.Ready::class.java)
             }
         }
     }
diff --git a/packages/SystemUI/res/drawable/ic_noise_aware.xml b/packages/SystemUI/res/drawable/ic_noise_aware.xml
deleted file mode 100644
index 5482641..0000000
--- a/packages/SystemUI/res/drawable/ic_noise_aware.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ 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.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M440,82Q450,81 460,80.5Q470,80 480,80Q491,80 500.5,80.5Q510,81 520,82L520,162Q510,160 500.5,160Q491,160 480,160Q469,160 459.5,160Q450,160 440,162L440,82ZM272,138Q289,127 306.5,119Q324,111 343,104L378,176Q358,182 340.5,190.5Q323,199 306,210L272,138ZM654,210Q637,199 619.5,190.5Q602,182 582,176L617,104Q636,111 653.5,119Q671,127 688,138L654,210ZM753,311Q742,294 729,278.5Q716,263 702,249L765,199Q779,213 792,228.5Q805,244 816,261L753,311ZM143,263Q154,246 166.5,230.5Q179,215 193,201L256,251Q242,265 229.5,280.5Q217,296 206,313L143,263ZM83,428Q85,408 90,388.5Q95,369 101,350L180,368Q173,387 168.5,406.5Q164,426 162,446L83,428ZM799,449Q797,429 792.5,409Q788,389 781,370L859,352Q865,371 870,390.5Q875,410 877,430L799,449ZM781,590Q788,571 792,552Q796,533 798,513L877,531Q875,551 870,570.5Q865,590 859,609L781,590ZM162,514Q164,534 168.5,553.5Q173,573 180,592L101,610Q95,591 90,571.5Q85,552 83,532L162,514ZM705,708Q719,694 731,678.5Q743,663 754,646L818,696Q807,713 794.5,728.5Q782,744 768,758L705,708ZM194,760Q180,746 167.5,730Q155,714 144,697L206,647Q217,664 229.5,680Q242,696 256,710L194,760ZM583,783Q603,776 620,768Q637,760 654,749L689,821Q672,832 654.5,840.5Q637,849 618,856L583,783ZM344,857Q325,850 307,841.5Q289,833 272,822L307,750Q324,761 341.5,769.5Q359,778 379,784L344,857ZM480,880Q470,880 460,879.5Q450,879 440,878L440,798Q453,800 480,800Q491,800 500.5,800Q510,800 520,798L520,878Q510,879 500.5,879.5Q491,880 480,880ZM520,720Q482,720 450.5,697Q419,674 406,638Q403,629 399.5,620.5Q396,612 389,605L334,550Q308,524 294,490.5Q280,457 280,420Q280,345 332.5,292.5Q385,240 460,240Q529,240 580,285.5Q631,331 639,400L558,400Q551,365 523.5,342.5Q496,320 460,320Q418,320 389,349Q360,378 360,420Q360,440 368,459.5Q376,479 391,494L445,548Q459,562 467.5,578.5Q476,595 482,612Q487,625 497,632.5Q507,640 520,640Q537,640 548.5,628.5Q560,617 560,600L640,600Q640,650 605.5,685Q571,720 520,720ZM540,560Q515,560 497.5,542.5Q480,525 480,500Q480,474 497.5,457Q515,440 540,440Q566,440 583,457Q600,474 600,500Q600,525 583,542.5Q566,560 540,560Z"/>
-</vector>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b95ee56..2338b3e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1985,4 +1985,7 @@
 
     <!-- SliceView grid gutter for ANC Slice -->
     <dimen name="abc_slice_grid_gutter">0dp</dimen>
+    <!-- SliceView icon size -->
+    <dimen name="abc_slice_big_pic_min_height">64dp</dimen>
+    <dimen name="abc_slice_big_pic_max_height">64dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
index 8f18aa8..8ce3b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -41,12 +41,14 @@
 interface AncSliceRepository {
 
     /**
-     * ANC slice with a given width. Emits null when there is no ANC slice available. This can mean
-     * that:
+     * ANC slice with a given width. [isCollapsed] slice shows a single button, and expanded shows a
+     * row buttons.
+     *
+     * Emits null when there is no ANC slice available. This can mean that:
      * - there is no supported device connected;
      * - there is no slice provider for the uri;
      */
-    fun ancSlice(width: Int): Flow<Slice?>
+    fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?>
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -60,9 +62,14 @@
 
     private val localMediaRepository = mediaRepositoryFactory.create(null)
 
-    override fun ancSlice(width: Int): Flow<Slice?> {
+    override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
         return localMediaRepository.currentConnectedDevice
-            .map { (it as? BluetoothMediaDevice)?.cachedDevice?.device?.getExtraControlUri(width) }
+            .map {
+                (it as? BluetoothMediaDevice)
+                    ?.cachedDevice
+                    ?.device
+                    ?.getExtraControlUri(width, isCollapsed, hideLabel)
+            }
             .distinctUntilChanged()
             .flatMapLatest { sliceUri ->
                 sliceUri ?: return@flatMapLatest flowOf(null)
@@ -71,7 +78,11 @@
             .flowOn(backgroundCoroutineContext)
     }
 
-    private fun BluetoothDevice.getExtraControlUri(width: Int): Uri? {
+    private fun BluetoothDevice.getExtraControlUri(
+        width: Int,
+        isCollapsed: Boolean,
+        hideLabel: Boolean
+    ): Uri? {
         val uri: String? = BluetoothUtils.getControlUriMetaData(this)
         uri ?: return null
 
@@ -81,7 +92,8 @@
             Uri.parse(
                 "$uri$width" +
                     "&version=${SliceParameters.VERSION}" +
-                    "&is_collapsed=${SliceParameters.IS_COLLAPSED}"
+                    "&is_collapsed=$isCollapsed" +
+                    "&hide_label=$hideLabel"
             )
         }
     }
@@ -98,11 +110,5 @@
          * 2) new slice
          */
         const val VERSION = 2
-
-        /**
-         * Collapsed slice shows a single button, and expanded shows a row buttons. Supported since
-         * [VERSION]==2.
-         */
-        const val IS_COLLAPSED = false
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
index 89b9274..dc4be26 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.component.anc.domain
 
 import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import javax.inject.Inject
@@ -31,5 +32,6 @@
     private val ancSliceInteractor: AncSliceInteractor,
 ) : ComponentAvailabilityCriteria {
 
-    override fun isAvailable(): Flow<Boolean> = ancSliceInteractor.ancSlice.map { it != null }
+    override fun isAvailable(): Flow<Boolean> =
+        ancSliceInteractor.ancSlices.map { it is AncSlices.Ready }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
index 91af622..cefa269 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -20,16 +20,19 @@
 import android.app.slice.SliceItem.FORMAT_SLICE
 import androidx.slice.Slice
 import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
 
 /** Provides a valid slice from [AncSliceRepository]. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -41,25 +44,35 @@
     scope: CoroutineScope,
 ) {
 
-    // Start with a positive width to check is the Slice is available.
-    private val width = MutableStateFlow(1)
+    // Any positive width to check if the Slice is available.
+    private val buttonSliceWidth = MutableStateFlow(1)
+    private val popupSliceWidth = MutableStateFlow(1)
 
-    /** Provides a valid ANC slice. */
-    val ancSlice: SharedFlow<Slice?> =
-        width
-            .flatMapLatest { width -> ancSliceRepository.ancSlice(width) }
-            .map { slice ->
-                if (slice?.isValidSlice() == true) {
-                    slice
+    val ancSlices: StateFlow<AncSlices> =
+        combine(
+                buttonSliceWidth.flatMapLatest {
+                    ancSlice(width = it, isCollapsed = true, hideLabel = true)
+                },
+                popupSliceWidth.flatMapLatest {
+                    ancSlice(width = it, isCollapsed = false, hideLabel = false)
+                }
+            ) { buttonSlice, popupSlice ->
+                if (buttonSlice != null && popupSlice != null) {
+                    AncSlices.Ready(buttonSlice = buttonSlice, popupSlice = popupSlice)
                 } else {
-                    null
+                    AncSlices.Unavailable
                 }
             }
-            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+            .stateIn(scope, SharingStarted.Eagerly, AncSlices.Unavailable)
 
-    /** Updates the width of the [ancSlice] */
-    fun changeWidth(newWidth: Int) {
-        width.value = newWidth
+    /**
+     * Provides a valid [isCollapsed] ANC slice for a given [width]. Use [hideLabel] == true to
+     * remove the labels from the [Slice].
+     */
+    private fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
+        return ancSliceRepository
+            .ancSlice(width = width, isCollapsed = isCollapsed, hideLabel = hideLabel)
+            .filter { it?.isValidSlice() != false }
     }
 
     private fun Slice.isValidSlice(): Boolean {
@@ -73,4 +86,20 @@
         }
         return false
     }
+
+    /**
+     * Call this to update [AncSlices.Ready.popupSlice] width in a reaction to container size
+     * change.
+     */
+    fun onPopupSliceWidthChanged(width: Int) {
+        popupSliceWidth.tryEmit(width)
+    }
+
+    /**
+     * Call this to update [AncSlices.Ready.buttonSlice] width in a reaction to container size
+     * change.
+     */
+    fun onButtonSliceWidthChanged(width: Int) {
+        buttonSliceWidth.tryEmit(width)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt
new file mode 100644
index 0000000..3cd4e67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.volume.panel.component.anc.domain.model
+
+import androidx.slice.Slice
+
+/** Modes current ANC slices state */
+sealed interface AncSlices {
+
+    data class Ready(
+        val popupSlice: Slice,
+        val buttonSlice: Slice,
+    ) : AncSlices
+
+    /** Couldn't one or both slices. */
+    data object Unavailable : AncSlices
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
index eb96f6c..bee79bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -16,52 +16,56 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.viewmodel
 
-import android.content.Context
 import androidx.slice.Slice
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
 import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Volume Panel ANC component view model. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class AncViewModel
 @Inject
 constructor(
-    @Application private val context: Context,
     @VolumePanelScope private val coroutineScope: CoroutineScope,
     private val interactor: AncSliceInteractor,
+    private val availabilityCriteria: AncAvailabilityCriteria,
 ) {
 
-    /** ANC [Slice]. Null when there is no slice available for ANC. */
-    val slice: StateFlow<Slice?> =
-        interactor.ancSlice.stateIn(coroutineScope, SharingStarted.Eagerly, null)
+    val isAvailable: Flow<Boolean>
+        get() = availabilityCriteria.isAvailable()
 
-    /**
-     * ButtonViewModel to be shown in the VolumePanel. Null when there is no ANC Slice available.
-     */
-    val button: StateFlow<ButtonViewModel?> =
-        interactor.ancSlice
-            .map { slice ->
-                slice?.let {
-                    ButtonViewModel(
-                        Icon.Resource(R.drawable.ic_noise_aware, null),
-                        context.getString(R.string.volume_panel_noise_control_title)
-                    )
-                }
-            }
+    /** ANC [Slice]. Null when there is no slice available for ANC. */
+    val popupSlice: StateFlow<Slice?> =
+        interactor.ancSlices
+            .filterIsInstance<AncSlices.Ready>()
+            .map { it.popupSlice }
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
 
-    /** Call this to update [slice] width in a reaction to container size change. */
-    fun changeSliceWidth(width: Int) {
-        interactor.changeWidth(width)
+    /** Button [Slice] to be shown in the VolumePanel. Null when there is no ANC Slice available. */
+    val buttonSlice: StateFlow<Slice?> =
+        interactor.ancSlices
+            .filterIsInstance<AncSlices.Ready>()
+            .map { it.buttonSlice }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+    /** Call this to update [popupSlice] width in a reaction to container size change. */
+    fun onPopupSliceWidthChanged(width: Int) {
+        interactor.onPopupSliceWidthChanged(width)
+    }
+
+    /** Call this to update [buttonSlice] width in a reaction to container size change. */
+    fun onButtonSliceWidthChanged(width: Int) {
+        interactor.onButtonSliceWidthChanged(width)
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
index b66d7f9..d4a72b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -24,8 +24,9 @@
 
     private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
 
-    override fun ancSlice(width: Int): Flow<Slice?> =
-        sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+    override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
+        return sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+    }
 
     fun putSlice(width: Int, slice: Slice?) {
         sliceByWidth.getOrPut(width) { MutableStateFlow(null) }.value = slice