Merge "[Audiosharing] Refine share then pair flow" into main

This commit is contained in:
Yiyi Shen
2024-09-19 13:29:37 +00:00
committed by Android (Google) Code Review
11 changed files with 672 additions and 75 deletions

View File

@@ -18,32 +18,94 @@ package com.android.settings.bluetooth;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityStatsLogUtils; import com.android.settings.accessibility.AccessibilityStatsLogUtils;
import com.android.settings.connecteddevice.audiosharing.AudioSharingIncompatibleDialogFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils; import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.utils.ThreadUtils;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/** /**
* Abstract class for providing basic interaction for a list of Bluetooth devices in bluetooth * Abstract class for providing basic interaction for a list of Bluetooth devices in bluetooth
* device pairing detail page. * device pairing detail page.
*/ */
public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment { public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10);
private static final int AUTO_DISMISS_MESSAGE_ID = 1001;
protected boolean mInitialScanStarted; protected boolean mInitialScanStarted;
@VisibleForTesting @VisibleForTesting
protected BluetoothProgressCategory mAvailableDevicesCategory; protected BluetoothProgressCategory mAvailableDevicesCategory;
@Nullable
private volatile BluetoothDevice mJustBonded = null;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@Nullable
private AlertDialog mLoadingDialog = null;
@VisibleForTesting
boolean mShouldTriggerAudioSharingShareThenPairFlow = false;
private CopyOnWriteArrayList<BluetoothDevice> mDevicesWithMetadataChangedListener =
new CopyOnWriteArrayList<>();
// BluetoothDevicePreference updates the summary based on several callbacks, including
// BluetoothAdapter.OnMetadataChangedListener and BluetoothCallback. In most cases,
// metadata changes callback will be triggered before onDeviceBondStateChanged(BOND_BONDED).
// And before we hear onDeviceBondStateChanged(BOND_BONDED), the BluetoothDevice.getState() has
// already been BOND_BONDED. These event sequence will lead to: before we hear
// onDeviceBondStateChanged(BOND_BONDED), BluetoothDevicePreference's summary has already
// change from "Pairing..." to empty since it listens to metadata changes happens earlier.
//
// In share then pair flow, we have to wait on this page till the device is connected.
// The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and
// "Connecting..." To help users better understand the process, we listen to metadata change
// as well and show a loading dialog with "Connecting to ...." once BluetoothDevice.getState()
// gets to BOND_BONDED.
final BluetoothAdapter.OnMetadataChangedListener mMetadataListener =
new BluetoothAdapter.OnMetadataChangedListener() {
@Override
public void onMetadataChanged(@NonNull BluetoothDevice device, int key,
@Nullable byte[] value) {
Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key = " + key);
if (mShouldTriggerAudioSharingShareThenPairFlow && mLoadingDialog == null
&& device.getBondState() == BluetoothDevice.BOND_BONDED
&& mSelectedList.contains(device)) {
triggerAudioSharingShareThenPairFlow(device);
// Once device is bonded, remove the listener
removeOnMetadataChangedListener(device);
}
}
};
public BluetoothDevicePairingDetailBase() { public BluetoothDevicePairingDetailBase() {
super(DISALLOW_CONFIG_BLUETOOTH); super(DISALLOW_CONFIG_BLUETOOTH);
@@ -68,6 +130,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
return; return;
} }
updateBluetooth(); updateBluetooth();
mShouldTriggerAudioSharingShareThenPairFlow = shouldTriggerAudioSharingShareThenPairFlow();
} }
@Override @Override
@@ -80,6 +143,26 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
disableScanning(); disableScanning();
} }
@Override
public void onDestroy() {
super.onDestroy();
var unused = ThreadUtils.postOnBackgroundThread(() -> {
mDevicesWithMetadataChangedListener.forEach(
device -> {
try {
if (mBluetoothAdapter != null) {
mBluetoothAdapter.removeOnMetadataChangedListener(device,
mMetadataListener);
mDevicesWithMetadataChangedListener.remove(device);
}
} catch (IllegalArgumentException e) {
Log.d(getLogTag(), "Fail to remove listener: " + e);
}
});
mDevicesWithMetadataChangedListener.clear();
});
}
@Override @Override
public void onBluetoothStateChanged(int bluetoothState) { public void onBluetoothStateChanged(int bluetoothState) {
super.onBluetoothStateChanged(bluetoothState); super.onBluetoothStateChanged(bluetoothState);
@@ -92,16 +175,37 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
@Override @Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
if (bondState == BluetoothDevice.BOND_BONDED) { if (bondState == BluetoothDevice.BOND_BONDED) {
if (cachedDevice != null && mShouldTriggerAudioSharingShareThenPairFlow) {
BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) {
triggerAudioSharingShareThenPairFlow(device);
removeOnMetadataChangedListener(device);
return;
}
}
// If one device is connected(bonded), then close this fragment. // If one device is connected(bonded), then close this fragment.
finish(); finish();
return; return;
} else if (bondState == BluetoothDevice.BOND_BONDING) { } else if (bondState == BluetoothDevice.BOND_BONDING) {
if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) {
addOnMetadataChangedListener(device);
}
}
// Set the bond entry where binding process starts for logging hearing aid device info // Set the bond entry where binding process starts for logging hearing aid device info
final int pageId = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() final int pageId = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
.getAttribution(getActivity()); .getAttribution(getActivity());
final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry( final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
pageId); pageId);
HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice); HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
} else if (bondState == BluetoothDevice.BOND_NONE) {
if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) {
removeOnMetadataChangedListener(device);
}
}
} }
if (mSelectedDevice != null && cachedDevice != null) { if (mSelectedDevice != null && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice(); BluetoothDevice device = cachedDevice.getDevice();
@@ -114,7 +218,8 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
} }
@Override @Override
public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state, public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice, @ConnectionState int state,
int bluetoothProfile) { int bluetoothProfile) {
// This callback is used to handle the case that bonded device is connected in pairing list. // This callback is used to handle the case that bonded device is connected in pairing list.
// 1. If user selected multiple bonded devices in pairing list, after connected // 1. If user selected multiple bonded devices in pairing list, after connected
@@ -123,8 +228,22 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
// removed from paring list. // removed from paring list.
if (cachedDevice != null && cachedDevice.isConnected()) { if (cachedDevice != null && cachedDevice.isConnected()) {
final BluetoothDevice device = cachedDevice.getDevice(); final BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) { if (device != null
&& mSelectedList.contains(device)) {
if (!BluetoothUtils.isAudioSharingEnabled()) {
finish(); finish();
return;
}
if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& state == BluetoothAdapter.STATE_CONNECTED
&& device.equals(mJustBonded)
&& mShouldTriggerAudioSharingShareThenPairFlow) {
Log.d(getLogTag(),
"onProfileConnectionStateChanged, assistant profile connected");
dismissConnectingDialog();
mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
finishFragmentWithResultForAudioSharing(device);
}
} else { } else {
onDeviceDeleted(cachedDevice); onDeviceDeleted(cachedDevice);
} }
@@ -148,6 +267,8 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
disableScanning(); disableScanning();
super.onDevicePreferenceClick(btPreference); super.onDevicePreferenceClick(btPreference);
// Clean up the previous bond value
mJustBonded = null;
} }
@VisibleForTesting @VisibleForTesting
@@ -187,4 +308,122 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast, Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
} }
@VisibleForTesting
boolean shouldTriggerAudioSharingShareThenPairFlow() {
if (!BluetoothUtils.isAudioSharingEnabled()) return false;
Activity activity = getActivity();
Intent intent = activity == null ? null : activity.getIntent();
Bundle args =
intent == null ? null :
intent.getBundleExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
return args != null
&& args.getBoolean(EXTRA_PAIR_AND_JOIN_SHARING, false);
}
private void addOnMetadataChangedListener(@Nullable BluetoothDevice device) {
var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (mBluetoothAdapter != null && device != null
&& !mDevicesWithMetadataChangedListener.contains(device)) {
mBluetoothAdapter.addOnMetadataChangedListener(device, mExecutor,
mMetadataListener);
mDevicesWithMetadataChangedListener.add(device);
}
});
}
private void removeOnMetadataChangedListener(@Nullable BluetoothDevice device) {
var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (mBluetoothAdapter != null && device != null
&& mDevicesWithMetadataChangedListener.contains(device)) {
try {
mBluetoothAdapter.removeOnMetadataChangedListener(device, mMetadataListener);
mDevicesWithMetadataChangedListener.remove(device);
} catch (IllegalArgumentException e) {
Log.d(getLogTag(), "Fail to remove listener: " + e);
}
}
});
}
private void triggerAudioSharingShareThenPairFlow(
@NonNull BluetoothDevice device) {
var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (mJustBonded != null) {
Log.d(getLogTag(), "Skip triggerAudioSharingShareThenPairFlow, already done");
return;
}
mJustBonded = device;
// Show connecting device loading state
String aliasName = device.getAlias();
String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress()
: aliasName;
showConnectingDialog("Connecting to " + deviceName + "...");
// Wait for AUTO_DISMISS_TIME_THRESHOLD_MS and check if the paired device supports audio
// sharing.
if (!mHandler.hasMessages(AUTO_DISMISS_MESSAGE_ID)) {
mHandler.postDelayed(() ->
postOnMainThread(
() -> {
Log.d(getLogTag(), "Show incompatible dialog when timeout");
dismissConnectingDialog();
AudioSharingIncompatibleDialogFragment.show(this, deviceName,
() -> finish());
}), AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
}
});
}
private void finishFragmentWithResultForAudioSharing(@Nullable BluetoothDevice device) {
Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, device);
if (getActivity() != null) {
getActivity().setResult(Activity.RESULT_OK, resultIntent);
}
finish();
}
// TODO: use DialogFragment
private void showConnectingDialog(@NonNull String message) {
postOnMainThread(() -> {
if (mLoadingDialog != null) {
Log.d(getLogTag(), "showConnectingDialog, is already showing");
TextView textView = mLoadingDialog.findViewById(R.id.message);
if (textView != null && !message.equals(textView.getText().toString())) {
Log.d(getLogTag(), "showConnectingDialog, update message");
// TODO: use string res once finalized
textView.setText(message);
}
return;
}
Log.d(getLogTag(), "showConnectingDialog, show dialog");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = LayoutInflater.from(builder.getContext());
View customView = inflater.inflate(
R.layout.dialog_audio_sharing_loading_state, /* root= */
null);
TextView textView = customView.findViewById(R.id.message);
if (textView != null) {
// TODO: use string res once finalized
textView.setText(message);
}
AlertDialog dialog = builder.setView(customView).setCancelable(false).create();
dialog.setCanceledOnTouchOutside(false);
mLoadingDialog = dialog;
dialog.show();
});
}
private void dismissConnectingDialog() {
postOnMainThread(() -> {
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
});
}
private void postOnMainThread(@NonNull Runnable runnable) {
getContext().getMainExecutor().execute(runnable);
}
} }

View File

@@ -16,10 +16,18 @@
package com.android.settings.connecteddevice.audiosharing; package com.android.settings.connecteddevice.audiosharing;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
@@ -27,16 +35,21 @@ import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController; import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.utils.ThreadUtils;
public class AudioSharingDashboardFragment extends DashboardFragment public class AudioSharingDashboardFragment extends DashboardFragment
implements AudioSharingSwitchBarController.OnAudioSharingStateChangedListener { implements AudioSharingSwitchBarController.OnAudioSharingStateChangedListener {
private static final String TAG = "AudioSharingDashboardFrag"; private static final String TAG = "AudioSharingDashboardFrag";
public static final int SHARE_THEN_PAIR_REQUEST_CODE = 1002;
SettingsMainSwitchBar mMainSwitchBar; SettingsMainSwitchBar mMainSwitchBar;
private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController; private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController;
private AudioSharingCallAudioPreferenceController mAudioSharingCallAudioPreferenceController; private AudioSharingCallAudioPreferenceController mAudioSharingCallAudioPreferenceController;
private AudioSharingPlaySoundPreferenceController mAudioSharingPlaySoundPreferenceController; private AudioSharingPlaySoundPreferenceController mAudioSharingPlaySoundPreferenceController;
private AudioStreamsCategoryController mAudioStreamsCategoryController; private AudioStreamsCategoryController mAudioStreamsCategoryController;
private AudioSharingSwitchBarController mAudioSharingSwitchBarController;
public AudioSharingDashboardFragment() { public AudioSharingDashboardFragment() {
super(); super();
@@ -84,13 +97,38 @@ public class AudioSharingDashboardFragment extends DashboardFragment
final SettingsActivity activity = (SettingsActivity) getActivity(); final SettingsActivity activity = (SettingsActivity) getActivity();
mMainSwitchBar = activity.getSwitchBar(); mMainSwitchBar = activity.getSwitchBar();
mMainSwitchBar.setTitle(getText(R.string.audio_sharing_switch_title)); mMainSwitchBar.setTitle(getText(R.string.audio_sharing_switch_title));
AudioSharingSwitchBarController switchBarController = mAudioSharingSwitchBarController =
new AudioSharingSwitchBarController(activity, mMainSwitchBar, this); new AudioSharingSwitchBarController(activity, mMainSwitchBar, this);
switchBarController.init(this); mAudioSharingSwitchBarController.init(this);
getSettingsLifecycle().addObserver(switchBarController); getSettingsLifecycle().addObserver(mAudioSharingSwitchBarController);
mMainSwitchBar.show(); mMainSwitchBar.show();
} }
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (!BluetoothUtils.isAudioSharingEnabled()) return;
// In share then pair flow, after users be routed to pair new device page and successfully
// pair and connect an LEA headset, the pair fragment will be finished with RESULT_OK
// and EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, pass the BT device to switch bar controller,
// which is responsible for adding source to the device with loading indicator.
if (requestCode == SHARE_THEN_PAIR_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
BluetoothDevice btDevice =
data != null
? data.getParcelableExtra(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE,
BluetoothDevice.class)
: null;
Log.d(TAG, "onActivityResult: RESULT_OK with device = " + btDevice);
if (btDevice != null) {
var unused = ThreadUtils.postOnBackgroundThread(
() -> mAudioSharingSwitchBarController.handleAutoAddSourceAfterPair(
btDevice));
}
}
}
}
@Override @Override
public void onAudioSharingStateChanged() { public void onAudioSharingStateChanged() {
updateVisibilityForAttachedPreferences(); updateVisibilityForAttachedPreferences();
@@ -107,11 +145,13 @@ public class AudioSharingDashboardFragment extends DashboardFragment
AudioSharingDeviceVolumeGroupController volumeGroupController, AudioSharingDeviceVolumeGroupController volumeGroupController,
AudioSharingCallAudioPreferenceController callAudioController, AudioSharingCallAudioPreferenceController callAudioController,
AudioSharingPlaySoundPreferenceController playSoundController, AudioSharingPlaySoundPreferenceController playSoundController,
AudioStreamsCategoryController streamsCategoryController) { AudioStreamsCategoryController streamsCategoryController,
AudioSharingSwitchBarController switchBarController) {
mAudioSharingDeviceVolumeGroupController = volumeGroupController; mAudioSharingDeviceVolumeGroupController = volumeGroupController;
mAudioSharingCallAudioPreferenceController = callAudioController; mAudioSharingCallAudioPreferenceController = callAudioController;
mAudioSharingPlaySoundPreferenceController = playSoundController; mAudioSharingPlaySoundPreferenceController = playSoundController;
mAudioStreamsCategoryController = streamsCategoryController; mAudioStreamsCategoryController = streamsCategoryController;
mAudioSharingSwitchBarController = switchBarController;
} }
private void updateVisibilityForAttachedPreferences() { private void updateVisibilityForAttachedPreferences() {

View File

@@ -16,6 +16,9 @@
package com.android.settings.connecteddevice.audiosharing; package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingDashboardFragment.SHARE_THEN_PAIR_REQUEST_CODE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;
import android.app.Dialog; import android.app.Dialog;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.os.Bundle; import android.os.Bundle;
@@ -48,19 +51,23 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
// The host creates an instance of this dialog fragment must implement this interface to receive // The host creates an instance of this dialog fragment must implement this interface to receive
// event callbacks. // event callbacks.
public interface DialogEventListener { public interface DialogEventListener {
/** Called when users click the positive button in the dialog. */
default void onPositiveClick() {}
/** /**
* Called when users click the device item for sharing in the dialog. * Called when users click the device item for sharing in the dialog.
* *
* @param item The device item clicked. * @param item The device item clicked.
*/ */
void onItemClick(AudioSharingDeviceItem item); default void onItemClick(@NonNull AudioSharingDeviceItem item) {}
/** Called when users click the cancel button in the dialog. */ /** Called when users click the cancel button in the dialog. */
void onCancelClick(); default void onCancelClick() {}
} }
@Nullable private static DialogEventListener sListener; @Nullable private static DialogEventListener sListener;
private static Pair<Integer, Object>[] sEventData = new Pair[0]; private static Pair<Integer, Object>[] sEventData = new Pair[0];
@Nullable private static Fragment sHost;
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
@@ -88,6 +95,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
Log.d(TAG, "Fail to show dialog: " + e.getMessage()); Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return; return;
} }
sHost = host;
sListener = listener; sListener = listener;
sEventData = eventData; sEventData = eventData;
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
@@ -136,23 +144,33 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
.setCustomPositiveButton( .setCustomPositiveButton(
R.string.audio_sharing_pair_button_label, R.string.audio_sharing_pair_button_label,
v -> { v -> {
dismiss(); if (sListener != null) {
new SubSettingLauncher(getContext()) sListener.onPositiveClick();
.setDestination(BluetoothPairingDetail.class.getName()) }
.setSourceMetricsCategory(getMetricsCategory())
.launch();
logDialogPositiveBtnClick(); logDialogPositiveBtnClick();
dismiss();
Bundle args = new Bundle();
args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, true);
SubSettingLauncher launcher =
new SubSettingLauncher(getContext())
.setDestination(
BluetoothPairingDetail.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args);
if (sHost != null) {
launcher.setResultListener(sHost, SHARE_THEN_PAIR_REQUEST_CODE);
}
launcher.launch();
}) })
.setCustomNegativeButton( .setCustomNegativeButton(
R.string.audio_sharing_qrcode_button_label, R.string.audio_sharing_qrcode_button_label,
v -> { v -> {
dismiss(); onCancelClick();
new SubSettingLauncher(getContext()) new SubSettingLauncher(getContext())
.setTitleRes(R.string.audio_streams_qr_code_page_title) .setTitleRes(R.string.audio_streams_qr_code_page_title)
.setDestination(AudioStreamsQrCodeFragment.class.getName()) .setDestination(AudioStreamsQrCodeFragment.class.getName())
.setSourceMetricsCategory(getMetricsCategory()) .setSourceMetricsCategory(getMetricsCategory())
.launch(); .launch();
logDialogNegativeBtnClick();
}); });
} else if (deviceItems.size() == 1) { } else if (deviceItems.size() == 1) {
AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems); AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems);
@@ -166,8 +184,8 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
v -> { v -> {
if (sListener != null) { if (sListener != null) {
sListener.onItemClick(deviceItem); sListener.onItemClick(deviceItem);
logDialogPositiveBtnClick();
} }
logDialogPositiveBtnClick();
dismiss(); dismiss();
}) })
.setCustomNegativeButton( .setCustomNegativeButton(
@@ -182,8 +200,8 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
(AudioSharingDeviceItem item) -> { (AudioSharingDeviceItem item) -> {
if (sListener != null) { if (sListener != null) {
sListener.onItemClick(item); sListener.onItemClick(item);
logDialogPositiveBtnClick();
} }
logDialogPositiveBtnClick();
dismiss(); dismiss();
}, },
AudioSharingDeviceAdapter.ActionType.SHARE)) AudioSharingDeviceAdapter.ActionType.SHARE))
@@ -196,8 +214,8 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
private void onCancelClick() { private void onCancelClick() {
if (sListener != null) { if (sListener != null) {
sListener.onCancelClick(); sListener.onCancelClick();
logDialogNegativeBtnClick();
} }
logDialogNegativeBtnClick();
dismiss(); dismiss();
} }

View File

@@ -29,7 +29,6 @@ import androidx.fragment.app.FragmentManager;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
public class AudioSharingIncompatibleDialogFragment extends InstrumentedDialogFragment { public class AudioSharingIncompatibleDialogFragment extends InstrumentedDialogFragment {
private static final String TAG = "AudioSharingIncompatDlg"; private static final String TAG = "AudioSharingIncompatDlg";
@@ -59,7 +58,7 @@ public class AudioSharingIncompatibleDialogFragment extends InstrumentedDialogFr
* *
* @param host The Fragment this dialog will be hosted. * @param host The Fragment this dialog will be hosted.
*/ */
public static void show(@Nullable Fragment host, @NonNull CachedBluetoothDevice cachedDevice, public static void show(@Nullable Fragment host, @NonNull String deviceName,
@NonNull DialogEventListener listener) { @NonNull DialogEventListener listener) {
if (host == null || !BluetoothUtils.isAudioSharingEnabled()) return; if (host == null || !BluetoothUtils.isAudioSharingEnabled()) return;
final FragmentManager manager; final FragmentManager manager;
@@ -77,7 +76,7 @@ public class AudioSharingIncompatibleDialogFragment extends InstrumentedDialogFr
} }
Log.d(TAG, "Show up the incompatible device dialog."); Log.d(TAG, "Show up the incompatible device dialog.");
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putString(BUNDLE_KEY_DEVICE_NAME, cachedDevice.getName()); bundle.putString(BUNDLE_KEY_DEVICE_NAME, deviceName);
AudioSharingIncompatibleDialogFragment dialogFrag = AudioSharingIncompatibleDialogFragment dialogFrag =
new AudioSharingIncompatibleDialogFragment(); new AudioSharingIncompatibleDialogFragment();
dialogFrag.setArguments(bundle); dialogFrag.setArguments(bundle);

View File

@@ -115,10 +115,6 @@ 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(() -> {
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 +128,26 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr
return dialog; return dialog;
} }
@Override
public void onStart() {
super.onStart();
if (mHandler != null) {
Log.d(TAG, "onStart, postTimeOut for auto dismiss");
mHandler.postDelayed(() -> {
Log.d(TAG, "Try to auto dismiss dialog after timeout");
try {
Dialog dialog = getDialog();
if (dialog != null) {
Log.d(TAG, "Dialog is not null, dismiss");
dismissAllowingStateLoss();
}
} catch (IllegalStateException e) {
Log.d(TAG, "Fail to dismiss: " + e.getMessage());
}
}, AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
}
}
@Override @Override
public void onDismiss(@NonNull DialogInterface dialog) { public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog); super.onDismiss(dialog);

View File

@@ -56,6 +56,7 @@ import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -464,6 +465,18 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
this.mFragment = fragment; this.mFragment = fragment;
} }
/** Handle auto add source to the just paired device in share then pair flow. */
public void handleAutoAddSourceAfterPair(@NonNull BluetoothDevice device) {
CachedBluetoothDeviceManager deviceManager =
mBtManager == null ? null : mBtManager.getCachedDeviceManager();
CachedBluetoothDevice cachedDevice =
deviceManager == null ? null : deviceManager.findDevice(device);
if (cachedDevice != null) {
Log.d(TAG, "handleAutoAddSourceAfterPair, device = " + device.getAnonymizedAddress());
addSourceToTargetSinks(ImmutableList.of(device), cachedDevice.getName());
}
}
/** Test only: set callback registration status in tests. */ /** Test only: set callback registration status in tests. */
@VisibleForTesting @VisibleForTesting
void setCallbacksRegistered(boolean registered) { void setCallbacksRegistered(boolean registered) {
@@ -638,6 +651,13 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
private void showDialog(Pair<Integer, Object>[] eventData) { private void showDialog(Pair<Integer, Object>[] eventData) {
AudioSharingDialogFragment.DialogEventListener listener = AudioSharingDialogFragment.DialogEventListener listener =
new AudioSharingDialogFragment.DialogEventListener() { new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onPositiveClick() {
// Could go to other pages, dismiss the loading dialog.
dismissLoadingStateDialogIfNeeded();
cleanUp();
}
@Override @Override
public void onItemClick(@NonNull AudioSharingDeviceItem item) { public void onItemClick(@NonNull AudioSharingDeviceItem item) {
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault( List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
@@ -648,6 +668,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
@Override @Override
public void onCancelClick() { public void onCancelClick() {
// Could go to other pages, dismiss the loading dialog.
dismissLoadingStateDialogIfNeeded(); dismissLoadingStateDialogIfNeeded();
cleanUp(); cleanUp();
} }

View File

@@ -16,52 +16,80 @@
package com.android.settings.bluetooth; package com.android.settings.bluetooth;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair; import android.util.Pair;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.flags.Flags;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers; import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow; import org.robolectric.shadow.api.Shadow;
import java.util.concurrent.Executor;
/** Tests for {@link BluetoothDevicePairingDetailBase}. */ /** Tests for {@link BluetoothDevicePairingDetailBase}. */
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = { @Config(shadows = {
ShadowBluetoothAdapter.class, ShadowBluetoothAdapter.class,
ShadowAlertDialogCompat.class,
com.android.settings.testutils.shadow.ShadowFragment.class, com.android.settings.testutils.shadow.ShadowFragment.class,
}) })
public class BluetoothDevicePairingDetailBaseTest { public class BluetoothDevicePairingDetailBaseTest {
@Rule @Rule
public final MockitoRule mockito = MockitoJUnit.rule(); public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
public static final String KEY_DEVICE_LIST_GROUP = "test_key"; public static final String KEY_DEVICE_LIST_GROUP = "test_key";
@@ -86,8 +114,12 @@ public class BluetoothDevicePairingDetailBaseTest {
@Before @Before
public void setUp() { public void setUp() {
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext)); mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device"); final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs); when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
@@ -155,8 +187,88 @@ public class BluetoothDevicePairingDetailBaseTest {
verify(mFragment).showBluetoothTurnedOnToast(); verify(mFragment).showBluetoothTurnedOnToast();
} }
@Test
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingDisabled_finish() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
verify(mFragment).finish();
}
@Test
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingEnabled_handle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
TextView message = dialog.findViewById(R.id.message);
assertThat(message).isNotNull();
// TODO: use stringr res once finalized
assertThat(message.getText().toString()).isEqualTo(
"Connecting to " + TEST_DEVICE_ADDRESS + "...");
verify(mFragment, never()).finish();
}
@Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingDisabled_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class),
any(Executor.class), any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingEnabled_addListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice),
any(Executor.class),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingDisabled_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(
any(BluetoothDevice.class), any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingEnabled_removeListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
verify(mBluetoothAdapter).removeOnMetadataChangedListener(eq(mBluetoothDevice),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test @Test
public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() { public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -165,13 +277,43 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device); when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED); BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
verify(mFragment).finish(); verify(mFragment).finish();
} }
@Test
public void
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_pairAndJoinSharing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle();
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
shadowOf(Looper.getMainLooper()).idle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture());
Intent intent = captor.getValue();
BluetoothDevice btDevice =
intent != null
? intent.getParcelableExtra(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE,
BluetoothDevice.class)
: null;
assertThat(btDevice).isNotNull();
assertThat(btDevice).isEqualTo(mBluetoothDevice);
verify(mFragment).finish();
}
@Test @Test
public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() { public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -179,13 +321,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED); BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
// not crash // not crash
} }
@Test @Test
public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() { public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -194,13 +337,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device); when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED); BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.A2DP);
// not crash // not crash
} }
@Test @Test
public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() { public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevicePreference preference = final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
true, BluetoothDevicePreference.SortType.TYPE_FIFO); true, BluetoothDevicePreference.SortType.TYPE_FIFO);
@@ -211,13 +355,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device); when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED); BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0); assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
} }
@Test @Test
public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() { public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
final BluetoothDevicePreference preference = final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
@@ -233,12 +378,26 @@ public class BluetoothDevicePairingDetailBaseTest {
when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B); when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B); when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP, mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothAdapter.STATE_CONNECTED,
BluetoothAdapter.STATE_CONNECTED); BluetoothProfile.A2DP);
// not crash // not crash
} }
private void setUpFragmentWithPairAndJoinSharingIntent(boolean enablePairAndJoinSharing) {
Bundle args = new Bundle();
args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, enablePairAndJoinSharing);
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
doReturn(intent).when(activity).getIntent();
doReturn(activity).when(mFragment).getActivity();
FragmentManager fragmentManager = mock(FragmentManager.class);
doReturn(fragmentManager).when(mFragment).getFragmentManager();
mFragment.mShouldTriggerAudioSharingShareThenPairFlow =
mFragment.shouldTriggerAudioSharingShareThenPairFlow();
}
private static class TestBluetoothDevicePairingDetailBase extends private static class TestBluetoothDevicePairingDetailBase extends
BluetoothDevicePairingDetailBase { BluetoothDevicePairingDetailBase {

View File

@@ -16,16 +16,29 @@
package com.android.settings.connecteddevice.audiosharing; package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingDashboardFragment.SHARE_THEN_PAIR_REQUEST_CODE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View; import android.view.View;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@@ -33,24 +46,29 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController; import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.flags.Flags;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowFragment.class}) @Config(shadows = {ShadowFragment.class, ShadowBluetoothAdapter.class})
public class AudioSharingDashboardFragmentTest { public class AudioSharingDashboardFragmentTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private SettingsActivity mActivity; @Mock private SettingsActivity mActivity;
@Mock private SettingsMainSwitchBar mSwitchBar; @Mock private SettingsMainSwitchBar mSwitchBar;
@@ -59,11 +77,19 @@ public class AudioSharingDashboardFragmentTest {
@Mock private AudioSharingCallAudioPreferenceController mCallAudioController; @Mock private AudioSharingCallAudioPreferenceController mCallAudioController;
@Mock private AudioSharingPlaySoundPreferenceController mPlaySoundController; @Mock private AudioSharingPlaySoundPreferenceController mPlaySoundController;
@Mock private AudioStreamsCategoryController mStreamsCategoryController; @Mock private AudioStreamsCategoryController mStreamsCategoryController;
@Mock private AudioSharingSwitchBarController mSwitchBarController;
private final Context mContext = ApplicationProvider.getApplicationContext(); private final Context mContext = ApplicationProvider.getApplicationContext();
private AudioSharingDashboardFragment mFragment; private AudioSharingDashboardFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before @Before
public void setUp() { public void setUp() {
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mSwitchBar.getRootView()).thenReturn(mView); when(mSwitchBar.getRootView()).thenReturn(mView);
mFragment = new AudioSharingDashboardFragment(); mFragment = new AudioSharingDashboardFragment();
} }
@@ -100,13 +126,73 @@ public class AudioSharingDashboardFragmentTest {
verify(mSwitchBar).show(); verify(mSwitchBar).show();
} }
@Test
public void onActivityResult_shareThenPairWithBadCode_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
BluetoothDevice device = Mockito.mock(BluetoothDevice.class);
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, device);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_CANCELED, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController, never()).handleAutoAddSourceAfterPair(device);
}
@Test
public void onActivityResult_shareThenPairWithNoDevice_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, null);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_CANCELED, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController, never()).handleAutoAddSourceAfterPair(any());
}
@Test
public void onActivityResult_shareThenPairWithDevice_handleAutoAddSource() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
BluetoothDevice device = Mockito.mock(BluetoothDevice.class);
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, device);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_OK, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController).handleAutoAddSourceAfterPair(device);
}
@Test @Test
public void onAudioSharingStateChanged_updateVisibilityForControllers() { public void onAudioSharingStateChanged_updateVisibilityForControllers() {
mFragment.setControllers( mFragment.setControllers(
mVolumeGroupController, mVolumeGroupController,
mCallAudioController, mCallAudioController,
mPlaySoundController, mPlaySoundController,
mStreamsCategoryController); mStreamsCategoryController,
mSwitchBarController);
mFragment.onAudioSharingStateChanged(); mFragment.onAudioSharingStateChanged();
verify(mVolumeGroupController).updateVisibility(); verify(mVolumeGroupController).updateVisibility();
verify(mCallAudioController).updateVisibility(); verify(mCallAudioController).updateVisibility();
@@ -120,7 +206,8 @@ public class AudioSharingDashboardFragmentTest {
mVolumeGroupController, mVolumeGroupController,
mCallAudioController, mCallAudioController,
mPlaySoundController, mPlaySoundController,
mStreamsCategoryController); mStreamsCategoryController,
mSwitchBarController);
mFragment.onAudioSharingProfilesConnected(); mFragment.onAudioSharingProfilesConnected();
verify(mVolumeGroupController).onAudioSharingProfilesConnected(); verify(mVolumeGroupController).onAudioSharingProfilesConnected();
} }

View File

@@ -34,6 +34,7 @@ import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
@@ -82,11 +83,6 @@ public class AudioSharingDialogFragmentTest {
new AudioSharingDeviceItem(TEST_DEVICE_NAME3, /* groupId= */ 3, /* isActive= */ false); new AudioSharingDeviceItem(TEST_DEVICE_NAME3, /* groupId= */ 3, /* isActive= */ false);
private static final AudioSharingDialogFragment.DialogEventListener EMPTY_EVENT_LISTENER = private static final AudioSharingDialogFragment.DialogEventListener EMPTY_EVENT_LISTENER =
new AudioSharingDialogFragment.DialogEventListener() { new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onItemClick(AudioSharingDeviceItem item) {}
@Override
public void onCancelClick() {}
}; };
private static final Pair<Integer, Object> TEST_EVENT_DATA = Pair.create(1, 1); private static final Pair<Integer, Object> TEST_EVENT_DATA = Pair.create(1, 1);
private static final Pair<Integer, Object>[] TEST_EVENT_DATA_LIST = private static final Pair<Integer, Object>[] TEST_EVENT_DATA_LIST =
@@ -176,8 +172,17 @@ public class AudioSharingDialogFragmentTest {
@Test @Test
public void onCreateDialog_noExtraConnectedDevice_pairNewDevice() { public void onCreateDialog_noExtraConnectedDevice_pairNewDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isPairBtnClicked = new AtomicBoolean(false);
AudioSharingDialogFragment.show( AudioSharingDialogFragment.show(
mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); mParent,
new ArrayList<>(),
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onPositiveClick() {
isPairBtnClicked.set(true);
}
},
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull(); assertThat(dialog).isNotNull();
@@ -191,14 +196,24 @@ public class AudioSharingDialogFragmentTest {
any(Context.class), any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED),
eq(TEST_EVENT_DATA)); eq(TEST_EVENT_DATA));
assertThat(isPairBtnClicked.get()).isTrue();
assertThat(dialog.isShowing()).isFalse(); assertThat(dialog.isShowing()).isFalse();
} }
@Test @Test
public void onCreateDialog_noExtraConnectedDevice_showQRCode() { public void onCreateDialog_noExtraConnectedDevice_showQRCode() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isQrCodeBtnClicked = new AtomicBoolean(false);
AudioSharingDialogFragment.show( AudioSharingDialogFragment.show(
mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); mParent,
new ArrayList<>(),
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onCancelClick() {
isQrCodeBtnClicked.set(true);
}
},
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull(); assertThat(dialog).isNotNull();
@@ -212,6 +227,7 @@ public class AudioSharingDialogFragmentTest {
any(Context.class), any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED),
eq(TEST_EVENT_DATA)); eq(TEST_EVENT_DATA));
assertThat(isQrCodeBtnClicked.get()).isTrue();
assertThat(dialog.isShowing()).isFalse(); assertThat(dialog.isShowing()).isFalse();
} }
@@ -286,12 +302,9 @@ public class AudioSharingDialogFragmentTest {
list, list,
new AudioSharingDialogFragment.DialogEventListener() { new AudioSharingDialogFragment.DialogEventListener() {
@Override @Override
public void onItemClick(AudioSharingDeviceItem item) { public void onItemClick(@NonNull AudioSharingDeviceItem item) {
isShareBtnClicked.set(true); isShareBtnClicked.set(true);
} }
@Override
public void onCancelClick() {}
}, },
TEST_EVENT_DATA_LIST); TEST_EVENT_DATA_LIST);
shadowMainLooper().idle(); shadowMainLooper().idle();
@@ -359,9 +372,6 @@ public class AudioSharingDialogFragmentTest {
mParent, mParent,
list, list,
new AudioSharingDialogFragment.DialogEventListener() { new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onItemClick(AudioSharingDeviceItem item) {}
@Override @Override
public void onCancelClick() { public void onCancelClick() {
isCancelBtnClicked.set(true); isCancelBtnClicked.set(true);

View File

@@ -18,7 +18,6 @@ package com.android.settings.connecteddevice.audiosharing;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
@@ -34,7 +33,6 @@ import androidx.fragment.app.FragmentActivity;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.flags.Flags; import com.android.settingslib.flags.Flags;
import org.junit.After; import org.junit.After;
@@ -42,7 +40,6 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -62,7 +59,6 @@ public class AudioSharingIncompatibleDialogFragmentTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
private Fragment mParent; private Fragment mParent;
private AudioSharingIncompatibleDialogFragment mFragment; private AudioSharingIncompatibleDialogFragment mFragment;
@@ -76,7 +72,6 @@ public class AudioSharingIncompatibleDialogFragmentTest {
BluetoothStatusCodes.FEATURE_SUPPORTED); BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED); BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME);
mFragment = new AudioSharingIncompatibleDialogFragment(); mFragment = new AudioSharingIncompatibleDialogFragment();
mParent = new Fragment(); mParent = new Fragment();
FragmentController.setupFragment(mParent, FragmentActivity.class, /* containerViewId= */ FragmentController.setupFragment(mParent, FragmentActivity.class, /* containerViewId= */
@@ -97,7 +92,7 @@ public class AudioSharingIncompatibleDialogFragmentTest {
@Test @Test
public void onCreateDialog_flagOff_dialogNotExist() { public void onCreateDialog_flagOff_dialogNotExist() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(mParent, mCachedBluetoothDevice, AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER); EMPTY_EVENT_LISTENER);
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -107,7 +102,7 @@ public class AudioSharingIncompatibleDialogFragmentTest {
@Test @Test
public void onCreateDialog_unattachedFragment_dialogNotExist() { public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(new Fragment(), mCachedBluetoothDevice, AudioSharingIncompatibleDialogFragment.show(new Fragment(), TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER); EMPTY_EVENT_LISTENER);
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -117,7 +112,7 @@ public class AudioSharingIncompatibleDialogFragmentTest {
@Test @Test
public void onCreateDialog_flagOn_showDialog() { public void onCreateDialog_flagOn_showDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(mParent, mCachedBluetoothDevice, AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER); EMPTY_EVENT_LISTENER);
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -134,7 +129,7 @@ public class AudioSharingIncompatibleDialogFragmentTest {
public void onCreateDialog_clickBtn_callbackTriggered() { public void onCreateDialog_clickBtn_callbackTriggered() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isBtnClicked = new AtomicBoolean(false); AtomicBoolean isBtnClicked = new AtomicBoolean(false);
AudioSharingIncompatibleDialogFragment.show(mParent, mCachedBluetoothDevice, AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
() -> isBtnClicked.set(true)); () -> isBtnClicked.set(true));
shadowMainLooper().idle(); shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();

View File

@@ -934,6 +934,19 @@ public class AudioSharingSwitchBarControllerTest {
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
} }
@Test
public void handleAutoAddSourceAfterPair() {
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.handleAutoAddSourceAfterPair(mDevice1);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingLoadingStateDialogFragment.class.getName());
}
private Fragment setUpFragmentWithStartSharingIntent() { 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);