Merge changes I6322ccbb,Idc96c23d into main
* changes: [Audiosharing] Add tests. [Audiosharing] Add logging 3.
This commit is contained in:
@@ -22,6 +22,7 @@ import android.content.Context;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
@@ -78,12 +79,10 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler {
|
|||||||
ThreadUtils.postOnMainThread(
|
ThreadUtils.postOnMainThread(
|
||||||
() -> {
|
() -> {
|
||||||
if (controller.getFragment() != null) {
|
if (controller.getFragment() != null) {
|
||||||
AudioStreamsDialogFragment.show(
|
showBroadcastUnavailableNoRetryDialog(
|
||||||
controller.getFragment(),
|
controller.getFragment(),
|
||||||
getBroadcastUnavailableNoRetryDialog(
|
preference.getContext(),
|
||||||
preference.getContext(),
|
AudioStreamsHelper.getBroadcastName(metadata));
|
||||||
AudioStreamsHelper.getBroadcastName(
|
|
||||||
metadata)));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -103,13 +102,21 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler {
|
|||||||
return AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE;
|
return AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableNoRetryDialog(
|
private void showBroadcastUnavailableNoRetryDialog(
|
||||||
Context context, String broadcastName) {
|
Fragment fragment, Context context, String broadcastName) {
|
||||||
return new AudioStreamsDialogFragment.DialogBuilder(context)
|
var broadcastUnavailableNoRetryDialog =
|
||||||
.setTitle(context.getString(R.string.audio_streams_dialog_stream_is_not_available))
|
new AudioStreamsDialogFragment.DialogBuilder(context)
|
||||||
.setSubTitle1(broadcastName)
|
.setTitle(
|
||||||
.setSubTitle2(context.getString(R.string.audio_streams_is_not_playing))
|
context.getString(
|
||||||
.setRightButtonText(context.getString(R.string.audio_streams_dialog_close))
|
R.string.audio_streams_dialog_stream_is_not_available))
|
||||||
.setRightButtonOnClickListener(AlertDialog::dismiss);
|
.setSubTitle1(broadcastName)
|
||||||
|
.setSubTitle2(context.getString(R.string.audio_streams_is_not_playing))
|
||||||
|
.setRightButtonText(context.getString(R.string.audio_streams_dialog_close))
|
||||||
|
.setRightButtonOnClickListener(AlertDialog::dismiss);
|
||||||
|
|
||||||
|
AudioStreamsDialogFragment.show(
|
||||||
|
fragment,
|
||||||
|
broadcastUnavailableNoRetryDialog,
|
||||||
|
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_JOIN_FAILED_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,101 +21,84 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.bluetooth.Utils;
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
|
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
|
|
||||||
public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||||
private static final String TAG = "AudioStreamConfirmDialog";
|
private static final String TAG = "AudioStreamConfirmDialog";
|
||||||
private static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device;
|
private static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device;
|
||||||
@Nullable private LocalBluetoothManager mLocalBluetoothManager;
|
private Context mContext;
|
||||||
@Nullable private LocalBluetoothProfileManager mProfileManager;
|
|
||||||
@Nullable private Activity mActivity;
|
@Nullable private Activity mActivity;
|
||||||
@Nullable private String mBroadcastMetadataStr;
|
|
||||||
@Nullable private BluetoothLeBroadcastMetadata mBroadcastMetadata;
|
@Nullable private BluetoothLeBroadcastMetadata mBroadcastMetadata;
|
||||||
private boolean mIsRequestValid = false;
|
@Nullable private BluetoothDevice mConnectedDevice;
|
||||||
|
private int mAudioStreamConfirmDialogId = SettingsEnums.PAGE_UNKNOWN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mActivity = getActivity();
|
||||||
|
if (mActivity == null) {
|
||||||
|
Log.w(TAG, "onAttach() mActivity is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = mActivity.getIntent();
|
||||||
|
mBroadcastMetadata = getMetadata(intent);
|
||||||
|
mConnectedDevice = getConnectedDevice();
|
||||||
|
mAudioStreamConfirmDialogId =
|
||||||
|
getDialogId(mBroadcastMetadata != null, mConnectedDevice != null);
|
||||||
|
super.onAttach(context);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setShowsDialog(true);
|
setShowsDialog(true);
|
||||||
mActivity = getActivity();
|
|
||||||
if (mActivity == null) {
|
|
||||||
Log.w(TAG, "onCreate() mActivity is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mLocalBluetoothManager = Utils.getLocalBluetoothManager(mActivity);
|
|
||||||
mProfileManager =
|
|
||||||
mLocalBluetoothManager == null ? null : mLocalBluetoothManager.getProfileManager();
|
|
||||||
mBroadcastMetadataStr = mActivity.getIntent().getStringExtra(KEY_BROADCAST_METADATA);
|
|
||||||
if (Strings.isNullOrEmpty(mBroadcastMetadataStr)) {
|
|
||||||
Log.w(TAG, "onCreate() mBroadcastMetadataStr is null or empty!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mBroadcastMetadata =
|
|
||||||
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(
|
|
||||||
mBroadcastMetadataStr);
|
|
||||||
if (mBroadcastMetadata == null) {
|
|
||||||
Log.w(TAG, "onCreate() mBroadcastMetadata is null!");
|
|
||||||
} else {
|
|
||||||
mIsRequestValid = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
if (!AudioSharingUtils.isFeatureEnabled()) {
|
return switch (mAudioStreamConfirmDialogId) {
|
||||||
return getUnsupporteDialog();
|
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED ->
|
||||||
}
|
getUnsupportedDialog();
|
||||||
if (AudioSharingUtils.isAudioSharingProfileReady(mProfileManager)) {
|
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE -> getNoLeDeviceDialog();
|
||||||
CachedBluetoothDevice connectedLeDevice =
|
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN -> getConfirmDialog();
|
||||||
AudioStreamsHelper.getCachedBluetoothDeviceInSharingOrLeConnected(
|
default -> getErrorDialog();
|
||||||
mLocalBluetoothManager)
|
};
|
||||||
.orElse(null);
|
|
||||||
if (connectedLeDevice == null) {
|
|
||||||
return getNoLeDeviceDialog();
|
|
||||||
}
|
|
||||||
String deviceName = connectedLeDevice.getName();
|
|
||||||
return mIsRequestValid ? getConfirmDialog(deviceName) : getErrorDialog(deviceName);
|
|
||||||
}
|
|
||||||
Log.d(TAG, "onCreateDialog() : profile not ready!");
|
|
||||||
String defaultDeviceName =
|
|
||||||
mActivity != null ? mActivity.getString(DEFAULT_DEVICE_NAME) : "";
|
|
||||||
return mIsRequestValid
|
|
||||||
? getConfirmDialog(defaultDeviceName)
|
|
||||||
: getErrorDialog(defaultDeviceName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
// TODO(chelseahao): update metrics id
|
return mAudioStreamConfirmDialogId;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dialog getConfirmDialog(String name) {
|
private Dialog getConfirmDialog() {
|
||||||
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
||||||
.setTitle(getString(R.string.audio_streams_dialog_listen_to_audio_stream))
|
.setTitle(getString(R.string.audio_streams_dialog_listen_to_audio_stream))
|
||||||
.setSubTitle1(
|
.setSubTitle1(
|
||||||
mBroadcastMetadata != null
|
mBroadcastMetadata != null
|
||||||
? AudioStreamsHelper.getBroadcastName(mBroadcastMetadata)
|
? AudioStreamsHelper.getBroadcastName(mBroadcastMetadata)
|
||||||
: "")
|
: "")
|
||||||
.setSubTitle2(getString(R.string.audio_streams_dialog_control_volume, name))
|
.setSubTitle2(
|
||||||
|
getString(
|
||||||
|
R.string.audio_streams_dialog_control_volume,
|
||||||
|
getConnectedDeviceName()))
|
||||||
.setLeftButtonText(getString(com.android.settings.R.string.cancel))
|
.setLeftButtonText(getString(com.android.settings.R.string.cancel))
|
||||||
.setLeftButtonOnClickListener(
|
.setLeftButtonOnClickListener(
|
||||||
unused -> {
|
unused -> {
|
||||||
@@ -127,6 +110,10 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
|||||||
.setRightButtonText(getString(R.string.audio_streams_dialog_listen))
|
.setRightButtonText(getString(R.string.audio_streams_dialog_listen))
|
||||||
.setRightButtonOnClickListener(
|
.setRightButtonOnClickListener(
|
||||||
unused -> {
|
unused -> {
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
getActivity(),
|
||||||
|
SettingsEnums
|
||||||
|
.ACTION_AUDIO_STREAM_CONFIRM_LAUNCH_MAIN_BUTTON_CLICK);
|
||||||
launchAudioStreamsActivity();
|
launchAudioStreamsActivity();
|
||||||
dismiss();
|
dismiss();
|
||||||
if (mActivity != null) {
|
if (mActivity != null) {
|
||||||
@@ -136,7 +123,7 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dialog getUnsupporteDialog() {
|
private Dialog getUnsupportedDialog() {
|
||||||
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
||||||
.setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
|
.setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
|
||||||
.setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle))
|
.setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle))
|
||||||
@@ -151,10 +138,13 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dialog getErrorDialog(String name) {
|
private Dialog getErrorDialog() {
|
||||||
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
||||||
.setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
|
.setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
|
||||||
.setSubTitle2(getString(R.string.audio_streams_dialog_cannot_play, name))
|
.setSubTitle2(
|
||||||
|
getString(
|
||||||
|
R.string.audio_streams_dialog_cannot_play,
|
||||||
|
getConnectedDeviceName()))
|
||||||
.setRightButtonText(getString(R.string.audio_streams_dialog_close))
|
.setRightButtonText(getString(R.string.audio_streams_dialog_close))
|
||||||
.setRightButtonOnClickListener(
|
.setRightButtonOnClickListener(
|
||||||
unused -> {
|
unused -> {
|
||||||
@@ -181,11 +171,12 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
|||||||
.setRightButtonText(getString(R.string.audio_streams_dialog_no_le_device_button))
|
.setRightButtonText(getString(R.string.audio_streams_dialog_no_le_device_button))
|
||||||
.setRightButtonOnClickListener(
|
.setRightButtonOnClickListener(
|
||||||
dialog -> {
|
dialog -> {
|
||||||
if (mActivity != null) {
|
new SubSettingLauncher(mContext)
|
||||||
mActivity.startActivity(
|
.setDestination(
|
||||||
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
|
ConnectedDeviceDashboardFragment.class.getName())
|
||||||
.setPackage(mActivity.getPackageName()));
|
.setSourceMetricsCategory(
|
||||||
}
|
SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE)
|
||||||
|
.launch();
|
||||||
dismiss();
|
dismiss();
|
||||||
if (mActivity != null) {
|
if (mActivity != null) {
|
||||||
mActivity.finish();
|
mActivity.finish();
|
||||||
@@ -196,14 +187,60 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
|||||||
|
|
||||||
private void launchAudioStreamsActivity() {
|
private void launchAudioStreamsActivity() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString(KEY_BROADCAST_METADATA, mBroadcastMetadataStr);
|
bundle.putParcelable(KEY_BROADCAST_METADATA, mBroadcastMetadata);
|
||||||
if (mActivity != null) {
|
if (mActivity != null) {
|
||||||
new SubSettingLauncher(getActivity())
|
new SubSettingLauncher(getActivity())
|
||||||
.setTitleText(getString(R.string.audio_streams_activity_title))
|
.setTitleText(getString(R.string.audio_streams_activity_title))
|
||||||
.setDestination(AudioStreamsDashboardFragment.class.getName())
|
.setDestination(AudioStreamsDashboardFragment.class.getName())
|
||||||
.setArguments(bundle)
|
.setArguments(bundle)
|
||||||
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
|
.setSourceMetricsCategory(getMetricsCategory())
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @Nullable BluetoothLeBroadcastMetadata getMetadata(Intent intent) {
|
||||||
|
String metadata = intent.getStringExtra(KEY_BROADCAST_METADATA);
|
||||||
|
if (metadata == null || metadata.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDialogId(boolean hasMetadata, boolean hasConnectedDevice) {
|
||||||
|
if (!AudioSharingUtils.isFeatureEnabled()) {
|
||||||
|
return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
if (!hasConnectedDevice) {
|
||||||
|
return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE;
|
||||||
|
}
|
||||||
|
return hasMetadata
|
||||||
|
? SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN
|
||||||
|
: SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BluetoothDevice getConnectedDevice() {
|
||||||
|
var localBluetoothManager = Utils.getLocalBluetoothManager(getActivity());
|
||||||
|
if (localBluetoothManager == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalBluetoothLeBroadcastAssistant assistant =
|
||||||
|
localBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||||
|
if (assistant == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var devices =
|
||||||
|
assistant.getDevicesMatchingConnectionStates(
|
||||||
|
new int[] {BluetoothProfile.STATE_CONNECTED});
|
||||||
|
return devices.isEmpty() ? null : devices.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConnectedDeviceName() {
|
||||||
|
if (mConnectedDevice != null) {
|
||||||
|
String alias = mConnectedDevice.getAlias();
|
||||||
|
return TextUtils.isEmpty(alias) ? getString(DEFAULT_DEVICE_NAME) : alias;
|
||||||
|
}
|
||||||
|
Log.w(TAG, "getConnectedDeviceName : no connected device!");
|
||||||
|
return getString(DEFAULT_DEVICE_NAME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,7 @@ import android.util.Log;
|
|||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.bluetooth.Utils;
|
import com.android.settings.bluetooth.Utils;
|
||||||
@@ -196,7 +197,7 @@ public class AudioStreamMediaService extends Service {
|
|||||||
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
||||||
private int mLatestPositiveVolume = 25;
|
private int mLatestPositiveVolume = 25;
|
||||||
private boolean mIsMuted = false;
|
private boolean mIsMuted = false;
|
||||||
@Nullable private MediaSession mLocalSession;
|
@VisibleForTesting @Nullable MediaSession mLocalSession;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
@@ -228,7 +229,7 @@ public class AudioStreamMediaService extends Service {
|
|||||||
NotificationChannel notificationChannel =
|
NotificationChannel notificationChannel =
|
||||||
new NotificationChannel(
|
new NotificationChannel(
|
||||||
CHANNEL_ID,
|
CHANNEL_ID,
|
||||||
this.getString(com.android.settings.R.string.bluetooth),
|
getString(com.android.settings.R.string.bluetooth),
|
||||||
NotificationManager.IMPORTANCE_HIGH);
|
NotificationManager.IMPORTANCE_HIGH);
|
||||||
mNotificationManager.createNotificationChannel(notificationChannel);
|
mNotificationManager.createNotificationChannel(notificationChannel);
|
||||||
}
|
}
|
||||||
@@ -403,7 +404,7 @@ public class AudioStreamMediaService extends Service {
|
|||||||
new Notification.Builder(this, CHANNEL_ID)
|
new Notification.Builder(this, CHANNEL_ID)
|
||||||
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
|
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
|
||||||
.setStyle(mediaStyle)
|
.setStyle(mediaStyle)
|
||||||
.setContentText(this.getString(BROADCAST_CONTENT_TEXT))
|
.setContentText(getString(BROADCAST_CONTENT_TEXT))
|
||||||
.setSilent(true);
|
.setSilent(true);
|
||||||
return notificationBuilder.build();
|
return notificationBuilder.build();
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,7 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart(@NonNull LifecycleOwner owner) {
|
public void onStart(@NonNull LifecycleOwner owner) {
|
||||||
|
if (!isAvailable()) return;
|
||||||
super.onStart(owner);
|
super.onStart(owner);
|
||||||
if (mLocalBtManager != null) {
|
if (mLocalBtManager != null) {
|
||||||
mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
|
mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
|
||||||
@@ -68,6 +69,7 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop(@NonNull LifecycleOwner owner) {
|
public void onStop(@NonNull LifecycleOwner owner) {
|
||||||
|
if (!isAvailable()) return;
|
||||||
super.onStop(owner);
|
super.onStop(owner);
|
||||||
if (mLocalBtManager != null) {
|
if (mLocalBtManager != null) {
|
||||||
mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
||||||
|
@@ -18,6 +18,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
|||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -43,15 +44,16 @@ import java.util.function.Consumer;
|
|||||||
public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
|
public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
|
||||||
private static final String TAG = "AudioStreamsDialogFragment";
|
private static final String TAG = "AudioStreamsDialogFragment";
|
||||||
private final DialogBuilder mDialogBuilder;
|
private final DialogBuilder mDialogBuilder;
|
||||||
|
private int mDialogId = SettingsEnums.PAGE_UNKNOWN;
|
||||||
|
|
||||||
AudioStreamsDialogFragment(DialogBuilder dialogBuilder) {
|
AudioStreamsDialogFragment(DialogBuilder dialogBuilder, int dialogId) {
|
||||||
mDialogBuilder = dialogBuilder;
|
mDialogBuilder = dialogBuilder;
|
||||||
|
mDialogId = dialogId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
// TODO(chelseahao): update metrics id
|
return mDialogId;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,14 +66,15 @@ public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
|
|||||||
*
|
*
|
||||||
* @param host The fragment to host the dialog.
|
* @param host The fragment to host the dialog.
|
||||||
* @param dialogBuilder The builder for constructing the dialog.
|
* @param dialogBuilder The builder for constructing the dialog.
|
||||||
|
* @param dialogId The dialog settings enum for logging
|
||||||
*/
|
*/
|
||||||
public static void show(Fragment host, DialogBuilder dialogBuilder) {
|
public static void show(Fragment host, DialogBuilder dialogBuilder, int dialogId) {
|
||||||
if (!host.isAdded()) {
|
if (!host.isAdded()) {
|
||||||
Log.w(TAG, "The host fragment is not added to the activity!");
|
Log.w(TAG, "The host fragment is not added to the activity!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FragmentManager manager = host.getChildFragmentManager();
|
FragmentManager manager = host.getChildFragmentManager();
|
||||||
(new AudioStreamsDialogFragment(dialogBuilder)).show(manager, TAG);
|
(new AudioStreamsDialogFragment(dialogBuilder, dialogId)).show(manager, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dismissAll(Fragment host) {
|
static void dismissAll(Fragment host) {
|
||||||
|
@@ -139,8 +139,10 @@ public class AudioStreamsHelper {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Retrieves LocalBluetoothLeBroadcastAssistant. */
|
||||||
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
|
public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
|
||||||
return mLeBroadcastAssistant;
|
return mLeBroadcastAssistant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -481,7 +481,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
mContext,
|
mContext,
|
||||||
() -> {
|
() -> {
|
||||||
if (mFragment != null) {
|
if (mFragment != null) {
|
||||||
AudioStreamsDialogFragment.show(mFragment, getNoLeDeviceDialog());
|
AudioStreamsDialogFragment.show(
|
||||||
|
mFragment,
|
||||||
|
getNoLeDeviceDialog(),
|
||||||
|
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_NO_LE_DEVICE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -68,13 +68,10 @@ class WaitForSyncState extends AudioStreamStateHandler {
|
|||||||
ThreadUtils.postOnMainThread(
|
ThreadUtils.postOnMainThread(
|
||||||
() -> {
|
() -> {
|
||||||
if (controller.getFragment() != null) {
|
if (controller.getFragment() != null) {
|
||||||
AudioStreamsDialogFragment.show(
|
showBroadcastUnavailableDialog(
|
||||||
controller.getFragment(),
|
controller.getFragment(),
|
||||||
getBroadcastUnavailableDialog(
|
preference.getContext(),
|
||||||
preference.getContext(),
|
AudioStreamsHelper.getBroadcastName(metadata));
|
||||||
AudioStreamsHelper.getBroadcastName(
|
|
||||||
metadata),
|
|
||||||
controller));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -93,24 +90,28 @@ class WaitForSyncState extends AudioStreamStateHandler {
|
|||||||
return AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC;
|
return AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableDialog(
|
private void showBroadcastUnavailableDialog(
|
||||||
Context context,
|
Fragment fragment, Context context, String broadcastName) {
|
||||||
String broadcastName,
|
var broadcastUnavailableDialog =
|
||||||
AudioStreamsProgressCategoryController controller) {
|
new AudioStreamsDialogFragment.DialogBuilder(context)
|
||||||
return new AudioStreamsDialogFragment.DialogBuilder(context)
|
.setTitle(
|
||||||
.setTitle(context.getString(R.string.audio_streams_dialog_stream_is_not_available))
|
context.getString(
|
||||||
.setSubTitle1(broadcastName)
|
R.string.audio_streams_dialog_stream_is_not_available))
|
||||||
.setSubTitle2(context.getString(R.string.audio_streams_is_not_playing))
|
.setSubTitle1(broadcastName)
|
||||||
.setLeftButtonText(context.getString(R.string.audio_streams_dialog_close))
|
.setSubTitle2(context.getString(R.string.audio_streams_is_not_playing))
|
||||||
.setLeftButtonOnClickListener(AlertDialog::dismiss)
|
.setLeftButtonText(context.getString(R.string.audio_streams_dialog_close))
|
||||||
.setRightButtonText(context.getString(R.string.audio_streams_dialog_retry))
|
.setLeftButtonOnClickListener(AlertDialog::dismiss)
|
||||||
.setRightButtonOnClickListener(
|
.setRightButtonText(context.getString(R.string.audio_streams_dialog_retry))
|
||||||
dialog -> {
|
.setRightButtonOnClickListener(
|
||||||
if (controller.getFragment() != null) {
|
dialog -> {
|
||||||
launchQrCodeScanFragment(context, controller.getFragment());
|
launchQrCodeScanFragment(context, fragment);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
AudioStreamsDialogFragment.show(
|
||||||
|
fragment,
|
||||||
|
broadcastUnavailableDialog,
|
||||||
|
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchQrCodeScanFragment(Context context, Fragment fragment) {
|
private void launchQrCodeScanFragment(Context context, Fragment fragment) {
|
||||||
|
@@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
ShadowBluetoothUtils.class,
|
||||||
|
})
|
||||||
|
public class AudioStreamConfirmDialogTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
private static final String VALID_METADATA =
|
||||||
|
"BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
|
||||||
|
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
|
||||||
|
private static final String DEVICE_NAME = "device_name";
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@Mock private LocalBluetoothManager mLocalBluetoothManager;
|
||||||
|
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||||
|
@Mock private LocalBluetoothLeBroadcast mBroadcast;
|
||||||
|
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
|
@Mock private VolumeControlProfile mVolumeControl;
|
||||||
|
@Mock private BluetoothDevice mBluetoothDevice;
|
||||||
|
private AudioStreamConfirmDialog mDialogFragment;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
||||||
|
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
shadowBluetoothAdapter.setEnabled(true);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
|
||||||
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||||
|
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||||
|
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
|
||||||
|
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
|
||||||
|
.thenReturn(mAssistant);
|
||||||
|
when(mLocalBluetoothProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
|
||||||
|
when(mBroadcast.isProfileReady()).thenReturn(true);
|
||||||
|
when(mAssistant.isProfileReady()).thenReturn(true);
|
||||||
|
when(mVolumeControl.isProfileReady()).thenReturn(true);
|
||||||
|
|
||||||
|
mDialogFragment = new AudioStreamConfirmDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowBluetoothUtils.reset();
|
||||||
|
mDialogFragment.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showDialog_unsupported() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
FragmentController.setupFragment(
|
||||||
|
mDialogFragment,
|
||||||
|
FragmentActivity.class,
|
||||||
|
/* containerViewId= */ 0,
|
||||||
|
/* bundle= */ null);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
assertThat(mDialogFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED);
|
||||||
|
|
||||||
|
var dialog = mDialogFragment.getDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
TextView title = dialog.findViewById(R.id.dialog_title);
|
||||||
|
assertThat(title).isNotNull();
|
||||||
|
assertThat(title.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_cannot_listen));
|
||||||
|
TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle);
|
||||||
|
assertThat(subtitle1).isNotNull();
|
||||||
|
assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
|
||||||
|
assertThat(subtitle2).isNotNull();
|
||||||
|
assertThat(subtitle2.getText())
|
||||||
|
.isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.audio_streams_dialog_unsupported_device_subtitle));
|
||||||
|
View leftButton = dialog.findViewById(R.id.left_button);
|
||||||
|
assertThat(leftButton).isNotNull();
|
||||||
|
assertThat(leftButton.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(leftButton.hasOnClickListeners()).isFalse();
|
||||||
|
View rightButton = dialog.findViewById(R.id.right_button);
|
||||||
|
assertThat(rightButton).isNotNull();
|
||||||
|
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(rightButton.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showDialog_noLeDevice() {
|
||||||
|
FragmentController.setupFragment(
|
||||||
|
mDialogFragment,
|
||||||
|
FragmentActivity.class,
|
||||||
|
/* containerViewId= */ 0,
|
||||||
|
/* bundle= */ null);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
assertThat(mDialogFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE);
|
||||||
|
|
||||||
|
var dialog = mDialogFragment.getDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
|
||||||
|
TextView title = dialog.findViewById(R.id.dialog_title);
|
||||||
|
assertThat(title).isNotNull();
|
||||||
|
assertThat(title.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title));
|
||||||
|
TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle);
|
||||||
|
assertThat(subtitle1).isNotNull();
|
||||||
|
assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
|
||||||
|
assertThat(subtitle2).isNotNull();
|
||||||
|
assertThat(subtitle2.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_subtitle));
|
||||||
|
View leftButton = dialog.findViewById(R.id.left_button);
|
||||||
|
assertThat(leftButton).isNotNull();
|
||||||
|
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
Button rightButton = dialog.findViewById(R.id.right_button);
|
||||||
|
assertThat(rightButton).isNotNull();
|
||||||
|
assertThat(rightButton.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_button));
|
||||||
|
assertThat(rightButton.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showDialog_noMetadata() {
|
||||||
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
|
devices.add(mBluetoothDevice);
|
||||||
|
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
|
||||||
|
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
|
||||||
|
|
||||||
|
FragmentController.setupFragment(
|
||||||
|
mDialogFragment,
|
||||||
|
FragmentActivity.class,
|
||||||
|
/* containerViewId= */ 0,
|
||||||
|
/* bundle= */ null);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
assertThat(mDialogFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
|
||||||
|
|
||||||
|
var dialog = mDialogFragment.getDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
TextView title = dialog.findViewById(R.id.dialog_title);
|
||||||
|
assertThat(title).isNotNull();
|
||||||
|
assertThat(title.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_cannot_listen));
|
||||||
|
TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle);
|
||||||
|
assertThat(subtitle1).isNotNull();
|
||||||
|
assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
|
||||||
|
assertThat(subtitle2).isNotNull();
|
||||||
|
assertThat(subtitle2.getText())
|
||||||
|
.isEqualTo(
|
||||||
|
mContext.getString(R.string.audio_streams_dialog_cannot_play, DEVICE_NAME));
|
||||||
|
View leftButton = dialog.findViewById(R.id.left_button);
|
||||||
|
assertThat(leftButton).isNotNull();
|
||||||
|
assertThat(leftButton.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(leftButton.hasOnClickListeners()).isFalse();
|
||||||
|
View rightButton = dialog.findViewById(R.id.right_button);
|
||||||
|
assertThat(rightButton).isNotNull();
|
||||||
|
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(rightButton.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showDialog_invalidMetadata() {
|
||||||
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
|
devices.add(mBluetoothDevice);
|
||||||
|
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
|
||||||
|
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(KEY_BROADCAST_METADATA, "invalid");
|
||||||
|
FragmentController.of(mDialogFragment, intent)
|
||||||
|
.create(/* containerViewId= */ 0, /* bundle= */ null)
|
||||||
|
.start()
|
||||||
|
.resume()
|
||||||
|
.visible()
|
||||||
|
.get();
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
assertThat(mDialogFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
|
||||||
|
|
||||||
|
var dialog = mDialogFragment.getDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
TextView title = dialog.findViewById(R.id.dialog_title);
|
||||||
|
assertThat(title).isNotNull();
|
||||||
|
assertThat(title.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_cannot_listen));
|
||||||
|
TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle);
|
||||||
|
assertThat(subtitle1).isNotNull();
|
||||||
|
assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
|
||||||
|
assertThat(subtitle2).isNotNull();
|
||||||
|
assertThat(subtitle2.getText())
|
||||||
|
.isEqualTo(
|
||||||
|
mContext.getString(R.string.audio_streams_dialog_cannot_play, DEVICE_NAME));
|
||||||
|
View leftButton = dialog.findViewById(R.id.left_button);
|
||||||
|
assertThat(leftButton).isNotNull();
|
||||||
|
assertThat(leftButton.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(leftButton.hasOnClickListeners()).isFalse();
|
||||||
|
View rightButton = dialog.findViewById(R.id.right_button);
|
||||||
|
assertThat(rightButton).isNotNull();
|
||||||
|
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(rightButton.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showDialog_confirmListen() {
|
||||||
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
|
devices.add(mBluetoothDevice);
|
||||||
|
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
|
||||||
|
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(KEY_BROADCAST_METADATA, VALID_METADATA);
|
||||||
|
FragmentController.of(mDialogFragment, intent)
|
||||||
|
.create(/* containerViewId= */ 0, /* bundle= */ null)
|
||||||
|
.start()
|
||||||
|
.resume()
|
||||||
|
.visible()
|
||||||
|
.get();
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
|
||||||
|
assertThat(mDialogFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN);
|
||||||
|
|
||||||
|
var dialog = mDialogFragment.getDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
TextView title = dialog.findViewById(R.id.dialog_title);
|
||||||
|
assertThat(title).isNotNull();
|
||||||
|
assertThat(title.getText())
|
||||||
|
.isEqualTo(
|
||||||
|
mContext.getString(R.string.audio_streams_dialog_listen_to_audio_stream));
|
||||||
|
TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle);
|
||||||
|
assertThat(subtitle1).isNotNull();
|
||||||
|
assertThat(subtitle1.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
|
||||||
|
assertThat(subtitle2).isNotNull();
|
||||||
|
assertThat(subtitle2.getText())
|
||||||
|
.isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.audio_streams_dialog_control_volume, DEVICE_NAME));
|
||||||
|
View leftButton = dialog.findViewById(R.id.left_button);
|
||||||
|
assertThat(leftButton).isNotNull();
|
||||||
|
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
Button rightButton = dialog.findViewById(R.id.right_button);
|
||||||
|
assertThat(rightButton).isNotNull();
|
||||||
|
assertThat(rightButton.getText())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_listen));
|
||||||
|
assertThat(rightButton.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
||||||
|
|
||||||
|
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.anyString;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.media.session.ISession;
|
||||||
|
import android.media.session.ISessionController;
|
||||||
|
import android.media.session.MediaSessionManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothEventManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
ShadowBluetoothUtils.class,
|
||||||
|
ShadowAudioStreamsHelper.class,
|
||||||
|
})
|
||||||
|
public class AudioStreamMediaServiceTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
@Mock private Resources mResources;
|
||||||
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
|
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||||
|
@Mock private AudioStreamsHelper mAudioStreamsHelper;
|
||||||
|
@Mock private NotificationManager mNotificationManager;
|
||||||
|
@Mock private MediaSessionManager mMediaSessionManager;
|
||||||
|
@Mock private BluetoothEventManager mBluetoothEventManager;
|
||||||
|
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||||
|
@Mock private VolumeControlProfile mVolumeControlProfile;
|
||||||
|
@Mock private BluetoothDevice mDevice;
|
||||||
|
@Mock private ISession mISession;
|
||||||
|
@Mock private ISessionController mISessionController;
|
||||||
|
@Mock private PackageManager mPackageManager;
|
||||||
|
@Mock private DisplayMetrics mDisplayMetrics;
|
||||||
|
@Mock private Context mContext;
|
||||||
|
private AudioStreamMediaService mAudioStreamMediaService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
|
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
||||||
|
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
||||||
|
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
shadowBluetoothAdapter.setEnabled(true);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||||
|
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
||||||
|
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||||
|
when(mLocalBluetoothProfileManager.getVolumeControlProfile())
|
||||||
|
.thenReturn(mVolumeControlProfile);
|
||||||
|
|
||||||
|
mAudioStreamMediaService = spy(new AudioStreamMediaService());
|
||||||
|
ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext);
|
||||||
|
when(mAudioStreamMediaService.getSystemService(anyString()))
|
||||||
|
.thenReturn(mMediaSessionManager);
|
||||||
|
when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession);
|
||||||
|
try {
|
||||||
|
when(mISession.getController()).thenReturn(mISessionController);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
doReturn(mNotificationManager)
|
||||||
|
.when(mAudioStreamMediaService)
|
||||||
|
.getSystemService(NotificationManager.class);
|
||||||
|
when(mAudioStreamMediaService.getApplicationInfo()).thenReturn(new ApplicationInfo());
|
||||||
|
when(mAudioStreamMediaService.getResources()).thenReturn(mResources);
|
||||||
|
when(mAudioStreamMediaService.getPackageManager()).thenReturn(mPackageManager);
|
||||||
|
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
|
||||||
|
mDisplayMetrics.density = 1.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
mAudioStreamMediaService.stopSelf();
|
||||||
|
ShadowBluetoothUtils.reset();
|
||||||
|
ShadowAudioStreamsHelper.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
|
||||||
|
verify(mNotificationManager, never()).createNotificationChannel(any());
|
||||||
|
verify(mBluetoothEventManager, never()).registerCallback(any());
|
||||||
|
verify(mLeBroadcastAssistant, never()).registerServiceCallBack(any(), any());
|
||||||
|
verify(mVolumeControlProfile, never()).registerCallback(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_flagOn_init() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
|
||||||
|
verify(mNotificationManager).createNotificationChannel(any());
|
||||||
|
verify(mBluetoothEventManager).registerCallback(any());
|
||||||
|
verify(mLeBroadcastAssistant).registerServiceCallBack(any(), any());
|
||||||
|
verify(mVolumeControlProfile).registerCallback(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDestroy_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
mAudioStreamMediaService.onDestroy();
|
||||||
|
|
||||||
|
verify(mBluetoothEventManager, never()).unregisterCallback(any());
|
||||||
|
verify(mLeBroadcastAssistant, never()).unregisterServiceCallBack(any());
|
||||||
|
verify(mVolumeControlProfile, never()).unregisterCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDestroy_flagOn_cleanup() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onCreate();
|
||||||
|
mAudioStreamMediaService.onDestroy();
|
||||||
|
|
||||||
|
verify(mBluetoothEventManager).unregisterCallback(any());
|
||||||
|
verify(mLeBroadcastAssistant).unregisterServiceCallBack(any());
|
||||||
|
verify(mVolumeControlProfile).unregisterCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStartCommand_noBroadcastId_stopSelf() {
|
||||||
|
mAudioStreamMediaService.onStartCommand(new Intent(), /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
||||||
|
verify(mAudioStreamMediaService).stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStartCommand_noDevice_stopSelf() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(BROADCAST_ID, 1);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
||||||
|
verify(mAudioStreamMediaService).stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStartCommand_createSessionAndStartForeground() {
|
||||||
|
var devices = new ArrayList<BluetoothDevice>();
|
||||||
|
devices.add(mDevice);
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(BROADCAST_ID, 1);
|
||||||
|
intent.putParcelableArrayListExtra(DEVICES, devices);
|
||||||
|
|
||||||
|
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
|
assertThat(mAudioStreamMediaService.mLocalSession).isNotNull();
|
||||||
|
verify(mAudioStreamMediaService, never()).stopSelf();
|
||||||
|
|
||||||
|
ArgumentCaptor<Notification> notification = ArgumentCaptor.forClass(Notification.class);
|
||||||
|
verify(mAudioStreamMediaService).startForeground(anyInt(), notification.capture());
|
||||||
|
assertThat(notification.getValue().getSmallIcon()).isNotNull();
|
||||||
|
assertThat(notification.getValue().isStyle(Notification.MediaStyle.class)).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothEventManager;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
ShadowBluetoothUtils.class,
|
||||||
|
ShadowAudioStreamsHelper.class,
|
||||||
|
})
|
||||||
|
public class AudioStreamsCategoryControllerTest {
|
||||||
|
private static final String KEY = "audio_streams_settings_category";
|
||||||
|
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
|
@Mock private LocalBluetoothProfileManager mBtProfileManager;
|
||||||
|
@Mock private BluetoothEventManager mBluetoothEventManager;
|
||||||
|
@Mock private LocalBluetoothLeBroadcast mBroadcast;
|
||||||
|
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
|
@Mock private VolumeControlProfile mVolumeControl;
|
||||||
|
@Mock private PreferenceScreen mScreen;
|
||||||
|
@Mock private AudioStreamsHelper mAudioStreamsHelper;
|
||||||
|
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||||
|
|
||||||
|
private AudioStreamsCategoryController mController;
|
||||||
|
private Lifecycle mLifecycle;
|
||||||
|
private LifecycleOwner mLifecycleOwner;
|
||||||
|
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||||
|
private LocalBluetoothManager mLocalBluetoothManager;
|
||||||
|
private Preference mPreference;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
|
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
mShadowBluetoothAdapter.setEnabled(true);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mLifecycleOwner = () -> mLifecycle;
|
||||||
|
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||||
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||||
|
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||||
|
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
||||||
|
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager);
|
||||||
|
when(mBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
|
||||||
|
when(mBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
|
||||||
|
when(mBtProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
|
||||||
|
when(mBroadcast.isProfileReady()).thenReturn(true);
|
||||||
|
when(mAssistant.isProfileReady()).thenReturn(true);
|
||||||
|
when(mVolumeControl.isProfileReady()).thenReturn(true);
|
||||||
|
mController = new AudioStreamsCategoryController(mContext, KEY);
|
||||||
|
mPreference = new Preference(mContext);
|
||||||
|
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mPreference.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowAudioStreamsHelper.reset();
|
||||||
|
ShadowBluetoothUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOn() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOff() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mBluetoothEventManager, never()).registerCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOn_registerCallback() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mBluetoothEventManager).registerCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mBluetoothEventManager, never()).unregisterCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOn_unregisterCallback() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mBluetoothEventManager).unregisterCallback(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_flagOff_invisible() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_noConnectedLe_invisible() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_isNotProfileReady_invisible() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
|
||||||
|
mCachedBluetoothDevice);
|
||||||
|
when(mVolumeControl.isProfileReady()).thenReturn(false);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_isBroadcasting_invisible() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
|
||||||
|
mCachedBluetoothDevice);
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(true);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_isBluetoothOff_invisible() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
|
||||||
|
mCachedBluetoothDevice);
|
||||||
|
mShadowBluetoothAdapter.setEnabled(false);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateVisibility_visible() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
|
||||||
|
mCachedBluetoothDevice);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.updateVisibility();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mPreference.isVisible()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
|
||||||
|
|
||||||
|
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.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioStreamsDashboardFragmentTest {
|
||||||
|
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
private static final String VALID_METADATA =
|
||||||
|
"BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
|
||||||
|
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private AudioStreamsProgressCategoryController mController;
|
||||||
|
private TestFragment mTestFragment;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mTestFragment = spy(new TestFragment());
|
||||||
|
doReturn(mContext).when(mTestFragment).getContext();
|
||||||
|
mController = spy(new AudioStreamsProgressCategoryController(mContext, "key"));
|
||||||
|
doReturn(mController).when(mTestFragment).use(AudioStreamsProgressCategoryController.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPreferenceScreenResId_returnsCorrectXml() {
|
||||||
|
assertThat(mTestFragment.getPreferenceScreenResId())
|
||||||
|
.isEqualTo(R.xml.bluetooth_le_audio_streams);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getLogTag_returnsCorrectTag() {
|
||||||
|
assertThat(mTestFragment.getLogTag()).isEqualTo("AudioStreamsDashboardFrag");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getHelpResource_returnsCorrectResource() {
|
||||||
|
assertThat(mTestFragment.getHelpResource()).isEqualTo(R.string.help_url_audio_sharing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onActivityResult_invalidRequestCode_doNothing() {
|
||||||
|
mTestFragment.onAttach(mContext);
|
||||||
|
|
||||||
|
mTestFragment.onActivityResult(0, 0, null);
|
||||||
|
verify(mController, never()).setSourceFromQrCode(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onActivityResult_invalidRequestResult_doNothing() {
|
||||||
|
mTestFragment.onAttach(mContext);
|
||||||
|
|
||||||
|
mTestFragment.onActivityResult(REQUEST_SCAN_BT_BROADCAST_QR_CODE, 0, null);
|
||||||
|
verify(mController, never()).setSourceFromQrCode(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onActivityResult_nullData_doNothing() {
|
||||||
|
mTestFragment.onAttach(mContext);
|
||||||
|
|
||||||
|
mTestFragment.onActivityResult(REQUEST_SCAN_BT_BROADCAST_QR_CODE, Activity.RESULT_OK, null);
|
||||||
|
verify(mController, never()).setSourceFromQrCode(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onActivityResult_setSourceFromQrCode() {
|
||||||
|
mTestFragment.onAttach(mContext);
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(KEY_BROADCAST_METADATA, VALID_METADATA);
|
||||||
|
|
||||||
|
mTestFragment.onActivityResult(
|
||||||
|
REQUEST_SCAN_BT_BROADCAST_QR_CODE, Activity.RESULT_OK, intent);
|
||||||
|
verify(mController).setSourceFromQrCode(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestFragment extends AudioStreamsDashboardFragment {
|
||||||
|
@Override
|
||||||
|
protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
|
||||||
|
return super.use(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioStreamsProgressCategoryPreferenceTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Mock PreferenceManager mPreferenceManager;
|
||||||
|
private Context mContext;
|
||||||
|
private AudioStreamsProgressCategoryPreference mPreference;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mPreference = spy(new AudioStreamsProgressCategoryPreference(mContext));
|
||||||
|
when(mPreference.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAudioStreamPreference_singlePreference() {
|
||||||
|
AudioStreamPreference first = new AudioStreamPreference(mContext, null);
|
||||||
|
mPreference.addAudioStreamPreference(first, (p1, p2) -> 0);
|
||||||
|
|
||||||
|
assertThat(mPreference.getPreferenceCount()).isEqualTo(1);
|
||||||
|
assertThat(mPreference.getPreference(0)).isEqualTo(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAudioStreamPreference_multiPreference_sorted() {
|
||||||
|
Comparator<AudioStreamPreference> c =
|
||||||
|
Comparator.comparingInt(AudioStreamPreference::getOrder);
|
||||||
|
AudioStreamPreference first = new AudioStreamPreference(mContext, null);
|
||||||
|
first.setOrder(1);
|
||||||
|
AudioStreamPreference second = new AudioStreamPreference(mContext, null);
|
||||||
|
second.setOrder(0);
|
||||||
|
mPreference.addAudioStreamPreference(first, c);
|
||||||
|
mPreference.addAudioStreamPreference(second, c);
|
||||||
|
|
||||||
|
assertThat(mPreference.getPreferenceCount()).isEqualTo(2);
|
||||||
|
assertThat(mPreference.getPreference(0)).isEqualTo(second);
|
||||||
|
assertThat(mPreference.getPreference(1)).isEqualTo(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeAudioStreamPreferences_shouldBeEmpty() {
|
||||||
|
Comparator<AudioStreamPreference> c =
|
||||||
|
Comparator.comparingInt(AudioStreamPreference::getOrder);
|
||||||
|
AudioStreamPreference first = new AudioStreamPreference(mContext, null);
|
||||||
|
first.setOrder(0);
|
||||||
|
AudioStreamPreference second = new AudioStreamPreference(mContext, null);
|
||||||
|
second.setOrder(1);
|
||||||
|
mPreference.addAudioStreamPreference(first, c);
|
||||||
|
mPreference.addAudioStreamPreference(second, c);
|
||||||
|
mPreference.removeAudioStreamPreferences();
|
||||||
|
|
||||||
|
assertThat(mPreference.getPreferenceCount()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioStreamsRepositoryTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
private static final String METADATA_STR =
|
||||||
|
"BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
|
||||||
|
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
|
||||||
|
private static final String TEST_SHARED_PREFERENCE = "AudioStreamsRepositoryTestPref";
|
||||||
|
private final BluetoothLeBroadcastMetadata mMetadata =
|
||||||
|
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(METADATA_STR);
|
||||||
|
private Context mContext;
|
||||||
|
private AudioStreamsRepository mAudioStreamsRepository;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
doReturn(getSharedPreferences()).when(mContext).getSharedPreferences(anyString(), anyInt());
|
||||||
|
mAudioStreamsRepository = AudioStreamsRepository.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cacheAndGetMetadata_sameId() {
|
||||||
|
mAudioStreamsRepository.cacheMetadata(mMetadata);
|
||||||
|
|
||||||
|
assertThat(mMetadata).isNotNull();
|
||||||
|
assertThat(mAudioStreamsRepository.getCachedMetadata(mMetadata.getBroadcastId()))
|
||||||
|
.isEqualTo(mMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cacheAndGetMetadata_differentId() {
|
||||||
|
mAudioStreamsRepository.cacheMetadata(mMetadata);
|
||||||
|
|
||||||
|
assertThat(mMetadata).isNotNull();
|
||||||
|
assertThat(mAudioStreamsRepository.getCachedMetadata(1)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveAndGetMetadata_sameId() {
|
||||||
|
mAudioStreamsRepository.saveMetadata(mContext, mMetadata);
|
||||||
|
|
||||||
|
assertThat(mMetadata).isNotNull();
|
||||||
|
assertThat(mAudioStreamsRepository.getSavedMetadata(mContext, mMetadata.getBroadcastId()))
|
||||||
|
.isEqualTo(mMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveAndGetMetadata_differentId() {
|
||||||
|
mAudioStreamsRepository.saveMetadata(mContext, mMetadata);
|
||||||
|
|
||||||
|
assertThat(mMetadata).isNotNull();
|
||||||
|
assertThat(mAudioStreamsRepository.getSavedMetadata(mContext, 1)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedPreferences getSharedPreferences() {
|
||||||
|
return mContext.getSharedPreferences(TEST_SHARED_PREFERENCE, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
}
|
@@ -22,6 +22,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper;
|
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
|
||||||
import org.robolectric.annotation.Implementation;
|
import org.robolectric.annotation.Implementation;
|
||||||
@@ -63,4 +64,9 @@ public class ShadowAudioStreamsHelper {
|
|||||||
LocalBluetoothManager manager) {
|
LocalBluetoothManager manager) {
|
||||||
return Optional.ofNullable(sCachedBluetoothDevice);
|
return Optional.ofNullable(sCachedBluetoothDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
|
||||||
|
return sMockHelper.getLeBroadcastAssistant();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user