diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
index 0043a2d12b0..1d2dd24e26c 100644
--- a/res/xml/connected_devices.xml
+++ b/res/xml/connected_devices.xml
@@ -26,6 +26,19 @@
settings:controller="com.android.settings.slices.SlicePreferenceController"
settings:allowDividerBelow="true"/>
+
+
+
+
sourceList =
+ assistant.getAllSources(cachedDevice.getDevice());
+ if (!sourceList.isEmpty()) return true;
+ // Return true if member device is in broadcast.
+ for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
+ List list =
+ assistant.getAllSources(device.getDevice());
+ if (!list.isEmpty()) return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected void update(CachedBluetoothDevice cachedBluetoothDevice) {
+ super.update(cachedBluetoothDevice);
+ Log.d(TAG, "Map : " + mPreferenceMap);
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
new file mode 100644
index 00000000000..d4803c63699
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -0,0 +1,260 @@
+/*
+ * 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.connecteddevice.audiosharing;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.flags.Flags;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class AudioSharingDevicePreferenceController extends BasePreferenceController
+ implements DefaultLifecycleObserver, DevicePreferenceCallback, BluetoothCallback {
+
+ private static final String TAG = "AudioSharingDevicePrefController";
+ private static final String KEY = "audio_sharing_device_list";
+ private static final String KEY_AUDIO_SHARING_SETTINGS =
+ "connected_device_audio_sharing_settings";
+
+ private final LocalBluetoothManager mLocalBtManager;
+ private final LocalBluetoothLeBroadcastAssistant mAssistant;
+ private final Executor mExecutor;
+ private PreferenceGroup mPreferenceGroup;
+ private Preference mAudioSharingSettingsPreference;
+ private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+
+ private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+ new BluetoothLeBroadcastAssistant.Callback() {
+ @Override
+ public void onSearchStarted(int reason) {}
+
+ @Override
+ public void onSearchStartFailed(int reason) {}
+
+ @Override
+ public void onSearchStopped(int reason) {}
+
+ @Override
+ public void onSearchStopFailed(int reason) {}
+
+ @Override
+ public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
+
+ @Override
+ public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ Log.d(
+ TAG,
+ "onSourceAdded(), sink = "
+ + sink
+ + ", sourceId = "
+ + sourceId
+ + ", reason = "
+ + reason);
+ mBluetoothDeviceUpdater.forceUpdate();
+ }
+
+ @Override
+ public void onSourceAddFailed(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastMetadata source,
+ int reason) {
+ Log.d(
+ TAG,
+ "onSourceAddFailed(), sink = "
+ + sink
+ + ", source = "
+ + source
+ + ", reason = "
+ + reason);
+ }
+
+ @Override
+ public void onSourceModified(
+ @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+ @Override
+ public void onSourceModifyFailed(
+ @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+ @Override
+ public void onSourceRemoved(
+ @NonNull BluetoothDevice sink, int sourceId, int reason) {
+ Log.d(
+ TAG,
+ "onSourceRemoved(), sink = "
+ + sink
+ + ", sourceId = "
+ + sourceId
+ + ", reason = "
+ + reason);
+ mBluetoothDeviceUpdater.forceUpdate();
+ }
+
+ @Override
+ public void onSourceRemoveFailed(
+ @NonNull BluetoothDevice sink, int sourceId, int reason) {
+ Log.d(
+ TAG,
+ "onSourceRemoveFailed(), sink = "
+ + sink
+ + ", sourceId = "
+ + sourceId
+ + ", reason = "
+ + reason);
+ }
+
+ @Override
+ public void onReceiveStateChanged(
+ BluetoothDevice sink,
+ int sourceId,
+ BluetoothLeBroadcastReceiveState state) {}
+ };
+
+ public AudioSharingDevicePreferenceController(Context context) {
+ super(context, KEY);
+ mLocalBtManager = Utils.getLocalBtManager(mContext);
+ mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ mExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager == null) {
+ Log.e(TAG, "onStart() Bluetooth is not supported on this device");
+ return;
+ }
+ if (mAssistant == null) {
+ Log.e(TAG, "onStart() Broadcast assistant is not supported on this device");
+ return;
+ }
+ if (mBluetoothDeviceUpdater == null) {
+ Log.e(TAG, "onStart() Bluetooth device updater is not initialized");
+ return;
+ }
+ mLocalBtManager.getEventManager().registerCallback(this);
+ mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
+ mBluetoothDeviceUpdater.registerCallback();
+ mBluetoothDeviceUpdater.refreshPreference();
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager == null) {
+ Log.e(TAG, "onStop() Bluetooth is not supported on this device");
+ return;
+ }
+ if (mAssistant == null) {
+ Log.e(TAG, "onStop() Broadcast assistant is not supported on this device");
+ return;
+ }
+ if (mBluetoothDeviceUpdater == null) {
+ Log.e(TAG, "onStop() Bluetooth device updater is not initialized");
+ return;
+ }
+ mLocalBtManager.getEventManager().unregisterCallback(this);
+ // TODO: verify the reason for failing to unregister
+ try {
+ mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Fail to unregister assistant callback due to " + e.getMessage());
+ }
+ mBluetoothDeviceUpdater.unregisterCallback();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreferenceGroup = screen.findPreference(KEY);
+ mAudioSharingSettingsPreference =
+ mPreferenceGroup.findPreference(KEY_AUDIO_SHARING_SETTINGS);
+ mPreferenceGroup.setVisible(false);
+ mAudioSharingSettingsPreference.setVisible(false);
+
+ if (isAvailable()) {
+ mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
+ mBluetoothDeviceUpdater.forceUpdate();
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+ && Flags.enableLeAudioSharing()
+ ? AVAILABLE_UNSEARCHABLE
+ : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public void onDeviceAdded(Preference preference) {
+ if (mPreferenceGroup.getPreferenceCount() == 1) {
+ mPreferenceGroup.setVisible(true);
+ mAudioSharingSettingsPreference.setVisible(true);
+ }
+ mPreferenceGroup.addPreference(preference);
+ }
+
+ @Override
+ public void onDeviceRemoved(Preference preference) {
+ mPreferenceGroup.removePreference(preference);
+ if (mPreferenceGroup.getPreferenceCount() == 1) {
+ mPreferenceGroup.setVisible(false);
+ mAudioSharingSettingsPreference.setVisible(false);
+ }
+ }
+
+ /**
+ * Initialize the controller.
+ *
+ * @param fragment The fragment to provide the context and metrics category for {@link
+ * AudioSharingBluetoothDeviceUpdater}.
+ */
+ public void init(DashboardFragment fragment) {
+ mBluetoothDeviceUpdater =
+ new AudioSharingBluetoothDeviceUpdater(
+ fragment.getContext(),
+ AudioSharingDevicePreferenceController.this,
+ fragment.getMetricsCategory());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
index d381975352e..1d1f2f8fc18 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
@@ -56,6 +56,9 @@ public class ConnectedDeviceDashboardFragmentTest {
private static final String KEY_FAST_PAIR_DEVICE_SEE_ALL = "fast_pair_devices_see_all";
private static final String KEY_FAST_PAIR_DEVICE_LIST = "fast_pair_devices";
private static final String KEY_ADD_BT_DEVICES = "add_bt_devices";
+ private static final String KEY_AUDIO_SHARING_DEVICE_LIST = "audio_sharing_device_list";
+ private static final String KEY_AUDIO_SHARING_SETTINGS =
+ "connected_device_audio_sharing_settings";
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
@@ -93,9 +96,17 @@ public class ConnectedDeviceDashboardFragmentTest {
final List niks = ConnectedDeviceDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
- assertThat(niks).containsExactly(KEY_CONNECTED_DEVICES, KEY_AVAILABLE_DEVICES,
- KEY_NEARBY_DEVICES, KEY_DISCOVERABLE_FOOTER, KEY_SAVED_DEVICE_SEE_ALL,
- KEY_FAST_PAIR_DEVICE_SEE_ALL, KEY_FAST_PAIR_DEVICE_LIST);
+ assertThat(niks)
+ .containsExactly(
+ KEY_CONNECTED_DEVICES,
+ KEY_AVAILABLE_DEVICES,
+ KEY_NEARBY_DEVICES,
+ KEY_DISCOVERABLE_FOOTER,
+ KEY_SAVED_DEVICE_SEE_ALL,
+ KEY_FAST_PAIR_DEVICE_SEE_ALL,
+ KEY_FAST_PAIR_DEVICE_LIST,
+ KEY_AUDIO_SHARING_DEVICE_LIST,
+ KEY_AUDIO_SHARING_SETTINGS);
}
@Test