diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt index a9c167dbf78..589e4a13ca6 100644 --- a/src/com/android/settings/notification/SeparateRingVolumePreference.kt +++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt @@ -18,7 +18,14 @@ package com.android.settings.notification import android.app.INotificationManager import android.app.NotificationManager +import android.app.NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED +import android.content.BroadcastReceiver import android.content.Context +import android.content.Context.NOTIFICATION_SERVICE +import android.content.Intent +import android.content.IntentFilter +import android.media.AudioManager +import android.media.AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION import android.media.AudioManager.RINGER_MODE_NORMAL import android.media.AudioManager.RINGER_MODE_SILENT import android.media.AudioManager.RINGER_MODE_VIBRATE @@ -36,6 +43,8 @@ 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.PreferenceLifecycleContext +import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.RangeValue import com.android.settingslib.metadata.ReadWritePermit @@ -49,19 +58,18 @@ open class SeparateRingVolumePreference : RangeValue, PreferenceAvailabilityProvider, PreferenceIconProvider, + PreferenceLifecycleProvider, PreferenceRestrictionMixin { + private var broadcastReceiver: BroadcastReceiver? = null + 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 getIcon(context: Context) = context.getIconRes() override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume @@ -103,67 +111,77 @@ open class SeparateRingVolumePreference : super.bind(preference, metadata) (preference as VolumeSeekBarPreference).apply { setStream(STREAM_RING) - setMuteIcon(getMuteIcon(preference.context)) - setListener { updateContentDescription(this) } - setSuppressionText(getSuppressionText(preference.context)) + setMuteIcon(context.getIconRes()) + updateContentDescription(context.getContentDescription()) + setListener { updateContentDescription(context.getContentDescription()) } + setSuppressionText(context.getSuppressionText()) } } + override fun onStart(context: PreferenceLifecycleContext) { + super.onStart(context) + val receiver = + object : BroadcastReceiver() { + override fun onReceive(receiverContext: Context, intent: Intent) { + context.notifyPreferenceChange(KEY) + } + } + context.registerReceiver( + receiver, + IntentFilter().apply { + addAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED) + addAction(INTERNAL_RINGER_MODE_CHANGED_ACTION) + }, + ) + broadcastReceiver = receiver + } + + override fun onStop(context: PreferenceLifecycleContext) { + super.onStop(context) + broadcastReceiver?.let { context.unregisterReceiver(it) } + } + open fun createAudioHelper(context: Context) = AudioHelper(context) - private 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 - } - } - - private fun getSuppressionText(context: Context): String? { - val suppressor = NotificationManager.from(context).getEffectsSuppressor() - val notificationManager = - INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE) - ) - val hints = notificationManager.hintsFromListenerNoToken - 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" } } + +fun Context.getContentDescription() = + when (getEffectiveRingerMode()) { + RINGER_MODE_VIBRATE -> getString(R.string.ringer_content_description_vibrate_mode) + RINGER_MODE_SILENT -> getString(R.string.ringer_content_description_silent_mode) + else -> getString(R.string.separate_ring_volume_option_title) + } + +fun Context.getIconRes() = + when (getEffectiveRingerMode()) { + 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 Context.getEffectiveRingerMode(): Int { + val hasVibrator = getSystemService(Vibrator::class.java)?.hasVibrator() ?: false + val ringerMode = + getSystemService(AudioManager::class.java)?.getRingerModeInternal() ?: RINGER_MODE_NORMAL + return when { + !hasVibrator && ringerMode == RINGER_MODE_VIBRATE -> RINGER_MODE_SILENT + else -> ringerMode + } +} + +fun Context.getSuppressionText(): String? { + val suppressor = NotificationManager.from(this).getEffectsSuppressor() + val hints = + INotificationManager.Stub.asInterface(ServiceManager.getService(NOTIFICATION_SERVICE)) + ?.hintsFromListenerNoToken ?: 0 + return when { + (hints and HINT_HOST_DISABLE_CALL_EFFECTS) != 0 || + (hints and HINT_HOST_DISABLE_EFFECTS) != 0 -> + SuppressorHelper.getSuppressionText(this, suppressor) + else -> null + } +} // LINT.ThenChange(SeparateRingVolumePreferenceController.java) diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt index 21ddd75304f..0cdd7c0b01c 100644 --- a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt +++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt @@ -17,6 +17,7 @@ package com.android.settings.notification import android.content.ContextWrapper +import android.media.AudioManager import android.media.AudioManager.RINGER_MODE_NORMAL import android.media.AudioManager.RINGER_MODE_SILENT import android.media.AudioManager.RINGER_MODE_VIBRATE @@ -36,14 +37,15 @@ import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class SeparateRingVolumePreferenceTest { private var audioHelper = mock() + private var audioManager = 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 + when (name) { + getSystemServiceName(Vibrator::class.java) -> vibrator + getSystemServiceName(AudioManager::class.java) -> audioManager else -> super.getSystemService(name) } } @@ -73,78 +75,76 @@ class SeparateRingVolumePreferenceTest { @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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } - assertThat(ringVolumePreference.getEffectiveRingerMode(context)) - .isEqualTo(RINGER_MODE_SILENT) + assertThat(context.getEffectiveRingerMode()).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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } - assertThat(ringVolumePreference.getEffectiveRingerMode(context)) - .isEqualTo(RINGER_MODE_VIBRATE) + assertThat(context.getEffectiveRingerMode()).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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_NORMAL } - assertThat(ringVolumePreference.getEffectiveRingerMode(context)) - .isEqualTo(RINGER_MODE_NORMAL) + assertThat(context.getEffectiveRingerMode()).isEqualTo(RINGER_MODE_NORMAL) } @Test - fun getMuteIcon_normalMode_shouldReturnRingVolumeIcon() { + fun getIconRes_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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_NORMAL } - assertThat(ringVolumePreference.getMuteIcon(context)).isEqualTo(R.drawable.ic_ring_volume) + assertThat(context.getIconRes()).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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } - assertThat(ringVolumePreference.getMuteIcon(context)) - .isEqualTo(R.drawable.ic_volume_ringer_vibrate) + assertThat(context.getIconRes()).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 - } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } - assertThat(ringVolumePreference.getMuteIcon(context)) - .isEqualTo(R.drawable.ic_ring_volume_off) + assertThat(context.getIconRes()).isEqualTo(R.drawable.ic_ring_volume_off) + } + + @Test + fun getContentDescription_normalMode_shouldReturnTitleDescription() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_NORMAL } + + assertThat(context.getContentDescription()) + .isEqualTo(context.getString(R.string.separate_ring_volume_option_title)) + } + + @Test + fun getContentDescription_vibrateMode_shouldReturnVibrateModeDescription() { + vibrator = mock { on { hasVibrator() } doReturn true } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } + + assertThat(context.getContentDescription()) + .isEqualTo(context.getString(R.string.ringer_content_description_vibrate_mode)) + } + + @Test + fun getContentDescription_silentMode_shouldReturnSilentModeDescription() { + vibrator = mock { on { hasVibrator() } doReturn false } + audioManager = mock { on { getRingerModeInternal() } doReturn RINGER_MODE_VIBRATE } + + assertThat(context.getContentDescription()) + .isEqualTo(context.getString(R.string.ringer_content_description_silent_mode)) } } // LINT.ThenChange(SeparateRingVolumePreferenceControllerTest.java)