diff --git a/src/com/android/settings/notification/CallVolumePreference.kt b/src/com/android/settings/notification/CallVolumePreference.kt new file mode 100644 index 00000000000..9b593ad5493 --- /dev/null +++ b/src/com/android/settings/notification/CallVolumePreference.kt @@ -0,0 +1,115 @@ +/* + * 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 +import android.media.AudioManager.STREAM_BLUETOOTH_SCO +import android.media.AudioManager.STREAM_VOICE_CALL +import android.os.UserHandle +import android.os.UserManager.DISALLOW_ADJUST_VOLUME +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 CallVolumePreference : + PreferenceMetadata, + PreferenceBinding, + PersistentPreference, + RangeValue, + PreferenceAvailabilityProvider, + PreferenceIconProvider, + PreferenceRestrictionProvider { + override val key: String + get() = KEY + + override val title: Int + get() = R.string.call_volume_option_title + + override fun getIcon(context: Context) = R.drawable.ic_local_phone_24_lib + + override fun order(context: Context) = -170 + + override fun isAvailable(context: Context) = + context.resources.getBoolean(R.bool.config_show_call_volume) && + !createAudioHelper(context).isSingleVolume() + + override fun isRestricted(context: Context) = + RestrictedLockUtilsInternal.hasBaseUserRestriction( + context, + DISALLOW_ADJUST_VOLUME, + UserHandle.myUserId() + ) || 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(getAudioStream(context)) as T + + override fun setValue(key: String, valueType: Class, value: T?) { + helper.setStreamVolume(getAudioStream(context), value as Int) + } + } + } + + override fun getMinValue(context: Context) = + createAudioHelper(context).getMinVolume(getAudioStream(context)) + + override fun getMaxValue(context: Context) = + createAudioHelper(context).getMaxVolume(getAudioStream(context)) + + override fun createWidget(context: Context) = VolumeSeekBarPreference(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as VolumeSeekBarPreference).setStream(getAudioStream(preference.context)) + } + + open fun createAudioHelper(context: Context) = AudioHelper(context) + + @Suppress("DEPRECATION") + fun getAudioStream(context: Context): Int { + val audioManager = context.getSystemService(AudioManager::class.java) + return when { + audioManager.isBluetoothScoOn() -> STREAM_BLUETOOTH_SCO + else -> STREAM_VOICE_CALL + } + } + + companion object { + const val KEY = "call_volume" + } +} +// LINT.ThenChange(CallVolumePreferenceController.java) diff --git a/src/com/android/settings/notification/CallVolumePreferenceController.java b/src/com/android/settings/notification/CallVolumePreferenceController.java index d5052270376..d7c5ddca90b 100644 --- a/src/com/android/settings/notification/CallVolumePreferenceController.java +++ b/src/com/android/settings/notification/CallVolumePreferenceController.java @@ -22,7 +22,7 @@ import android.text.TextUtils; import com.android.settings.R; - +// LINT.IfChange public class CallVolumePreferenceController extends VolumeSeekBarPreferenceController { private AudioManager mAudioManager; @@ -69,3 +69,4 @@ public class CallVolumePreferenceController extends VolumeSeekBarPreferenceContr } } +// LINT.ThenChange(CallVolumePreference.kt) diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt index 6b6096794da..f29437b934b 100644 --- a/src/com/android/settings/notification/SoundScreen.kt +++ b/src/com/android/settings/notification/SoundScreen.kt @@ -49,6 +49,7 @@ class SoundScreen : PreferenceScreenCreator, PreferenceIconProvider { override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { + +CallVolumePreference() +DialPadTonePreference() } diff --git a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java index ed65d5b9432..f9f3be753f5 100644 --- a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceControllerTest.java @@ -36,6 +36,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) public class CallVolumePreferenceControllerTest { private static final String TEST_KEY = "Test_Key"; @@ -108,3 +109,4 @@ public class CallVolumePreferenceControllerTest { assertThat(mController.isPublicSlice()).isTrue(); } } +// LINT.ThenChange(CallVolumePreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt new file mode 100644 index 00000000000..d6bc6d0a675 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/CallVolumePreferenceTest.kt @@ -0,0 +1,100 @@ +/* + * 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.content.ContextWrapper +import android.content.res.Resources +import android.media.AudioManager +import android.media.AudioManager.STREAM_BLUETOOTH_SCO +import android.media.AudioManager.STREAM_VOICE_CALL +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.spy +import org.mockito.kotlin.stub + +// LINT.IfChange +@RunWith(AndroidJUnit4::class) +class CallVolumePreferenceTest { + private var audioHelper = mock() + private var mockResources = mock() + + private var audioManager: AudioManager? = null + + private var callVolumePreference = CallVolumePreference() + private val context = object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when (name) { + Context.AUDIO_SERVICE -> audioManager + else -> super.getSystemService(name) + } + + override fun getResources(): Resources = mockResources + } + + @Test + fun isAvailable_configTrueAndNoSingleVolume_shouldReturnTrue() { + mockResources.stub { on { getBoolean(anyInt()) } doReturn true } + audioHelper = mock { on { isSingleVolume } doReturn false } + callVolumePreference = spy(callVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(callVolumePreference.isAvailable(context)).isTrue() + } + + @Test + fun isAvailable_configTrueAndSingleVolume_shouldReturnFalse() { + mockResources.stub { on { getBoolean(anyInt()) } doReturn true } + audioHelper = mock { on { isSingleVolume } doReturn true } + callVolumePreference = spy(callVolumePreference).stub { + onGeneric { createAudioHelper(context) } doReturn audioHelper + } + + assertThat(callVolumePreference.isAvailable(context)).isFalse() + } + + @Test + fun isAvailable_configFalse_shouldReturnFalse() { + mockResources.stub { on { getBoolean(anyInt()) } doReturn false } + + assertThat(callVolumePreference.isAvailable(context)).isFalse() + } + + @Test + @Suppress("DEPRECATION") + fun getAudioStream_onBluetoothScoOn_shouldEqualToStreamBluetoothSco() { + audioManager = mock { on { isBluetoothScoOn } doReturn true } + + assertThat(callVolumePreference.getAudioStream(context)).isEqualTo(STREAM_BLUETOOTH_SCO) + } + + @Test + @Suppress("DEPRECATION") + fun getAudioStream_onBluetoothScoOff_shouldEqualToStreamVoiceCall() { + audioManager = mock { on { isBluetoothScoOn } doReturn false } + + assertThat(callVolumePreference.getAudioStream(context)).isEqualTo(STREAM_VOICE_CALL) + } +} +// LINT.ThenChange(CallVolumePreferenceControllerTest.java)