diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 20554429d0b..04523665ef8 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -265,6 +265,23 @@
- 5
+
+
+ - Decide automatically
+ - Play on hearing device
+ - Play on phone speaker
+
+
+
+
+
+ - 0
+
+ - 1
+
+ - 2
+
+
diff --git a/res/xml/bluetooth_audio_routing_fragment.xml b/res/xml/bluetooth_audio_routing_fragment.xml
new file mode 100644
index 00000000000..18f18f20740
--- /dev/null
+++ b/res/xml/bluetooth_audio_routing_fragment.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
index 509a9b0c05d..4e40323c0d8 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
@@ -17,10 +17,13 @@
package com.android.settings.bluetooth;
import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_AUDIO_ROUTING_ORDER;
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
import android.content.Context;
+import android.os.Bundle;
import android.util.FeatureFlagUtils;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
@@ -36,7 +39,8 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
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";
+ @VisibleForTesting
+ static final String KEY_AUDIO_ROUTING = "audio_routing";
public BluetoothDetailsAudioRoutingController(Context context,
PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
@@ -71,9 +75,13 @@ public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsCont
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));
+ final Bundle extras = preference.getExtras();
+ extras.putString(KEY_DEVICE_ADDRESS, mCachedDevice.getAddress());
+ preference.setFragment(BluetoothDetailsAudioRoutingFragment.class.getName());
return preference;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
new file mode 100644
index 00000000000..691aceeab1c
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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 android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Settings fragment containing bluetooth audio routing. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class BluetoothDetailsAudioRoutingFragment extends RestrictedDashboardFragment {
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.bluetooth_audio_routing_fragment);
+ private static final String TAG = "BluetoothDetailsAudioRoutingFragment";
+ @VisibleForTesting
+ CachedBluetoothDevice mCachedDevice;
+
+ public BluetoothDetailsAudioRoutingFragment() {
+ super(DISALLOW_CONFIG_BLUETOOTH);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ final LocalBluetoothManager localBtMgr = Utils.getLocalBtManager(context);
+ final CachedBluetoothDeviceManager cachedDeviceMgr = localBtMgr.getCachedDeviceManager();
+ final BluetoothDevice bluetoothDevice = localBtMgr.getBluetoothAdapter().getRemoteDevice(
+ getArguments().getString(KEY_DEVICE_ADDRESS));
+
+ mCachedDevice = cachedDeviceMgr.findDevice(bluetoothDevice);
+ if (mCachedDevice == null) {
+ // Close this page if device is null with invalid device mac address
+ Log.w(TAG, "onAttach() CachedDevice is null! Can not find address: "
+ + bluetoothDevice.getAnonymizedAddress());
+ finish();
+ return;
+ }
+
+ // TODO: mCachedDevice will pass to control in next CLs.
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/262839191): To be updated settings_enums.proto
+ return 0;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.bluetooth_audio_routing_fragment;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
index 92174f30222..ea65856de61 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
@@ -16,12 +16,17 @@
package com.android.settings.bluetooth;
+import static com.android.settings.bluetooth.BluetoothDetailsAudioRoutingController.KEY_AUDIO_ROUTING;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.util.FeatureFlagUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +41,8 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
private BluetoothDetailsAudioRoutingController mController;
@Override
@@ -44,7 +51,9 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
mController = new BluetoothDetailsAudioRoutingController(mContext, mFragment, mCachedDevice,
mLifecycle);
- mController.init(mScreen);
+ final PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
+ preferenceCategory.setKey(mController.getPreferenceKey());
+ mScreen.addPreference(preferenceCategory);
}
@Test
@@ -64,4 +73,20 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
assertThat(mController.isAvailable()).isFalse();
}
+
+ @Test
+ public void init_isHearingAidDevice_expectedAudioRoutingPreference() {
+ when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+
+ mController.init(mScreen);
+ final Preference preference = mScreen.findPreference(KEY_AUDIO_ROUTING);
+ final String address = preference.getExtras().getString(
+ BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS);
+ final String fragment = preference.getFragment();
+
+ assertThat(address).isEqualTo(TEST_ADDRESS);
+ assertThat(fragment).isEqualTo(BluetoothDetailsAudioRoutingFragment.class.getName());
+
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
new file mode 100644
index 00000000000..b2da5798ead
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 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.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+/** Tests for {@link BluetoothDetailsAudioRoutingFragment}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class BluetoothDetailsAudioRoutingFragmentTest {
+
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ private BluetoothDetailsAudioRoutingFragment mFragment;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private LocalBluetoothAdapter mLocalBluetoothAdapter;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice;
+
+ @Before
+ public void setUp() {
+ setupEnvironment();
+
+ when(mLocalBluetoothAdapter.getRemoteDevice(TEST_ADDRESS)).thenReturn(mBluetoothDevice);
+ when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
+
+ mFragment = new BluetoothDetailsAudioRoutingFragment();
+ }
+
+ @Test
+ public void onAttach_setArgumentsWithAddress_expectedCachedDeviceWithAddress() {
+ final Bundle args = new Bundle();
+ args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
+ mFragment.setArguments(args);
+
+ mFragment.onAttach(mContext);
+
+ assertThat(mFragment.mCachedDevice.getAddress()).isEqualTo(TEST_ADDRESS);
+ }
+
+ @Test
+ public void getNonIndexableKeys_existInXmlLayout() {
+ final List niks = BluetoothDetailsAudioRoutingFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.bluetooth_audio_routing_fragment);
+
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+
+ private void setupEnvironment() {
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+ when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
+ }
+}