[Audiosharing] Listen to onProfileConnectionStateChanged of LE_AUDIO_BROADCAST_ASSISTANT to be more precise on device connection status upon bluetooth on/off. Also increase test coverage.

Test: atest -c com.android.settings.connecteddevice.audiosharing.audiostreams
Flag: com.android.settingslib.flags.enable_le_audio_qr_code_private_broadcast_sharing
Bug: 345686602
Change-Id: Ia78b1fe19bff3cb179794db1dc09374db13818d8
This commit is contained in:
chelseahao
2024-06-21 10:43:28 +08:00
parent 4b85389124
commit 44a0b59ad2
10 changed files with 493 additions and 43 deletions

View File

@@ -16,24 +16,22 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.utils.ThreadUtils;
public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback {
private static final String TAG = "AudioStreamsActiveDeviceSummaryUpdater";
private static final boolean DEBUG = BluetoothUtils.D;
private final LocalBluetoothManager mBluetoothManager;
private Context mContext;
@Nullable private String mSummary;
@@ -47,17 +45,20 @@ public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback
}
@Override
public void onActiveDeviceChanged(
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
if (DEBUG) {
Log.d(
TAG,
"onActiveDeviceChanged() with activeDevice : "
+ (activeDevice == null ? "null" : activeDevice.getAddress())
+ " on profile : "
+ bluetoothProfile);
public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
if (bluetoothState == BluetoothAdapter.STATE_OFF) {
notifyChangeIfNeeded();
}
if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
}
@Override
public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& (state == BluetoothAdapter.STATE_CONNECTED
|| state == BluetoothAdapter.STATE_DISCONNECTED)) {
notifyChangeIfNeeded();
}
}

View File

@@ -16,12 +16,12 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import com.android.settings.bluetooth.Utils;
@@ -44,9 +44,13 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo
private final BluetoothCallback mBluetoothCallback =
new BluetoothCallback() {
@Override
public void onActiveDeviceChanged(
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& (state == BluetoothAdapter.STATE_CONNECTED
|| state == BluetoothAdapter.STATE_DISCONNECTED)) {
updateVisibility();
}
}

View File

@@ -19,7 +19,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.util.Log;
public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastAssistantCallback {
private static final String TAG = "AudioStreamsProgressCategoryCallback";
@@ -53,10 +52,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
@Override
public void onSearchStarted(int reason) {
super.onSearchStarted(reason);
if (mCategoryController == null) {
Log.w(TAG, "onSearchStarted() : mCategoryController is null!");
return;
}
mCategoryController.setScanning(true);
}
@@ -69,10 +64,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
@Override
public void onSearchStopped(int reason) {
super.onSearchStopped(reason);
if (mCategoryController == null) {
Log.w(TAG, "onSearchStopped() : mCategoryController is null!");
return;
}
mCategoryController.setScanning(false);
}
@@ -86,10 +77,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
@Override
public void onSourceFound(BluetoothLeBroadcastMetadata source) {
super.onSourceFound(source);
if (mCategoryController == null) {
Log.w(TAG, "onSourceFound() : mCategoryController is null!");
return;
}
mCategoryController.handleSourceFound(source);
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -47,9 +48,13 @@ public class AudioStreamsScanQrCodeController extends BasePreferenceController
final BluetoothCallback mBluetoothCallback =
new BluetoothCallback() {
@Override
public void onActiveDeviceChanged(
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& (state == BluetoothAdapter.STATE_CONNECTED
|| state == BluetoothAdapter.STATE_DISCONNECTED)) {
updateVisibility();
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
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.content.Context;
import androidx.preference.Preference;
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;
@RunWith(RobolectricTestRunner.class)
public class AudioStreamStateHandlerTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final int SUMMARY_RES = 1;
private static final String SUMMARY = "summary";
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
@Mock private AudioStreamsProgressCategoryController mController;
@Mock private AudioStreamsHelper mHelper;
@Mock private AudioStreamPreference mPreference;
private AudioStreamStateHandler mHandler;
@Before
public void setUp() {
mHandler = spy(new AudioStreamStateHandler());
}
@Test
public void testHandleStateChange_noChange_doNothing() {
when(mHandler.getStateEnum())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference, never()).setAudioStreamState(any());
verify(mHandler, never()).performAction(any(), any(), any());
verify(mPreference, never()).setIsConnected(anyBoolean(), anyString(), any());
}
@Test
public void testHandleStateChange_setNewState() {
when(mHandler.getStateEnum())
.thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference)
.setAudioStreamState(
AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED);
verify(mHandler).performAction(any(), any(), any());
verify(mPreference).setIsConnected(eq(true), eq(""), eq(null));
}
@Test
public void testHandleStateChange_setNewState_newSummary_newListener() {
Preference.OnPreferenceClickListener listener =
mock(Preference.OnPreferenceClickListener.class);
when(mHandler.getStateEnum())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
when(mHandler.getSummary()).thenReturn(SUMMARY_RES);
when(mHandler.getOnClickListener(any())).thenReturn(listener);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED);
when(mPreference.getContext()).thenReturn(mContext);
doReturn(SUMMARY).when(mContext).getString(anyInt());
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference)
.setAudioStreamState(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
verify(mHandler).performAction(any(), any(), any());
verify(mPreference).setIsConnected(eq(false), eq(SUMMARY), eq(listener));
}
@Test
public void testGetSummary() {
int res = mHandler.getSummary();
assertThat(res).isEqualTo(AudioStreamStateHandler.EMPTY_STRING_RES);
}
@Test
public void testGetOnClickListener() {
Preference.OnPreferenceClickListener listener = mHandler.getOnClickListener(mController);
assertThat(listener).isNull();
}
@Test
public void testGetStateEnum() {
var state = mHandler.getStateEnum();
assertThat(state)
.isEqualTo(AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN);
}
}

View File

@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -76,25 +77,46 @@ public class AudioStreamsActiveDeviceSummaryUpdaterTest {
}
@Test
public void onActiveDeviceChanged_notLeProfile_doNothing() {
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, 0);
public void unregister_doNothing() {
mUpdater.register(false);
assertThat(mUpdatedSummary).isNull();
}
@Test
public void onActiveDeviceChanged_leProfile_summaryUpdated() {
public void onProfileConnectionStateChanged_notLeAssistProfile_doNothing() {
mUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, 0, 0);
assertThat(mUpdatedSummary).isNull();
}
@Test
public void onProfileConnectionStateChanged_leAssistantProfile_summaryUpdated() {
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO);
mUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mUpdatedSummary).isEqualTo(DEVICE_NAME);
}
@Test
public void onActiveDeviceChanged_leProfile_noDevice_summaryUpdated() {
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO);
public void onActiveDeviceChanged_leAssistantProfile_noDevice_summaryUpdated() {
mUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mUpdatedSummary)
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title));
}
@Test
public void onBluetoothStateOff_summaryUpdated() {
mUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
assertThat(mUpdatedSummary)
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title));

View File

@@ -23,11 +23,13 @@ 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.spy;
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.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.os.Looper;
@@ -42,6 +44,7 @@ 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.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -57,6 +60,7 @@ 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;
@@ -116,7 +120,7 @@ public class AudioStreamsCategoryControllerTest {
when(mBroadcast.isProfileReady()).thenReturn(true);
when(mAssistant.isProfileReady()).thenReturn(true);
when(mVolumeControl.isProfileReady()).thenReturn(true);
mController = new AudioStreamsCategoryController(mContext, KEY);
mController = spy(new AudioStreamsCategoryController(mContext, KEY));
mPreference = new Preference(mContext);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -228,4 +232,21 @@ public class AudioStreamsCategoryControllerTest {
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void onProfileConnectionStateChanged_updateVisibility() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
ArgumentCaptor<BluetoothCallback> argumentCaptor =
ArgumentCaptor.forClass(BluetoothCallback.class);
mController.onStart(mLifecycleOwner);
verify(mBluetoothEventManager).registerCallback(argumentCaptor.capture());
BluetoothCallback callback = argumentCaptor.getValue();
callback.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
BluetoothAdapter.STATE_DISCONNECTED);
verify(mController).updateVisibility();
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.verify;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import androidx.test.core.app.ApplicationProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.androidx.fragment.FragmentController;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowAlertDialog.class,
})
public class AudioStreamsDialogFragmentTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private AudioStreamsDialogFragment.DialogBuilder mDialogBuilder;
private AudioStreamsDialogFragment mFragment;
@Before
public void setUp() {
mDialogBuilder = spy(new AudioStreamsDialogFragment.DialogBuilder(mContext));
mFragment = new AudioStreamsDialogFragment(mDialogBuilder, SettingsEnums.PAGE_UNKNOWN);
}
@After
public void tearDown() {
ShadowAlertDialog.reset();
}
@Test
public void testGetMetricsCategory() {
int dialogId = mFragment.getMetricsCategory();
assertThat(dialogId).isEqualTo(SettingsEnums.PAGE_UNKNOWN);
}
@Test
public void testOnCreateDialog() {
mFragment.onCreateDialog(Bundle.EMPTY);
verify(mDialogBuilder).build();
}
@Test
public void testShowDialog() {
FragmentController.setupFragment(mFragment);
AudioStreamsDialogFragment.show(mFragment, mDialogBuilder, SettingsEnums.PAGE_UNKNOWN);
ShadowLooper.idleMainLooper();
var dialog = ShadowAlertDialog.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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 org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
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.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class AudioStreamsProgressCategoryCallbackTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock private AudioStreamsProgressCategoryController mController;
@Mock private BluetoothDevice mDevice;
@Mock private BluetoothLeBroadcastReceiveState mState;
@Mock private BluetoothLeBroadcastMetadata mMetadata;
private AudioStreamsProgressCategoryCallback mCallback;
@Before
public void setUp() {
mCallback = new AudioStreamsProgressCategoryCallback(mController);
}
@Test
public void testOnReceiveStateChanged_connected() {
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(mState.getBisSyncState()).thenReturn(bisSyncState);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
verify(mController).handleSourceConnected(any());
}
@Test
public void testOnReceiveStateChanged_badCode() {
when(mState.getPaSyncState())
.thenReturn(BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED);
when(mState.getBigEncryptionState())
.thenReturn(BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
verify(mController).handleSourceConnectBadCode(any());
}
@Test
public void testOnSearchStartFailed() {
mCallback.onSearchStartFailed(/* reason= */ 0);
verify(mController).showToast(anyString());
verify(mController).setScanning(anyBoolean());
}
@Test
public void testOnSearchStarted() {
mCallback.onSearchStarted(/* reason= */ 0);
verify(mController).setScanning(anyBoolean());
}
@Test
public void testOnSearchStopFailed() {
mCallback.onSearchStopFailed(/* reason= */ 0);
verify(mController).showToast(anyString());
}
@Test
public void testOnSearchStopped() {
mCallback.onSearchStopped(/* reason= */ 0);
verify(mController).setScanning(anyBoolean());
}
@Test
public void testOnSourceAddFailed() {
when(mMetadata.getBroadcastId()).thenReturn(1);
mCallback.onSourceAddFailed(mDevice, mMetadata, /* reason= */ 0);
verify(mController).handleSourceFailedToConnect(1);
}
@Test
public void testOnSourceFound() {
mCallback.onSourceFound(mMetadata);
verify(mController).handleSourceFound(mMetadata);
}
@Test
public void testOnSourceLost() {
mCallback.onSourceLost(/* broadcastId= */ 1);
verify(mController).handleSourceLost(1);
}
@Test
public void testOnSourceRemoveFailed() {
mCallback.onSourceRemoveFailed(mDevice, /* sourceId= */ 0, /* reason= */ 0);
verify(mController).showToast(anyString());
}
@Test
public void testOnSourceRemoved() {
mCallback.onSourceRemoved(mDevice, /* sourceId= */ 0, /* reason= */ 0);
verify(mController).handleSourceRemoved();
}
}

View File

@@ -16,29 +16,38 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static android.app.settings.SettingsEnums.AUDIO_STREAM_MAIN;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -46,6 +55,7 @@ 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;
@@ -139,17 +149,46 @@ public class AudioStreamsScanQrCodeControllerTest {
public void onPreferenceClick_hasFragment_launchSubSetting() {
mController.displayPreference(mScreen);
mController.setFragment(mFragment);
when(mFragment.getMetricsCategory()).thenReturn(AUDIO_STREAM_MAIN);
var listener = mPreference.getOnPreferenceClickListener();
assertThat(listener).isNotNull();
// mContext is not an Activity context, calling startActivity() from outside of an Activity
// context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this
// AndroidRuntimeException.
Context activityContext = mock(Context.class);
when(mPreference.getContext()).thenReturn(activityContext);
when(mPreference.getKey()).thenReturn(AudioStreamsScanQrCodeController.KEY);
var clicked = listener.onPreferenceClick(mPreference);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
ArgumentCaptor<Integer> requestCodeCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mFragment)
.startActivityForResult(intentCaptor.capture(), requestCodeCaptor.capture());
Intent intent = intentCaptor.getValue();
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(AudioStreamsQrCodeScanFragment.class.getName());
assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
.isEqualTo(R.string.audio_streams_main_page_scan_qr_code_title);
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0))
.isEqualTo(AUDIO_STREAM_MAIN);
int requestCode = requestCodeCaptor.getValue();
assertThat(requestCode).isEqualTo(REQUEST_SCAN_BT_BROADCAST_QR_CODE);
assertThat(clicked).isTrue();
}
@Test
public void updateVisibility_noConnected_invisible() {
mController.displayPreference(mScreen);
mController.mBluetoothCallback.onActiveDeviceChanged(mDevice, BluetoothProfile.LE_AUDIO);
mController.mBluetoothCallback.onProfileConnectionStateChanged(
mDevice,
BluetoothAdapter.STATE_DISCONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mPreference.isVisible()).isFalse();
}
@@ -158,7 +197,10 @@ public class AudioStreamsScanQrCodeControllerTest {
public void updateVisibility_hasConnected_visible() {
mController.displayPreference(mScreen);
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
mController.mBluetoothCallback.onActiveDeviceChanged(mDevice, BluetoothProfile.LE_AUDIO);
mController.mBluetoothCallback.onProfileConnectionStateChanged(
mDevice,
BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mPreference.isVisible()).isTrue();
}