diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index b44a93db62f..f330b19e2a1 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -66,6 +66,18 @@ + + + + 1) { + return mContext.getString(R.string.accessibility_hearingaid_more_device_summary, name); + } + if (subDevice != null && subDevice.isConnected()) { + return mContext.getString( + R.string.accessibility_hearingaid_left_and_right_side_device_summary, name); + } + if (side == HearingAidProfile.DeviceSide.SIDE_INVALID) { + return mContext.getString( + R.string.accessibility_hearingaid_active_device_summary, name); + } + return (side == HearingAidProfile.DeviceSide.SIDE_LEFT) + ? mContext.getString( + R.string.accessibility_hearingaid_left_side_device_summary, name) + : mContext.getString( + R.string.accessibility_hearingaid_right_side_device_summary, name); } public void setFragmentManager(FragmentManager fragmentManager) { @@ -150,33 +165,44 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC @VisibleForTesting CachedBluetoothDevice getConnectedHearingAidDevice() { - if (!mHearingAidProfileSupported) { + if (!isHearingAidProfileSupported()) { return null; } - if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { - return null; - } - final List deviceList = mLocalBluetoothManager.getProfileManager() - .getHearingAidProfile().getConnectedDevices(); - final Iterator it = deviceList.iterator(); - while (it.hasNext()) { - BluetoothDevice obj = (BluetoothDevice)it.next(); - if (!mLocalBluetoothManager.getCachedDeviceManager().isSubDevice(obj)) { - return mLocalBluetoothManager.getCachedDeviceManager().findDevice(obj); + + final CachedBluetoothDeviceManager deviceManager = + mLocalBluetoothManager.getCachedDeviceManager(); + final HearingAidProfile hearingAidProfile = + mLocalBluetoothManager.getProfileManager().getHearingAidProfile(); + final List deviceList = hearingAidProfile.getConnectedDevices(); + for (BluetoothDevice obj : deviceList) { + if (!deviceManager.isSubDevice(obj)) { + return deviceManager.findDevice(obj); } } return null; } + private int getConnectedHearingAidDeviceNum() { + if (!isHearingAidProfileSupported()) { + return 0; + } + + final CachedBluetoothDeviceManager deviceManager = + mLocalBluetoothManager.getCachedDeviceManager(); + final HearingAidProfile hearingAidProfile = + mLocalBluetoothManager.getProfileManager().getHearingAidProfile(); + final List deviceList = hearingAidProfile.getConnectedDevices(); + return (int) deviceList.stream() + .filter(device -> !deviceManager.isSubDevice(device)) + .count(); + } + private boolean isHearingAidProfileSupported() { if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { return false; } final List supportedList = mBluetoothAdapter.getSupportedProfiles(); - if (supportedList.contains(BluetoothProfile.HEARING_AID)) { - return true; - } - return false; + return supportedList.contains(BluetoothProfile.HEARING_AID); } private LocalBluetoothManager getLocalBluetoothManager() { diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java index 9c7aa58cf7c..06a71f06483 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java @@ -75,10 +75,8 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController if (TextUtils.isEmpty(summaryText)) { // If first summary is unavailable, not to show second summary. mHeaderController.setSecondSummary((CharSequence)null); - } else { - // If both the hearing aids are connected, two device status should be shown. - mHeaderController.setSecondSummary(mDeviceManager.getSubDeviceSummary(mCachedDevice)); } + mHeaderController.setLabel(mCachedDevice.getName()); mHeaderController.setIcon(pair.first); mHeaderController.setIconContentDescription(pair.second); diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java new file mode 100644 index 00000000000..71d60490f25 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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.bluetooth; + + +import android.content.Context; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * This class adds related tools preference. + */ +public class BluetoothDetailsRelatedToolsController extends BluetoothDetailsController{ + private static final String KEY_RELATED_TOOLS_GROUP = "bluetooth_related_tools"; + private static final String KEY_LIVE_CAPTION = "live_caption"; + + public BluetoothDetailsRelatedToolsController(Context context, + PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + lifecycle.addObserver(this); + } + + @Override + public boolean isAvailable() { + return mCachedDevice.isHearingAidDevice(); + } + + @Override + protected void init(PreferenceScreen screen) { + if (!mCachedDevice.isHearingAidDevice()) { + return; + } + + final PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey()); + final Preference liveCaptionPreference = screen.findPreference(KEY_LIVE_CAPTION); + if (!liveCaptionPreference.isVisible()) { + preferenceCategory.removePreference(liveCaptionPreference); + } + + if (preferenceCategory.getPreferenceCount() == 0) { + screen.removePreference(preferenceCategory); + } + } + + @Override + protected void refresh() {} + + @Override + public String getPreferenceKey() { + return KEY_RELATED_TOOLS_GROUP; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index fdf0f383dab..ca212f3b128 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -246,6 +246,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsRelatedToolsController(context, this, mCachedDevice, + lifecycle)); } return controllers; } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java index a45564812e2..1b8c8c3b5a7 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java @@ -18,9 +18,7 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,7 +46,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -63,7 +60,6 @@ import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Ignore @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class}) public class AccessibilityHearingAidPreferenceControllerTest { private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"; @@ -82,6 +78,8 @@ public class AccessibilityHearingAidPreferenceControllerTest { @Mock private CachedBluetoothDevice mCachedBluetoothDevice; @Mock + private CachedBluetoothDevice mCachedSubBluetoothDevice; + @Mock private CachedBluetoothDeviceManager mCachedDeviceManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; @@ -111,18 +109,54 @@ public class AccessibilityHearingAidPreferenceControllerTest { } @Test - public void onHearingAidStateChanged_connected_updateHearingAidSummary() { + public void getSummary_connectedHearingAidRightSide_connectedRightSideSummary() { + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_RIGHT); when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList()); mPreferenceController.onStart(); Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED); sendIntent(intent); - assertThat(mHearingAidPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME); + assertThat(mHearingAidPreference.getSummary().toString().contentEquals( + "TEST_HEARING_AID_BT_DEVICE_NAME, right only")).isTrue(); } @Test - public void onHearingAidStateChanged_disconnected_updateHearingAidSummary() { + public void getSummary_connectedHearingAidBothSide_connectedBothSideSummary() { + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + when(mCachedSubBluetoothDevice.isConnected()).thenReturn(true); + when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mCachedSubBluetoothDevice); + when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList()); + mPreferenceController.onStart(); + Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); + intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED); + sendIntent(intent); + + assertThat(mHearingAidPreference.getSummary().toString().contentEquals( + "TEST_HEARING_AID_BT_DEVICE_NAME, left and right")).isTrue(); + } + + @Test + public void getSummary_connectedMultipleHearingAids_connectedBothSideSummary() { + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + when(mCachedSubBluetoothDevice.isConnected()).thenReturn(true); + when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mCachedSubBluetoothDevice); + when(mHearingAidProfile.getConnectedDevices()).thenReturn( + generateMultipleHearingAidDeviceList()); + mPreferenceController.onStart(); + Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); + intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED); + sendIntent(intent); + + assertThat(mHearingAidPreference.getSummary().toString().contentEquals( + "TEST_HEARING_AID_BT_DEVICE_NAME +1 more")).isTrue(); + } + + @Test + public void getSummary_disconnectedHearingAid_disconnectedSummary() { mPreferenceController.onStart(); Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_DISCONNECTED); @@ -133,7 +167,7 @@ public class AccessibilityHearingAidPreferenceControllerTest { } @Test - public void onBluetoothStateChanged_bluetoothOff_updateHearingAidSummary() { + public void getSummary_bluetoothOff_disconnectedSummary() { mPreferenceController.onStart(); Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF); @@ -168,19 +202,14 @@ public class AccessibilityHearingAidPreferenceControllerTest { } @Test - public void onNotSupportHearingAidProfile_doNotDoReceiverOperation() { + public void onNotSupportHearingAidProfile_isNotAvailable() { //clear bluetooth supported profile mShadowBluetoothAdapter.clearSupportedProfiles(); mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext, HEARING_AID_PREFERENCE); mPreferenceController.setPreference(mHearingAidPreference); - //not call registerReceiver() - mPreferenceController.onStart(); - verify(mContext, never()).registerReceiver(any(), any()); - //not call unregisterReceiver() - mPreferenceController.onStop(); - verify(mContext, never()).unregisterReceiver(any()); + assertThat(mPreferenceController.isAvailable()).isFalse(); } @Test @@ -224,4 +253,11 @@ public class AccessibilityHearingAidPreferenceControllerTest { deviceList.add(mBluetoothDevice); return deviceList; } + + private List generateMultipleHearingAidDeviceList() { + final List deviceList = new ArrayList<>(2); + deviceList.add(mBluetoothDevice); + deviceList.add(mBluetoothDevice); + return deviceList; + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java new file mode 100644 index 00000000000..2035f061323 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link BluetoothDetailsRelatedToolsController}. */ +@RunWith(RobolectricTestRunner.class) +public class BluetoothDetailsRelatedToolsControllerTest extends BluetoothDetailsControllerTestBase { + private BluetoothDetailsRelatedToolsController mController; + + @Override + public void setUp() { + super.setUp(); + mController = new BluetoothDetailsRelatedToolsController(mContext, mFragment, mCachedDevice, + mLifecycle); + } + + @Test + public void isAvailable_isHearingAidDevice_available() { + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_notHearingAidDevice_notAvailable() { + when(mCachedDevice.isHearingAidDevice()).thenReturn(false); + + assertThat(mController.isAvailable()).isFalse(); + } +}