[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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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));
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user