Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a70f5a8 authored by Matías Hernández's avatar Matías Hernández
Browse files

Show the name of the mode blocking audio streams in volume panel

Instead of the fixed string "do not disturb".

Bug: 372015450
Test: AudioStreamSliderViewModelTest
Flag: android.app.modes_ui
Change-Id: Ia39c97ed47aec2eac3842a72211b3d7e95e8b985
parent c54807b8
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -78,6 +78,10 @@ class FakeZenModeRepository : ZenModeRepository {
        mutableModesFlow.value = (mutableModesFlow.value.filter { it.id != modeId }) + mode
    }

    fun clearModes() {
        mutableModesFlow.value = listOf()
    }

    fun getMode(id: String): ZenMode? {
        return mutableModesFlow.value.find { it.id == id }
    }
+117 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.policy.domain.interactor

import android.app.AutomaticZenRule
import android.app.Flags
import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
import android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY
import android.app.NotificationManager.Policy
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
@@ -25,6 +27,7 @@ import android.provider.Settings.Secure.ZEN_DURATION
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.SystemZenRules
import android.service.notification.ZenPolicy
import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -382,6 +385,120 @@ class ZenModeInteractorTest : SysuiTestCase() {
            assertThat(dndMode!!.isActive).isTrue()
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI)
    fun activeModesBlockingEverything_hasModesWithFilterNone() =
        testScope.runTest {
            val blockingEverything by collectLastValue(underTest.activeModesBlockingEverything)

            zenModeRepository.addModes(
                listOf(
                    TestModeBuilder()
                        .setName("Filter=None, Not active")
                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                        .setActive(false)
                        .build(),
                    TestModeBuilder()
                        .setName("Filter=Priority, Active")
                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Filter=None, Active")
                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Filter=None, Active Too")
                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                        .setActive(true)
                        .build(),
                )
            )
            runCurrent()

            assertThat(blockingEverything!!.mainMode!!.name).isEqualTo("Filter=None, Active")
            assertThat(blockingEverything!!.modeNames)
                .containsExactly("Filter=None, Active", "Filter=None, Active Too")
                .inOrder()
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI)
    fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() =
        testScope.runTest {
            val blockingMedia by collectLastValue(underTest.activeModesBlockingMedia)

            zenModeRepository.addModes(
                listOf(
                    TestModeBuilder()
                        .setName("Blocks media, Not active")
                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
                        .setActive(false)
                        .build(),
                    TestModeBuilder()
                        .setName("Allows media, Active")
                        .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build())
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Blocks media, Active")
                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Blocks media, Active Too")
                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
                        .setActive(true)
                        .build(),
                )
            )
            runCurrent()

            assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active")
            assertThat(blockingMedia!!.modeNames)
                .containsExactly("Blocks media, Active", "Blocks media, Active Too")
                .inOrder()
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI)
    fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() =
        testScope.runTest {
            val blockingAlarms by collectLastValue(underTest.activeModesBlockingAlarms)

            zenModeRepository.addModes(
                listOf(
                    TestModeBuilder()
                        .setName("Blocks alarms, Not active")
                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
                        .setActive(false)
                        .build(),
                    TestModeBuilder()
                        .setName("Allows alarms, Active")
                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build())
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Blocks alarms, Active")
                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
                        .setActive(true)
                        .build(),
                    TestModeBuilder()
                        .setName("Blocks alarms, Active Too")
                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
                        .setActive(true)
                        .build(),
                )
            )
            runCurrent()

            assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active")
            assertThat(blockingAlarms!!.modeNames)
                .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too")
                .inOrder()
        }

    @Test
    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
    fun modesHidingNotifications_onlyIncludesModesWithNotifListSuppression() =
+174 −0
Original line number Diff line number Diff line
/*
 * 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.volume.slider.ui.viewmodel

import android.app.Flags
import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
import android.media.AudioManager
import android.platform.test.annotations.EnableFlags
import android.service.notification.ZenPolicy
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLogger
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
import com.android.systemui.volume.shared.volumePanelLogger
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 AudioStreamSliderViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val zenModeRepository = kosmos.fakeZenModeRepository

    private lateinit var mediaStream: AudioStreamSliderViewModel
    private lateinit var alarmsStream: AudioStreamSliderViewModel
    private lateinit var notificationStream: AudioStreamSliderViewModel
    private lateinit var otherStream: AudioStreamSliderViewModel

    @Before
    fun setUp() {
        mediaStream = audioStreamSliderViewModel(AudioManager.STREAM_MUSIC)
        alarmsStream = audioStreamSliderViewModel(AudioManager.STREAM_ALARM)
        notificationStream = audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION)
        otherStream = audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL)
    }

    private fun audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
        return AudioStreamSliderViewModel(
            AudioStreamSliderViewModel.FactoryAudioStreamWrapper(AudioStream(stream)),
            testScope.backgroundScope,
            context,
            kosmos.audioVolumeInteractor,
            kosmos.zenModeInteractor,
            kosmos.uiEventLogger,
            kosmos.volumePanelLogger,
        )
    }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
    fun slider_media_hasDisabledByModesText() =
        testScope.runTest {
            val mediaSlider by collectLastValue(mediaStream.slider)

            zenModeRepository.addMode(
                TestModeBuilder()
                    .setName("Media is ok")
                    .setZenPolicy(ZenPolicy.Builder().allowAllSounds().build())
                    .setActive(true)
                    .build()
            )
            zenModeRepository.addMode(
                TestModeBuilder()
                    .setName("No media plz")
                    .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
                    .setActive(true)
                    .build()
            )
            runCurrent()

            assertThat(mediaSlider!!.disabledMessage)
                .isEqualTo("Unavailable because No media plz is on")

            zenModeRepository.clearModes()
            runCurrent()

            assertThat(mediaSlider!!.disabledMessage).isEqualTo("Unavailable")
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
    fun slider_alarms_hasDisabledByModesText() =
        testScope.runTest {
            val alarmsSlider by collectLastValue(alarmsStream.slider)

            zenModeRepository.addMode(
                TestModeBuilder()
                    .setName("Alarms are ok")
                    .setZenPolicy(ZenPolicy.Builder().allowAllSounds().build())
                    .setActive(true)
                    .build()
            )
            zenModeRepository.addMode(
                TestModeBuilder()
                    .setName("Zzzzz")
                    .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
                    .setActive(true)
                    .build()
            )
            runCurrent()

            assertThat(alarmsSlider!!.disabledMessage).isEqualTo("Unavailable because Zzzzz is on")

            zenModeRepository.clearModes()
            runCurrent()

            assertThat(alarmsSlider!!.disabledMessage).isEqualTo("Unavailable")
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
    fun slider_other_hasDisabledByModesText() =
        testScope.runTest {
            val otherSlider by collectLastValue(otherStream.slider)

            zenModeRepository.addMode(
                TestModeBuilder()
                    .setName("Everything blocked")
                    .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                    .setActive(true)
                    .build()
            )
            runCurrent()

            assertThat(otherSlider!!.disabledMessage)
                .isEqualTo("Unavailable because Everything blocked is on")

            zenModeRepository.clearModes()
            runCurrent()

            assertThat(otherSlider!!.disabledMessage).isEqualTo("Unavailable")
        }

    @Test
    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
    fun slider_notification_hasSpecialDisabledText() =
        testScope.runTest {
            val notificationSlider by collectLastValue(notificationStream.slider)
            runCurrent()

            assertThat(notificationSlider!!.disabledMessage)
                .isEqualTo("Unavailable because ring is muted")
        }
}
+5 −0
Original line number Diff line number Diff line
@@ -1746,6 +1746,11 @@
    <!-- A message shown when the media volume changing is disabled because of the don't disturb mode [CHAR_LIMIT=50]-->
    <string name="stream_media_unavailable">Unavailable because Do Not Disturb is on</string>

    <!-- A message shown when a specific volume (e.g. Alarms, Media, etc) is disabled because an active mode is muting that audio stream altogether [CHAR_LIMIT=50]-->
    <string name="stream_unavailable_by_modes">Unavailable because <xliff:g id="mode" example="Bedtime">%s</xliff:g> is on</string>
    <!-- A message shown when a specific volume (e.g. Alarms, Media, etc) is disabled but we don't know which mode (or anything else) is responsible. [CHAR_LIMIT=50]-->
    <string name="stream_unavailable_by_unknown">Unavailable</string>

    <!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on vibrate. [CHAR_LIMIT=NONE] -->
    <!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on silent (muted). [CHAR_LIMIT=NONE] -->

+22 −0
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package com.android.systemui.statusbar.policy.domain.interactor

import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
import android.content.Context
import android.provider.Settings
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.ZenPolicy.STATE_DISALLOW
import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
import android.util.Log
import androidx.concurrent.futures.await
@@ -115,6 +117,26 @@ constructor(
            .flowOn(bgDispatcher)
            .distinctUntilChanged()

    val activeModesBlockingEverything: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
        mode.interruptionFilter == INTERRUPTION_FILTER_NONE
    }

    val activeModesBlockingMedia: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
        mode.policy.priorityCategoryMedia == STATE_DISALLOW
    }

    val activeModesBlockingAlarms: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
        mode.policy.priorityCategoryAlarms == STATE_DISALLOW
    }

    private fun getFilteredActiveModesFlow(predicate: (ZenMode) -> Boolean): Flow<ActiveZenModes> {
        return modes
            .map { modes -> modes.filter { mode -> predicate(mode) } }
            .map { modes -> buildActiveZenModes(modes) }
            .flowOn(bgDispatcher)
            .distinctUntilChanged()
    }

    suspend fun getActiveModes() = buildActiveZenModes(zenModeRepository.getModes())

    private suspend fun buildActiveZenModes(modes: List<ZenMode>): ActiveZenModes {
Loading