diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java index 2cb74262f0a..3dde687328c 100644 --- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java +++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java @@ -38,6 +38,7 @@ 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.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; @@ -54,7 +55,7 @@ import java.util.concurrent.FutureTask; * Controller that shows and updates the bluetooth device name */ public class AccessibilityHearingAidPreferenceController extends BasePreferenceController - implements LifecycleObserver, OnStart, OnStop { + implements LifecycleObserver, OnStart, OnStop, BluetoothCallback { private static final String TAG = "AccessibilityHearingAidPreferenceController"; private Preference mHearingAidPreference; @@ -109,11 +110,13 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mHearingAidChangedReceiver, filter); + mLocalBluetoothManager.getEventManager().registerCallback(this); } @Override public void onStop() { mContext.unregisterReceiver(mHearingAidChangedReceiver); + mLocalBluetoothManager.getEventManager().unregisterCallback(this); } @Override @@ -159,6 +162,17 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC R.string.accessibility_hearingaid_right_side_device_summary, name); } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + if (activeDevice == null) { + return; + } + + if (bluetoothProfile == BluetoothProfile.HEARING_AID) { + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice); + } + } + public void setFragmentManager(FragmentManager fragmentManager) { mFragmentManager = fragmentManager; } diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java new file mode 100644 index 00000000000..a3d2c93f438 --- /dev/null +++ b/src/com/android/settings/accessibility/HearingAidUtils.java @@ -0,0 +1,59 @@ +/* + * 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.accessibility; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; + +import com.android.settings.bluetooth.HearingAidPairingDialogFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HearingAidProfile; + +/** Provides utility methods related hearing aids. */ +public final class HearingAidUtils { + private static final String TAG = "HearingAidUtils"; + + private HearingAidUtils(){} + + /** + * Launches pairing dialog when hearing aid device needs other side of hearing aid device to + * work. + * + * @param fragmentManager The {@link FragmentManager} used to show dialog fragment + * @param device The {@link CachedBluetoothDevice} need to be hearing aid device + */ + public static void launchHearingAidPairingDialog(FragmentManager fragmentManager, + @NonNull CachedBluetoothDevice device) { + if (device.isConnectedHearingAidDevice() + && device.getDeviceMode() == HearingAidProfile.DeviceMode.MODE_BINAURAL + && device.getSubDevice() == null) { + launchHearingAidPairingDialogInternal(fragmentManager, device); + } + } + + private static void launchHearingAidPairingDialogInternal(FragmentManager fragmentManager, + @NonNull CachedBluetoothDevice device) { + if (device.getDeviceSide() == HearingAidProfile.DeviceSide.SIDE_INVALID) { + Log.w(TAG, "Can not launch hearing aid pairing dialog for invalid side"); + return; + } + HearingAidPairingDialogFragment.newInstance(device).show(fragmentManager, + HearingAidPairingDialogFragment.TAG); + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java index 272d142e3e8..e3f0f093505 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java @@ -186,7 +186,7 @@ public class BluetoothDetailsCompanionAppsController extends BluetoothDetailsCon String address, PreferenceCategory container) { // If the device is FastPair, remove CDM companion apps. final BluetoothFeatureProvider bluetoothFeatureProvider = FeatureFactory.getFactory(context) - .getBluetoothFeatureProvider(context); + .getBluetoothFeatureProvider(); final boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true); final Uri settingsUri = bluetoothFeatureProvider.getBluetoothDeviceSettingsUri( diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java index 71d60490f25..bfaea85d20f 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsController.java @@ -17,22 +17,40 @@ package com.android.settings.bluetooth; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityShortcutInfo; +import android.content.ComponentName; import android.content.Context; +import android.os.UserHandle; +import android.view.accessibility.AccessibilityManager; +import androidx.annotation.NonNull; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; +import com.android.net.module.util.CollectionUtils; +import com.android.settings.accessibility.RestrictedPreferenceHelper; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.RestrictedPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.lifecycle.Lifecycle; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * 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"; + private static final int ORDINAL = 99; + + private PreferenceCategory mPreferenceCategory; public BluetoothDetailsRelatedToolsController(Context context, PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { @@ -51,14 +69,20 @@ public class BluetoothDetailsRelatedToolsController extends BluetoothDetailsCont return; } - final PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey()); + mPreferenceCategory = screen.findPreference(getPreferenceKey()); final Preference liveCaptionPreference = screen.findPreference(KEY_LIVE_CAPTION); if (!liveCaptionPreference.isVisible()) { - preferenceCategory.removePreference(liveCaptionPreference); + mPreferenceCategory.removePreference(liveCaptionPreference); } - if (preferenceCategory.getPreferenceCount() == 0) { - screen.removePreference(preferenceCategory); + final List relatedToolsList = FeatureFactory.getFactory( + mContext).getBluetoothFeatureProvider().getRelatedTools(); + if (!CollectionUtils.isEmpty(relatedToolsList)) { + addAccessibilityInstalledRelatedPreference(relatedToolsList); + } + + if (mPreferenceCategory.getPreferenceCount() == 0) { + screen.removePreference(mPreferenceCategory); } } @@ -69,4 +93,32 @@ public class BluetoothDetailsRelatedToolsController extends BluetoothDetailsCont public String getPreferenceKey() { return KEY_RELATED_TOOLS_GROUP; } + + private void addAccessibilityInstalledRelatedPreference( + @NonNull List componentNameList) { + final AccessibilityManager a11yManager = AccessibilityManager.getInstance(mContext); + final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper( + mContext); + + final List a11yServiceInfoList = + a11yManager.getInstalledAccessibilityServiceList().stream() + .filter(info -> componentNameList.contains(info.getComponentName())) + .collect(Collectors.toList()); + final List a11yShortcutInfoList = + a11yManager.getInstalledAccessibilityShortcutListAsUser(mContext, + UserHandle.myUserId()).stream() + .filter(info -> componentNameList.contains(info.getComponentName())) + .collect(Collectors.toList()); + + final List preferences = Stream.of( + preferenceHelper.createAccessibilityServicePreferenceList(a11yServiceInfoList), + preferenceHelper.createAccessibilityActivityPreferenceList(a11yShortcutInfoList)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + for (RestrictedPreference preference : preferences) { + preference.setOrder(ORDINAL); + mPreferenceCategory.addPreference(preference); + } + } } diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index ca212f3b128..a9ac2868878 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -129,7 +129,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager); final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory( - context).getBluetoothFeatureProvider(context); + context).getBluetoothFeatureProvider(); final boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true); @@ -141,7 +141,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment private void updateExtraControlUri(int viewWidth) { BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory( - getContext()).getBluetoothFeatureProvider(getContext()); + getContext()).getBluetoothFeatureProvider(); boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true); Uri controlUri = null; diff --git a/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java b/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java index 51ef8e2a649..648ca307d59 100644 --- a/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java +++ b/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java @@ -17,15 +17,18 @@ package com.android.settings.bluetooth; import android.bluetooth.BluetoothDevice; +import android.content.ComponentName; import android.net.Uri; +import java.util.List; + /** - * Provider for bluetooth related feature + * Provider for bluetooth related features. */ public interface BluetoothFeatureProvider { /** - * Get the {@link Uri} that represents extra settings for a specific bluetooth device + * Gets the {@link Uri} that represents extra settings for a specific bluetooth device * * @param bluetoothDevice bluetooth device * @return {@link Uri} for extra settings @@ -33,10 +36,18 @@ public interface BluetoothFeatureProvider { Uri getBluetoothDeviceSettingsUri(BluetoothDevice bluetoothDevice); /** - * Get the {@link Uri} that represents extra control for a specific bluetooth device + * Gets the {@link Uri} that represents extra control for a specific bluetooth device * * @param bluetoothDevice bluetooth device * @return {@link String} uri string for extra control */ String getBluetoothDeviceControlUri(BluetoothDevice bluetoothDevice); + + /** + * Gets the {@link ComponentName} of services or activities that need to be shown in related + * tools. + * + * @return list of {@link ComponentName} + */ + List getRelatedTools(); } diff --git a/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java b/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java index 04d3ec4cc1f..5ddf0626724 100644 --- a/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java +++ b/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java @@ -17,21 +17,20 @@ package com.android.settings.bluetooth; import android.bluetooth.BluetoothDevice; +import android.content.ComponentName; import android.content.Context; import android.net.Uri; import com.android.settingslib.bluetooth.BluetoothUtils; +import java.util.List; + /** * Impl of {@link BluetoothFeatureProvider} */ public class BluetoothFeatureProviderImpl implements BluetoothFeatureProvider { - private Context mContext; - - public BluetoothFeatureProviderImpl(Context context) { - mContext = context; - } + public BluetoothFeatureProviderImpl(Context context) {} @Override public Uri getBluetoothDeviceSettingsUri(BluetoothDevice bluetoothDevice) { @@ -44,4 +43,9 @@ public class BluetoothFeatureProviderImpl implements BluetoothFeatureProvider { public String getBluetoothDeviceControlUri(BluetoothDevice bluetoothDevice) { return BluetoothUtils.getControlUriMetaData(bluetoothDevice); } + + @Override + public List getRelatedTools() { + return null; + } } diff --git a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java new file mode 100644 index 00000000000..ac48217732a --- /dev/null +++ b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java @@ -0,0 +1,88 @@ +/* + * 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.app.Dialog; +import android.app.settings.SettingsEnums; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +import com.android.settings.R; +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; + +/** + * Provides a dialog to pair another side of hearing aid device. + */ +public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment { + public static final String TAG = "HearingAidPairingDialogFragment"; + private static final String KEY_CACHED_DEVICE_SIDE = "cached_device_side"; + + /** + * Creates a new {@link HearingAidPairingDialogFragment} and shows pair another side of hearing + * aid device according to {@code CachedBluetoothDevice} side. + * + * @param device The remote Bluetooth device, that needs to be hearing aid device. + * @return a DialogFragment + */ + public static HearingAidPairingDialogFragment newInstance(CachedBluetoothDevice device) { + Bundle args = new Bundle(1); + args.putInt(KEY_CACHED_DEVICE_SIDE, device.getDeviceSide()); + final HearingAidPairingDialogFragment fragment = new HearingAidPairingDialogFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public int getMetricsCategory() { + // TODO(b/225117454): Need to update SettingsEnums later + return SettingsEnums.ACCESSIBILITY; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final int deviceSide = getArguments().getInt(KEY_CACHED_DEVICE_SIDE); + 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 + : R.string.bluetooth_pair_other_ear_dialog_right_ear_message; + final int pairBtnId = (deviceSide == HearingAidProfile.DeviceSide.SIDE_LEFT) + ? R.string.bluetooth_pair_other_ear_dialog_right_ear_positive_button + : R.string.bluetooth_pair_other_ear_dialog_left_ear_positive_button; + + return new AlertDialog.Builder(getActivity()) + .setTitle(titleId) + .setMessage(messageId) + .setNegativeButton( + android.R.string.cancel, /* listener= */ null) + .setPositiveButton(pairBtnId, (dialog, which) -> positiveButtonListener()) + .create(); + } + + private void positiveButtonListener() { + new SubSettingLauncher(getActivity()) + .setDestination(BluetoothPairingDetail.class.getName()) + .setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY) + .launch(); + } +} diff --git a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java index ec122dfd506..6623b979066 100644 --- a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java @@ -17,22 +17,26 @@ package com.android.settings.connecteddevice; import static com.android.settingslib.Utils.isAudioModeOngoingCall; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.PackageManager; import android.util.Log; import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.accessibility.HearingAidUtils; import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; @@ -54,6 +58,7 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle @VisibleForTesting LocalBluetoothManager mLocalBluetoothManager; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private FragmentManager mFragmentManager; public AvailableMediaDeviceGroupController(Context context) { super(context, KEY); @@ -124,6 +129,7 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle } public void init(DashboardFragment fragment) { + mFragmentManager = fragment.getParentFragmentManager(); mBluetoothDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(fragment.getContext(), fragment, AvailableMediaDeviceGroupController.this); } @@ -138,6 +144,18 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle updateTitle(); } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + // exclude inactive device + if (activeDevice == null) { + return; + } + + if (bluetoothProfile == BluetoothProfile.HEARING_AID) { + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice); + } + } + private void updateTitle() { if (isAudioModeOngoingCall(mContext)) { // in phone call diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 8b78882ce41..aff7197e274 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -148,7 +148,10 @@ public abstract class FeatureFactory { public abstract ContextualCardFeatureProvider getContextualCardFeatureProvider(Context context); - public abstract BluetoothFeatureProvider getBluetoothFeatureProvider(Context context); + /** + * Retrieves implementation for Bluetooth feature. + */ + public abstract BluetoothFeatureProvider getBluetoothFeatureProvider(); public abstract AwareFeatureProvider getAwareFeatureProvider(); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 4d8dad07e6b..89f74de8233 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -280,10 +280,9 @@ public class FeatureFactoryImpl extends FeatureFactory { } @Override - public BluetoothFeatureProvider getBluetoothFeatureProvider(Context context) { + public BluetoothFeatureProvider getBluetoothFeatureProvider() { if (mBluetoothFeatureProvider == null) { - mBluetoothFeatureProvider = new BluetoothFeatureProviderImpl( - context.getApplicationContext()); + mBluetoothFeatureProvider = new BluetoothFeatureProviderImpl(getAppContext()); } return mBluetoothFeatureProvider; } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java index 1b8c8c3b5a7..affb28be97d 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java @@ -19,6 +19,7 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,12 +33,16 @@ import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Intent; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; import com.android.settings.R; 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.BluetoothEventManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; @@ -59,6 +64,7 @@ import org.robolectric.shadows.ShadowApplication; import java.util.ArrayList; import java.util.List; +/** Tests for {@link AccessibilityHearingAidPreferenceController}. */ @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class}) public class AccessibilityHearingAidPreferenceControllerTest { @@ -84,6 +90,8 @@ public class AccessibilityHearingAidPreferenceControllerTest { @Mock private LocalBluetoothManager mLocalBluetoothManager; @Mock + private BluetoothEventManager mEventManager; + @Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager; @Mock private HearingAidProfile mHearingAidProfile; @@ -221,6 +229,24 @@ public class AccessibilityHearingAidPreferenceControllerTest { assertThat(mPreferenceController.getConnectedHearingAidDevice()).isNull(); } + @Test + @Config(shadows = ShadowAlertDialogCompat.class) + public void onActiveDeviceChanged_hearingAidProfile_launchHearingAidPairingDialog() { + final FragmentActivity mActivity = Robolectric.setupActivity(FragmentActivity.class); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + mPreferenceController.setFragmentManager(mActivity.getSupportFragmentManager()); + + mPreferenceController.onActiveDeviceChanged(mCachedBluetoothDevice, + BluetoothProfile.HEARING_AID); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog.isShowing()).isTrue(); + } + private void setupBluetoothEnvironment() { ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; mLocalBluetoothManager = Utils.getLocalBtManager(mContext); @@ -229,6 +255,7 @@ public class AccessibilityHearingAidPreferenceControllerTest { when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); + doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); } private void setupHearingAidEnvironment() { @@ -239,7 +266,6 @@ public class AccessibilityHearingAidPreferenceControllerTest { mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID); when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME); - when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); } private void sendIntent(Intent intent) { diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 7c52754765f..891439ec05e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -58,6 +58,7 @@ import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexableRaw; import org.junit.Before; @@ -93,6 +94,7 @@ public class AccessibilitySettingsTest { private static final String DEFAULT_LABEL = "default label"; private static final Boolean SERVICE_ENABLED = true; private static final Boolean SERVICE_DISABLED = false; + @Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Spy @@ -114,6 +116,8 @@ public class AccessibilitySettingsTest { @Mock private AppOpsManager mAppOpsManager; + private Lifecycle mLifecycle; + @Before public void setup() { mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext)); @@ -127,6 +131,8 @@ public class AccessibilitySettingsTest { when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager); when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS), anyInt(), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED); + mLifecycle = new Lifecycle(() -> mLifecycle); + when(mFragment.getSettingsLifecycle()).thenReturn(mLifecycle); } @Test diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java new file mode 100644 index 00000000000..5c440d2e7ce --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java @@ -0,0 +1,105 @@ +/* + * 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + +import com.android.settings.SettingsActivity; +import com.android.settings.bluetooth.BluetoothPairingDetail; +import com.android.settings.bluetooth.HearingAidPairingDialogFragment; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HearingAidProfile; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@link HearingAidPairingDialogFragment}. */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowAlertDialogCompat.class) +public class HearingAidPairingDialogFragmentTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private static final String KEY_CACHED_DEVICE_SIDE = "cached_device_side"; + + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + private FragmentActivity mActivity; + private HearingAidPairingDialogFragment mFragment; + + @Before + public void setUp() { + mFragment = spy(HearingAidPairingDialogFragment.newInstance(mCachedBluetoothDevice)); + mActivity = Robolectric.setupActivity(FragmentActivity.class); + when(mFragment.getActivity()).thenReturn(mActivity); + } + + @Test + public void newInstance_deviceSideRight_argumentSideRight() { + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_RIGHT); + + mFragment = HearingAidPairingDialogFragment.newInstance(mCachedBluetoothDevice); + + final Bundle bundle = mFragment.getArguments(); + assertThat(bundle.getInt(KEY_CACHED_DEVICE_SIDE)).isEqualTo( + HearingAidProfile.DeviceSide.SIDE_RIGHT); + } + + @Test + public void dialogPositiveButtonClick_intentToExpectedClass() { + final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY); + dialog.show(); + + dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + + final Intent intent = shadowOf(mActivity).getNextStartedActivity(); + assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(BluetoothPairingDetail.class.getName()); + } + + @Test + public void dialogNegativeButtonClick_dismissDialog() { + final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY); + dialog.show(); + + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick(); + + assertThat(dialog.isShowing()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java new file mode 100644 index 00000000000..6918fb679e5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java @@ -0,0 +1,130 @@ +/* + * 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HearingAidProfile; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@link HearingAidUtils}. */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowAlertDialogCompat.class) +public class HearingAidUtilsTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private CachedBluetoothDevice mSubCachedBluetoothDevice; + + private FragmentManager mFragmentManager; + + @Before + public void setUp() { + final FragmentActivity mActivity = Robolectric.setupActivity(FragmentActivity.class); + mFragmentManager = mActivity.getSupportFragmentManager(); + } + + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + } + @Test + public void launchHearingAidPairingDialog_deviceNotConnectedHearingAid_noDialog() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(false); + + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNull(); + } + + @Test + public void launchHearingAidPairingDialog_deviceIsModeMonaural_noDialog() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_MONAURAL); + + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNull(); + } + + @Test + public void launchHearingAidPairingDialog_deviceHasSubDevice_noDialog() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice); + + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNull(); + } + + @Test + public void launchHearingAidPairingDialog_deviceIsInvalidSide_noDialog() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_INVALID); + + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNull(); + } + + @Test + public void launchHearingAidPairingDialog_dialogShown() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + + HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog.isShowing()).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java index 2035f061323..31d6397fff6 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsRelatedToolsControllerTest.java @@ -20,20 +20,65 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.view.accessibility.AccessibilityManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; + +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowAccessibilityManager; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; /** Tests for {@link BluetoothDetailsRelatedToolsController}. */ @RunWith(RobolectricTestRunner.class) public class BluetoothDetailsRelatedToolsControllerTest extends BluetoothDetailsControllerTestBase { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private static final String PACKAGE_NAME = "com.android.test"; + private static final String PACKAGE_NAME2 = "com.android.test2"; + private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service"; + private static final String KEY_RELATED_TOOLS_GROUP = "bluetooth_related_tools"; + private static final String KEY_LIVE_CAPTION = "live_caption"; + + private BluetoothDetailsRelatedToolsController mController; + private BluetoothFeatureProvider mFeatureProvider; + private ShadowAccessibilityManager mShadowAccessibilityManager; + private PreferenceCategory mPreferenceCategory; @Override public void setUp() { super.setUp(); + final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); + mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider(); + mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext)); + final Preference preference = new Preference(mContext); + preference.setKey(KEY_LIVE_CAPTION); + mPreferenceCategory = new PreferenceCategory(mContext); + mPreferenceCategory.setKey(KEY_RELATED_TOOLS_GROUP); + mScreen.addPreference(mPreferenceCategory); + mScreen.addPreference(preference); + mController = new BluetoothDetailsRelatedToolsController(mContext, mFragment, mCachedDevice, mLifecycle); + mController.init(mScreen); } @Test @@ -49,4 +94,68 @@ public class BluetoothDetailsRelatedToolsControllerTest extends BluetoothDetails assertThat(mController.isAvailable()).isFalse(); } + + @Test + public void displayPreference_oneRelatedToolsMatchA11yService_showOnePreference() { + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + mShadowAccessibilityManager.setInstalledAccessibilityServiceList( + List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME))); + when(mFeatureProvider.getRelatedTools()).thenReturn( + List.of(new ComponentName(PACKAGE_NAME, CLASS_NAME))); + + mController.displayPreference(mScreen); + + assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1); + } + + @Test + public void displayPreference_oneRelatedToolsNotMatchA11yService_showNoPreference() { + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + mShadowAccessibilityManager.setInstalledAccessibilityServiceList( + List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME))); + when(mFeatureProvider.getRelatedTools()).thenReturn( + List.of(new ComponentName(PACKAGE_NAME2, CLASS_NAME))); + + mController.displayPreference(mScreen); + + assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void displayPreference_noRelatedTools_showNoPreference() { + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + mShadowAccessibilityManager.setInstalledAccessibilityServiceList( + List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME))); + when(mFeatureProvider.getRelatedTools()).thenReturn(null); + + mController.displayPreference(mScreen); + + assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0); + } + + private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName, + String className) { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + final ServiceInfo serviceInfo = new ServiceInfo(); + applicationInfo.packageName = packageName; + serviceInfo.packageName = packageName; + serviceInfo.name = className; + serviceInfo.applicationInfo = applicationInfo; + + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = serviceInfo; + + try { + final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo, + mContext); + ComponentName componentName = ComponentName.unflattenFromString( + packageName + "/" + className); + info.setComponentName(componentName); + return info; + } catch (XmlPullParserException | IOException e) { + // Do nothing + } + + return null; + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java index da1c4930d81..e1a3da6329a 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java @@ -27,10 +27,13 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioManager; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; @@ -40,25 +43,31 @@ import com.android.settings.R; import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater; import com.android.settings.bluetooth.Utils; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowAudioManager; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 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.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +/** Tests for {@link AvailableMediaDeviceGroupController}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothUtils.class}) +@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class}) public class AvailableMediaDeviceGroupControllerTest { private static final String PREFERENCE_KEY_1 = "pref_key_1"; @@ -77,6 +86,8 @@ public class AvailableMediaDeviceGroupControllerTest { private BluetoothEventManager mEventManager; @Mock private LocalBluetoothManager mLocalManager; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; private PreferenceGroup mPreferenceGroup; private Context mContext; @@ -93,27 +104,26 @@ public class AvailableMediaDeviceGroupControllerTest { mPreference = new Preference(mContext); mPreference.setKey(PREFERENCE_KEY_1); mPreferenceGroup = spy(new PreferenceScreen(mContext, null)); + final FragmentActivity mActivity = Robolectric.setupActivity(FragmentActivity.class); when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager); doReturn(mContext).when(mDashboardFragment).getContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); + when(mDashboardFragment.getParentFragmentManager()).thenReturn( + mActivity.getSupportFragmentManager()); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager; mLocalBluetoothManager = Utils.getLocalBtManager(mContext); mAudioManager = mContext.getSystemService(AudioManager.class); doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager(); - mAvailableMediaDeviceGroupController = new AvailableMediaDeviceGroupController(mContext); + mAvailableMediaDeviceGroupController = spy( + new AvailableMediaDeviceGroupController(mContext)); mAvailableMediaDeviceGroupController. setBluetoothDeviceUpdater(mAvailableMediaBluetoothDeviceUpdater); mAvailableMediaDeviceGroupController.mPreferenceGroup = mPreferenceGroup; } - @After - public void tearDown() { - ShadowBluetoothUtils.reset(); - } - @Test public void onDeviceAdded_firstAdd_becomeVisibleAndPreferenceAdded() { mAvailableMediaDeviceGroupController.onDeviceAdded(mPreference); @@ -222,4 +232,21 @@ public class AvailableMediaDeviceGroupControllerTest { // Shouldn't crash mAvailableMediaDeviceGroupController.onStop(); } + + @Test + @Config(shadows = ShadowAlertDialogCompat.class) + public void onActiveDeviceChanged_hearingAidProfile_launchHearingAidPairingDialog() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDeviceMode()).thenReturn( + HearingAidProfile.DeviceMode.MODE_BINAURAL); + when(mCachedBluetoothDevice.getDeviceSide()).thenReturn( + HearingAidProfile.DeviceSide.SIDE_LEFT); + mAvailableMediaDeviceGroupController.init(mDashboardFragment); + + mAvailableMediaDeviceGroupController.onActiveDeviceChanged(mCachedBluetoothDevice, + BluetoothProfile.HEARING_AID); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog.isShowing()).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index d430232b8f7..518aee9d23a 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -239,7 +239,7 @@ public class FakeFeatureFactory extends FeatureFactory { } @Override - public BluetoothFeatureProvider getBluetoothFeatureProvider(Context context) { + public BluetoothFeatureProvider getBluetoothFeatureProvider() { return mBluetoothFeatureProvider; } diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index e9764cbbab4..717de791c55 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -225,7 +225,7 @@ public class FakeFeatureFactory extends FeatureFactory { } @Override - public BluetoothFeatureProvider getBluetoothFeatureProvider(Context context) { + public BluetoothFeatureProvider getBluetoothFeatureProvider() { return mBluetoothFeatureProvider; }