diff --git a/src/com/android/settings/notification/MediaVolumePreference.kt b/src/com/android/settings/notification/MediaVolumePreference.kt new file mode 100644 index 00000000000..13fd0297540 --- /dev/null +++ b/src/com/android/settings/notification/MediaVolumePreference.kt @@ -0,0 +1,118 @@ +/* + * 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.settings.notification + +import android.content.Context +import android.media.AudioManager.STREAM_MUSIC +import android.os.UserHandle +import android.os.UserManager +import androidx.preference.Preference +import com.android.settings.R +import com.android.settingslib.RestrictedLockUtilsInternal +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.NoOpKeyedObservable +import com.android.settingslib.metadata.* +import com.android.settingslib.preference.PreferenceBinding + +// LINT.IfChange +open class MediaVolumePreference : + PreferenceMetadata, + PreferenceBinding, + PersistentPreference, + RangeValue, + PreferenceAvailabilityProvider, + PreferenceIconProvider, + PreferenceRestrictionProvider { + override val key: String + get() = KEY + + override val title: Int + get() = R.string.media_volume_option_title + + override fun getIcon(context: Context) = + when { + VolumeHelper.isMuted(context, STREAM_MUSIC) -> R.drawable.ic_media_stream_off + else -> R.drawable.ic_media_stream + } + + override fun isAvailable(context: Context) = + context.resources.getBoolean(R.bool.config_show_media_volume) + + override fun isRestricted(context: Context) = + RestrictedLockUtilsInternal.hasBaseUserRestriction( + context, + UserManager.DISALLOW_ADJUST_VOLUME, + UserHandle.myUserId(), + ) || + RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + context, + UserManager.DISALLOW_ADJUST_VOLUME, + UserHandle.myUserId(), + ) != null + + override fun storage(context: Context): KeyValueStore { + val helper = createAudioHelper(context) + return object : NoOpKeyedObservable(), KeyValueStore { + override fun contains(key: String) = key == KEY + + @Suppress("UNCHECKED_CAST") + override fun getValue(key: String, valueType: Class) = + helper.getStreamVolume(STREAM_MUSIC) as T + + override fun setValue(key: String, valueType: Class, value: T?) { + helper.setStreamVolume(STREAM_MUSIC, value as Int) + } + } + } + + override fun getMinValue(context: Context) = + createAudioHelper(context).getMinVolume(STREAM_MUSIC) + + override fun getMaxValue(context: Context) = + createAudioHelper(context).getMaxVolume(STREAM_MUSIC) + + override fun createWidget(context: Context) = VolumeSeekBarPreference(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as VolumeSeekBarPreference).apply { + setStream(STREAM_MUSIC) + setMuteIcon(R.drawable.ic_media_stream_off) + setListener { updateContentDescription(this) } + } + } + + open fun createAudioHelper(context: Context) = AudioHelper(context) + + fun updateContentDescription(preference: VolumeSeekBarPreference) { + when { + preference.isMuted() -> + preference.updateContentDescription( + preference.context.getString( + R.string.volume_content_description_silent_mode, + preference.title, + ) + ) + else -> preference.updateContentDescription(preference.title) + } + } + + companion object { + const val KEY = "media_volume" + } +} +// LINT.ThenChange(MediaVolumePreferenceController.java) diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index e70cf95e232..431806a494d 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -42,6 +42,7 @@ import com.android.settingslib.media.BluetoothMediaDevice; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputConstants; +// LINT.IfChange public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceController { private static final String TAG = "MediaVolumePreCtrl"; private static final String KEY_MEDIA_VOLUME = "media_volume"; @@ -204,3 +205,4 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont return MediaOutputIndicatorWorker.class; } } +// LINT.ThenChange(MediaVolumePreference.kt) diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt new file mode 100644 index 00000000000..2f696b1c0f9 --- /dev/null +++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt @@ -0,0 +1,172 @@ +/* + * 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.settings.notification + +import android.app.INotificationManager +import android.app.NotificationManager +import android.content.Context +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.media.AudioManager.STREAM_RING +import android.os.ServiceManager +import android.os.UserHandle +import android.os.UserManager.DISALLOW_ADJUST_VOLUME +import android.os.Vibrator +import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS +import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS +import androidx.preference.Preference +import com.android.settings.R +import com.android.settingslib.RestrictedLockUtilsInternal +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.NoOpKeyedObservable +import com.android.settingslib.metadata.PersistentPreference +import com.android.settingslib.metadata.PreferenceAvailabilityProvider +import com.android.settingslib.metadata.PreferenceIconProvider +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceRestrictionProvider +import com.android.settingslib.metadata.RangeValue +import com.android.settingslib.preference.PreferenceBinding + +// LINT.IfChange +open class SeparateRingVolumePreference : + PreferenceMetadata, + PreferenceBinding, + PersistentPreference, + RangeValue, + PreferenceAvailabilityProvider, + PreferenceIconProvider, + PreferenceRestrictionProvider { + override val key: String + get() = KEY + + override val title: Int + get() = R.string.separate_ring_volume_option_title + + override fun getIcon(context: Context) = + when { + VolumeHelper.isMuted(context, STREAM_RING) -> getMuteIcon(context) + else -> R.drawable.ic_ring_volume + } + + override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume() + + override fun isEnabled(context: Context) = + !RestrictedLockUtilsInternal.hasBaseUserRestriction( + context, + DISALLOW_ADJUST_VOLUME, + UserHandle.myUserId(), + ) + + override fun isRestricted(context: Context) = + RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + context, + DISALLOW_ADJUST_VOLUME, + UserHandle.myUserId(), + ) != null + + override fun storage(context: Context): KeyValueStore { + val helper = createAudioHelper(context) + return object : NoOpKeyedObservable(), KeyValueStore { + override fun contains(key: String) = key == KEY + + @Suppress("UNCHECKED_CAST") + override fun getValue(key: String, valueType: Class) = + helper.getStreamVolume(STREAM_RING) as T + + override fun setValue(key: String, valueType: Class, value: T?) { + helper.setStreamVolume(STREAM_RING, value as Int) + } + } + } + + override fun getMinValue(context: Context) = + createAudioHelper(context).getMinVolume(STREAM_RING) + + override fun getMaxValue(context: Context) = + createAudioHelper(context).getMaxVolume(STREAM_RING) + + override fun createWidget(context: Context) = VolumeSeekBarPreference(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as VolumeSeekBarPreference).apply { + setStream(STREAM_RING) + setMuteIcon(getMuteIcon(preference.context)) + setListener { updateContentDescription(this) } + setSuppressionText(getSuppressionText(preference.context)) + } + } + + open fun createAudioHelper(context: Context) = AudioHelper(context) + + fun updateContentDescription(preference: VolumeSeekBarPreference) { + val context = preference.context + val ringerMode = getEffectiveRingerMode(context) + when (ringerMode) { + RINGER_MODE_VIBRATE -> + preference.updateContentDescription( + context.getString(R.string.ringer_content_description_vibrate_mode) + ) + RINGER_MODE_SILENT -> + preference.updateContentDescription( + context.getString(R.string.ringer_content_description_silent_mode) + ) + else -> preference.updateContentDescription(preference.title) + } + } + + fun getMuteIcon(context: Context): Int { + val ringerMode = getEffectiveRingerMode(context) + return when (ringerMode) { + RINGER_MODE_NORMAL -> R.drawable.ic_ring_volume + RINGER_MODE_VIBRATE -> R.drawable.ic_volume_ringer_vibrate + else -> R.drawable.ic_ring_volume_off + } + } + + fun getEffectiveRingerMode(context: Context): Int { + val hasVibrator = context.getSystemService(Vibrator::class.java)?.hasVibrator() ?: false + val ringerMode = createAudioHelper(context).ringerModeInternal + return when { + !hasVibrator && ringerMode == RINGER_MODE_VIBRATE -> RINGER_MODE_SILENT + else -> ringerMode + } + } + + fun getSuppressionText(context: Context): String? { + val suppressor = NotificationManager.from(context).getEffectsSuppressor() + val notificationManager = + INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE) + ) + val hints = notificationManager.getHintsFromListenerNoToken() + return when { + hintsMatch(hints) -> SuppressorHelper.getSuppressionText(context, suppressor) + else -> null + } + } + + private fun hintsMatch(hints: Int) = + (hints and HINT_HOST_DISABLE_CALL_EFFECTS) != 0 || + (hints and HINT_HOST_DISABLE_EFFECTS) != 0 + + companion object { + const val KEY = "separate_ring_volume" + } +} +// LINT.ThenChange(SeparateRingVolumePreferenceController.java) diff --git a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java index 91926e3c977..feb976fe8cd 100644 --- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java +++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java @@ -35,6 +35,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle; /** * This slider is used to represent ring volume when ring is separated from notification */ +// LINT.IfChange public class SeparateRingVolumePreferenceController extends RingerModeAffectedVolumePreferenceController { @@ -149,3 +150,4 @@ public class SeparateRingVolumePreferenceController extends } } +// LINT.ThenChange(SeparateRingVolumePreference.kt) diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt index e36576db4f8..e074301ca6c 100644 --- a/src/com/android/settings/notification/SoundScreen.kt +++ b/src/com/android/settings/notification/SoundScreen.kt @@ -49,7 +49,9 @@ class SoundScreen : PreferenceScreenCreator, PreferenceIconProvider { override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { + +MediaVolumePreference() order -180 +CallVolumePreference() order -170 + +SeparateRingVolumePreference() order -155 +DialPadTonePreference() order -50 } diff --git a/src/com/android/settings/notification/VolumeHelper.kt b/src/com/android/settings/notification/VolumeHelper.kt new file mode 100644 index 00000000000..73e490edbc5 --- /dev/null +++ b/src/com/android/settings/notification/VolumeHelper.kt @@ -0,0 +1,62 @@ +/* + * 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.settings.notification + +import android.app.NotificationManager +import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS +import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA +import android.content.Context +import android.media.AudioManager +import android.media.AudioManager.* +import android.provider.Settings.Global.ZEN_MODE_ALARMS +import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS +import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS +import android.service.notification.ZenModeConfig + +class VolumeHelper { + companion object { + fun isMuted(context: Context, streamType: Int): Boolean { + val audioManager = context.getSystemService(AudioManager::class.java) + return audioManager.isStreamMute(streamType) && !isZenMuted(context, streamType) + } + + fun isZenMuted(context: Context, streamType: Int): Boolean { + val notificationManager = context.getSystemService(NotificationManager::class.java) + val zenMode = notificationManager.getZenMode() + val notificationPolicy = notificationManager.getConsolidatedNotificationPolicy() + val isAllowAlarms = + (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_ALARMS) != 0 + val isAllowMedia = + (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_MEDIA) != 0 + val isAllowRinger = + !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(notificationPolicy) + return isNotificationOrRingStream(streamType) + && zenMode == ZEN_MODE_ALARMS || zenMode == ZEN_MODE_NO_INTERRUPTIONS + || (zenMode == ZEN_MODE_IMPORTANT_INTERRUPTIONS + && (!isAllowRinger && isNotificationOrRingStream(streamType) + || !isAllowMedia && isMediaStream(streamType) + || !isAllowAlarms && isAlarmStream(streamType))) + } + + private fun isNotificationOrRingStream(streamType: Int) = + streamType == STREAM_RING || streamType == STREAM_NOTIFICATION + + private fun isAlarmStream(streamType: Int) = streamType == STREAM_ALARM + + private fun isMediaStream(streamType: Int) = streamType == STREAM_MUSIC + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java index a25f47200a3..2965acfa3fb 100644 --- a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java @@ -58,6 +58,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) @Config(shadows = MediaVolumePreferenceControllerTest.ShadowSliceBackgroundWorker.class) public class MediaVolumePreferenceControllerTest { @@ -269,3 +270,4 @@ public class MediaVolumePreferenceControllerTest { PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } +// LINT.ThenChange(MediaVolumePreference.kt) diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt new file mode 100644 index 00000000000..383acfbc277 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt @@ -0,0 +1,56 @@ +/* + * 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.settings.notification + +import android.content.ContextWrapper +import android.content.res.Resources +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub + +// LINT.IfChange +@RunWith(AndroidJUnit4::class) +class MediaVolumePreferenceTest { + private val mockResources = mock() + + private val mediaVolumePreference = MediaVolumePreference() + private val context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getResources(): Resources = mockResources + } + + @Test + fun isAvailable_configTrue_shouldReturnTrue() { + mockResources.stub { on { getBoolean(anyInt()) } doReturn true } + + assertThat(mediaVolumePreference.isAvailable(context)).isTrue() + } + + @Test + fun isAvailable_configFalse_shouldReturnFalse() { + mockResources.stub { on { getBoolean(anyInt()) } doReturn false } + + assertThat(mediaVolumePreference.isAvailable(context)).isFalse() + } +} +// LINT.ThenChange(MediaVolumePreferenceControllerTest.java) diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java index 2974af6f185..a243643d38a 100644 --- a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java @@ -41,6 +41,7 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDeviceConfig.class}) public class SeparateRingVolumePreferenceControllerTest { @@ -108,3 +109,4 @@ public class SeparateRingVolumePreferenceControllerTest { } } +// LINT.ThenChange(SeparateRingVolumePreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt new file mode 100644 index 00000000000..21ddd75304f --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt @@ -0,0 +1,150 @@ +/* + * 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.settings.notification + +import android.content.ContextWrapper +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.os.Vibrator +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.R +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub + +// LINT.IfChange +@RunWith(AndroidJUnit4::class) +class SeparateRingVolumePreferenceTest { + private var audioHelper = mock() + private var vibrator: Vibrator? = null + private var ringVolumePreference = SeparateRingVolumePreference() + + private val context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when { + name == getSystemServiceName(Vibrator::class.java) -> vibrator + else -> super.getSystemService(name) + } + } + + @Test + fun isAvailable_singleVolume_shouldReturnFalse() { + audioHelper = mock { on { isSingleVolume } doReturn true } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.isAvailable(context)).isFalse() + } + + @Test + fun isAvailable_noSingleVolume_shouldReturnTrue() { + audioHelper = mock { on { isSingleVolume } doReturn false } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.isAvailable(context)).isTrue() + } + + @Test + fun getEffectiveRingerMode_noVibratorAndVibrateMode_shouldReturnSilentMode() { + vibrator = mock { on { hasVibrator() } doReturn false } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getEffectiveRingerMode(context)) + .isEqualTo(RINGER_MODE_SILENT) + } + + @Test + fun getEffectiveRingerMode_hasVibratorAndVibrateMode_shouldReturnVibrateMode() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getEffectiveRingerMode(context)) + .isEqualTo(RINGER_MODE_VIBRATE) + } + + @Test + fun getEffectiveRingerMode_hasVibratorAndNormalMode_shouldReturnNormalMode() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getEffectiveRingerMode(context)) + .isEqualTo(RINGER_MODE_NORMAL) + } + + @Test + fun getMuteIcon_normalMode_shouldReturnRingVolumeIcon() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getMuteIcon(context)).isEqualTo(R.drawable.ic_ring_volume) + } + + @Test + fun getMuteIcon_vibrateMode_shouldReturnVibrateIcon() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getMuteIcon(context)) + .isEqualTo(R.drawable.ic_volume_ringer_vibrate) + } + + @Test + fun getMuteIcon_silentMode_shouldReturnSilentIcon() { + vibrator = mock { on { hasVibrator() } doReturn false } + audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE } + ringVolumePreference = + spy(ringVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(ringVolumePreference.getMuteIcon(context)) + .isEqualTo(R.drawable.ic_ring_volume_off) + } +} +// LINT.ThenChange(SeparateRingVolumePreferenceControllerTest.java)