Merge "Add VolumeDialogInteractor to capture the show/dismiss UI events of VolumeDialog." into main
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f457470..4205dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,6 +135,7 @@
 import com.android.systemui.util.AlphaTintDrawableWrapper;
 import com.android.systemui.util.RoundedCornerProgressDrawable;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
 import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
 import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
@@ -315,6 +316,7 @@
     private final com.android.systemui.util.time.SystemClock mSystemClock;
     private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
     private final VolumePanelFlag mVolumePanelFlag;
+    private final VolumeDialogInteractor mInteractor;
 
     public VolumeDialogImpl(
             Context context,
@@ -335,7 +337,8 @@
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
             VolumeDialogMenuIconBinder volumeDialogMenuIconBinder,
-            com.android.systemui.util.time.SystemClock systemClock) {
+            com.android.systemui.util.time.SystemClock systemClock,
+            VolumeDialogInteractor interactor) {
         mContext =
                 new ContextThemeWrapper(context, R.style.volume_dialog_theme);
         mHandler = new H(looper);
@@ -370,6 +373,7 @@
         mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder;
         mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
         mVolumePanelFlag = volumePanelFlag;
+        mInteractor = interactor;
 
         dumpManager.registerDumpable("VolumeDialogImpl", this);
 
@@ -1519,6 +1523,7 @@
         mShowing = true;
         mIsAnimatingDismiss = false;
         mDialog.show();
+        mInteractor.onDialogShown();
         Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked);
         mController.notifyVisible(true);
         mController.getCaptionsComponentState(false);
@@ -1605,6 +1610,7 @@
         }
         mIsAnimatingDismiss = true;
         mDialogView.animate().cancel();
+        mInteractor.onDialogDismissed();
         if (mShowing) {
             mShowing = false;
             // Only logs when the volume dialog visibility is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index f8ddc42..8003f39 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -41,6 +41,7 @@
 import com.android.systemui.volume.VolumeDialogModule;
 import com.android.systemui.volume.VolumePanelDialogReceiver;
 import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
 import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
 import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
@@ -118,7 +119,8 @@
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
             VolumeDialogMenuIconBinder volumeDialogMenuIconBinder,
-            SystemClock systemClock) {
+            SystemClock systemClock,
+            VolumeDialogInteractor interactor) {
         VolumeDialogImpl impl = new VolumeDialogImpl(
                 context,
                 volumeDialogController,
@@ -138,7 +140,8 @@
                 secureSettings,
                 vibratorHelper,
                 volumeDialogMenuIconBinder,
-                systemClock);
+                systemClock,
+                interactor);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
         impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt
new file mode 100644
index 0000000..75e1c5ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** A repository that encapsulates the states for Volume Dialog. */
+@SysUISingleton
+class VolumeDialogRepository @Inject constructor() {
+    private val _isDialogVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    /** Whether the Volume Dialog is visible. */
+    val isDialogVisible = _isDialogVisible.asStateFlow()
+
+    /** Sets whether the Volume Dialog is visible. */
+    fun setDialogVisibility(isVisible: Boolean) {
+        _isDialogVisible.value = isVisible
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt
new file mode 100644
index 0000000..813e707
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.data.repository.VolumeDialogRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** An interactor that propagates the UI states of the Volume Dialog. */
+@SysUISingleton
+class VolumeDialogInteractor
+@Inject
+constructor(
+    private val repository: VolumeDialogRepository,
+) {
+    /** Whether the Volume Dialog is visible. */
+    val isDialogVisible: StateFlow<Boolean> = repository.isDialogVisible
+
+    /** Notifies that the Volume Dialog is shown. */
+    fun onDialogShown() {
+        repository.setDialogVisibility(true)
+    }
+
+    /** Notifies that the Volume Dialog has been dismissed. */
+    fun onDialogDismissed() {
+        repository.setDialogVisibility(false)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index fac6a4c..57ddcde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -86,6 +86,7 @@
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
 import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
 import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
@@ -153,6 +154,8 @@
     private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
     @Mock
     private VolumePanelFlag mVolumePanelFlag;
+    @Mock
+    private VolumeDialogInteractor mVolumeDialogInteractor;
 
     private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
             new CsdWarningDialog.Factory() {
@@ -218,7 +221,8 @@
                 mLazySecureSettings,
                 mVibratorHelper,
                 mVolumeDialogMenuIconBinder,
-                new FakeSystemClock());
+                new FakeSystemClock(),
+                mVolumeDialogInteractor);
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
@@ -778,6 +782,15 @@
         assertFalse(foundDnDIcon);
     }
 
+    @Test
+    public void testInteractor_onShow() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        mTestableLooper.processAllMessages();
+
+        verify(mVolumeDialogInteractor).onDialogShown();
+        verify(mVolumeDialogInteractor).onDialogDismissed(); // dismiss by timeout
+    }
+
     /**
      * @return true if at least one volume row has the DND icon
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
new file mode 100644
index 0000000..dcac85e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogRepositoryTest : SysuiTestCase() {
+    private lateinit var underTest: VolumeDialogRepository
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogRepository
+    }
+
+    @Test
+    fun isDialogVisible_initialValueFalse() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            runCurrent()
+
+            assertThat(isVisible).isFalse()
+        }
+    }
+
+    @Test
+    fun isDialogVisible_onChange() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            runCurrent()
+
+            underTest.setDialogVisibility(true)
+            assertThat(isVisible).isTrue()
+
+            underTest.setDialogVisibility(false)
+            assertThat(isVisible).isFalse()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
new file mode 100644
index 0000000..5c735e3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogInteractorTest : SysuiTestCase() {
+    private lateinit var underTest: VolumeDialogInteractor
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogInteractor
+    }
+
+    @Test
+    fun onDialogDismissed() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            underTest.onDialogDismissed()
+            runCurrent()
+
+            assertThat(isVisible).isFalse()
+        }
+    }
+
+    @Test
+    fun onDialogShown() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            underTest.onDialogShown()
+            runCurrent()
+
+            assertThat(isVisible).isTrue()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 67f8443..31cdbc7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -68,6 +68,7 @@
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.util.time.systemClock
+import com.android.systemui.volume.domain.interactor.volumeDialogInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
@@ -136,6 +137,7 @@
     val shadeInteractor by lazy { kosmos.shadeInteractor }
     val wifiInteractor by lazy { kosmos.wifiInteractor }
     val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
+    val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
 
     val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
     val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
new file mode 100644
index 0000000..2f4eef5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumeDialogRepository by Kosmos.Fixture { VolumeDialogRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
new file mode 100644
index 0000000..2e5d040
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.data.repository.volumeDialogRepository
+
+val Kosmos.volumeDialogInteractor by
+    Kosmos.Fixture { VolumeDialogInteractor(volumeDialogRepository) }