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