[Audiosharing] Show retry dialog when error occurs in start sharing
Errors including: 1. onBroadcastStartFailed 2. onSourceAddFailed Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Bug: 362858894 Change-Id: I0fe13bd675672c57219d0f6f3e735515a7ed7a06
This commit is contained in:
@@ -41,7 +41,9 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
@@ -69,7 +71,6 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -113,14 +114,21 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
private final Executor mExecutor;
|
private final Executor mExecutor;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final OnAudioSharingStateChangedListener mListener;
|
private final OnAudioSharingStateChangedListener mListener;
|
||||||
|
@VisibleForTesting IntentFilter mIntentFilter;
|
||||||
private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
|
private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
|
||||||
@Nullable private AudioSharingDeviceItem mTargetActiveItem;
|
@Nullable private AudioSharingDeviceItem mTargetActiveItem;
|
||||||
private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
|
private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
|
||||||
@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());
|
||||||
|
// The sinks in adding source process. We show the progress dialog based on this list.
|
||||||
private CopyOnWriteArrayList<BluetoothDevice> mSinksInAdding = new CopyOnWriteArrayList<>();
|
private CopyOnWriteArrayList<BluetoothDevice> mSinksInAdding = new CopyOnWriteArrayList<>();
|
||||||
|
// The primary/active sinks in adding source process.
|
||||||
|
// To avoid users advance to share then pair flow before the primary/active sinks successfully
|
||||||
|
// join the audio sharing, we will wait for the process complete for this list of sinks and then
|
||||||
|
// popup audio sharing dialog with options to pair new device.
|
||||||
|
private CopyOnWriteArrayList<BluetoothDevice> mSinksToWaitFor = new CopyOnWriteArrayList<>();
|
||||||
|
private AtomicBoolean mStoppingSharing = new AtomicBoolean(false);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BroadcastReceiver mReceiver =
|
BroadcastReceiver mReceiver =
|
||||||
@@ -153,6 +161,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
public void onBroadcastStartFailed(int reason) {
|
public void onBroadcastStartFailed(int reason) {
|
||||||
Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
|
Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
|
||||||
updateSwitch();
|
updateSwitch();
|
||||||
|
showRetryDialog();
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mContext,
|
mContext,
|
||||||
SettingsEnums.ACTION_AUDIO_SHARING_START_FAILED,
|
SettingsEnums.ACTION_AUDIO_SHARING_START_FAILED,
|
||||||
@@ -178,7 +187,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
+ reason
|
+ reason
|
||||||
+ ", broadcastId = "
|
+ ", broadcastId = "
|
||||||
+ broadcastId);
|
+ broadcastId);
|
||||||
|
mStoppingSharing.compareAndSet(true, false);
|
||||||
updateSwitch();
|
updateSwitch();
|
||||||
|
AudioSharingUtils.postOnMainThread(mContext,
|
||||||
|
() -> dismissStaleDialogsOtherThanRetryDialog());
|
||||||
AudioSharingUtils.toastMessage(
|
AudioSharingUtils.toastMessage(
|
||||||
mContext,
|
mContext,
|
||||||
mContext.getString(R.string.audio_sharing_sharing_stopped_label));
|
mContext.getString(R.string.audio_sharing_sharing_stopped_label));
|
||||||
@@ -219,7 +231,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
TAG,
|
TAG,
|
||||||
"Skip handleOnBroadcastReady: null assistant or "
|
"Skip handleOnBroadcastReady: null assistant or "
|
||||||
+ "sink has active local source.");
|
+ "sink has active local source.");
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleOnBroadcastReady();
|
handleOnBroadcastReady();
|
||||||
@@ -264,17 +276,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
+ source
|
+ source
|
||||||
+ ", reason = "
|
+ ", reason = "
|
||||||
+ reason);
|
+ reason);
|
||||||
|
if (mSinksInAdding.contains(sink)) {
|
||||||
|
stopAudioSharing();
|
||||||
|
showRetryDialog();
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mContext,
|
mContext,
|
||||||
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
|
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
|
||||||
SettingsEnums.AUDIO_SHARING_SETTINGS);
|
SettingsEnums.AUDIO_SHARING_SETTINGS);
|
||||||
AudioSharingUtils.toastMessage(
|
}
|
||||||
mContext,
|
|
||||||
String.format(
|
|
||||||
Locale.US,
|
|
||||||
"Fail to add source to %s reason %d",
|
|
||||||
sink.getAddress(),
|
|
||||||
reason));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -298,6 +307,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
@NonNull BluetoothDevice sink,
|
@NonNull BluetoothDevice sink,
|
||||||
int sourceId,
|
int sourceId,
|
||||||
@NonNull BluetoothLeBroadcastReceiveState state) {
|
@NonNull BluetoothLeBroadcastReceiveState state) {
|
||||||
|
if (mStoppingSharing.get()) {
|
||||||
|
Log.d(TAG, "Skip onReceiveStateChanged, stopping broadcast");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (BluetoothUtils.isConnected(state)) {
|
if (BluetoothUtils.isConnected(state)) {
|
||||||
if (mSinksInAdding.contains(sink)) {
|
if (mSinksInAdding.contains(sink)) {
|
||||||
mSinksInAdding.remove(sink);
|
mSinksInAdding.remove(sink);
|
||||||
@@ -305,6 +318,22 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
dismissLoadingStateDialogIfNeeded();
|
dismissLoadingStateDialogIfNeeded();
|
||||||
Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink
|
Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink
|
||||||
+ ", remaining sinks = " + mSinksInAdding);
|
+ ", remaining sinks = " + mSinksInAdding);
|
||||||
|
if (mSinksToWaitFor.contains(sink)) {
|
||||||
|
mSinksToWaitFor.remove(sink);
|
||||||
|
if (mSinksToWaitFor.isEmpty()) {
|
||||||
|
// To avoid users advance to share then pair flow before the
|
||||||
|
// primary/active sinks successfully join the audio sharing,
|
||||||
|
// popup dialog till adding source complete for mSinksToWaitFor.
|
||||||
|
Pair<Integer, Object>[] eventData =
|
||||||
|
AudioSharingUtils.buildAudioSharingDialogEventData(
|
||||||
|
SettingsEnums.AUDIO_SHARING_SETTINGS,
|
||||||
|
SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE,
|
||||||
|
/* userTriggered= */ false,
|
||||||
|
/* deviceCountInSharing= */ 1,
|
||||||
|
/* candidateDeviceCount= */ 0);
|
||||||
|
showAudioSharingDialog(eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -411,6 +440,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stopAudioSharing();
|
stopAudioSharing();
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
mContext, SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_OFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,9 +584,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
|
|
||||||
private void stopAudioSharing() {
|
private void stopAudioSharing() {
|
||||||
if (mBroadcast != null) {
|
if (mBroadcast != null) {
|
||||||
mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
|
int broadcastId = mBroadcast.getLatestBroadcastId();
|
||||||
mMetricsFeatureProvider.action(
|
if (broadcastId != -1) {
|
||||||
mContext, SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_OFF);
|
mBroadcast.stopBroadcast(broadcastId);
|
||||||
|
mStoppingSharing.compareAndSet(false, true);
|
||||||
|
mSinksInAdding.clear();
|
||||||
|
mSinksToWaitFor.clear();
|
||||||
|
}
|
||||||
|
cleanUpStatesForStartSharing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,11 +653,22 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
/* userTriggered= */ false,
|
/* userTriggered= */ false,
|
||||||
/* deviceCountInSharing= */ targetActiveSinks.isEmpty() ? 0 : 1,
|
/* deviceCountInSharing= */ targetActiveSinks.isEmpty() ? 0 : 1,
|
||||||
/* candidateDeviceCount= */ mDeviceItemsForSharing.size());
|
/* candidateDeviceCount= */ mDeviceItemsForSharing.size());
|
||||||
|
// Auto add primary/active sinks w/o user interactions.
|
||||||
if (!targetActiveSinks.isEmpty() && mTargetActiveItem != null) {
|
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.");
|
||||||
addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName());
|
addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName());
|
||||||
|
// To avoid users advance to share then pair flow before the primary/active sinks
|
||||||
|
// successfully join the audio sharing, save the primary/active sinks in mSinksToWaitFor
|
||||||
|
// and popup dialog till adding source complete for these sinks.
|
||||||
|
if (mDeviceItemsForSharing.isEmpty()) {
|
||||||
|
mSinksToWaitFor.clear();
|
||||||
|
mSinksToWaitFor.addAll(targetActiveSinks);
|
||||||
|
}
|
||||||
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING);
|
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING);
|
||||||
mTargetActiveItem = null;
|
mTargetActiveItem = null;
|
||||||
|
// When audio sharing page is brought up by intent with EXTRA_START_LE_AUDIO_SHARING
|
||||||
|
// == true, plus there is one active lea headset and one connected lea headset, we
|
||||||
|
// should auto add these sinks without user interactions.
|
||||||
if (mIntentHandleStage.compareAndSet(
|
if (mIntentHandleStage.compareAndSet(
|
||||||
StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
|
StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
|
||||||
StartIntentHandleStage.HANDLED.ordinal())
|
StartIntentHandleStage.HANDLED.ordinal())
|
||||||
@@ -631,31 +678,42 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
||||||
target.getGroupId(), ImmutableList.of());
|
target.getGroupId(), ImmutableList.of());
|
||||||
addSourceToTargetSinks(targetSinks, target.getName());
|
addSourceToTargetSinks(targetSinks, target.getName());
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
// TODO: Add metric for auto add by intent
|
// TODO: Add metric for auto add by intent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Still mark intent as handled if early returned due to preconditions not met
|
||||||
mIntentHandleStage.compareAndSet(
|
mIntentHandleStage.compareAndSet(
|
||||||
StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
|
StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
|
||||||
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.");
|
||||||
|
// Clean up states before early return.
|
||||||
dismissLoadingStateDialogIfNeeded();
|
dismissLoadingStateDialogIfNeeded();
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showDialog(eventData);
|
// To avoid users advance to share then pair flow before the primary/active sinks
|
||||||
|
// successfully join the audio sharing, popup dialog till adding source complete for
|
||||||
|
// mSinksToWaitFor.
|
||||||
|
if (mSinksToWaitFor.isEmpty() && !mStoppingSharing.get()) {
|
||||||
|
showAudioSharingDialog(eventData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDialog(Pair<Integer, Object>[] eventData) {
|
private void showAudioSharingDialog(Pair<Integer, Object>[] eventData) {
|
||||||
|
if (!BluetoothUtils.isBroadcasting(mBtManager)) {
|
||||||
|
Log.d(TAG, "Skip showAudioSharingDialog, broadcast is stopped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
AudioSharingDialogFragment.DialogEventListener listener =
|
AudioSharingDialogFragment.DialogEventListener listener =
|
||||||
new AudioSharingDialogFragment.DialogEventListener() {
|
new AudioSharingDialogFragment.DialogEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPositiveClick() {
|
public void onPositiveClick() {
|
||||||
// Could go to other pages, dismiss the loading dialog.
|
// Could go to other pages, dismiss the loading dialog.
|
||||||
dismissLoadingStateDialogIfNeeded();
|
dismissLoadingStateDialogIfNeeded();
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -663,14 +721,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
||||||
item.getGroupId(), ImmutableList.of());
|
item.getGroupId(), ImmutableList.of());
|
||||||
addSourceToTargetSinks(targetSinks, item.getName());
|
addSourceToTargetSinks(targetSinks, item.getName());
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancelClick() {
|
public void onCancelClick() {
|
||||||
// Could go to other pages, dismiss the loading dialog.
|
// Could go to other pages, dismiss the loading dialog.
|
||||||
dismissLoadingStateDialogIfNeeded();
|
dismissLoadingStateDialogIfNeeded();
|
||||||
cleanUp();
|
cleanUpStatesForStartSharing();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
AudioSharingUtils.postOnMainThread(
|
AudioSharingUtils.postOnMainThread(
|
||||||
@@ -684,6 +742,36 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showRetryDialog() {
|
||||||
|
AudioSharingUtils.postOnMainThread(mContext,
|
||||||
|
() -> {
|
||||||
|
// Remove all opening dialogs before show retry dialog
|
||||||
|
dismissStaleDialogsOtherThanRetryDialog();
|
||||||
|
AudioSharingRetryDialogFragment.show(mFragment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void dismissStaleDialogsOtherThanRetryDialog() {
|
||||||
|
List<Fragment> fragments = new ArrayList<Fragment>();
|
||||||
|
try {
|
||||||
|
if (mFragment != null) {
|
||||||
|
fragments =
|
||||||
|
mFragment.getChildFragmentManager().getFragments();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Fail to dismiss stale dialogs: " + e.getMessage());
|
||||||
|
}
|
||||||
|
for (Fragment fragment : fragments) {
|
||||||
|
if (fragment != null && fragment instanceof DialogFragment
|
||||||
|
&& !(fragment instanceof AudioSharingRetryDialogFragment)
|
||||||
|
&& ((DialogFragment) fragment).getDialog() != null) {
|
||||||
|
Log.d(TAG, "Remove stale dialog = " + fragment.getTag());
|
||||||
|
((DialogFragment) fragment).dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate {
|
private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate {
|
||||||
@Override
|
@Override
|
||||||
public boolean onRequestSendAccessibilityEvent(
|
public boolean onRequestSendAccessibilityEvent(
|
||||||
@@ -742,10 +830,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
private void addSourceToTargetSinks(List<BluetoothDevice> targetActiveSinks,
|
private void addSourceToTargetSinks(List<BluetoothDevice> targetActiveSinks,
|
||||||
@NonNull String sinkName) {
|
@NonNull String sinkName) {
|
||||||
mSinksInAdding.addAll(targetActiveSinks);
|
mSinksInAdding.addAll(targetActiveSinks);
|
||||||
AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager);
|
|
||||||
// TODO: move to res once finalized
|
// TODO: move to res once finalized
|
||||||
String loadingMessage = "Sharing with " + sinkName + "...";
|
String loadingMessage = "Sharing with " + sinkName + "...";
|
||||||
showLoadingStateDialog(loadingMessage);
|
showLoadingStateDialog(loadingMessage);
|
||||||
|
AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showLoadingStateDialog(@NonNull String loadingMessage) {
|
private void showLoadingStateDialog(@NonNull String loadingMessage) {
|
||||||
@@ -760,7 +848,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUp() {
|
private void cleanUpStatesForStartSharing() {
|
||||||
mGroupedConnectedDevices.clear();
|
mGroupedConnectedDevices.clear();
|
||||||
mDeviceItemsForSharing.clear();
|
mDeviceItemsForSharing.clear();
|
||||||
}
|
}
|
||||||
|
@@ -146,6 +146,7 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
@Mock private VolumeControlProfile mVolumeControl;
|
@Mock private VolumeControlProfile mVolumeControl;
|
||||||
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
||||||
|
@Mock private BluetoothLeBroadcastReceiveState mState;
|
||||||
@Mock private CompoundButton mBtnView;
|
@Mock private CompoundButton mBtnView;
|
||||||
@Mock private CachedBluetoothDevice mCachedDevice1;
|
@Mock private CachedBluetoothDevice mCachedDevice1;
|
||||||
@Mock private CachedBluetoothDevice mCachedDevice2;
|
@Mock private CachedBluetoothDevice mCachedDevice2;
|
||||||
@@ -519,10 +520,9 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
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));
|
||||||
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
when(mState.getBroadcastId()).thenReturn(1);
|
||||||
when(state.getBroadcastId()).thenReturn(1);
|
|
||||||
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
|
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
|
||||||
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
|
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(mState));
|
||||||
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);
|
||||||
@@ -549,14 +549,77 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onPlaybackStarted_showJoinAudioSharingDialog() {
|
public void onPlaybackStarted_singleActiveDevice_showJoinAudioSharingDialog() {
|
||||||
|
FeatureFlagUtils.setEnabled(
|
||||||
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
|
when(mBtnView.isEnabled()).thenReturn(true);
|
||||||
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2));
|
||||||
|
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
||||||
|
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());
|
||||||
|
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(true);
|
||||||
|
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||||
|
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
|
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
|
||||||
|
|
||||||
|
when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L));
|
||||||
|
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice2, /* sourceId= */ 1,
|
||||||
|
mState);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||||
|
assertThat(childFragments)
|
||||||
|
.comparingElementsUsing(CLAZZNAME_EQUALS)
|
||||||
|
.containsExactly(AudioSharingDialogFragment.class.getName());
|
||||||
|
|
||||||
|
Pair<Integer, Object>[] eventData = new Pair[0];
|
||||||
|
for (Fragment fragment : childFragments) {
|
||||||
|
if (fragment instanceof AudioSharingDialogFragment) {
|
||||||
|
eventData = ((AudioSharingDialogFragment) fragment).getEventData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(eventData)
|
||||||
|
.asList()
|
||||||
|
.containsExactly(
|
||||||
|
Pair.create(
|
||||||
|
AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(),
|
||||||
|
SettingsEnums.AUDIO_SHARING_SETTINGS),
|
||||||
|
Pair.create(
|
||||||
|
AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(),
|
||||||
|
SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE),
|
||||||
|
Pair.create(
|
||||||
|
AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0),
|
||||||
|
Pair.create(
|
||||||
|
AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING
|
||||||
|
.ordinal(),
|
||||||
|
1),
|
||||||
|
Pair.create(
|
||||||
|
AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
|
||||||
|
.ordinal(),
|
||||||
|
0));
|
||||||
|
|
||||||
|
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPlaybackStarted_oneActiveOnConnected_showJoinAudioSharingDialog() {
|
||||||
FeatureFlagUtils.setEnabled(
|
FeatureFlagUtils.setEnabled(
|
||||||
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
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(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();
|
||||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
|
||||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
@@ -570,6 +633,8 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
String expectedMessage = "Starting audio stream...";
|
String expectedMessage = "Starting audio stream...";
|
||||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||||
|
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(true);
|
||||||
|
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
@@ -616,18 +681,19 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onPlaybackStarted_clickShareBtnOnDialog_addSource() {
|
public void onPlaybackStarted_oneActiveOnConnected_clickShareBtnOnDialog_addSource() {
|
||||||
FeatureFlagUtils.setEnabled(
|
FeatureFlagUtils.setEnabled(
|
||||||
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
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(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
||||||
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();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
verify(mBroadcast).startPrivateBroadcast();
|
verify(mBroadcast).startPrivateBroadcast();
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(true);
|
||||||
|
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
@@ -656,18 +722,19 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onPlaybackStarted_clickCancelBtnOnDialog_doNothing() {
|
public void onPlaybackStarted_oneActiveOnConnected_clickCancelBtnOnDialog_doNothing() {
|
||||||
FeatureFlagUtils.setEnabled(
|
FeatureFlagUtils.setEnabled(
|
||||||
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
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(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
||||||
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();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
verify(mBroadcast).startPrivateBroadcast();
|
verify(mBroadcast).startPrivateBroadcast();
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(true);
|
||||||
|
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
@@ -754,14 +821,50 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAssistantCallbacks_onSourceAddFailed_logAction() {
|
public void testAssistantCallbacks_onSourceAddFailed_twoDevices_showRetryAndLogAction() {
|
||||||
|
FeatureFlagUtils.setEnabled(
|
||||||
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
|
when(mBtnView.isEnabled()).thenReturn(true);
|
||||||
|
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);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
verify(mBroadcast).startPrivateBroadcast();
|
||||||
|
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(true);
|
||||||
|
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||||
|
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
|
||||||
|
|
||||||
|
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
View btnView = dialog.findViewById(R.id.positive_btn);
|
||||||
|
assertThat(btnView).isNotNull();
|
||||||
|
btnView.performClick();
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
|
||||||
|
assertThat(dialog.isShowing()).isFalse();
|
||||||
|
|
||||||
mController.mBroadcastAssistantCallback.onSourceAddFailed(
|
mController.mBroadcastAssistantCallback.onSourceAddFailed(
|
||||||
mDevice1, mMetadata, /* reason= */ 1);
|
mDevice1, mMetadata, /* reason= */ 1);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
// Loading state dialog shows sharing state for the user chosen sink.
|
||||||
|
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||||
|
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||||
|
AudioSharingRetryDialogFragment.class.getName());
|
||||||
verify(mFeatureFactory.metricsFeatureProvider)
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
.action(
|
.action(
|
||||||
mContext,
|
mContext,
|
||||||
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
|
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
|
||||||
SettingsEnums.AUDIO_SHARING_SETTINGS);
|
SettingsEnums.AUDIO_SHARING_SETTINGS);
|
||||||
|
|
||||||
|
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -772,10 +875,9 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||||
|
|
||||||
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L));
|
||||||
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
|
|
||||||
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1,
|
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1,
|
||||||
state);
|
mState);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||||
assertThat(childFragments).isEmpty();
|
assertThat(childFragments).isEmpty();
|
||||||
@@ -783,11 +885,9 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAssistantCallbacks_doNothing() {
|
public void testAssistantCallbacks_doNothing() {
|
||||||
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
|
||||||
|
|
||||||
// Do nothing
|
// Do nothing
|
||||||
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
|
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
mDevice1, /* sourceId= */ 1, state);
|
mDevice1, /* sourceId= */ 1, mState);
|
||||||
mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
|
mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
|
||||||
mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
|
mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
|
||||||
mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
|
mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
|
||||||
|
Reference in New Issue
Block a user