OSDN Git Service

Add Hearing Aid UI into Settings-Accessibility App
authortimhypeng <timhypeng@google.com>
Thu, 14 Jun 2018 05:54:05 +0000 (13:54 +0800)
committertimhypeng <timhypeng@google.com>
Mon, 30 Jul 2018 08:48:39 +0000 (16:48 +0800)
- dynamically show/hide preference by HearingAid profile is supported or not
- add AccessibilityHearingAidPreferenceController to handle hearingAid preference
- add HearingAidDialogFragment to handle dialog behavior

Bug: 109948484
Test: make -j50 RunSettingsRoboTests

Change-Id: Ic55dde475dc40311f7e652f4a86d342597f09f0e

res/values/strings.xml
res/xml/accessibility_settings.xml
src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java [new file with mode: 0644]
src/com/android/settings/accessibility/AccessibilitySettings.java
src/com/android/settings/accessibility/HearingAidDialogFragment.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java [new file with mode: 0644]

index 06f4123..3198f98 100644 (file)
     <!-- Used in the Captions settings screen to control turning on/off the feature entirely -->
     <string name="accessibility_caption_master_switch_title">Use captions</string>
 
+    <!-- Button text for the accessibility dialog continue to the next screen for hearing aid. [CHAR LIMIT=32] -->
+    <string name="accessibility_hearingaid_instruction_continue_button">Continue</string>
     <!-- Title for the accessibility preference for hearing aid. [CHAR LIMIT=35] -->
     <string name="accessibility_hearingaid_title">Hearing aids</string>
     <!-- Summary for the accessibility preference for hearing aid when not connected. [CHAR LIMIT=50] -->
index 9dda18f..e36f578 100644 (file)
                 android:persistent="false"/>
 
         <Preference
+            android:key="hearing_aid_preference"
+            android:summary="@string/accessibility_hearingaid_not_connected_summary"
+            android:title="@string/accessibility_hearingaid_title"/>
+
+        <Preference
                 android:fragment="com.android.settings.accessibility.CaptionPropertiesFragment"
                 android:key="captioning_preference_screen"
                 android:title="@string/accessibility_captioning_title" />
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
new file mode 100644 (file)
index 0000000..b946e3e
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Controller that shows and updates the bluetooth device name
+ */
+public class AccessibilityHearingAidPreferenceController extends BasePreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
+    private static final String TAG = "AccessibilityHearingAidPreferenceController";
+    private Preference mHearingAidPreference;
+
+    private final BroadcastReceiver mHearingAidChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
+                final int state = intent.getIntExtra(BluetoothHearingAid.EXTRA_STATE,
+                        BluetoothHearingAid.STATE_DISCONNECTED);
+                if (state == BluetoothHearingAid.STATE_CONNECTED) {
+                    updateState(mHearingAidPreference);
+                } else {
+                    mHearingAidPreference
+                            .setSummary(R.string.accessibility_hearingaid_not_connected_summary);
+                }
+            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                        BluetoothAdapter.ERROR);
+                if (state != BluetoothAdapter.STATE_ON) {
+                    mHearingAidPreference
+                            .setSummary(R.string.accessibility_hearingaid_not_connected_summary);
+                }
+            }
+        }
+    };
+
+    private final LocalBluetoothManager mLocalBluetoothManager;
+    //cache value of supporting hearing aid or not
+    private boolean mHearingAidProfileSupported;
+    private FragmentManager mFragmentManager;
+
+    public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mLocalBluetoothManager = getLocalBluetoothManager();
+        mHearingAidProfileSupported = isHearingAidProfileSupported();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mHearingAidPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mHearingAidProfileSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public void onResume() {
+        if (mHearingAidProfileSupported) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+            mContext.registerReceiver(mHearingAidChangedReceiver, filter);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mHearingAidProfileSupported) {
+            mContext.unregisterReceiver(mHearingAidChangedReceiver);
+        }
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (TextUtils.equals(preference.getKey(), getPreferenceKey())){
+            final CachedBluetoothDevice device = getConnectedHearingAidDevice();
+            if (device == null) {
+                launchHearingAidInstructionDialog();
+            } else {
+                launchBluetoothDeviceDetailSetting(device);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        final CachedBluetoothDevice device = getConnectedHearingAidDevice();
+        if (device == null) {
+            return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary);
+        }
+        return device.getName();
+    }
+
+    public void setFragmentManager(FragmentManager fragmentManager) {
+        mFragmentManager = fragmentManager;
+    }
+
+    private CachedBluetoothDevice getConnectedHearingAidDevice() {
+        if (!mHearingAidProfileSupported) {
+            return null;
+        }
+        final LocalBluetoothAdapter localAdapter = mLocalBluetoothManager.getBluetoothAdapter();
+        if (!localAdapter.isEnabled()) {
+            return null;
+        }
+        final List<BluetoothDevice> deviceList = mLocalBluetoothManager.getProfileManager()
+                .getHearingAidProfile().getConnectedDevices();
+        final Iterator it = deviceList.iterator();
+        if (it.hasNext()) {
+            BluetoothDevice obj = (BluetoothDevice)it.next();
+            return mLocalBluetoothManager.getCachedDeviceManager().findDevice(obj);
+        }
+        return null;
+    }
+
+    private boolean isHearingAidProfileSupported() {
+        final LocalBluetoothAdapter localAdapter = mLocalBluetoothManager.getBluetoothAdapter();
+        final List<Integer> supportedList = localAdapter.getSupportedProfiles();
+        if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
+            return true;
+        }
+        return false;
+    }
+
+    private LocalBluetoothManager getLocalBluetoothManager() {
+        final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
+                // Avoid StrictMode ThreadPolicy violation
+                () -> com.android.settings.bluetooth.Utils.getLocalBtManager(mContext));
+        try {
+            localBtManagerFutureTask.run();
+            return localBtManagerFutureTask.get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.w(TAG, "Error getting LocalBluetoothManager.", e);
+            return null;
+        }
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    void setPreference(Preference preference) {
+        mHearingAidPreference = preference;
+    }
+
+    @VisibleForTesting
+    void launchBluetoothDeviceDetailSetting(final CachedBluetoothDevice device) {
+        if (device == null) {
+            return;
+        }
+        final Bundle args = new Bundle();
+        args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
+                device.getDevice().getAddress());
+
+        new SubSettingLauncher(mContext)
+                .setDestination(BluetoothDeviceDetailsFragment.class.getName())
+                .setArguments(args)
+                .setTitleRes(R.string.device_details_title)
+                .setSourceMetricsCategory(MetricsProto.MetricsEvent.ACCESSIBILITY)
+                .launch();
+    }
+
+    @VisibleForTesting
+    void launchHearingAidInstructionDialog() {
+        HearingAidDialogFragment fragment = HearingAidDialogFragment.newInstance();
+        fragment.show(mFragmentManager, HearingAidDialogFragment.class.toString());
+    }
+}
\ No newline at end of file
index 22cff3e..d1b1ad4 100644 (file)
@@ -110,6 +110,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
             "select_long_press_timeout_preference";
     private static final String ACCESSIBILITY_SHORTCUT_PREFERENCE =
             "accessibility_shortcut_preference";
+    private static final String HEARING_AID_PREFERENCE =
+            "hearing_aid_preference";
     private static final String CAPTIONING_PREFERENCE_SCREEN =
             "captioning_preference_screen";
     private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN =
@@ -221,9 +223,11 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
     private Preference mAutoclickPreferenceScreen;
     private Preference mAccessibilityShortcutPreferenceScreen;
     private Preference mDisplayDaltonizerPreferenceScreen;
+    private Preference mHearingAidPreference;
     private Preference mVibrationPreferenceScreen;
     private SwitchPreference mToggleInversionPreference;
     private ColorInversionPreferenceController mInversionPreferenceController;
+    private AccessibilityHearingAidPreferenceController mHearingAidPreferenceController;
 
     private int mLongPressTimeoutDefault;
 
@@ -276,6 +280,15 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mHearingAidPreferenceController = new AccessibilityHearingAidPreferenceController
+                (context, HEARING_AID_PREFERENCE);
+        mHearingAidPreferenceController.setFragmentManager(getFragmentManager());
+        getLifecycle().addObserver(mHearingAidPreferenceController);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         updateAllPreferences();
@@ -335,6 +348,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
         } else if (mToggleMasterMonoPreference == preference) {
             handleToggleMasterMonoPreferenceClick();
             return true;
+        } else if (mHearingAidPreferenceController.handlePreferenceTreeClick(preference)) {
+            return true;
         }
         return super.onPreferenceTreeClick(preference);
     }
@@ -452,6 +467,10 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
             }
         }
 
+        // Hearing Aid.
+        mHearingAidPreference = findPreference(HEARING_AID_PREFERENCE);
+        mHearingAidPreferenceController.displayPreference(getPreferenceScreen());
+
         // Captioning.
         mCaptioningPreferenceScreen = findPreference(CAPTIONING_PREFERENCE_SCREEN);
 
@@ -686,6 +705,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
 
         updateVibrationSummary(mVibrationPreferenceScreen);
 
+        mHearingAidPreferenceController.updateState(mHearingAidPreference);
+
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
                 mCaptioningPreferenceScreen);
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
diff --git a/src/com/android/settings/accessibility/HearingAidDialogFragment.java b/src/com/android/settings/accessibility/HearingAidDialogFragment.java
new file mode 100644 (file)
index 0000000..0380ed3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothPairingDetail;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class HearingAidDialogFragment extends InstrumentedDialogFragment {
+    public static HearingAidDialogFragment newInstance() {
+        HearingAidDialogFragment frag = new HearingAidDialogFragment();
+        return frag;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.accessibility_hearingaid_pair_instructions_first_message)
+                .setMessage(R.string.accessibility_hearingaid_pair_instructions_second_message)
+                .setPositiveButton(R.string.accessibility_hearingaid_instruction_continue_button,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                launchBluetoothAddDeviceSetting();
+                            }
+                        })
+                .setNegativeButton(android.R.string.cancel,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) { }
+                        })
+                .create();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.DIALOG_ACCESSIBILITY_HEARINGAID;
+    }
+
+    private void launchBluetoothAddDeviceSetting() {
+        new SubSettingLauncher(getActivity())
+                .setDestination(BluetoothPairingDetail.class.getName())
+                .setSourceMetricsCategory(MetricsProto.MetricsEvent.ACCESSIBILITY)
+                .launch();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
new file mode 100644 (file)
index 0000000..8957f85
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2018 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.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;
+
+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.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class AccessibilityHearingAidPreferenceControllerTest {
+    private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+    private static final String TEST_DEVICE_NAME = "TEST_HEARING_AID_BT_DEVICE_NAME";
+    private static final String HEARING_AID_PREFERENCE = "hearing_aid_preference";
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothManager mBluetoothManager;
+    private BluetoothDevice mBluetoothDevice;
+    private Context mContext;
+    private Preference mHearingAidPreference;
+    private List<Integer> mProfileSupportedList;
+    private AccessibilityHearingAidPreferenceController mPreferenceController;
+
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        setupBluetoothEnvironment();
+        setupHearingAidEnvironment();
+        mHearingAidPreference = new Preference(mContext);
+        mHearingAidPreference.setKey(HEARING_AID_PREFERENCE);
+        mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext,
+                HEARING_AID_PREFERENCE);
+        mPreferenceController.setPreference(mHearingAidPreference);
+        mHearingAidPreference.setSummary("");
+    }
+
+    @Test
+    public void onHearingAidStateChanged_connected_updateHearingAidSummary() {
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
+        mPreferenceController.onResume();
+        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);
+    }
+
+    @Test
+    public void onHearingAidStateChanged_disconnected_updateHearingAidSummary() {
+        mPreferenceController.onResume();
+        Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_DISCONNECTED);
+        sendIntent(intent);
+
+        assertThat(mHearingAidPreference.getSummary()).isEqualTo(
+                mContext.getText(R.string.accessibility_hearingaid_not_connected_summary));
+    }
+
+    @Test
+    public void onBluetoothStateChanged_bluetoothOff_updateHearingAidSummary() {
+        mPreferenceController.onResume();
+        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+        intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
+        sendIntent(intent);
+
+        assertThat(mHearingAidPreference.getSummary()).isEqualTo(
+                mContext.getText(R.string.accessibility_hearingaid_not_connected_summary));
+    }
+
+    @Test
+    public void handleHearingAidPreferenceClick_noHearingAid_launchHearingAidInstructionDialog() {
+        mPreferenceController = spy(new AccessibilityHearingAidPreferenceController(mContext,
+                HEARING_AID_PREFERENCE));
+        mPreferenceController.setPreference(mHearingAidPreference);
+        doNothing().when(mPreferenceController).launchHearingAidInstructionDialog();
+        mPreferenceController.handlePreferenceTreeClick(mHearingAidPreference);
+
+        verify(mPreferenceController).launchHearingAidInstructionDialog();
+    }
+
+    @Test
+    public void handleHearingAidPreferenceClick_withHearingAid_launchBluetoothDeviceDetailSetting()
+    {
+        mPreferenceController = spy(new AccessibilityHearingAidPreferenceController(mContext,
+                HEARING_AID_PREFERENCE));
+        mPreferenceController.setPreference(mHearingAidPreference);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        mPreferenceController.handlePreferenceTreeClick(mHearingAidPreference);
+
+        verify(mPreferenceController).launchBluetoothDeviceDetailSetting(mCachedBluetoothDevice);
+    }
+
+    @Test
+    public void onNotSupportHearingAidProfile_doNotDoReceiverOperation() {
+        //clear bluetooth supported profile
+        mProfileSupportedList.clear();
+        mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext, HEARING_AID_PREFERENCE);
+        mPreferenceController.setPreference(mHearingAidPreference);
+        //not call registerReceiver()
+        mPreferenceController.onResume();
+        verify(mContext, never()).registerReceiver((BroadcastReceiver) any(), (IntentFilter) any());
+
+        //not call unregisterReceiver()
+        mPreferenceController.onPause();
+        verify(mContext, never()).unregisterReceiver((BroadcastReceiver) any());
+    }
+
+    private void setupBluetoothEnvironment() {
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        mLocalBluetoothManager = ShadowBluetoothUtils.getLocalBtManager(mContext);
+        mBluetoothManager = new BluetoothManager(mContext);
+        mBluetoothAdapter = mBluetoothManager.getAdapter();
+        when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
+        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+        when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+    }
+
+    private void setupHearingAidEnvironment() {
+        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+        mProfileSupportedList = new ArrayList<Integer>();
+        mProfileSupportedList.add(BluetoothProfile.HEARING_AID);
+        when(mLocalBluetoothAdapter.getSupportedProfiles()).thenReturn(mProfileSupportedList);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME);
+        when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
+    }
+
+    private void sendIntent(Intent intent) {
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext).registerReceiver(
+                broadcastReceiverCaptor.capture(), (IntentFilter) any());
+        BroadcastReceiver br = broadcastReceiverCaptor.getValue();
+        br.onReceive(mContext, intent);
+    }
+
+    private List<BluetoothDevice> generateHearingAidDeviceList() {
+        final List<BluetoothDevice> deviceList = new ArrayList<>(1);
+        deviceList.add(mBluetoothDevice);
+        return deviceList;
+    }
+}