[Audiosharing] Show loading state on audio sharing page

1. Show "Sharing audio stream..." once starting audio sharing
2. Show "Sharing with <devicename>..." once adding source to the device

Test: atest
Flag: com.android.settingslib.flags.enable_le_audio_sharing
Bug: 362858894
Change-Id: I6c19d999baaa91d6a5365f24e88efe79c2b38072
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java
index b00f407..79cc56e 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java
@@ -44,7 +44,7 @@
     private static final String TAG = "AudioSharingLoadingDlg";
 
     private static final String BUNDLE_KEY_MESSAGE = "bundle_key_message";
-    private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10);
+    private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15);
     private static final int AUTO_DISMISS_MESSAGE_ID = R.id.message;
 
     private static String sMessage = "";
@@ -74,13 +74,15 @@
         }
         AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
         if (dialog != null) {
-            if (sMessage.equals(message)) {
-                Log.d(TAG, "Dialog is showing with same message, return.");
-                return;
-            } else {
-                Log.d(TAG, "Dialog is showing with different message, dismiss and reshow.");
-                dialog.dismiss();
+            if (!sMessage.equals(message)) {
+                Log.d(TAG, "Update dialog message.");
+                TextView messageView = dialog.findViewById(R.id.message);
+                if (messageView != null) {
+                    messageView.setText(message);
+                }
             }
+            Log.d(TAG, "Dialog is showing, return.");
+            return;
         }
         sMessage = message;
         Log.d(TAG, "Show up the loading dialog.");
@@ -113,8 +115,10 @@
     @NonNull
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
         mHandler = new Handler(Looper.getMainLooper());
-        mHandler.postDelayed(() -> dismiss(), AUTO_DISMISS_MESSAGE_ID,
-                AUTO_DISMISS_TIME_THRESHOLD_MS);
+        mHandler.postDelayed(() -> {
+            Log.d(TAG, "Auto dismiss dialog after timeout");
+            dismiss();
+        }, AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
         Bundle args = requireArguments();
         String message = args.getString(BUNDLE_KEY_MESSAGE, "");
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
@@ -132,6 +136,7 @@
     public void onDismiss(@NonNull DialogInterface dialog) {
         super.onDismiss(dialog);
         if (mHandler != null) {
+            Log.d(TAG, "Dialog dismissed, remove auto dismiss task");
             mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
         }
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 2040694..c0f463d 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -70,6 +70,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -112,12 +113,13 @@
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private final OnAudioSharingStateChangedListener mListener;
     private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
-    private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
+    @Nullable private AudioSharingDeviceItem mTargetActiveItem;
     private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
     @VisibleForTesting IntentFilter mIntentFilter;
     private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
     private AtomicInteger mIntentHandleStage =
             new AtomicInteger(StartIntentHandleStage.TO_HANDLE.ordinal());
+    private CopyOnWriteArrayList<BluetoothDevice> mSinksInAdding = new CopyOnWriteArrayList<>();
 
     @VisibleForTesting
     BroadcastReceiver mReceiver =
@@ -294,7 +296,16 @@
                 public void onReceiveStateChanged(
                         @NonNull BluetoothDevice sink,
                         int sourceId,
-                        @NonNull BluetoothLeBroadcastReceiveState state) {}
+                        @NonNull BluetoothLeBroadcastReceiveState state) {
+                    if (BluetoothUtils.isConnected(state)) {
+                        if (mSinksInAdding.contains(sink)) {
+                            mSinksInAdding.remove(sink);
+                        }
+                        dismissLoadingStateDialogIfNeeded();
+                        Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink
+                                + ", remaining sinks = " + mSinksInAdding);
+                    }
+                }
             };
 
     AudioSharingSwitchBarController(
@@ -506,17 +517,20 @@
                         mBtManager, mGroupedConnectedDevices, /* filterByInSharing= */ false);
         // deviceItems is ordered. The active device is the first place if exits.
         mDeviceItemsForSharing = new ArrayList<>(deviceItems);
-        mTargetActiveSinks = new ArrayList<>();
+        mTargetActiveItem = null;
         if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
             // If active device exists for audio sharing, share to it
             // automatically once the broadcast is started.
-            mTargetActiveSinks =
-                    mGroupedConnectedDevices.getOrDefault(
-                            deviceItems.get(0).getGroupId(), ImmutableList.of());
+            mTargetActiveItem = deviceItems.get(0);
             mDeviceItemsForSharing.remove(0);
         }
         if (mBroadcast != null) {
             mBroadcast.startPrivateBroadcast();
+            mSinksInAdding.clear();
+            // TODO: use string res once finalized.
+            AudioSharingUtils.postOnMainThread(mContext,
+                    () -> AudioSharingLoadingStateDialogFragment.show(mFragment,
+                            "Starting audio stream..."));
             mMetricsFeatureProvider.action(
                     mContext,
                     SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_ON,
@@ -580,27 +594,30 @@
     }
 
     private void handleOnBroadcastReady() {
+        List<BluetoothDevice> targetActiveSinks = mTargetActiveItem == null ? ImmutableList.of()
+                : mGroupedConnectedDevices.getOrDefault(
+                        mTargetActiveItem.getGroupId(), ImmutableList.of());
         Pair<Integer, Object>[] eventData =
                 AudioSharingUtils.buildAudioSharingDialogEventData(
                         SettingsEnums.AUDIO_SHARING_SETTINGS,
                         SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE,
                         /* userTriggered= */ false,
-                        /* deviceCountInSharing= */ mTargetActiveSinks.isEmpty() ? 0 : 1,
+                        /* deviceCountInSharing= */ targetActiveSinks.isEmpty() ? 0 : 1,
                         /* candidateDeviceCount= */ mDeviceItemsForSharing.size());
-        if (!mTargetActiveSinks.isEmpty()) {
+        if (!targetActiveSinks.isEmpty() && mTargetActiveItem != null) {
             Log.d(TAG, "handleOnBroadcastReady: automatically add source to active sinks.");
-            AudioSharingUtils.addSourceToTargetSinks(mTargetActiveSinks, mBtManager);
+            addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName());
             mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING);
-            mTargetActiveSinks.clear();
+            mTargetActiveItem = null;
             if (mIntentHandleStage.compareAndSet(
                             StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
                             StartIntentHandleStage.HANDLED.ordinal())
                     && mDeviceItemsForSharing.size() == 1) {
                 Log.d(TAG, "handleOnBroadcastReady: auto add source to the second device");
-                AudioSharingUtils.addSourceToTargetSinks(
-                        mGroupedConnectedDevices.getOrDefault(
-                                mDeviceItemsForSharing.get(0).getGroupId(), ImmutableList.of()),
-                        mBtManager);
+                AudioSharingDeviceItem target = mDeviceItemsForSharing.get(0);
+                List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
+                        target.getGroupId(), ImmutableList.of());
+                addSourceToTargetSinks(targetSinks, target.getName());
                 cleanUp();
                 // TODO: Add metric for auto add by intent
                 return;
@@ -611,6 +628,7 @@
                 StartIntentHandleStage.HANDLED.ordinal());
         if (mFragment == null) {
             Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment.");
+            dismissLoadingStateDialogIfNeeded();
             cleanUp();
             return;
         }
@@ -622,15 +640,15 @@
                 new AudioSharingDialogFragment.DialogEventListener() {
                     @Override
                     public void onItemClick(@NonNull AudioSharingDeviceItem item) {
-                        AudioSharingUtils.addSourceToTargetSinks(
-                                mGroupedConnectedDevices.getOrDefault(
-                                        item.getGroupId(), ImmutableList.of()),
-                                mBtManager);
+                        List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
+                                item.getGroupId(), ImmutableList.of());
+                        addSourceToTargetSinks(targetSinks, item.getName());
                         cleanUp();
                     }
 
                     @Override
                     public void onCancelClick() {
+                        dismissLoadingStateDialogIfNeeded();
                         cleanUp();
                     }
                 };
@@ -700,6 +718,27 @@
                         });
     }
 
+    private void addSourceToTargetSinks(List<BluetoothDevice> targetActiveSinks,
+            @NonNull String sinkName) {
+        mSinksInAdding.addAll(targetActiveSinks);
+        AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager);
+        // TODO: move to res once finalized
+        String loadingMessage = "Sharing with " + sinkName + "...";
+        showLoadingStateDialog(loadingMessage);
+    }
+
+    private void showLoadingStateDialog(@NonNull String loadingMessage) {
+        AudioSharingUtils.postOnMainThread(mContext,
+                () -> AudioSharingLoadingStateDialogFragment.show(mFragment, loadingMessage));
+    }
+
+    private void dismissLoadingStateDialogIfNeeded() {
+        if (mSinksInAdding.isEmpty()) {
+            AudioSharingUtils.postOnMainThread(mContext,
+                    () -> AudioSharingLoadingStateDialogFragment.dismiss(mFragment));
+        }
+    }
+
     private void cleanUp() {
         mGroupedConnectedDevices.clear();
         mDeviceItemsForSharing.clear();
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java
index b5da88c..ff15f52 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java
@@ -150,7 +150,7 @@
     }
 
     @Test
-    public void showDialog_newMessage_dismissAndShowNewDialog() {
+    public void showDialog_newMessage_keepAndUpdateDialog() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1);
         shadowMainLooper().idle();
@@ -163,12 +163,7 @@
 
         AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE2);
         shadowMainLooper().idle();
-        assertThat(dialog.isShowing()).isFalse();
-        AlertDialog newDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        assertThat(newDialog).isNotNull();
-        assertThat(newDialog.isShowing()).isTrue();
-        view = newDialog.findViewById(R.id.message);
-        assertThat(view).isNotNull();
+        assertThat(dialog.isShowing()).isTrue();
         assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE2);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
index 354c5c7..0d21f18 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
@@ -57,7 +57,9 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.CompoundButton;
+import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
@@ -235,6 +237,7 @@
 
     @After
     public void tearDown() {
+        ShadowAlertDialogCompat.reset();
         ShadowBluetoothUtils.reset();
         ShadowThreadUtils.reset();
     }
@@ -426,6 +429,8 @@
         assertThat(childFragments)
                 .comparingElementsUsing(CLAZZNAME_EQUALS)
                 .containsExactly(AudioSharingConfirmDialogFragment.class.getName());
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
     @Test
@@ -490,14 +495,21 @@
                             public void onAudioSharingProfilesConnected() {}
                         });
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
         verify(mBroadcast).startPrivateBroadcast();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        // No loading state dialog.
+        assertThat(childFragments).isEmpty();
+
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
 
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
 
-        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        // No audio sharing dialog.
         assertThat(childFragments).isEmpty();
     }
 
@@ -514,7 +526,13 @@
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
         verify(mBroadcast).startPrivateBroadcast();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
 
@@ -522,8 +540,12 @@
         verify(mFeatureFactory.metricsFeatureProvider, never())
                 .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
 
-        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
-        assertThat(childFragments).isEmpty();
+        childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        // No audio sharing dialog.
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).doesNotContain(
+                AudioSharingDialogFragment.class.getName());
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
     @Test
@@ -534,23 +556,42 @@
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
         when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
         doNothing().when(mBroadcast).startPrivateBroadcast();
-        mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
+        mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
         verify(mBroadcast).startPrivateBroadcast();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+        AudioSharingLoadingStateDialogFragment loadingFragment =
+                (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
+        // TODO: use string res once finalized
+        String expectedMessage = "Starting audio stream...";
+        checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
+
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
 
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
+        // TODO: use string res once finalized
+        expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
+        checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
 
-        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        childFragments = mParentFragment.getChildFragmentManager().getFragments();
         assertThat(childFragments)
                 .comparingElementsUsing(CLAZZNAME_EQUALS)
-                .containsExactly(AudioSharingDialogFragment.class.getName());
+                .containsExactly(AudioSharingDialogFragment.class.getName(),
+                        AudioSharingLoadingStateDialogFragment.class.getName());
 
-        AudioSharingDialogFragment fragment =
-                (AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments);
-        Pair<Integer, Object>[] eventData = fragment.getEventData();
+        Pair<Integer, Object>[] eventData = new Pair[0];
+        for (Fragment fragment : childFragments) {
+            if (fragment instanceof AudioSharingDialogFragment) {
+                eventData = ((AudioSharingDialogFragment) fragment).getEventData();
+                break;
+            }
+        }
         assertThat(eventData)
                 .asList()
                 .containsExactly(
@@ -570,6 +611,8 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 1));
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
     @Test
@@ -582,6 +625,8 @@
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
         verify(mBroadcast).startPrivateBroadcast();
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
@@ -597,6 +642,17 @@
 
         verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
         assertThat(dialog.isShowing()).isFalse();
+        // Loading state dialog shows sharing state for the user chosen sink.
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+        AudioSharingLoadingStateDialogFragment loadingFragment =
+                (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
+        // TODO: use string res once finalized
+        String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
+        checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
     @Test
@@ -609,6 +665,8 @@
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
         verify(mBroadcast).startPrivateBroadcast();
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
@@ -624,10 +682,21 @@
 
         verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
         assertThat(dialog.isShowing()).isFalse();
+        // Loading state dialog shows sharing state for the auto add active sink.
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+        AudioSharingLoadingStateDialogFragment loadingFragment =
+                (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
+        // TODO: use string res once finalized
+        String expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
+        checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
     @Test
-    public void testBluetoothLeBroadcastCallbacks_updateSwitch() {
+    public void testBroadcastCallbacks_updateSwitch() {
         mOnAudioSharingStateChanged = false;
         mSwitchBar.setChecked(false);
         when(mBroadcast.isEnabled(any())).thenReturn(false);
@@ -673,7 +742,7 @@
     }
 
     @Test
-    public void testBluetoothLeBroadcastCallbacks_doNothing() {
+    public void testBroadcastCallbacks_doNothing() {
         mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata);
         mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
         mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
@@ -685,7 +754,7 @@
     }
 
     @Test
-    public void testBluetoothLeBroadcastAssistantCallbacks_logAction() {
+    public void testAssistantCallbacks_onSourceAddFailed_logAction() {
         mController.mBroadcastAssistantCallback.onSourceAddFailed(
                 mDevice1, mMetadata, /* reason= */ 1);
         verify(mFeatureFactory.metricsFeatureProvider)
@@ -696,7 +765,24 @@
     }
 
     @Test
-    public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
+    public void testAssistantCallbacks_onReceiveStateChanged_dismissLoadingDialog() {
+        AudioSharingLoadingStateDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1);
+        shadowOf(Looper.getMainLooper()).idle();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+
+        BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+        when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1,
+                state);
+        shadowOf(Looper.getMainLooper()).idle();
+        childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).isEmpty();
+    }
+
+    @Test
+    public void testAssistantCallbacks_doNothing() {
         BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
 
         // Do nothing
@@ -784,7 +870,7 @@
     @Test
     public void handleStartAudioSharingFromIntent_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        setUpStartSharingIntent();
+        var unused = setUpFragmentWithStartSharingIntent();
         mController.onStart(mLifecycleOwner);
         shadowOf(Looper.getMainLooper()).idle();
 
@@ -795,7 +881,7 @@
     public void handleStartAudioSharingFromIntent_profileNotReady_doNothing() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         when(mAssistant.isProfileReady()).thenReturn(false);
-        setUpStartSharingIntent();
+        var unused = setUpFragmentWithStartSharingIntent();
         mController.onServiceConnected();
         shadowOf(Looper.getMainLooper()).idle();
 
@@ -817,13 +903,16 @@
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
-        setUpStartSharingIntent();
+        Fragment parentFragment = setUpFragmentWithStartSharingIntent();
         mController.onServiceConnected();
         shadowOf(Looper.getMainLooper()).idle();
 
         verify(mSwitchBar).setChecked(true);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        verify(mBroadcast).startPrivateBroadcast();
         mController.mBroadcastCallback.onPlaybackStarted(0, 0);
         shadowOf(Looper.getMainLooper()).idle();
 
@@ -831,11 +920,21 @@
                 .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
         verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
         verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
-        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
-        assertThat(childFragments).isEmpty();
+        List<Fragment> childFragments = parentFragment.getChildFragmentManager().getFragments();
+        // Skip audio sharing dialog.
+        assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
+                AudioSharingLoadingStateDialogFragment.class.getName());
+        // The loading state dialog shows sharing state for the auto add second sink.
+        AudioSharingLoadingStateDialogFragment loadingFragment =
+                (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
+        // TODO: use string res once finalized
+        String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
+        checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
+
+        childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
     }
 
-    private void setUpStartSharingIntent() {
+    private Fragment setUpFragmentWithStartSharingIntent() {
         Bundle args = new Bundle();
         args.putBoolean(EXTRA_START_LE_AUDIO_SHARING, true);
         Intent intent = new Intent();
@@ -849,5 +948,15 @@
                 .get();
         shadowOf(Looper.getMainLooper()).idle();
         mController.init(fragment);
+        return fragment;
+    }
+
+    private void checkLoadingStateDialogMessage(
+            @NonNull AudioSharingLoadingStateDialogFragment fragment,
+            @NonNull String expectedMessage) {
+        TextView loadingMessage = fragment.getDialog() == null ? null
+                : fragment.getDialog().findViewById(R.id.message);
+        assertThat(loadingMessage).isNotNull();
+        assertThat(loadingMessage.getText().toString()).isEqualTo(expectedMessage);
     }
 }