From 88f36dccc7f945f77b12312b564549a9091be1bf Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Wed, 31 Aug 2022 01:39:20 +0800 Subject: [PATCH 1/2] Fix after dis/reconnect HA, DUT will request to re-pair one side of HA Root Cause: Another side of HA jumps to connecting state after pair-other-ear dialog appears. Solution: Listen to onDeviceAttributesChanged() callback to know when the sub device gets connected, then to dismiss dialog. Bug: 236782832 Test: make RunSettingsRoboTests ROBOTEST_FILTER=HearingAidUtilsTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=HearingAidPairingDialogFragmentTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=AvailableMediaDeviceGroupControllerTest Change-Id: I8dafefbb05e7cf1cadf37a7acbb2c5d66f2d0a78 --- .../accessibility/HearingAidUtils.java | 2 +- .../HearingAidPairingDialogFragment.java | 58 ++++++++++++--- ...ityHearingAidPreferenceControllerTest.java | 1 + .../HearingAidPairingDialogFragmentTest.java | 70 ++++++++++++++++--- .../accessibility/HearingAidUtilsTest.java | 40 ++++++++++- ...ailableMediaDeviceGroupControllerTest.java | 14 +++- 6 files changed, 162 insertions(+), 23 deletions(-) diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java index a3d2c93f438..dcbac5b2b90 100644 --- a/src/com/android/settings/accessibility/HearingAidUtils.java +++ b/src/com/android/settings/accessibility/HearingAidUtils.java @@ -53,7 +53,7 @@ public final class HearingAidUtils { Log.w(TAG, "Can not launch hearing aid pairing dialog for invalid side"); return; } - HearingAidPairingDialogFragment.newInstance(device).show(fragmentManager, + HearingAidPairingDialogFragment.newInstance(device.getAddress()).show(fragmentManager, HearingAidPairingDialogFragment.TAG); } } diff --git a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java index 60a100d538a..5ae7c1711d8 100644 --- a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java +++ b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java @@ -18,6 +18,9 @@ package com.android.settings.bluetooth; import android.app.Dialog; import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; import android.os.Bundle; import androidx.annotation.NonNull; @@ -29,29 +32,59 @@ import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LocalBluetoothManager; /** * Provides a dialog to pair another side of hearing aid device. */ -public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment { +public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment implements + CachedBluetoothDevice.Callback { public static final String TAG = "HearingAidPairingDialogFragment"; - private static final String KEY_CACHED_DEVICE_SIDE = "cached_device_side"; + private static final String KEY_DEVICE_ADDRESS = "device_address"; + private LocalBluetoothManager mLocalBluetoothManager; + private CachedBluetoothDevice mDevice; /** * Creates a new {@link HearingAidPairingDialogFragment} and shows pair another side of hearing - * aid device according to {@code CachedBluetoothDevice} side. + * aid device according to {@code deviceAddress}. * - * @param device The remote Bluetooth device, that needs to be hearing aid device. + * @param deviceAddress The remote Bluetooth device address, that needs to be a hearing aid + * device. * @return a DialogFragment */ - public static HearingAidPairingDialogFragment newInstance(CachedBluetoothDevice device) { + public static HearingAidPairingDialogFragment newInstance(String deviceAddress) { Bundle args = new Bundle(1); - args.putInt(KEY_CACHED_DEVICE_SIDE, device.getDeviceSide()); + args.putString(KEY_DEVICE_ADDRESS, deviceAddress); final HearingAidPairingDialogFragment fragment = new HearingAidPairingDialogFragment(); fragment.setArguments(args); return fragment; } + @Override + public void onAttach(Context context) { + super.onAttach(context); + mLocalBluetoothManager = Utils.getLocalBtManager(context); + mDevice = getDevice(); + if (mDevice != null) { + mDevice.registerCallback(this); + } + } + + @Override + public void onDetach() { + super.onDetach(); + if (mDevice != null) { + mDevice.unregisterCallback(this); + } + } + + private CachedBluetoothDevice getDevice() { + final String deviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS); + final BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + deviceAddress); + return mLocalBluetoothManager.getCachedDeviceManager().findDevice(device); + } + @Override public int getMetricsCategory() { return SettingsEnums.DIALOG_ACCESSIBILITY_HEARING_AID_PAIR_ANOTHER; @@ -60,7 +93,7 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final int deviceSide = getArguments().getInt(KEY_CACHED_DEVICE_SIDE); + final int deviceSide = mDevice.getDeviceSide(); final int titleId = R.string.bluetooth_pair_other_ear_dialog_title; final int messageId = (deviceSide == HearingAidProfile.DeviceSide.SIDE_LEFT) ? R.string.bluetooth_pair_other_ear_dialog_left_ear_message @@ -72,8 +105,7 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment return new AlertDialog.Builder(getActivity()) .setTitle(titleId) .setMessage(messageId) - .setNegativeButton( - android.R.string.cancel, /* listener= */ null) + .setNegativeButton(android.R.string.cancel, /* listener= */ null) .setPositiveButton(pairBtnId, (dialog, which) -> positiveButtonListener()) .create(); } @@ -84,4 +116,12 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment .setSourceMetricsCategory(getMetricsCategory()) .launch(); } + + @Override + public void onDeviceAttributesChanged() { + final CachedBluetoothDevice subDevice = mDevice.getSubDevice(); + if (subDevice != null && subDevice.isConnectedHearingAidDevice()) { + this.dismiss(); + } + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java index affb28be97d..94483150caa 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java @@ -265,6 +265,7 @@ public class AccessibilityHearingAidPreferenceControllerTest { mBluetoothAdapter.enable(); mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME); } diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java index 5990a3db2a7..bda60d49995 100644 --- a/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java @@ -18,24 +18,38 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; 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.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.bluetooth.BluetoothPairingDetail; import com.android.settings.bluetooth.HearingAidPairingDialogFragment; +import com.android.settings.bluetooth.Utils; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.junit.Before; import org.junit.Rule; @@ -47,39 +61,57 @@ import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; /** Tests for {@link HearingAidPairingDialogFragment}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowAlertDialogCompat.class) +@Config(shadows = {ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class}) public class HearingAidPairingDialogFragmentTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - private static final String KEY_CACHED_DEVICE_SIDE = "cached_device_side"; + private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"; + private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private CachedBluetoothDevice mCachedSubBluetoothDevice; + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private CachedBluetoothDeviceManager mCachedDeviceManager; + private BluetoothAdapter mBluetoothAdapter; private FragmentActivity mActivity; private HearingAidPairingDialogFragment mFragment; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private BluetoothDevice mBluetoothDevice; + private FragmentManager mFragmentManager; @Before public void setUp() { - mFragment = spy(HearingAidPairingDialogFragment.newInstance(mCachedBluetoothDevice)); + setupEnvironment(); + mFragment = spy(HearingAidPairingDialogFragment.newInstance(TEST_DEVICE_ADDRESS)); mActivity = Robolectric.setupActivity(FragmentActivity.class); + mFragmentManager = mActivity.getSupportFragmentManager(); when(mFragment.getActivity()).thenReturn(mActivity); + doReturn(mFragmentManager).when(mFragment).getParentFragmentManager(); + mFragment.onAttach(mContext); } @Test public void newInstance_deviceSideRight_argumentSideRight() { when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( HearingAidProfile.DeviceSide.SIDE_RIGHT); + final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY); + dialog.show(); - mFragment = HearingAidPairingDialogFragment.newInstance(mCachedBluetoothDevice); - - final Bundle bundle = mFragment.getArguments(); - assertThat(bundle.getInt(KEY_CACHED_DEVICE_SIDE)).isEqualTo( - HearingAidProfile.DeviceSide.SIDE_RIGHT); + final String pairLeftString = mContext.getText( + R.string.bluetooth_pair_other_ear_dialog_left_ear_positive_button).toString(); + assertThat(dialog.getButton( + DialogInterface.BUTTON_POSITIVE).getText().toString()).isEqualTo(pairLeftString); } @Test @@ -109,4 +141,26 @@ public class HearingAidPairingDialogFragmentTest { assertThat(mFragment.getMetricsCategory()).isEqualTo( SettingsEnums.DIALOG_ACCESSIBILITY_HEARING_AID_PAIR_ANOTHER); } + + @Test + public void onDeviceAttributesChanged_subHearingAidDeviceConnected_dialogDismiss() { + when(mCachedSubBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mCachedSubBluetoothDevice); + + mFragment.onDeviceAttributesChanged(); + + verify(mFragment).dismiss(); + } + + private void setupEnvironment() { + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mLocalBluetoothManager = Utils.getLocalBtManager(mContext); + mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter); + mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); + mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java index efeb27f0686..6fdd210a860 100644 --- a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java @@ -20,13 +20,24 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; + import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; +import androidx.test.core.app.ApplicationProvider; +import com.android.settings.bluetooth.Utils; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.junit.Before; import org.junit.Rule; @@ -38,27 +49,40 @@ import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; /** Tests for {@link HearingAidUtils}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowAlertDialogCompat.class) +@Config(shadows = {ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class}) public class HearingAidUtilsTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + private final Context mContext = ApplicationProvider.getApplicationContext(); + + private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"; @Mock private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private CachedBluetoothDevice mSubCachedBluetoothDevice; - + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private CachedBluetoothDeviceManager mCachedDeviceManager; + private BluetoothDevice mBluetoothDevice; + private BluetoothAdapter mBluetoothAdapter; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; private FragmentManager mFragmentManager; @Before public void setUp() { + setupEnvironment(); final FragmentActivity mActivity = Robolectric.setupActivity(FragmentActivity.class); mFragmentManager = mActivity.getSupportFragmentManager(); ShadowAlertDialogCompat.reset(); + when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); } @Test @@ -123,4 +147,16 @@ public class HearingAidUtilsTest { final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog.isShowing()).isTrue(); } + + private void setupEnvironment() { + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mLocalBluetoothManager = Utils.getLocalBtManager(mContext); + mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter); + mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); + mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java index e1a3da6329a..2b6250396b1 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.PackageManager; @@ -50,6 +51,7 @@ 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.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -70,6 +72,7 @@ import org.robolectric.annotation.Config; ShadowBluetoothUtils.class}) public class AvailableMediaDeviceGroupControllerTest { + private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"; private static final String PREFERENCE_KEY_1 = "pref_key_1"; @Mock @@ -85,7 +88,9 @@ public class AvailableMediaDeviceGroupControllerTest { @Mock private BluetoothEventManager mEventManager; @Mock - private LocalBluetoothManager mLocalManager; + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private CachedBluetoothDeviceManager mCachedDeviceManager; @Mock private CachedBluetoothDevice mCachedBluetoothDevice; @@ -93,7 +98,6 @@ public class AvailableMediaDeviceGroupControllerTest { private Context mContext; private Preference mPreference; private AvailableMediaDeviceGroupController mAvailableMediaDeviceGroupController; - private LocalBluetoothManager mLocalBluetoothManager; private AudioManager mAudioManager; @Before @@ -112,10 +116,14 @@ public class AvailableMediaDeviceGroupControllerTest { when(mDashboardFragment.getParentFragmentManager()).thenReturn( mActivity.getSupportFragmentManager()); - ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager; + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; mLocalBluetoothManager = Utils.getLocalBtManager(mContext); mAudioManager = mContext.getSystemService(AudioManager.class); doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); + when(mCachedDeviceManager.findDevice(any(BluetoothDevice.class))).thenReturn( + mCachedBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); mAvailableMediaDeviceGroupController = spy( new AvailableMediaDeviceGroupController(mContext)); From 6e7a8422ac78416f5a00fa4a096c83b04fa74b00 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Wed, 31 Aug 2022 02:06:24 +0800 Subject: [PATCH 2/2] Fix 'Hearing aids' of Accessibility page didn't display connected info promptly when mobile restart Root Cause: get empty result in hearingAidProfile.getConnectedDevices() Solution: Need to listen to onServiceConnected() to wait for HearingAidService get connected Bug: 243489972 Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityHearingAidPreferenceControllerTest Change-Id: If6c3b2f3df81388c44e744e364e6258bde8bb645 --- ...ibilityHearingAidPreferenceController.java | 42 ++++++++++++------ ...ityHearingAidPreferenceControllerTest.java | 43 ++++++++++++------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java index 3dde687328c..4e933894db0 100644 --- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java +++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java @@ -43,6 +43,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; @@ -55,7 +56,8 @@ import java.util.concurrent.FutureTask; * Controller that shows and updates the bluetooth device name */ public class AccessibilityHearingAidPreferenceController extends BasePreferenceController - implements LifecycleObserver, OnStart, OnStop, BluetoothCallback { + implements LifecycleObserver, OnStart, OnStop, BluetoothCallback, + LocalBluetoothProfileManager.ServiceListener { private static final String TAG = "AccessibilityHearingAidPreferenceController"; private Preference mHearingAidPreference; @@ -84,12 +86,16 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC private final LocalBluetoothManager mLocalBluetoothManager; private final BluetoothAdapter mBluetoothAdapter; + private final LocalBluetoothProfileManager mProfileManager; + private final CachedBluetoothDeviceManager mCachedDeviceManager; private FragmentManager mFragmentManager; public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mLocalBluetoothManager = getLocalBluetoothManager(); + mProfileManager = mLocalBluetoothManager.getProfileManager(); + mCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager(); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } @@ -111,12 +117,14 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mHearingAidChangedReceiver, filter); mLocalBluetoothManager.getEventManager().registerCallback(this); + mProfileManager.addServiceListener(this); } @Override public void onStop() { mContext.unregisterReceiver(mHearingAidChangedReceiver); mLocalBluetoothManager.getEventManager().unregisterCallback(this); + mProfileManager.removeServiceListener(this); } @Override @@ -173,6 +181,22 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC } } + @Override + public void onServiceConnected() { + // Every registered ProfileService will callback. So we need to use isProfileReady() to + // check is the HearingAidService callback here, not other service. + // When hearing aids service connected, updating the UI. + if (mProfileManager.getHearingAidProfile().isProfileReady()) { + updateState(mHearingAidPreference); + mProfileManager.removeServiceListener(this); + } + } + + @Override + public void onServiceDisconnected() { + // Do nothing + } + public void setFragmentManager(FragmentManager fragmentManager) { mFragmentManager = fragmentManager; } @@ -183,14 +207,11 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC return null; } - final CachedBluetoothDeviceManager deviceManager = - mLocalBluetoothManager.getCachedDeviceManager(); - final HearingAidProfile hearingAidProfile = - mLocalBluetoothManager.getProfileManager().getHearingAidProfile(); + final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile(); final List deviceList = hearingAidProfile.getConnectedDevices(); for (BluetoothDevice obj : deviceList) { - if (!deviceManager.isSubDevice(obj)) { - return deviceManager.findDevice(obj); + if (!mCachedDeviceManager.isSubDevice(obj)) { + return mCachedDeviceManager.findDevice(obj); } } return null; @@ -201,13 +222,10 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC return 0; } - final CachedBluetoothDeviceManager deviceManager = - mLocalBluetoothManager.getCachedDeviceManager(); - final HearingAidProfile hearingAidProfile = - mLocalBluetoothManager.getProfileManager().getHearingAidProfile(); + final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile(); final List deviceList = hearingAidProfile.getConnectedDevices(); return (int) deviceList.stream() - .filter(device -> !deviceManager.isSubDevice(device)) + .filter(device -> !mCachedDeviceManager.isSubDevice(device)) .count(); } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java index 94483150caa..dc37d234ff3 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java @@ -28,7 +28,6 @@ import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Intent; @@ -74,7 +73,6 @@ public class AccessibilityHearingAidPreferenceControllerTest { private BluetoothAdapter mBluetoothAdapter; private ShadowBluetoothAdapter mShadowBluetoothAdapter; - private BluetoothManager mBluetoothManager; private BluetoothDevice mBluetoothDevice; private Activity mContext; private Preference mHearingAidPreference; @@ -101,8 +99,8 @@ public class AccessibilityHearingAidPreferenceControllerTest { MockitoAnnotations.initMocks(this); mShadowApplication = ShadowApplication.getInstance(); mContext = spy(Robolectric.setupActivity(Activity.class)); - setupBluetoothEnvironment(); - setupHearingAidEnvironment(); + setupEnvironment(); + mHearingAidPreference = new Preference(mContext); mHearingAidPreference.setKey(HEARING_AID_PREFERENCE); mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext, @@ -247,26 +245,39 @@ public class AccessibilityHearingAidPreferenceControllerTest { assertThat(dialog.isShowing()).isTrue(); } - private void setupBluetoothEnvironment() { + @Test + public void onServiceConnected_updateSummary() { + mPreferenceController.onStart(); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList()); + + mPreferenceController.onServiceConnected(); + + assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo( + "TEST_HEARING_AID_BT_DEVICE_NAME, left only"); + } + + private void setupEnvironment() { ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; mLocalBluetoothManager = Utils.getLocalBtManager(mContext); - mBluetoothManager = mContext.getSystemService(BluetoothManager.class); - mBluetoothAdapter = mBluetoothManager.getAdapter(); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter); + mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); + mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); + mBluetoothAdapter.enable(); + + doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); - doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); - } - - private void setupHearingAidEnvironment() { - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter); - mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); - mBluetoothAdapter.enable(); - mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME); + when(mHearingAidProfile.isProfileReady()).thenReturn(true); } private void sendIntent(Intent intent) {