diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index c031960edc9..68b234da046 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -71,7 +71,7 @@ android:key="device_companion_apps"/> + android:key="feature_controls_group"/> diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java new file mode 100644 index 00000000000..509a9b0c05d --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.bluetooth; + +import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_AUDIO_ROUTING_ORDER; + +import android.content.Context; +import android.util.FeatureFlagUtils; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * The controller of the audio routing in the bluetooth detail settings. + */ +public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsController { + + private static final String KEY_FEATURE_CONTROLS_GROUP = "feature_controls_group"; + private static final String KEY_AUDIO_ROUTING = "audio_routing"; + + public BluetoothDetailsAudioRoutingController(Context context, + PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + } + + @Override + public boolean isAvailable() { + return mCachedDevice.isHearingAidDevice() && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUDIO_ROUTING); + } + + @Override + protected void init(PreferenceScreen screen) { + if (!mCachedDevice.isHearingAidDevice()) { + return; + } + + final PreferenceCategory prefCategory = screen.findPreference(getPreferenceKey()); + final Preference pref = createAudioRoutingPreference(prefCategory.getContext()); + pref.setOrder(FEATURE_AUDIO_ROUTING_ORDER); + prefCategory.addPreference(pref); + } + + @Override + protected void refresh() {} + + @Override + public String getPreferenceKey() { + return KEY_FEATURE_CONTROLS_GROUP; + } + + private Preference createAudioRoutingPreference(Context context) { + final Preference preference = new Preference(context); + preference.setKey(KEY_AUDIO_ROUTING); + preference.setTitle(context.getString(R.string.bluetooth_audio_routing_title)); + preference.setSummary(context.getString(R.string.bluetooth_audio_routing_summary)); + + return preference; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java new file mode 100644 index 00000000000..585a5f9a2d0 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 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.bluetooth; + +import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_HEARING_DEVICE_CONTROLS_ORDER; + +import android.content.Context; +import android.util.FeatureFlagUtils; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * The controller of the hearing device controls in the bluetooth detail settings. + */ +public class BluetoothDetailsHearingDeviceControlsController extends BluetoothDetailsController { + + private static final String KEY_FEATURE_CONTROLS_GROUP = "feature_controls_group"; + private static final String KEY_HEARING_DEVICE_CONTROLS = "hearing_device_controls"; + + public BluetoothDetailsHearingDeviceControlsController(Context context, + PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + lifecycle.addObserver(this); + } + + @Override + public boolean isAvailable() { + return mCachedDevice.isHearingAidDevice() && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE); + } + + @Override + protected void init(PreferenceScreen screen) { + if (!mCachedDevice.isHearingAidDevice()) { + return; + } + + final PreferenceCategory prefCategory = screen.findPreference(getPreferenceKey()); + final Preference pref = createHearingDeviceControlsPreference(prefCategory.getContext()); + pref.setOrder(FEATURE_HEARING_DEVICE_CONTROLS_ORDER); + prefCategory.addPreference(pref); + } + + @Override + protected void refresh() {} + + @Override + public String getPreferenceKey() { + return KEY_FEATURE_CONTROLS_GROUP; + } + + private Preference createHearingDeviceControlsPreference(Context context) { + final Preference preference = new Preference(context); + preference.setKey(KEY_HEARING_DEVICE_CONTROLS); + preference.setTitle(context.getString(R.string.bluetooth_device_controls_title)); + preference.setSummary(context.getString(R.string.bluetooth_device_controls_summary)); + + return preference; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java index 29066b8822d..b5e4c3256e3 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java @@ -16,6 +16,9 @@ package com.android.settings.bluetooth; +import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_HEAD_TRACKING_ORDER; +import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_SPATIAL_AUDIO_ORDER; + import android.content.Context; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; @@ -42,7 +45,7 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont implements Preference.OnPreferenceClickListener { private static final String TAG = "BluetoothSpatialAudioController"; - private static final String KEY_SPATIAL_AUDIO_GROUP = "spatial_audio_group"; + private static final String KEY_FEATURE_CONTROLS_GROUP = "feature_controls_group"; private static final String KEY_SPATIAL_AUDIO = "spatial_audio"; private static final String KEY_HEAD_TRACKING = "head_tracking"; @@ -95,13 +98,12 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont @Override public String getPreferenceKey() { - return KEY_SPATIAL_AUDIO_GROUP; + return KEY_FEATURE_CONTROLS_GROUP; } @Override protected void init(PreferenceScreen screen) { mProfilesContainer = screen.findPreference(getPreferenceKey()); - mProfilesContainer.setLayoutResource(R.layout.preference_bluetooth_profile_category); refresh(); } @@ -110,6 +112,7 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont SwitchPreference spatialAudioPref = mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO); if (spatialAudioPref == null) { spatialAudioPref = createSpatialAudioPreference(mProfilesContainer.getContext()); + spatialAudioPref.setOrder(FEATURE_SPATIAL_AUDIO_ORDER); mProfilesContainer.addPreference(spatialAudioPref); } @@ -120,6 +123,7 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont SwitchPreference headTrackingPref = mProfilesContainer.findPreference(KEY_HEAD_TRACKING); if (headTrackingPref == null) { headTrackingPref = createHeadTrackingPreference(mProfilesContainer.getContext()); + headTrackingPref.setOrder(FEATURE_HEAD_TRACKING_ORDER); mProfilesContainer.addPreference(headTrackingPref); } diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index f68cc40b4f8..4cebbef295d 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -63,6 +63,11 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment public static final String KEY_DEVICE_ADDRESS = "device_address"; private static final String TAG = "BTDeviceDetailsFrg"; + static final int FEATURE_HEARING_DEVICE_CONTROLS_ORDER = 1; + static final int FEATURE_AUDIO_ROUTING_ORDER = 2; + static final int FEATURE_SPATIAL_AUDIO_ORDER = 3; + static final int FEATURE_HEAD_TRACKING_ORDER = 4; + @VisibleForTesting static int EDIT_DEVICE_NAME_ITEM_ID = Menu.FIRST; @@ -312,6 +317,10 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment lifecycle)); controllers.add(new BluetoothDetailsPairOtherController(context, this, mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsHearingDeviceControlsController(context, this, + mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsAudioRoutingController(context, this, mCachedDevice, + lifecycle)); } return controllers; } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java new file mode 100644 index 00000000000..92174f30222 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.util.FeatureFlagUtils; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link BluetoothDetailsAudioRoutingController}. */ +@RunWith(RobolectricTestRunner.class) +public class BluetoothDetailsAudioRoutingControllerTest extends + BluetoothDetailsControllerTestBase { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private BluetoothDetailsAudioRoutingController mController; + + @Override + public void setUp() { + super.setUp(); + + mController = new BluetoothDetailsAudioRoutingController(mContext, mFragment, mCachedDevice, + mLifecycle); + mController.init(mScreen); + } + + @Test + public void isAvailable_isHearingAidDevice_available() { + FeatureFlagUtils.setEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true); + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_isNotHearingAidDevice_notAvailable() { + FeatureFlagUtils.setEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true); + when(mCachedDevice.isHearingAidDevice()).thenReturn(false); + + assertThat(mController.isAvailable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java new file mode 100644 index 00000000000..7590d617075 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.util.FeatureFlagUtils; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link BluetoothDetailsHearingDeviceControlsController}. */ +@RunWith(RobolectricTestRunner.class) +public class BluetoothDetailsHearingDeviceControlsControllerTest extends + BluetoothDetailsControllerTestBase { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private BluetoothDetailsHearingDeviceControlsController mController; + + @Override + public void setUp() { + super.setUp(); + + mController = new BluetoothDetailsHearingDeviceControlsController(mContext, mFragment, + mCachedDevice, mLifecycle); + } + + @Test + public void isAvailable_isHearingAidDevice_available() { + FeatureFlagUtils.setEnabled(mContext, + FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true); + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_isNotHearingAidDevice_notAvailable() { + FeatureFlagUtils.setEnabled(mContext, + FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true); + when(mCachedDevice.isHearingAidDevice()).thenReturn(false); + + assertThat(mController.isAvailable()).isFalse(); + } +}