[Audiosharing] Refine share then pair flow
Currently when there is one active LEA headset and users toggle on the audio sharing, a dialog will pop up to ask users to pair new headset and share audio with it. After user click pair new device button on the dialog: 1. Route users to pair new device page. 2. If users pair an LEA headset, finish the pair new device page and auto add source to the headset with loading indicators on audio sharing page. 3. If users pair a classic headset, wait for timeout, pop up dialog saying the paired headset is not compatible for audio sharing and finish the pair new device page. Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Bug: 331892035 Change-Id: Ifb9579db0ef57d3a379cb5d17c66a604d1396bb4
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user