[BT LE broadcast sink] Initialize the sourceId

If the sink device has connected the broadcast source, then the device
should initialize the sourceId and create the sourceId's preference UI.

Bug: 231576575
Test: manually test
Change-Id: I24d9e6d4c78dc4e583c5253e552456a68b1114c3
diff --git a/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java b/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
index 17b604c..733a4a9 100644
--- a/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
@@ -16,7 +16,9 @@
 
 package com.android.settings.bluetooth;
 
+import android.bluetooth.BluetoothLeAudioContentMetadata;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothLeBroadcastSubgroup;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -43,16 +45,15 @@
     private static final int RESOURCE_ID_ICON = R.drawable.settings_input_antenna;
 
     private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
+    private BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState;
     private ImageView mFrictionImageView;
     private String mTitle;
     private boolean mStatus;
     private boolean mIsEncrypted;
 
-    BluetoothBroadcastSourcePreference(@NonNull Context context,
-            @NonNull BluetoothLeBroadcastMetadata source) {
+    BluetoothBroadcastSourcePreference(@NonNull Context context) {
         super(context);
         initUi();
-        updateMetadataAndRefreshUi(source, false);
     }
 
     @Override
@@ -68,7 +69,7 @@
     private void initUi() {
         setLayoutResource(R.layout.preference_access_point);
         setWidgetLayoutResource(R.layout.access_point_friction_widget);
-
+        mTitle = getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO);
         mStatus = false;
         final Drawable drawable = getContext().getDrawable(RESOURCE_ID_ICON);
         if (drawable != null) {
@@ -105,9 +106,20 @@
      */
     public void updateMetadataAndRefreshUi(BluetoothLeBroadcastMetadata source, boolean status) {
         mBluetoothLeBroadcastMetadata = source;
-        mTitle = getBroadcastMetadataProgramInfo();
+        mTitle = getProgramInfo();
         mIsEncrypted = mBluetoothLeBroadcastMetadata.isEncrypted();
-        mStatus = status;
+        mStatus = status || mBluetoothLeBroadcastReceiveState != null;
+
+        refresh();
+    }
+
+    /**
+     * Updates the title and status from BluetoothLeBroadcastReceiveState.
+     */
+    public void updateReceiveStateAndRefreshUi(BluetoothLeBroadcastReceiveState receiveState) {
+        mBluetoothLeBroadcastReceiveState = receiveState;
+        mTitle = getProgramInfo();
+        mStatus = true;
 
         refresh();
     }
@@ -124,7 +136,17 @@
         updateStatusButton();
     }
 
-    private String getBroadcastMetadataProgramInfo() {
+    private String getProgramInfo() {
+        if (mBluetoothLeBroadcastReceiveState != null) {
+            List<BluetoothLeAudioContentMetadata> bluetoothLeAudioContentMetadata =
+                    mBluetoothLeBroadcastReceiveState.getSubgroupMetadata();
+            if (!bluetoothLeAudioContentMetadata.isEmpty()) {
+                return bluetoothLeAudioContentMetadata.stream()
+                        .map(i -> i.getProgramInfo())
+                        .findFirst().orElse(
+                                getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO));
+            }
+        }
         if (mBluetoothLeBroadcastMetadata == null) {
             return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO);
         }
@@ -138,4 +160,24 @@
                 .filter(i -> !TextUtils.isEmpty(i))
                 .findFirst().orElse(getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO));
     }
+
+    /**
+     * Whether the broadcast source is encrypted or not.
+     * @return If true, the broadcast source needs the broadcast code. If false, the broadcast
+     * source does not need the broadcast code.
+     */
+    public boolean isEncrypted() {
+        return mIsEncrypted;
+    }
+
+    /**
+     * Clear the BluetoothLeBroadcastReceiveState and reset the state when the user clicks the
+     * "leave broadcast" button.
+     */
+    public void clearReceiveState() {
+        mBluetoothLeBroadcastReceiveState = null;
+        mTitle = getProgramInfo();
+        mStatus = false;
+        refresh();
+    }
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
index 07a3156..13388b3 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
@@ -86,9 +86,7 @@
                 @Override
                 public void onSearchStarted(int reason) {
                     Log.d(TAG, "onSearchStarted: " + reason);
-
-                    getActivity().runOnUiThread(
-                            () -> cacheRemoveAllPrefs(mBroadcastSourceListCategory));
+                    getActivity().runOnUiThread(() -> handleSearchStarted());
                 }
 
                 @Override
@@ -109,7 +107,8 @@
                 @Override
                 public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
                     Log.d(TAG, "onSourceFound:");
-                    getActivity().runOnUiThread(() -> updateListCategory(source, false));
+                    getActivity().runOnUiThread(
+                            () -> updateListCategoryFromBroadcastMetadata(source, false));
                 }
 
                 @Override
@@ -119,7 +118,7 @@
                         Log.w(TAG, "onSourceAdded: mSelectedPreference == null!");
                         return;
                     }
-                    getActivity().runOnUiThread(() -> updateListCategory(
+                    getActivity().runOnUiThread(() -> updateListCategoryFromBroadcastMetadata(
                             mSelectedPreference.getBluetoothLeBroadcastMetadata(), true));
                 }
 
@@ -144,6 +143,7 @@
                 public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
                         int reason) {
                     Log.d(TAG, "onSourceRemoved:");
+                    getActivity().runOnUiThread(() -> handleSourceRemoved());
                 }
 
                 @Override
@@ -215,6 +215,8 @@
         //check assistant status. Start searching...
         if (mLeBroadcastAssistant != null && !mLeBroadcastAssistant.isSearchInProgress()) {
             mLeBroadcastAssistant.startSearchingForSources(getScanFilter());
+        } else {
+            addConnectedSourcePreference();
         }
     }
 
@@ -310,11 +312,13 @@
         return Collections.emptyList();
     }
 
-    private void updateListCategory(BluetoothLeBroadcastMetadata source, boolean isConnected) {
+    private void updateListCategoryFromBroadcastMetadata(BluetoothLeBroadcastMetadata source,
+            boolean isConnected) {
         BluetoothBroadcastSourcePreference item = mBroadcastSourceListCategory.findPreference(
                 Integer.toString(source.getBroadcastId()));
         if (item == null) {
-            item = createBluetoothBroadcastSourcePreference(source);
+            item = createBluetoothBroadcastSourcePreference();
+            item.setKey(Integer.toString(source.getBroadcastId()));
             mBroadcastSourceListCategory.addPreference(item);
         }
         item.updateMetadataAndRefreshUi(source, isConnected);
@@ -326,13 +330,36 @@
         }
     }
 
-    private BluetoothBroadcastSourcePreference createBluetoothBroadcastSourcePreference(
-            BluetoothLeBroadcastMetadata source) {
+    private void updateListCategoryFromBroadcastReceiveState(
+            BluetoothLeBroadcastReceiveState receiveState) {
+        BluetoothBroadcastSourcePreference item = mBroadcastSourceListCategory.findPreference(
+                Integer.toString(receiveState.getBroadcastId()));
+        if (item == null) {
+            item = createBluetoothBroadcastSourcePreference();
+            item.setKey(Integer.toString(receiveState.getBroadcastId()));
+            mBroadcastSourceListCategory.addPreference(item);
+        }
+        item.updateReceiveStateAndRefreshUi(receiveState);
+        item.setOrder(0);
+
+        setSourceId(receiveState.getSourceId());
+        mSelectedPreference = item;
+
+        //refresh the header
+        if (mBluetoothFindBroadcastsHeaderController != null) {
+            mBluetoothFindBroadcastsHeaderController.refreshUi();
+        }
+    }
+
+    private BluetoothBroadcastSourcePreference createBluetoothBroadcastSourcePreference() {
         BluetoothBroadcastSourcePreference pref = new BluetoothBroadcastSourcePreference(
-                getContext(), source);
-        pref.setKey(Integer.toString(source.getBroadcastId()));
+                getContext());
         pref.setOnPreferenceClickListener(preference -> {
-            if (source.isEncrypted()) {
+            if (pref.getBluetoothLeBroadcastMetadata() == null) {
+                Log.d(TAG, "BluetoothLeBroadcastMetadata is null, do nothing.");
+                return false;
+            }
+            if (pref.isEncrypted()) {
                 launchBroadcastCodeDialog(pref);
             } else {
                 addSource(pref);
@@ -383,6 +410,10 @@
                 .setPositiveButton(R.string.bluetooth_connect_access_dialog_positive,
                         (d, w) -> {
                             Log.d(TAG, "setPositiveButton: clicked");
+                            if (pref.getBluetoothLeBroadcastMetadata() == null) {
+                                Log.d(TAG, "BluetoothLeBroadcastMetadata is null, do nothing.");
+                                return;
+                            }
                             addBroadcastCodeIntoPreference(pref, editText.getText().toString());
                             addSource(pref);
                         })
@@ -392,6 +423,30 @@
         alertDialog.show();
     }
 
+    private void handleSearchStarted() {
+        cacheRemoveAllPrefs(mBroadcastSourceListCategory);
+        addConnectedSourcePreference();
+    }
+
+    private void handleSourceRemoved() {
+        if (mSelectedPreference != null) {
+            if (mSelectedPreference.getBluetoothLeBroadcastMetadata() == null) {
+                mBroadcastSourceListCategory.removePreference(mSelectedPreference);
+            } else {
+                mSelectedPreference.clearReceiveState();
+            }
+        }
+        mSelectedPreference = null;
+    }
+
+    private void addConnectedSourcePreference() {
+        List<BluetoothLeBroadcastReceiveState> receiveStateList =
+                mLeBroadcastAssistant.getAllSources(mCachedDevice.getDevice());
+        if (!receiveStateList.isEmpty()) {
+            updateListCategoryFromBroadcastReceiveState(receiveStateList.get(0));
+        }
+    }
+
     public int getSourceId() {
         return mSourceId;
     }