diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java index c70a56ab866..a9e89e914cb 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingController.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java @@ -17,8 +17,10 @@ package com.android.settings.bluetooth; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; +import android.provider.DeviceConfig; import android.text.Editable; import android.util.Log; import android.widget.CompoundButton; @@ -28,6 +30,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.bluetooth.BluetoothPairingDialogFragment.BluetoothPairingDialogListener; +import com.android.settings.core.SettingsUIDeviceConfig; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfile; @@ -54,7 +57,7 @@ public class BluetoothPairingController implements OnCheckedChangeListener, private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6; // Bluetooth dependencies for the connection we are trying to establish - private LocalBluetoothManager mBluetoothManager; + LocalBluetoothManager mBluetoothManager; private BluetoothDevice mDevice; @VisibleForTesting int mType; @@ -66,6 +69,8 @@ public class BluetoothPairingController implements OnCheckedChangeListener, private LocalBluetoothProfile mPbapClientProfile; private boolean mPbapAllowed; private boolean mIsCoordinatedSetMember; + private boolean mIsLeAudio; + private boolean mIsLeContactSharingEnabled; /** * Creates an instance of a BluetoothPairingController. @@ -92,10 +97,26 @@ public class BluetoothPairingController implements OnCheckedChangeListener, mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice); mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile(); mPasskeyFormatted = formatKey(mPasskey); + final CachedBluetoothDevice cachedDevice = mBluetoothManager.getCachedDeviceManager().findDevice(mDevice); - mIsCoordinatedSetMember = cachedDevice != null - ? cachedDevice.isCoordinatedSetMemberDevice() : false; + + mIsCoordinatedSetMember = false; + mIsLeAudio = false; + mIsLeContactSharingEnabled = true; + if (cachedDevice != null) { + mIsCoordinatedSetMember = cachedDevice.isCoordinatedSetMemberDevice(); + + for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) { + if (profile.getProfileId() == BluetoothProfile.LE_AUDIO) { + mIsLeAudio = true; + } + } + + mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true); + Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED is " + mIsLeContactSharingEnabled); + } } @Override @@ -180,6 +201,30 @@ public class BluetoothPairingController implements OnCheckedChangeListener, return mPbapClientProfile != null && mPbapClientProfile.isProfileReady(); } + @VisibleForTesting + boolean isLeAudio() { + return mIsLeAudio; + } + + @VisibleForTesting + boolean isLeContactSharingEnabled() { + return mIsLeContactSharingEnabled; + } + + /** + * A method whether the device allows to show the le audio's contact sharing. + * + * @return A boolean whether the device allows to show the contact sharing. + */ + public boolean isContactSharingVisible() { + boolean isContactSharingVisible = !isProfileReady(); + // If device do not support the ContactSharing of LE audio device, hiding ContactSharing UI + if (isLeAudio() && !isLeContactSharingEnabled()) { + isContactSharingVisible = false; + } + return isContactSharingVisible; + } + /** * A method for querying if the bluetooth device has access to contacts on the device. * @@ -447,4 +492,9 @@ public class BluetoothPairingController implements OnCheckedChangeListener, public boolean deviceEquals(BluetoothDevice device) { return mDevice == device; } + + @VisibleForTesting + void mockPbapClientProfile(LocalBluetoothProfile mockPbapClientProfile) { + mPbapClientProfile = mockPbapClientProfile; + } } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java index 8dd00b389ed..bae2471dd9b 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java @@ -254,8 +254,8 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i mPairingController.getDeviceName())); EditText pairingView = (EditText) view.findViewById(R.id.text); - contactSharing.setVisibility(mPairingController.isProfileReady() - ? View.GONE : View.VISIBLE); + contactSharing.setVisibility( + mPairingController.isContactSharingVisible() ? View.VISIBLE : View.GONE); mPairingController.setContactSharingState(); contactSharing.setOnCheckedChangeListener(mPairingController); contactSharing.setChecked(mPairingController.getContactSharingState()); @@ -346,7 +346,7 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i mPairingController.getDeviceName())); contactSharing.setVisibility( - mPairingController.isProfileReady() ? View.GONE : View.VISIBLE); + mPairingController.isContactSharingVisible() ? View.VISIBLE : View.GONE); mPairingController.setContactSharingState(); contactSharing.setChecked(mPairingController.getContactSharingState()); contactSharing.setOnCheckedChangeListener(mPairingController); @@ -363,5 +363,4 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i ? View.VISIBLE : View.GONE); return view; } - } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java index 88e15ebc13e..a7dc17da0e0 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java @@ -17,16 +17,28 @@ package com.android.settings.bluetooth; import static android.bluetooth.BluetoothDevice.PAIRING_VARIANT_CONSENT; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.Parcel; +import com.android.settings.core.SettingsUIDeviceConfig; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import org.junit.Before; import org.junit.Test; @@ -36,16 +48,36 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; + +import java.util.ArrayList; +import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBluetoothAdapter.class}) +@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class, + ShadowDeviceConfig.class}) public class BluetoothPairingControllerTest { private final BluetoothClass mBluetoothClass = createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); + + @Mock + private CachedBluetoothDeviceManager mCachedDeviceManager; + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private LocalBluetoothProfileManager mLocalBluetoothProfileManager; @Mock private BluetoothDevice mBluetoothDevice; + @Mock + private CachedBluetoothDevice mCachedDevice; + @Mock + private LocalBluetoothProfile mLocalBluetoothProfile; + @Mock + private LocalBluetoothProfile mPbapLocalBluetoothProfile; + private Context mContext; private BluetoothPairingController mBluetoothPairingController; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; private BluetoothClass createBtClass(int deviceClass) { Parcel p = Parcel.obtain(); @@ -57,14 +89,32 @@ public class BluetoothPairingControllerTest { return bluetoothClass; } + private BluetoothPairingController createBluetoothPairingController() { + final Intent intent = new Intent(); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + return new BluetoothPairingController(intent, mContext); + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - final Intent intent = new Intent(); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); - mBluetoothPairingController = new BluetoothPairingController(intent, mContext); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mShadowBluetoothAdapter.setEnabled(true); + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; + mLocalBluetoothManager = Utils.getLocalBtManager(mContext); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); + when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice); + List localBluetoothProfiles = new ArrayList<>(); + mockIsLeAudio(false); + localBluetoothProfiles.add(mLocalBluetoothProfile); + when(mCachedDevice.getProfiles()).thenReturn(localBluetoothProfiles); + when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); } @Test @@ -108,4 +158,124 @@ public class BluetoothPairingControllerTest { verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); } + + @Test + public void isLeAudio_noLeProfile_returnsFalse() { + mockIsLeAudio(false); + + mBluetoothPairingController = createBluetoothPairingController(); + + assertThat(mBluetoothPairingController.isLeAudio()).isFalse(); + } + + @Test + public void isLeAudio_isLeProfile_returnsTrue() { + mockIsLeAudio(true); + + mBluetoothPairingController = createBluetoothPairingController(); + + assertThat(mBluetoothPairingController.isLeAudio()).isTrue(); + } + + @Test + public void isLeContactSharingEnabled_configIsFalse_returnsFalse() { + mockIsLeContactSharingEnabled(false); + + mBluetoothPairingController = createBluetoothPairingController(); + + assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isFalse(); + } + + @Test + public void isLeContactSharingEnabled_configIsTrue_returnsTrue() { + mockIsLeContactSharingEnabled(true); + + mBluetoothPairingController = createBluetoothPairingController(); + + assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isTrue(); + } + + @Test + public void isContactSharingVisible_profileIsNotReady_returnsTrue() { + // isProfileReady=false, isLeAudio=false, isLeContactSharingEnabled=true + mockIsProfileReady(false); + mockIsLeAudio(false); + mockIsLeContactSharingEnabled(true); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); + + assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue(); + } + + @Test + public void isContactSharingVisible_profileIsReady_returnsFalse() { + // isProfileReady=true, isLeAudio=false, isLeContactSharingEnabled=true + mockIsProfileReady(true); + mockIsLeAudio(false); + mockIsLeContactSharingEnabled(true); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); + + assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse(); + } + + @Test + public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsReady_returnsFalse() { + // isProfileReady=true, isLeAudio=true, isLeContactSharingEnabled=true + mockIsProfileReady(true); + mockIsLeAudio(true); + mockIsLeContactSharingEnabled(true); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); + + assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse(); + } + + @Test + public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsNotReady_returnsTrue() { + // isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=true + mockIsProfileReady(false); + mockIsLeAudio(true); + mockIsLeContactSharingEnabled(true); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); + + assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue(); + } + + @Test + public void isContactSharingVisible_DeviceIsLeAndContactSharingIsNotEnabled_returnsFalse() { + // isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=false + mockIsProfileReady(false); + mockIsLeAudio(true); + mockIsLeContactSharingEnabled(false); + + mBluetoothPairingController = createBluetoothPairingController(); + mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile); + + assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse(); + } + + private void mockIsProfileReady(boolean mockValue) { + when(mPbapLocalBluetoothProfile.isProfileReady()).thenReturn(mockValue); + } + + private void mockIsLeAudio(boolean mockValue) { + int profileId = BluetoothProfile.HEADSET; + if (mockValue) { + profileId = BluetoothProfile.LE_AUDIO; + } + when(mLocalBluetoothProfile.getProfileId()).thenReturn(profileId); + } + + private void mockIsLeContactSharingEnabled(boolean mockValue) { + android.provider.DeviceConfig.setProperty( + android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, + /* value= */ mockValue ? "true" : "false", true); + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java index a53e693976e..874ddf0d158 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java @@ -274,13 +274,12 @@ public class BluetoothPairingDialogTest { } @Test - public void dialogShowsContactSharingCheckboxWhenBluetoothProfileNotReady() { + public void contactSharingCheckbox_conditionIsReady_showsUi() { // set the dialog variant to confirmation/consent when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG); - // set a fake device name and pretend the profile has not been set up for it when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME); - when(controller.isProfileReady()).thenReturn(false); + when(controller.isContactSharingVisible()).thenReturn(true); // build the fragment BluetoothPairingDialogFragment frag = makeFragment(); @@ -292,13 +291,12 @@ public class BluetoothPairingDialogTest { } @Test - public void dialogHidesContactSharingCheckboxWhenBluetoothProfileIsReady() { + public void contactSharingCheckbox_conditionIsNotReady_doesNotShowUi() { // set the dialog variant to confirmation/consent when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG); - // set a fake device name and pretend the profile has been set up for it when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME); - when(controller.isProfileReady()).thenReturn(true); + when(controller.isContactSharingVisible()).thenReturn(false); // build the fragment BluetoothPairingDialogFragment frag = makeFragment();