diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java index 00342b14fd0..24a28dd0602 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java @@ -22,6 +22,7 @@ import android.content.Context; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.Fragment; import com.android.settings.R; import com.android.settingslib.utils.ThreadUtils; @@ -78,12 +79,10 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler { ThreadUtils.postOnMainThread( () -> { if (controller.getFragment() != null) { - AudioStreamsDialogFragment.show( + showBroadcastUnavailableNoRetryDialog( controller.getFragment(), - getBroadcastUnavailableNoRetryDialog( - preference.getContext(), - AudioStreamsHelper.getBroadcastName( - metadata))); + preference.getContext(), + AudioStreamsHelper.getBroadcastName(metadata)); } }); } @@ -103,13 +102,21 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler { return AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE; } - private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableNoRetryDialog( - Context context, String broadcastName) { - return new AudioStreamsDialogFragment.DialogBuilder(context) - .setTitle(context.getString(R.string.audio_streams_dialog_stream_is_not_available)) - .setSubTitle1(broadcastName) - .setSubTitle2(context.getString(R.string.audio_streams_is_not_playing)) - .setRightButtonText(context.getString(R.string.audio_streams_dialog_close)) - .setRightButtonOnClickListener(AlertDialog::dismiss); + private void showBroadcastUnavailableNoRetryDialog( + Fragment fragment, Context context, String broadcastName) { + var broadcastUnavailableNoRetryDialog = + new AudioStreamsDialogFragment.DialogBuilder(context) + .setTitle( + context.getString( + R.string.audio_streams_dialog_stream_is_not_available)) + .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); } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java index 3130bbcdb42..6c449a41000 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java @@ -21,101 +21,84 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import android.app.Activity; import android.app.Dialog; import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothProfile; +import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.bluetooth.Utils; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; - -import com.google.common.base.Strings; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { private static final String TAG = "AudioStreamConfirmDialog"; private static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device; - @Nullable private LocalBluetoothManager mLocalBluetoothManager; - @Nullable private LocalBluetoothProfileManager mProfileManager; + private Context mContext; @Nullable private Activity mActivity; - @Nullable private String mBroadcastMetadataStr; @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 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 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 public Dialog onCreateDialog(Bundle savedInstanceState) { - if (!AudioSharingUtils.isFeatureEnabled()) { - return getUnsupporteDialog(); - } - if (AudioSharingUtils.isAudioSharingProfileReady(mProfileManager)) { - CachedBluetoothDevice connectedLeDevice = - AudioStreamsHelper.getCachedBluetoothDeviceInSharingOrLeConnected( - 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); + return switch (mAudioStreamConfirmDialogId) { + case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED -> + getUnsupportedDialog(); + case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE -> getNoLeDeviceDialog(); + case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN -> getConfirmDialog(); + default -> getErrorDialog(); + }; } @Override public int getMetricsCategory() { - // TODO(chelseahao): update metrics id - return 0; + return mAudioStreamConfirmDialogId; } - private Dialog getConfirmDialog(String name) { + private Dialog getConfirmDialog() { return new AudioStreamsDialogFragment.DialogBuilder(getActivity()) .setTitle(getString(R.string.audio_streams_dialog_listen_to_audio_stream)) .setSubTitle1( mBroadcastMetadata != null ? 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)) .setLeftButtonOnClickListener( unused -> { @@ -127,6 +110,10 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { .setRightButtonText(getString(R.string.audio_streams_dialog_listen)) .setRightButtonOnClickListener( unused -> { + mMetricsFeatureProvider.action( + getActivity(), + SettingsEnums + .ACTION_AUDIO_STREAM_CONFIRM_LAUNCH_MAIN_BUTTON_CLICK); launchAudioStreamsActivity(); dismiss(); if (mActivity != null) { @@ -136,7 +123,7 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { .build(); } - private Dialog getUnsupporteDialog() { + private Dialog getUnsupportedDialog() { return new AudioStreamsDialogFragment.DialogBuilder(getActivity()) .setTitle(getString(R.string.audio_streams_dialog_cannot_listen)) .setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle)) @@ -151,10 +138,13 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { .build(); } - private Dialog getErrorDialog(String name) { + private Dialog getErrorDialog() { return new AudioStreamsDialogFragment.DialogBuilder(getActivity()) .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)) .setRightButtonOnClickListener( unused -> { @@ -181,11 +171,12 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { .setRightButtonText(getString(R.string.audio_streams_dialog_no_le_device_button)) .setRightButtonOnClickListener( dialog -> { - if (mActivity != null) { - mActivity.startActivity( - new Intent(Settings.ACTION_BLUETOOTH_SETTINGS) - .setPackage(mActivity.getPackageName())); - } + new SubSettingLauncher(mContext) + .setDestination( + ConnectedDeviceDashboardFragment.class.getName()) + .setSourceMetricsCategory( + SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE) + .launch(); dismiss(); if (mActivity != null) { mActivity.finish(); @@ -196,14 +187,60 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment { private void launchAudioStreamsActivity() { Bundle bundle = new Bundle(); - bundle.putString(KEY_BROADCAST_METADATA, mBroadcastMetadataStr); + bundle.putParcelable(KEY_BROADCAST_METADATA, mBroadcastMetadata); if (mActivity != null) { new SubSettingLauncher(getActivity()) .setTitleText(getString(R.string.audio_streams_activity_title)) .setDestination(AudioStreamsDashboardFragment.class.getName()) .setArguments(bundle) - .setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN) + .setSourceMetricsCategory(getMetricsCategory()) .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); + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java index 275b811d633..6d297f4cd7c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java @@ -37,6 +37,7 @@ import android.util.Log; import androidx.annotation.IntRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.settings.R; 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. private int mLatestPositiveVolume = 25; private boolean mIsMuted = false; - @Nullable private MediaSession mLocalSession; + @VisibleForTesting @Nullable MediaSession mLocalSession; @Override public void onCreate() { @@ -228,7 +229,7 @@ public class AudioStreamMediaService extends Service { NotificationChannel notificationChannel = new NotificationChannel( CHANNEL_ID, - this.getString(com.android.settings.R.string.bluetooth), + getString(com.android.settings.R.string.bluetooth), NotificationManager.IMPORTANCE_HIGH); mNotificationManager.createNotificationChannel(notificationChannel); } @@ -403,7 +404,7 @@ public class AudioStreamMediaService extends Service { new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) .setStyle(mediaStyle) - .setContentText(this.getString(BROADCAST_CONTENT_TEXT)) + .setContentText(getString(BROADCAST_CONTENT_TEXT)) .setSilent(true); return notificationBuilder.build(); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java index 0f164bb313a..3174ace8520 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java @@ -60,6 +60,7 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo @Override public void onStart(@NonNull LifecycleOwner owner) { + if (!isAvailable()) return; super.onStart(owner); if (mLocalBtManager != null) { mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback); @@ -68,6 +69,7 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo @Override public void onStop(@NonNull LifecycleOwner owner) { + if (!isAvailable()) return; super.onStop(owner); if (mLocalBtManager != null) { mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback); diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java index eb99b96fc29..83b7d9a456a 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java @@ -18,6 +18,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import android.app.AlertDialog; import android.app.Dialog; +import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; import android.util.Log; @@ -43,15 +44,16 @@ import java.util.function.Consumer; public class AudioStreamsDialogFragment extends InstrumentedDialogFragment { private static final String TAG = "AudioStreamsDialogFragment"; private final DialogBuilder mDialogBuilder; + private int mDialogId = SettingsEnums.PAGE_UNKNOWN; - AudioStreamsDialogFragment(DialogBuilder dialogBuilder) { + AudioStreamsDialogFragment(DialogBuilder dialogBuilder, int dialogId) { mDialogBuilder = dialogBuilder; + mDialogId = dialogId; } @Override public int getMetricsCategory() { - // TODO(chelseahao): update metrics id - return 0; + return mDialogId; } @Override @@ -64,14 +66,15 @@ public class AudioStreamsDialogFragment extends InstrumentedDialogFragment { * * @param host The fragment to host 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()) { Log.w(TAG, "The host fragment is not added to the activity!"); return; } FragmentManager manager = host.getChildFragmentManager(); - (new AudioStreamsDialogFragment(dialogBuilder)).show(manager, TAG); + (new AudioStreamsDialogFragment(dialogBuilder, dialogId)).show(manager, TAG); } static void dismissAll(Fragment host) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java index 04e791f8432..775186a859e 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java @@ -139,8 +139,10 @@ public class AudioStreamsHelper { .toList(); } + /** Retrieves LocalBluetoothLeBroadcastAssistant. */ + @VisibleForTesting @Nullable - LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { + public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { return mLeBroadcastAssistant; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java index 0777dd47703..890879e817c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java @@ -481,7 +481,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro mContext, () -> { if (mFragment != null) { - AudioStreamsDialogFragment.show(mFragment, getNoLeDeviceDialog()); + AudioStreamsDialogFragment.show( + mFragment, + getNoLeDeviceDialog(), + SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_NO_LE_DEVICE); } }); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java index 4554a4de80f..55f61fdd0e2 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java @@ -68,13 +68,10 @@ class WaitForSyncState extends AudioStreamStateHandler { ThreadUtils.postOnMainThread( () -> { if (controller.getFragment() != null) { - AudioStreamsDialogFragment.show( + showBroadcastUnavailableDialog( controller.getFragment(), - getBroadcastUnavailableDialog( - preference.getContext(), - AudioStreamsHelper.getBroadcastName( - metadata), - controller)); + preference.getContext(), + AudioStreamsHelper.getBroadcastName(metadata)); } }); } @@ -93,24 +90,28 @@ class WaitForSyncState extends AudioStreamStateHandler { return AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC; } - private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableDialog( - Context context, - String broadcastName, - AudioStreamsProgressCategoryController controller) { - return new AudioStreamsDialogFragment.DialogBuilder(context) - .setTitle(context.getString(R.string.audio_streams_dialog_stream_is_not_available)) - .setSubTitle1(broadcastName) - .setSubTitle2(context.getString(R.string.audio_streams_is_not_playing)) - .setLeftButtonText(context.getString(R.string.audio_streams_dialog_close)) - .setLeftButtonOnClickListener(AlertDialog::dismiss) - .setRightButtonText(context.getString(R.string.audio_streams_dialog_retry)) - .setRightButtonOnClickListener( - dialog -> { - if (controller.getFragment() != null) { - launchQrCodeScanFragment(context, controller.getFragment()); - dialog.dismiss(); - } - }); + private void showBroadcastUnavailableDialog( + Fragment fragment, Context context, String broadcastName) { + var broadcastUnavailableDialog = + new AudioStreamsDialogFragment.DialogBuilder(context) + .setTitle( + context.getString( + R.string.audio_streams_dialog_stream_is_not_available)) + .setSubTitle1(broadcastName) + .setSubTitle2(context.getString(R.string.audio_streams_is_not_playing)) + .setLeftButtonText(context.getString(R.string.audio_streams_dialog_close)) + .setLeftButtonOnClickListener(AlertDialog::dismiss) + .setRightButtonText(context.getString(R.string.audio_streams_dialog_retry)) + .setRightButtonOnClickListener( + dialog -> { + launchQrCodeScanFragment(context, fragment); + dialog.dismiss(); + }); + + AudioStreamsDialogFragment.show( + fragment, + broadcastUnavailableDialog, + SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT); } private void launchQrCodeScanFragment(Context context, Fragment fragment) { diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java new file mode 100644 index 00000000000..c7aafe89dbb --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java @@ -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 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 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 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(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java new file mode 100644 index 00000000000..b184d882aba --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java @@ -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(); + 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 = ArgumentCaptor.forClass(Notification.class); + verify(mAudioStreamMediaService).startForeground(anyInt(), notification.capture()); + assertThat(notification.getValue().getSmallIcon()).isNotNull(); + assertThat(notification.getValue().isStyle(Notification.MediaStyle.class)).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java new file mode 100644 index 00000000000..e4b6903e800 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java @@ -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(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragmentTest.java new file mode 100644 index 00000000000..9058ab65957 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragmentTest.java @@ -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 use(Class clazz) { + return super.use(clazz); + } + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreferenceTest.java new file mode 100644 index 00000000000..337d64de293 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreferenceTest.java @@ -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 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 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); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsRepositoryTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsRepositoryTest.java new file mode 100644 index 00000000000..037b1953626 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsRepositoryTest.java @@ -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); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java index 331a30bec20..3a0a6c4b757 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java @@ -22,6 +22,7 @@ import androidx.annotation.Nullable; import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.robolectric.annotation.Implementation; @@ -63,4 +64,9 @@ public class ShadowAudioStreamsHelper { LocalBluetoothManager manager) { return Optional.ofNullable(sCachedBluetoothDevice); } + + @Implementation + public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { + return sMockHelper.getLeBroadcastAssistant(); + } }