[Audiosharing] Impl audio sharing feature provider in Settings

Add createAvailableMediaDeviceGroupController interface to provide
different controller in Settings and SettingsGoogle.

Bug: 324023639
Test: atest
Change-Id: Ibf2ea2620c878e609eb937ff6947f5aaa0b89e7a
This commit is contained in:
Yiyi Shen
2024-02-23 15:11:11 +08:00
parent 3bc291a3c5
commit 42b8fbb74f
9 changed files with 136 additions and 197 deletions

View File

@@ -41,8 +41,7 @@
<PreferenceCategory <PreferenceCategory
android:key="available_device_list" android:key="available_device_list"
android:title="@string/connected_device_media_device_title" android:title="@string/connected_device_media_device_title"/>
settings:controller="com.android.settings.connecteddevice.AvailableMediaDeviceGroupController" />
<PreferenceCategory <PreferenceCategory
android:key="connected_device_list" android:key="connected_device_list"

View File

@@ -17,18 +17,17 @@ package com.android.settings.connecteddevice;
import static com.android.settingslib.Utils.isAudioModeOngoingCall; import static com.android.settingslib.Utils.isAudioModeOngoingCall;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@@ -38,138 +37,66 @@ import com.android.settings.accessibility.HearingAidUtils;
import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater; import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.BluetoothDeviceUpdater;
import com.android.settings.bluetooth.Utils; import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** /**
* Controller to maintain the {@link androidx.preference.PreferenceGroup} for all available media * Controller to maintain the {@link androidx.preference.PreferenceGroup} for all available media
* devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} * devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference}
*/ */
public class AvailableMediaDeviceGroupController extends BasePreferenceController public class AvailableMediaDeviceGroupController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback, BluetoothCallback { implements DefaultLifecycleObserver, DevicePreferenceCallback, BluetoothCallback {
private static final boolean DEBUG = BluetoothUtils.D; private static final boolean DEBUG = BluetoothUtils.D;
private static final String TAG = "AvailableMediaDeviceGroupController"; private static final String TAG = "AvailableMediaDeviceGroupController";
private static final String KEY = "available_device_list"; private static final String KEY = "available_device_list";
@VisibleForTesting PreferenceGroup mPreferenceGroup; @VisibleForTesting @Nullable PreferenceGroup mPreferenceGroup;
@VisibleForTesting LocalBluetoothManager mLocalBluetoothManager; @VisibleForTesting LocalBluetoothManager mLocalBluetoothManager;
private final Executor mExecutor; @Nullable private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @Nullable private FragmentManager mFragmentManager;
private FragmentManager mFragmentManager;
private BluetoothLeBroadcastAssistant.Callback mAssistantCallback =
new BluetoothLeBroadcastAssistant.Callback() {
@Override
public void onSearchStarted(int reason) {}
@Override public AvailableMediaDeviceGroupController(
public void onSearchStartFailed(int reason) {} Context context,
@Nullable DashboardFragment fragment,
@Override @Nullable Lifecycle lifecycle) {
public void onSearchStopped(int reason) {}
@Override
public void onSearchStopFailed(int reason) {}
@Override
public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
@Override
public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
mBluetoothDeviceUpdater.forceUpdate();
}
@Override
public void onSourceAddFailed(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastMetadata source,
int reason) {}
@Override
public void onSourceModified(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onSourceModifyFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onSourceRemoved(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
mBluetoothDeviceUpdater.forceUpdate();
}
@Override
public void onSourceRemoveFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onReceiveStateChanged(
BluetoothDevice sink,
int sourceId,
BluetoothLeBroadcastReceiveState state) {}
};
public AvailableMediaDeviceGroupController(Context context) {
super(context, KEY); super(context, KEY);
if (fragment != null) {
init(fragment);
}
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mLocalBluetoothManager = Utils.getLocalBtManager(mContext); mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
mExecutor = Executors.newSingleThreadExecutor();
} }
@Override @Override
public void onStart() { public void onStart(@NonNull LifecycleOwner owner) {
if (mLocalBluetoothManager == null) { if (mLocalBluetoothManager == null) {
Log.e(TAG, "onStart() Bluetooth is not supported on this device"); Log.e(TAG, "onStart() Bluetooth is not supported on this device");
return; return;
} }
if (AudioSharingUtils.isFeatureEnabled()) {
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager
.getProfileManager()
.getLeAudioBroadcastAssistantProfile();
if (assistant != null) {
if (DEBUG) {
Log.d(TAG, "onStart() Register callbacks for assistant.");
}
assistant.registerServiceCallBack(mExecutor, mAssistantCallback);
}
}
mBluetoothDeviceUpdater.registerCallback();
mLocalBluetoothManager.getEventManager().registerCallback(this); mLocalBluetoothManager.getEventManager().registerCallback(this);
if (mBluetoothDeviceUpdater != null) {
mBluetoothDeviceUpdater.registerCallback();
mBluetoothDeviceUpdater.refreshPreference(); mBluetoothDeviceUpdater.refreshPreference();
} }
}
@Override @Override
public void onStop() { public void onStop(@NonNull LifecycleOwner owner) {
if (mLocalBluetoothManager == null) { if (mLocalBluetoothManager == null) {
Log.e(TAG, "onStop() Bluetooth is not supported on this device"); Log.e(TAG, "onStop() Bluetooth is not supported on this device");
return; return;
} }
if (AudioSharingUtils.isFeatureEnabled()) { if (mBluetoothDeviceUpdater != null) {
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager
.getProfileManager()
.getLeAudioBroadcastAssistantProfile();
if (assistant != null) {
if (DEBUG) {
Log.d(TAG, "onStop() Register callbacks for assistant.");
}
assistant.unregisterServiceCallBack(mAssistantCallback);
}
}
mBluetoothDeviceUpdater.unregisterCallback(); mBluetoothDeviceUpdater.unregisterCallback();
}
mLocalBluetoothManager.getEventManager().unregisterCallback(this); mLocalBluetoothManager.getEventManager().unregisterCallback(this);
} }
@@ -178,14 +105,18 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle
super.displayPreference(screen); super.displayPreference(screen);
mPreferenceGroup = screen.findPreference(KEY); mPreferenceGroup = screen.findPreference(KEY);
if (mPreferenceGroup != null) {
mPreferenceGroup.setVisible(false); mPreferenceGroup.setVisible(false);
}
if (isAvailable()) { if (isAvailable()) {
updateTitle(); updateTitle();
if (mBluetoothDeviceUpdater != null) {
mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
mBluetoothDeviceUpdater.forceUpdate(); mBluetoothDeviceUpdater.forceUpdate();
} }
} }
}
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
@@ -201,19 +132,23 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle
@Override @Override
public void onDeviceAdded(Preference preference) { public void onDeviceAdded(Preference preference) {
if (mPreferenceGroup != null) {
if (mPreferenceGroup.getPreferenceCount() == 0) { if (mPreferenceGroup.getPreferenceCount() == 0) {
mPreferenceGroup.setVisible(true); mPreferenceGroup.setVisible(true);
} }
mPreferenceGroup.addPreference(preference); mPreferenceGroup.addPreference(preference);
} }
}
@Override @Override
public void onDeviceRemoved(Preference preference) { public void onDeviceRemoved(Preference preference) {
if (mPreferenceGroup != null) {
mPreferenceGroup.removePreference(preference); mPreferenceGroup.removePreference(preference);
if (mPreferenceGroup.getPreferenceCount() == 0) { if (mPreferenceGroup.getPreferenceCount() == 0) {
mPreferenceGroup.setVisible(false); mPreferenceGroup.setVisible(false);
} }
} }
}
public void init(DashboardFragment fragment) { public void init(DashboardFragment fragment) {
mFragmentManager = fragment.getParentFragmentManager(); mFragmentManager = fragment.getParentFragmentManager();
@@ -253,6 +188,7 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle
} }
private void updateTitle() { private void updateTitle() {
if (mPreferenceGroup != null) {
if (isAudioModeOngoingCall(mContext)) { if (isAudioModeOngoingCall(mContext)) {
// in phone call // in phone call
mPreferenceGroup.setTitle( mPreferenceGroup.setTitle(
@@ -263,4 +199,5 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle
mContext.getString(R.string.connected_device_media_device_title)); mContext.getString(R.string.connected_device_media_device_title));
} }
} }
}
} }

View File

@@ -24,12 +24,10 @@ import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.core.SettingsUIDeviceConfig; import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
@@ -38,6 +36,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.slices.SlicePreferenceController; import com.android.settings.slices.SlicePreferenceController;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils; import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList; import java.util.ArrayList;
@@ -92,7 +91,6 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
+ ", action : " + ", action : "
+ action); + action);
} }
use(AvailableMediaDeviceGroupController.class).init(this);
use(ConnectedDeviceGroupController.class).init(this); use(ConnectedDeviceGroupController.class).init(this);
use(PreviouslyConnectedDevicePreferenceController.class).init(this); use(PreviouslyConnectedDevicePreferenceController.class).init(this);
use(SlicePreferenceController.class) use(SlicePreferenceController.class)
@@ -124,16 +122,18 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
@Nullable ConnectedDeviceDashboardFragment fragment, @Nullable ConnectedDeviceDashboardFragment fragment,
@Nullable Lifecycle lifecycle) { @Nullable Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
if (AudioSharingUtils.isFeatureEnabled()) { AbstractPreferenceController availableMediaController =
FeatureFactory.getFeatureFactory()
.getAudioSharingFeatureProvider()
.createAvailableMediaDeviceGroupController(context, fragment, lifecycle);
controllers.add(availableMediaController);
AbstractPreferenceController audioSharingController = AbstractPreferenceController audioSharingController =
FeatureFactory.getFeatureFactory() FeatureFactory.getFeatureFactory()
.getAudioSharingFeatureProvider() .getAudioSharingFeatureProvider()
.createAudioSharingDevicePreferenceController( .createAudioSharingDevicePreferenceController(context, fragment, lifecycle);
context, fragment, lifecycle);
if (audioSharingController != null) { if (audioSharingController != null) {
controllers.add(audioSharingController); controllers.add(audioSharingController);
} }
}
return controllers; return controllers;
} }

View File

@@ -20,12 +20,12 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
/** Feature provider for the audio sharing related features, */ /** Feature provider for the audio sharing related features, */
public interface AudioSharingFeatureProvider { public interface AudioSharingFeatureProvider {
@@ -37,6 +37,12 @@ public interface AudioSharingFeatureProvider {
@Nullable DashboardFragment fragment, @Nullable DashboardFragment fragment,
@Nullable Lifecycle lifecycle); @Nullable Lifecycle lifecycle);
/** Create available media device preference controller. */
AbstractPreferenceController createAvailableMediaDeviceGroupController(
@NonNull Context context,
@Nullable DashboardFragment fragment,
@Nullable Lifecycle lifecycle);
/** /**
* Check if the device match the audio sharing filter. * Check if the device match the audio sharing filter.
* *

View File

@@ -20,12 +20,13 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import com.android.settings.connecteddevice.AvailableMediaDeviceGroupController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class AudioSharingFeatureProviderImpl implements AudioSharingFeatureProvider { public class AudioSharingFeatureProviderImpl implements AudioSharingFeatureProvider {
@@ -38,6 +39,14 @@ public class AudioSharingFeatureProviderImpl implements AudioSharingFeatureProvi
return null; return null;
} }
@Override
public AbstractPreferenceController createAvailableMediaDeviceGroupController(
@NonNull Context context,
@Nullable DashboardFragment fragment,
@Nullable Lifecycle lifecycle) {
return new AvailableMediaDeviceGroupController(context, fragment, lifecycle);
}
@Override @Override
public boolean isAudioSharingFilterMatched( public boolean isAudioSharingFilterMatched(
@NonNull CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) { @NonNull CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {

View File

@@ -22,27 +22,23 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@@ -51,19 +47,16 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater; import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
import com.android.settings.bluetooth.Utils; import com.android.settings.bluetooth.Utils;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowAudioManager; import com.android.settings.testutils.shadow.ShadowAudioManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidInfo; import com.android.settingslib.bluetooth.HearingAidInfo;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@@ -76,16 +69,12 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.concurrent.Executor;
/** Tests for {@link AvailableMediaDeviceGroupController}. */ /** Tests for {@link AvailableMediaDeviceGroupController}. */
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config( @Config(
shadows = { shadows = {
ShadowAudioManager.class, ShadowAudioManager.class,
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class, ShadowBluetoothUtils.class,
ShadowAlertDialogCompat.class, ShadowAlertDialogCompat.class,
}) })
@@ -105,9 +94,7 @@ public class AvailableMediaDeviceGroupControllerTest {
@Mock private PackageManager mPackageManager; @Mock private PackageManager mPackageManager;
@Mock private BluetoothEventManager mEventManager; @Mock private BluetoothEventManager mEventManager;
@Mock private LocalBluetoothManager mLocalBluetoothManager; @Mock private LocalBluetoothManager mLocalBluetoothManager;
@Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager; @Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private CachedBluetoothDevice mCachedBluetoothDevice;
private PreferenceGroup mPreferenceGroup; private PreferenceGroup mPreferenceGroup;
@@ -115,13 +102,16 @@ public class AvailableMediaDeviceGroupControllerTest {
private Preference mPreference; private Preference mPreference;
private AvailableMediaDeviceGroupController mAvailableMediaDeviceGroupController; private AvailableMediaDeviceGroupController mAvailableMediaDeviceGroupController;
private AudioManager mAudioManager; private AudioManager mAudioManager;
private ShadowBluetoothAdapter mShadowBluetoothAdapter; private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mPreference = new Preference(mContext); mPreference = new Preference(mContext);
mPreference.setKey(PREFERENCE_KEY_1); mPreference.setKey(PREFERENCE_KEY_1);
mPreferenceGroup = spy(new PreferenceScreen(mContext, null)); mPreferenceGroup = spy(new PreferenceScreen(mContext, null));
@@ -130,24 +120,17 @@ public class AvailableMediaDeviceGroupControllerTest {
doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mLocalBluetoothManager = Utils.getLocalBtManager(mContext); mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
mAudioManager = mContext.getSystemService(AudioManager.class); mAudioManager = mContext.getSystemService(AudioManager.class);
doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager();
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mCachedDeviceManager.findDevice(any(BluetoothDevice.class))) when(mCachedDeviceManager.findDevice(any(BluetoothDevice.class)))
.thenReturn(mCachedBluetoothDevice); .thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
mAvailableMediaDeviceGroupController = mAvailableMediaDeviceGroupController =
spy(new AvailableMediaDeviceGroupController(mContext)); spy(new AvailableMediaDeviceGroupController(mContext, null, mLifecycle));
mAvailableMediaDeviceGroupController.setBluetoothDeviceUpdater( mAvailableMediaDeviceGroupController.setBluetoothDeviceUpdater(
mAvailableMediaBluetoothDeviceUpdater); mAvailableMediaBluetoothDeviceUpdater);
mAvailableMediaDeviceGroupController.setFragmentManager( mAvailableMediaDeviceGroupController.setFragmentManager(
@@ -197,7 +180,7 @@ public class AvailableMediaDeviceGroupControllerTest {
@Test @Test
public void testRegister() { public void testRegister() {
// register the callback in onStart() // register the callback in onStart()
mAvailableMediaDeviceGroupController.onStart(); mAvailableMediaDeviceGroupController.onStart(mLifecycleOwner);
verify(mAvailableMediaBluetoothDeviceUpdater).registerCallback(); verify(mAvailableMediaBluetoothDeviceUpdater).registerCallback();
verify(mLocalBluetoothManager.getEventManager()) verify(mLocalBluetoothManager.getEventManager())
@@ -205,36 +188,15 @@ public class AvailableMediaDeviceGroupControllerTest {
verify(mAvailableMediaBluetoothDeviceUpdater).refreshPreference(); verify(mAvailableMediaBluetoothDeviceUpdater).refreshPreference();
} }
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void testRegister_audioSharingOn() {
setUpBroadcast();
// register the callback in onStart()
mAvailableMediaDeviceGroupController.onStart();
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test @Test
public void testUnregister() { public void testUnregister() {
// unregister the callback in onStop() // unregister the callback in onStop()
mAvailableMediaDeviceGroupController.onStop(); mAvailableMediaDeviceGroupController.onStop(mLifecycleOwner);
verify(mAvailableMediaBluetoothDeviceUpdater).unregisterCallback(); verify(mAvailableMediaBluetoothDeviceUpdater).unregisterCallback();
verify(mLocalBluetoothManager.getEventManager()) verify(mLocalBluetoothManager.getEventManager())
.unregisterCallback(any(BluetoothCallback.class)); .unregisterCallback(any(BluetoothCallback.class));
} }
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void testUnregister_audioSharingOn() {
setUpBroadcast();
// unregister the callback in onStop()
mAvailableMediaDeviceGroupController.onStop();
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test @Test
public void testGetAvailabilityStatus_noBluetoothFeature_returnUnSupported() { public void testGetAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
@@ -274,7 +236,7 @@ public class AvailableMediaDeviceGroupControllerTest {
mAvailableMediaDeviceGroupController.mLocalBluetoothManager = null; mAvailableMediaDeviceGroupController.mLocalBluetoothManager = null;
// Shouldn't crash // Shouldn't crash
mAvailableMediaDeviceGroupController.onStart(); mAvailableMediaDeviceGroupController.onStart(mLifecycleOwner);
} }
@Test @Test
@@ -282,7 +244,7 @@ public class AvailableMediaDeviceGroupControllerTest {
mAvailableMediaDeviceGroupController.mLocalBluetoothManager = null; mAvailableMediaDeviceGroupController.mLocalBluetoothManager = null;
// Shouldn't crash // Shouldn't crash
mAvailableMediaDeviceGroupController.onStop(); mAvailableMediaDeviceGroupController.onStop(mLifecycleOwner);
} }
@Test @Test
@@ -300,19 +262,4 @@ public class AvailableMediaDeviceGroupControllerTest {
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog.isShowing()).isTrue(); assertThat(dialog.isShowing()).isTrue();
} }
private void setUpBroadcast() {
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
doNothing()
.when(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
doNothing()
.when(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
} }

View File

@@ -20,19 +20,25 @@ import static com.android.settings.connecteddevice.ConnectedDeviceDashboardFragm
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.connecteddevice.fastpair.FastPairDeviceUpdater;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerListHelper; import com.android.settings.core.PreferenceControllerListHelper;
import com.android.settings.flags.Flags;
import com.android.settings.slices.SlicePreferenceController; import com.android.settings.slices.SlicePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowConnectivityManager; import com.android.settings.testutils.shadow.ShadowConnectivityManager;
import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -60,6 +66,8 @@ public class ConnectedDeviceDashboardFragmentTest {
@Rule @Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String KEY_NEARBY_DEVICES = "bt_nearby_slice"; private static final String KEY_NEARBY_DEVICES = "bt_nearby_slice";
private static final String KEY_DISCOVERABLE_FOOTER = "discoverable_footer"; private static final String KEY_DISCOVERABLE_FOOTER = "discoverable_footer";
private static final String KEY_SAVED_DEVICE_SEE_ALL = "previously_connected_devices_see_all"; private static final String KEY_SAVED_DEVICE_SEE_ALL = "previously_connected_devices_see_all";
@@ -75,8 +83,11 @@ public class ConnectedDeviceDashboardFragmentTest {
private static final String TEST_ACTION = "com.testapp.settings.ACTION_START"; private static final String TEST_ACTION = "com.testapp.settings.ACTION_START";
@Mock private PackageManager mPackageManager; @Mock private PackageManager mPackageManager;
@Mock private FastPairDeviceUpdater mFastPairDeviceUpdater;
private Context mContext; private Context mContext;
private ConnectedDeviceDashboardFragment mFragment; private ConnectedDeviceDashboardFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
private AvailableMediaDeviceGroupController mMediaDeviceGroupController;
@Before @Before
public void setUp() { public void setUp() {
@@ -84,6 +95,22 @@ public class ConnectedDeviceDashboardFragmentTest {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mFragment = new ConnectedDeviceDashboardFragment(); mFragment = new ConnectedDeviceDashboardFragment();
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mFeatureFactory
.getFastPairFeatureProvider()
.getFastPairDeviceUpdater(
any(Context.class), any(DevicePreferenceCallback.class)))
.thenReturn(mFastPairDeviceUpdater);
when(mFeatureFactory
.getAudioSharingFeatureProvider()
.createAudioSharingDevicePreferenceController(mContext, null, null))
.thenReturn(null);
mMediaDeviceGroupController = new AvailableMediaDeviceGroupController(mContext, null, null);
when(mFeatureFactory
.getAudioSharingFeatureProvider()
.createAvailableMediaDeviceGroupController(mContext, null, null))
.thenReturn(mMediaDeviceGroupController);
doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
} }

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.connecteddevice.AvailableMediaDeviceGroupController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -59,6 +60,14 @@ public class AudioSharingFeatureProviderImplTest {
.isNull(); .isNull();
} }
@Test
public void createAvailableMediaDeviceGroupController_returnsNull() {
assertThat(
mFeatureProvider.createAvailableMediaDeviceGroupController(
mContext, /* fragment= */ null, /* lifecycle= */ null))
.isInstanceOf(AvailableMediaDeviceGroupController.class);
}
@Test @Test
public void isAudioSharingFilterMatched_returnsFalse() { public void isAudioSharingFilterMatched_returnsFalse() {
assertThat(mFeatureProvider.isAudioSharingFilterMatched(mCachedDevice, mLocalBtManager)) assertThat(mFeatureProvider.isAudioSharingFilterMatched(mCachedDevice, mLocalBtManager))

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 The Android Open Source Project * Copyright (C) 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** Robolectric shadow for the AudioManager. */
@Implements(value = AudioManager.class) @Implements(value = AudioManager.class)
public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManager { public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManager {
private int mRingerMode; private int mRingerMode;
@@ -58,11 +59,13 @@ public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManag
mRingerMode = mode; mRingerMode = mode;
} }
/** Register audio device callback. */
@Implementation @Implementation
public void registerAudioDeviceCallback(AudioDeviceCallback callback, Handler handler) { public void registerAudioDeviceCallback(AudioDeviceCallback callback, Handler handler) {
mDeviceCallbacks.add(callback); mDeviceCallbacks.add(callback);
} }
/** Unregister audio device callback. */
@Implementation @Implementation
public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) { public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) {
if (mDeviceCallbacks.contains(callback)) { if (mDeviceCallbacks.contains(callback)) {
@@ -79,10 +82,12 @@ public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManag
return mMusicActiveRemotely; return mMusicActiveRemotely;
} }
/** Set output device. */
public void setOutputDevice(int deviceCodes) { public void setOutputDevice(int deviceCodes) {
mDeviceCodes = deviceCodes; mDeviceCodes = deviceCodes;
} }
/** Get devices for stream. */
@Implementation @Implementation
public int getDevicesForStream(int streamType) { public int getDevicesForStream(int streamType) {
switch (streamType) { switch (streamType) {