Merge "[Audiosharing] Refine share then pair flow" into main
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