diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java index b00f407030d..79cc56ea6dd 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java @@ -44,7 +44,7 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr 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 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr } 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 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr @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 class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr 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 2040694c935..c0f463d863f 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.HashMap; 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 @@ public class AudioSharingSwitchBarController extends BasePreferenceController private final MetricsFeatureProvider mMetricsFeatureProvider; private final OnAudioSharingStateChangedListener mListener; private Map> mGroupedConnectedDevices = new HashMap<>(); - private List mTargetActiveSinks = new ArrayList<>(); + @Nullable private AudioSharingDeviceItem mTargetActiveItem; private List mDeviceItemsForSharing = new ArrayList<>(); @VisibleForTesting IntentFilter mIntentFilter; private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); private AtomicInteger mIntentHandleStage = new AtomicInteger(StartIntentHandleStage.TO_HANDLE.ordinal()); + private CopyOnWriteArrayList mSinksInAdding = new CopyOnWriteArrayList<>(); @VisibleForTesting BroadcastReceiver mReceiver = @@ -294,7 +296,16 @@ public class AudioSharingSwitchBarController extends BasePreferenceController 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 @@ public class AudioSharingSwitchBarController extends BasePreferenceController 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 @@ public class AudioSharingSwitchBarController extends BasePreferenceController } private void handleOnBroadcastReady() { + List targetActiveSinks = mTargetActiveItem == null ? ImmutableList.of() + : mGroupedConnectedDevices.getOrDefault( + mTargetActiveItem.getGroupId(), ImmutableList.of()); Pair[] 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 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 @@ public class AudioSharingSwitchBarController extends BasePreferenceController 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 @@ public class AudioSharingSwitchBarController extends BasePreferenceController new AudioSharingDialogFragment.DialogEventListener() { @Override public void onItemClick(@NonNull AudioSharingDeviceItem item) { - AudioSharingUtils.addSourceToTargetSinks( - mGroupedConnectedDevices.getOrDefault( - item.getGroupId(), ImmutableList.of()), - mBtManager); + List targetSinks = mGroupedConnectedDevices.getOrDefault( + item.getGroupId(), ImmutableList.of()); + addSourceToTargetSinks(targetSinks, item.getName()); cleanUp(); } @Override public void onCancelClick() { + dismissLoadingStateDialogIfNeeded(); cleanUp(); } }; @@ -700,6 +718,27 @@ public class AudioSharingSwitchBarController extends BasePreferenceController }); } + private void addSourceToTargetSinks(List 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 b5da88cff1c..ff15f5269d4 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 @@ public class AudioSharingLoadingStateDialogFragmentTest { } @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 @@ public class AudioSharingLoadingStateDialogFragmentTest { 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 354c5c7f08e..0d21f18b821 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.util.Pair; 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 @@ public class AudioSharingSwitchBarControllerTest { @After public void tearDown() { + ShadowAlertDialogCompat.reset(); ShadowBluetoothUtils.reset(); ShadowThreadUtils.reset(); } @@ -426,6 +429,8 @@ public class AudioSharingSwitchBarControllerTest { assertThat(childFragments) .comparingElementsUsing(CLAZZNAME_EQUALS) .containsExactly(AudioSharingConfirmDialogFragment.class.getName()); + + childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @Test @@ -490,14 +495,21 @@ public class AudioSharingSwitchBarControllerTest { public void onAudioSharingProfilesConnected() {} }); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); + shadowOf(Looper.getMainLooper()).idle(); + verify(mBroadcast).startPrivateBroadcast(); + List 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 childFragments = mParentFragment.getChildFragmentManager().getFragments(); + childFragments = mParentFragment.getChildFragmentManager().getFragments(); + // No audio sharing dialog. assertThat(childFragments).isEmpty(); } @@ -514,7 +526,13 @@ public class AudioSharingSwitchBarControllerTest { when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); + shadowOf(Looper.getMainLooper()).idle(); + verify(mBroadcast).startPrivateBroadcast(); + List 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 @@ public class AudioSharingSwitchBarControllerTest { verify(mFeatureFactory.metricsFeatureProvider, never()) .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING)); - List 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 @@ public class AudioSharingSwitchBarControllerTest { 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 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 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[] eventData = fragment.getEventData(); + Pair[] 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 @@ public class AudioSharingSwitchBarControllerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 1)); + + childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @Test @@ -582,6 +625,8 @@ public class AudioSharingSwitchBarControllerTest { 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 @@ public class AudioSharingSwitchBarControllerTest { verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); assertThat(dialog.isShowing()).isFalse(); + // Loading state dialog shows sharing state for the user chosen sink. + List 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 @@ public class AudioSharingSwitchBarControllerTest { 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 @@ public class AudioSharingSwitchBarControllerTest { 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 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 @@ public class AudioSharingSwitchBarControllerTest { } @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 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void testBluetoothLeBroadcastAssistantCallbacks_logAction() { + public void testAssistantCallbacks_onSourceAddFailed_logAction() { mController.mBroadcastAssistantCallback.onSourceAddFailed( mDevice1, mMetadata, /* reason= */ 1); verify(mFeatureFactory.metricsFeatureProvider) @@ -696,7 +765,24 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { + public void testAssistantCallbacks_onReceiveStateChanged_dismissLoadingDialog() { + AudioSharingLoadingStateDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1); + shadowOf(Looper.getMainLooper()).idle(); + List 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 @@ public class AudioSharingSwitchBarControllerTest { @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 class AudioSharingSwitchBarControllerTest { 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 @@ public class AudioSharingSwitchBarControllerTest { 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 @@ public class AudioSharingSwitchBarControllerTest { .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 childFragments = mParentFragment.getChildFragmentManager().getFragments(); - assertThat(childFragments).isEmpty(); + List 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 @@ public class AudioSharingSwitchBarControllerTest { .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); } }