Merge changes I13495cad,I3a44c4c4,I15bff230,I8a492866,Ia7ffe34a
* changes:
[Pair hearing devices] Add pair hearing device functionality
[Pair hearing devices] Extract common behavior in BluetoothPairingDetail into Base class
[Pair hearing devices] Add "Hearing devices" to show connected hearing devices
[Pair hearing devices] Add "Saved devices" to show bonded but not connected hearing devices
[Audio routing] Setup basic structure for audio routing page
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index e4bd550..f0de6d0 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -265,6 +265,23 @@
<item>5</item>
</string-array>
+ <!-- Bluetooth audio routing settings: Titles for audio routing options -->
+ <string-array name="bluetooth_audio_routing_titles">
+ <item>Decide automatically</item>
+ <item>Play on hearing device</item>
+ <item>Play on phone speaker</item>
+ </string-array>
+
+ <!-- Bluetooth audio routing settings: Values for audio routing options -->
+ <string-array name="bluetooth_audio_routing_values" translatable="false">
+ <!-- Decide automatically -->
+ <item>0</item>
+ <!-- Play on hearing device -->
+ <item>1</item>
+ <!-- Play on phone speaker -->
+ <item>2</item>
+ </string-array>
+
<!-- Match this with drawable.wifi_signal. --> <skip />
<!-- Wi-Fi settings. The signal strength a Wi-Fi network has. -->
<string-array name="wifi_signal">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 83698eb..31dd0b8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4545,6 +4545,8 @@
<!-- Title for the hearing device pairing preference. [CHAR LIMIT=20] -->
<string name="accessibility_hearing_device_pairing_title">Pair new device</string>
<!-- Title for the preference category containing the connected hearing device group. [CHAR LIMIT=20]-->
+ <string name="accessibility_hearing_device_connected_title">Hearing devices</string>
+ <!-- Title for the preference category containing the previously connected hearing device group. [CHAR LIMIT=20]-->
<string name="accessibility_hearing_device_saved_title">Saved devices</string>
<!-- Title for the preference category containing the controls of the hearing device. [CHAR LIMIT=35] -->
<string name="accessibility_hearing_device_control">Hearing device controls</string>
@@ -4559,7 +4561,7 @@
<!-- Title for the pair hearing device page. [CHAR LIMIT=25] -->
<string name="accessibility_hearing_device_pairing_page_title">Pair hearing device</string>
<!-- Title for the preference category containing the list of the available hearing during and after bluetooth scanning devices. [CHAR LIMIT=30] -->
- <string name="accessibility_found_hearing_devices">Available devices</string>
+ <string name="accessibility_found_hearing_devices">Available hearing devices</string>
<!-- Title for the preference category containing the all bluetooth devices during and after bluetooth scanning devices. Used when people can not find their hearing device in hearing device pairing list. [CHAR LIMIT=45] -->
<string name="accessibility_found_all_devices">Don\u2019t see your hearing device?</string>
<!-- Title for listing all bluetooth devices preference in the accessibility page. [CHAR LIMIT=40] -->
diff --git a/res/xml/accessibility_hearing_aids.xml b/res/xml/accessibility_hearing_aids.xml
index f5e65ae..76910a0 100644
--- a/res/xml/accessibility_hearing_aids.xml
+++ b/res/xml/accessibility_hearing_aids.xml
@@ -19,13 +19,25 @@
android:key="accessibility_hearing_devices_screen"
android:title="@string/accessibility_hearingaid_title">
+ <PreferenceCategory
+ android:key="available_hearing_devices"
+ android:title="@string/accessibility_hearing_device_connected_title"
+ settings:controller="com.android.settings.accessibility.AvailableHearingDevicePreferenceController"/>
+
<com.android.settingslib.RestrictedPreference
android:key="add_bt_devices"
android:title="@string/bluetooth_pairing_pref_title"
android:icon="@drawable/ic_add_24dp"
android:summary="@string/connected_device_add_device_summary"
+ android:fragment="com.android.settings.accessibility.HearingDevicePairingDetail"
settings:userRestriction="no_config_bluetooth"
- settings:useAdminDisabledSummary="true" />
+ settings:useAdminDisabledSummary="true"
+ settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
+
+ <PreferenceCategory
+ android:key="previously_connected_hearing_devices"
+ android:title="@string/accessibility_hearing_device_saved_title"
+ settings:controller="com.android.settings.accessibility.SavedHearingDevicePreferenceController"/>
<PreferenceCategory
android:key="device_control_category"
diff --git a/res/xml/bluetooth_audio_routing_fragment.xml b/res/xml/bluetooth_audio_routing_fragment.xml
new file mode 100644
index 0000000..18f18f2
--- /dev/null
+++ b/res/xml/bluetooth_audio_routing_fragment.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/bluetooth_audio_routing_title">
+
+ <com.android.settingslib.widget.TopIntroPreference
+ android:key="bluetooth_audio_routing_top_intro"
+ android:title="@string/bluetooth_audio_routing_summary"
+ settings:searchable="false"/>
+
+ <ListPreference
+ android:entries="@array/bluetooth_audio_routing_titles"
+ android:entryValues="@array/bluetooth_audio_routing_values"
+ android:summary="%s"
+ android:key="audio_routing_ringtone"
+ android:persistent="false"
+ android:title="@string/bluetooth_ringtone_title" />
+
+ <ListPreference
+ android:entries="@array/bluetooth_audio_routing_titles"
+ android:entryValues="@array/bluetooth_audio_routing_values"
+ android:summary="%s"
+ android:key="audio_routing_call"
+ android:persistent="false"
+ android:title="@string/bluetooth_call_title" />
+
+ <ListPreference
+ android:entries="@array/bluetooth_audio_routing_titles"
+ android:entryValues="@array/bluetooth_audio_routing_values"
+ android:summary="%s"
+ android:key="audio_routing_media"
+ android:persistent="false"
+ android:title="@string/bluetooth_media_title" />
+
+ <ListPreference
+ android:entries="@array/bluetooth_audio_routing_titles"
+ android:entryValues="@array/bluetooth_audio_routing_values"
+ android:summary="%s"
+ android:key="audio_routing_system_sounds"
+ android:persistent="false"
+ android:title="@string/bluetooth_system_sounds_title" />
+
+</PreferenceScreen>
diff --git a/res/xml/bluetooth_pairing_detail.xml b/res/xml/bluetooth_pairing_detail.xml
index 86fb9e4..460a708 100644
--- a/res/xml/bluetooth_pairing_detail.xml
+++ b/res/xml/bluetooth_pairing_detail.xml
@@ -27,7 +27,7 @@
<com.android.settings.bluetooth.BluetoothProgressCategory
android:key="available_devices"
- android:title="@string/bluetooth_paired_device_title"/>
+ android:title="@string/bluetooth_preference_found_media_devices"/>
<com.android.settingslib.widget.FooterPreference/>
diff --git a/res/xml/hearing_device_pairing_detail.xml b/res/xml/hearing_device_pairing_detail.xml
new file mode 100644
index 0000000..65cc6c72
--- /dev/null
+++ b/res/xml/hearing_device_pairing_detail.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/bluetooth_pairing_pref_title">
+
+ <com.android.settings.bluetooth.BluetoothProgressCategory
+ android:key="available_hearing_devices"
+ android:title="@string/accessibility_found_hearing_devices" />
+
+ <PreferenceCategory
+ android:key="device_control_category"
+ android:title="@string/accessibility_found_all_devices">
+ <com.android.settingslib.RestrictedPreference
+ android:key="add_bt_devices"
+ android:title="@string/accessibility_list_all_devices_title"
+ android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
+ settings:userRestriction="no_config_bluetooth"
+ settings:useAdminDisabledSummary="true" />
+ </PreferenceCategory>
+
+ <com.android.settings.accessibility.AccessibilityFooterPreference
+ android:key="hearing_device_footer"
+ android:title="@string/accessibility_hearing_device_footer_summary"
+ android:selectable="false"
+ settings:searchable="false" />
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index cec48bb..cd76b47 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -29,7 +29,6 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
-import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
@@ -55,8 +54,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;
/**
@@ -84,7 +81,8 @@
public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
- mLocalBluetoothManager = getLocalBluetoothManager();
+ mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager(
+ context);
mProfileManager = mLocalBluetoothManager.getProfileManager();
mCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -269,19 +267,6 @@
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;
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
index 5df8afb..85783b73 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
@@ -19,6 +19,7 @@
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import android.content.ComponentName;
+import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -45,6 +46,13 @@
}
@Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ use(AvailableHearingDevicePreferenceController.class).init(this);
+ use(SavedHearingDevicePreferenceController.class).init(this);
+ }
+
+ @Override
public void onCreate(Bundle savedInstanceState) {
mFeatureName = getContext().getString(R.string.accessibility_hearingaid_title);
super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java b/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java
new file mode 100644
index 0000000..076432c
--- /dev/null
+++ b/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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.BluetoothProfile;
+import android.content.Context;
+
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+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;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Controller to update the {@link androidx.preference.PreferenceCategory} for all
+ * connected hearing devices, including ASHA and HAP profile.
+ * Parent class {@link BaseBluetoothDevicePreferenceController} will use
+ * {@link DevicePreferenceCallback} to add/remove {@link Preference}.
+ */
+public class AvailableHearingDevicePreferenceController extends
+ BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnStop,
+ BluetoothCallback {
+
+ private static final String TAG = "AvailableHearingDevicePreferenceController";
+
+ private BluetoothDeviceUpdater mAvailableHearingDeviceUpdater;
+ private final LocalBluetoothManager mLocalBluetoothManager;
+ private FragmentManager mFragmentManager;
+
+ public AvailableHearingDevicePreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager(
+ context);
+ }
+
+ /**
+ * Initializes objects in this controller. Need to call this before onStart().
+ *
+ * <p>Should not call this more than 1 time.
+ *
+ * @param fragment The {@link DashboardFragment} uses the controller.
+ */
+ public void init(DashboardFragment fragment) {
+ if (mAvailableHearingDeviceUpdater != null) {
+ throw new IllegalStateException("Should not call init() more than 1 time.");
+ }
+ mAvailableHearingDeviceUpdater = new AvailableHearingDeviceUpdater(fragment.getContext(),
+ this, fragment.getMetricsCategory());
+ mFragmentManager = fragment.getParentFragmentManager();
+ }
+
+ @Override
+ public void onStart() {
+ mAvailableHearingDeviceUpdater.registerCallback();
+ mAvailableHearingDeviceUpdater.refreshPreference();
+ mLocalBluetoothManager.getEventManager().registerCallback(this);
+ }
+
+ @Override
+ public void onStop() {
+ mAvailableHearingDeviceUpdater.unregisterCallback();
+ mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ if (isAvailable()) {
+ final Context context = screen.getContext();
+ mAvailableHearingDeviceUpdater.setPrefContext(context);
+ mAvailableHearingDeviceUpdater.forceUpdate();
+ }
+ }
+
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ if (activeDevice == null) {
+ return;
+ }
+
+ if (bluetoothProfile == BluetoothProfile.HEARING_AID) {
+ HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice);
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java b/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java
new file mode 100644
index 0000000..b3d3715
--- /dev/null
+++ b/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 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.BluetoothDevice;
+import android.content.Context;
+
+import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Maintains and updates connected hearing devices, including ASHA and HAP profile.
+ */
+public class AvailableHearingDeviceUpdater extends AvailableMediaBluetoothDeviceUpdater {
+
+ private static final String PREF_KEY = "connected_hearing_device";
+
+ public AvailableHearingDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+ super(context, devicePreferenceCallback, metricsCategory);
+ }
+
+ @Override
+ public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
+ final BluetoothDevice device = cachedDevice.getDevice();
+ final boolean isConnectedHearingAidDevice = (cachedDevice.isConnectedHearingAidDevice()
+ && (device.getBondState() == BluetoothDevice.BOND_BONDED));
+
+ return isConnectedHearingAidDevice && isDeviceInCachedDevicesList(cachedDevice);
+ }
+
+ @Override
+ protected String getPreferenceKey() {
+ return PREF_KEY;
+ }
+}
diff --git a/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java b/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java
new file mode 100644
index 0000000..c5c6297
--- /dev/null
+++ b/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Abstract base class for bluetooth preference controller to handle UI logic, e.g. availability
+ * status, preference added, and preference removed.
+ */
+public abstract class BaseBluetoothDevicePreferenceController extends BasePreferenceController
+ implements DevicePreferenceCallback {
+
+ private PreferenceCategory mPreferenceCategory;
+
+ public BaseBluetoothDevicePreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH))
+ ? AVAILABLE
+ : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreferenceCategory = screen.findPreference(getPreferenceKey());
+ mPreferenceCategory.setVisible(false);
+ }
+
+ @Override
+ public void onDeviceAdded(Preference preference) {
+ if (mPreferenceCategory.getPreferenceCount() == 0) {
+ mPreferenceCategory.setVisible(true);
+ }
+ mPreferenceCategory.addPreference(preference);
+ }
+
+ @Override
+ public void onDeviceRemoved(Preference preference) {
+ mPreferenceCategory.removePreference(preference);
+ if (mPreferenceCategory.getPreferenceCount() == 0) {
+ mPreferenceCategory.setVisible(false);
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingDetail.java b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
new file mode 100644
index 0000000..aa9b587
--- /dev/null
+++ b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDevicePairingDetailBase;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+import java.util.Collections;
+
+/**
+ * HearingDevicePairingDetail is a page to scan hearing devices. This page shows scanning icons and
+ * pairing them.
+ */
+public class HearingDevicePairingDetail extends BluetoothDevicePairingDetailBase {
+
+ private static final String TAG = "HearingDevicePairingDetail";
+ @VisibleForTesting
+ static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices";
+
+ public HearingDevicePairingDetail() {
+ super();
+ final ScanFilter filter = new ScanFilter.Builder()
+ .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}, new byte[]{0})
+ .build();
+ setFilter(Collections.singletonList(filter));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isEnabled());
+ }
+
+ @Override
+ public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ super.onDeviceBondStateChanged(cachedDevice, bondState);
+
+ mAvailableDevicesCategory.setProgress(bondState == BluetoothDevice.BOND_NONE);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/262839191): To be updated settings_enums.proto
+ return 0;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.hearing_device_pairing_detail;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public String getDeviceListKey() {
+ return KEY_AVAILABLE_HEARING_DEVICES;
+ }
+}
diff --git a/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java b/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java
new file mode 100644
index 0000000..20e227c
--- /dev/null
+++ b/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 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.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Controller to update the {@link androidx.preference.PreferenceCategory} for all
+ * saved ((bonded but not connected)) hearing devices, including ASHA and HAP profile.
+ * Parent class {@link BaseBluetoothDevicePreferenceController} will use
+ * {@link DevicePreferenceCallback} to add/remove {@link Preference}.
+ */
+public class SavedHearingDevicePreferenceController extends
+ BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnResume,
+ OnStop {
+
+ private BluetoothDeviceUpdater mSavedHearingDeviceUpdater;
+
+ public SavedHearingDevicePreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * Initializes objects in this controller. Need to call this before onStart().
+ *
+ * <p>Should not call this more than 1 time.
+ *
+ * @param fragment The {@link DashboardFragment} uses the controller.
+ */
+ public void init(DashboardFragment fragment) {
+ if (mSavedHearingDeviceUpdater != null) {
+ throw new IllegalStateException("Should not call init() more than 1 time.");
+ }
+ mSavedHearingDeviceUpdater = new SavedHearingDeviceUpdater(fragment.getContext(), this,
+ fragment.getMetricsCategory());
+ }
+
+ @Override
+ public void onStart() {
+ mSavedHearingDeviceUpdater.registerCallback();
+ }
+
+ @Override
+ public void onResume() {
+ mSavedHearingDeviceUpdater.refreshPreference();
+ }
+
+ @Override
+ public void onStop() {
+ mSavedHearingDeviceUpdater.unregisterCallback();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ if (isAvailable()) {
+ final Context context = screen.getContext();
+ mSavedHearingDeviceUpdater.setPrefContext(context);
+ mSavedHearingDeviceUpdater.forceUpdate();
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java b/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java
new file mode 100644
index 0000000..645d091
--- /dev/null
+++ b/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.BluetoothDevice;
+import android.content.Context;
+
+import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Maintains and updates saved (bonded but not connected) hearing devices, including ASHA and HAP
+ * profile.
+ */
+public class SavedHearingDeviceUpdater extends SavedBluetoothDeviceUpdater {
+
+ private static final String PREF_KEY = "saved_hearing_device";
+
+ public SavedHearingDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+ super(context, devicePreferenceCallback, /* showConnectedDevice= */ false, metricsCategory);
+ }
+
+ @Override
+ public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
+ final BluetoothDevice device = cachedDevice.getDevice();
+ final boolean isSavedHearingAidDevice = cachedDevice.isHearingAidDevice()
+ && device.getBondState() == BluetoothDevice.BOND_BONDED
+ && !device.isConnected();
+
+ return isSavedHearingAidDevice && isDeviceInCachedDevicesList(cachedDevice);
+ }
+
+ @Override
+ protected String getPreferenceKey() {
+ return PREF_KEY;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index c3d3b82..ec131d4 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -23,7 +23,6 @@
import androidx.preference.Preference;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -39,9 +38,9 @@
private final AudioManager mAudioManager;
- public AvailableMediaBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
- DevicePreferenceCallback devicePreferenceCallback) {
- super(context, fragment, devicePreferenceCallback);
+ public AvailableMediaBluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+ super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
@@ -102,7 +101,7 @@
@Override
public boolean onPreferenceClick(Preference preference) {
- mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+ mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
.getBluetoothDevice();
return device.setActive();
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
index 509a9b0..4e40323 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
@@ -17,10 +17,13 @@
package com.android.settings.bluetooth;
import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_AUDIO_ROUTING_ORDER;
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
import android.content.Context;
+import android.os.Bundle;
import android.util.FeatureFlagUtils;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
@@ -36,7 +39,8 @@
public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsController {
private static final String KEY_FEATURE_CONTROLS_GROUP = "feature_controls_group";
- private static final String KEY_AUDIO_ROUTING = "audio_routing";
+ @VisibleForTesting
+ static final String KEY_AUDIO_ROUTING = "audio_routing";
public BluetoothDetailsAudioRoutingController(Context context,
PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
@@ -71,9 +75,13 @@
private Preference createAudioRoutingPreference(Context context) {
final Preference preference = new Preference(context);
+
preference.setKey(KEY_AUDIO_ROUTING);
preference.setTitle(context.getString(R.string.bluetooth_audio_routing_title));
preference.setSummary(context.getString(R.string.bluetooth_audio_routing_summary));
+ final Bundle extras = preference.getExtras();
+ extras.putString(KEY_DEVICE_ADDRESS, mCachedDevice.getAddress());
+ preference.setFragment(BluetoothDetailsAudioRoutingFragment.class.getName());
return preference;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
new file mode 100644
index 0000000..691acee
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Settings fragment containing bluetooth audio routing. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class BluetoothDetailsAudioRoutingFragment extends RestrictedDashboardFragment {
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.bluetooth_audio_routing_fragment);
+ private static final String TAG = "BluetoothDetailsAudioRoutingFragment";
+ @VisibleForTesting
+ CachedBluetoothDevice mCachedDevice;
+
+ public BluetoothDetailsAudioRoutingFragment() {
+ super(DISALLOW_CONFIG_BLUETOOTH);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ final LocalBluetoothManager localBtMgr = Utils.getLocalBtManager(context);
+ final CachedBluetoothDeviceManager cachedDeviceMgr = localBtMgr.getCachedDeviceManager();
+ final BluetoothDevice bluetoothDevice = localBtMgr.getBluetoothAdapter().getRemoteDevice(
+ getArguments().getString(KEY_DEVICE_ADDRESS));
+
+ mCachedDevice = cachedDeviceMgr.findDevice(bluetoothDevice);
+ if (mCachedDevice == null) {
+ // Close this page if device is null with invalid device mac address
+ Log.w(TAG, "onAttach() CachedDevice is null! Can not find address: "
+ + bluetoothDevice.getAnonymizedAddress());
+ finish();
+ return;
+ }
+
+ // TODO: mCachedDevice will pass to control in next CLs.
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/262839191): To be updated settings_enums.proto
+ return 0;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.bluetooth_audio_routing_fragment;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
new file mode 100644
index 0000000..c71decb
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityStatsLogUtils;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
+
+/**
+ * Abstract class for providing basic interaction for a list of Bluetooth devices in bluetooth
+ * device pairing detail page.
+ */
+public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
+
+ protected boolean mInitialScanStarted;
+ @VisibleForTesting
+ protected BluetoothProgressCategory mAvailableDevicesCategory;
+
+ public BluetoothDevicePairingDetailBase() {
+ super(DISALLOW_CONFIG_BLUETOOTH);
+ }
+
+ @Override
+ public void initPreferencesFromPreferenceScreen() {
+ mAvailableDevicesCategory = findPreference(getDeviceListKey());
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ mInitialScanStarted = false;
+ super.onViewCreated(view, savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mLocalManager == null) {
+ Log.e(getLogTag(), "Bluetooth is not supported on this device");
+ return;
+ }
+ updateBluetooth();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mLocalManager == null) {
+ Log.e(getLogTag(), "Bluetooth is not supported on this device");
+ return;
+ }
+ disableScanning();
+ }
+
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ super.onBluetoothStateChanged(bluetoothState);
+ updateContent(bluetoothState);
+ if (bluetoothState == BluetoothAdapter.STATE_ON) {
+ showBluetoothTurnedOnToast();
+ }
+ }
+
+ @Override
+ public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ if (bondState == BluetoothDevice.BOND_BONDED) {
+ // If one device is connected(bonded), then close this fragment.
+ finish();
+ return;
+ } else if (bondState == BluetoothDevice.BOND_BONDING) {
+ // Set the bond entry where binding process starts for logging hearing aid device info
+ final int pageId = FeatureFactory.getFactory(
+ getContext()).getMetricsFeatureProvider().getAttribution(getActivity());
+ final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
+ pageId);
+ HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
+ }
+ if (mSelectedDevice != null && cachedDevice != null) {
+ BluetoothDevice device = cachedDevice.getDevice();
+ if (device != null && mSelectedDevice.equals(device)
+ && bondState == BluetoothDevice.BOND_NONE) {
+ // If currently selected device failed to bond, restart scanning
+ enableScanning();
+ }
+ }
+ }
+
+ @Override
+ public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
+ int bluetoothProfile) {
+ // This callback is used to handle the case that bonded device is connected in pairing list.
+ // 1. If user selected multiple bonded devices in pairing list, after connected
+ // finish this page.
+ // 2. If the bonded devices auto connected in paring list, after connected it will be
+ // removed from paring list.
+ if (cachedDevice != null && cachedDevice.isConnected()) {
+ final BluetoothDevice device = cachedDevice.getDevice();
+ if (device != null && mSelectedList.contains(device)) {
+ finish();
+ } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
+ onDeviceDeleted(cachedDevice);
+ }
+ }
+ }
+
+ @Override
+ public void enableScanning() {
+ // Clear all device states before first scan
+ if (!mInitialScanStarted) {
+ if (mAvailableDevicesCategory != null) {
+ removeAllDevices();
+ }
+ mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
+ mInitialScanStarted = true;
+ }
+ super.enableScanning();
+ }
+
+ @Override
+ public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+ disableScanning();
+ super.onDevicePreferenceClick(btPreference);
+ }
+
+ @VisibleForTesting
+ void updateBluetooth() {
+ if (mBluetoothAdapter.isEnabled()) {
+ updateContent(mBluetoothAdapter.getState());
+ } else {
+ // Turn on bluetooth if it is disabled
+ mBluetoothAdapter.enable();
+ }
+ }
+
+ /**
+ * Enables the scanning when {@code bluetoothState} is on, or finish the page when
+ * {@code bluetoothState} is off.
+ *
+ * @param bluetoothState the current Bluetooth state, the possible values that will handle here:
+ * {@link android.bluetooth.BluetoothAdapter#STATE_OFF},
+ * {@link android.bluetooth.BluetoothAdapter#STATE_ON},
+ */
+ @VisibleForTesting
+ public void updateContent(int bluetoothState) {
+ switch (bluetoothState) {
+ case BluetoothAdapter.STATE_ON:
+ mDevicePreferenceMap.clear();
+ clearPreferenceGroupCache();
+ mBluetoothAdapter.enable();
+ enableScanning();
+ break;
+
+ case BluetoothAdapter.STATE_OFF:
+ finish();
+ break;
+ }
+ }
+
+ /**
+ * Clears all cached preferences in {@code preferenceGroup}.
+ */
+ private void clearPreferenceGroupCache() {
+ cacheRemoveAllPrefs(mAvailableDevicesCategory);
+ removeCachedPrefs(mAvailableDevicesCategory);
+ }
+
+ @VisibleForTesting
+ void showBluetoothTurnedOnToast() {
+ Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
+ Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 8934676..25e5fbb 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -27,11 +27,9 @@
import com.android.settings.R;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.GearPreference;
import com.android.settingslib.bluetooth.BluetoothCallback;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -46,39 +44,42 @@
* {@link BluetoothCallback}. It notifies the upper level whether to add/remove the preference
* through {@link DevicePreferenceCallback}
*
- * In {@link BluetoothDeviceUpdater}, it uses {@link BluetoothDeviceFilter.Filter} to detect
- * whether the {@link CachedBluetoothDevice} is relevant.
+ * In {@link BluetoothDeviceUpdater}, it uses {@link #isFilterMatched(CachedBluetoothDevice)} to
+ * detect whether the {@link CachedBluetoothDevice} is relevant.
*/
public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
LocalBluetoothProfileManager.ServiceListener {
- private static final String TAG = "BluetoothDeviceUpdater";
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
protected final MetricsFeatureProvider mMetricsFeatureProvider;
protected final DevicePreferenceCallback mDevicePreferenceCallback;
protected final Map<BluetoothDevice, Preference> mPreferenceMap;
+ protected Context mContext;
protected Context mPrefContext;
- protected DashboardFragment mFragment;
@VisibleForTesting
protected LocalBluetoothManager mLocalManager;
+ protected int mMetricsCategory;
+
+ private static final String TAG = "BluetoothDeviceUpdater";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
launchDeviceDetails(pref);
};
- public BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
- DevicePreferenceCallback devicePreferenceCallback) {
- this(context, fragment, devicePreferenceCallback, Utils.getLocalBtManager(context));
+ public BluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+ this(context, devicePreferenceCallback, Utils.getLocalBtManager(context), metricsCategory);
}
@VisibleForTesting
- BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
- DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager) {
- mFragment = fragment;
+ BluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager,
+ int metricsCategory) {
+ mContext = context;
mDevicePreferenceCallback = devicePreferenceCallback;
mPreferenceMap = new HashMap<>();
mLocalManager = localManager;
+ mMetricsCategory = metricsCategory;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
@@ -90,7 +91,7 @@
Log.e(TAG, "registerCallback() Bluetooth is not supported on this device");
return;
}
- mLocalManager.setForegroundActivity(mFragment.getContext());
+ mLocalManager.setForegroundActivity(mContext);
mLocalManager.getEventManager().registerCallback(this);
mLocalManager.getProfileManager().addServiceListener(this);
forceUpdate();
@@ -283,7 +284,7 @@
* {@link SubSettingLauncher} to launch {@link BluetoothDeviceDetailsFragment}
*/
protected void launchDeviceDetails(Preference preference) {
- mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+ mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
final CachedBluetoothDevice device =
((BluetoothDevicePreference) preference).getBluetoothDevice();
if (device == null) {
@@ -293,11 +294,11 @@
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
device.getDevice().getAddress());
- new SubSettingLauncher(mFragment.getContext())
+ new SubSettingLauncher(mContext)
.setDestination(BluetoothDeviceDetailsFragment.class.getName())
.setArguments(args)
.setTitleRes(R.string.device_details_title)
- .setSourceMetricsCategory(mFragment.getMetricsCategory())
+ .setSourceMetricsCategory(mMetricsCategory)
.launch();
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index 9a92783..a78bf27 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -16,31 +16,25 @@
package com.android.settings.bluetooth;
-import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
-
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
-import com.android.settings.accessibility.AccessibilityStatsLogUtils;
-import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.widget.FooterPreference;
/**
* BluetoothPairingDetail is a page to scan bluetooth devices and pair them.
*/
-public class BluetoothPairingDetail extends DeviceListPreferenceFragment implements
+public class BluetoothPairingDetail extends BluetoothDevicePairingDetailBase implements
Indexable {
private static final String TAG = "BluetoothPairingDetail";
@@ -50,34 +44,12 @@
static final String KEY_FOOTER_PREF = "footer_preference";
@VisibleForTesting
- BluetoothProgressCategory mAvailableDevicesCategory;
- @VisibleForTesting
FooterPreference mFooterPreference;
@VisibleForTesting
AlwaysDiscoverable mAlwaysDiscoverable;
- private boolean mInitialScanStarted;
-
public BluetoothPairingDetail() {
- super(DISALLOW_CONFIG_BLUETOOTH);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mInitialScanStarted = false;
- mAlwaysDiscoverable = new AlwaysDiscoverable(getContext());
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (mLocalManager == null){
- Log.e(TAG, "Bluetooth is not supported on this device");
- return;
- }
- updateBluetooth();
- mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
+ super();
}
@Override
@@ -86,32 +58,29 @@
use(BluetoothDeviceRenamePreferenceController.class).setFragment(this);
}
- @VisibleForTesting
- void updateBluetooth() {
- if (mBluetoothAdapter.isEnabled()) {
- updateContent(mBluetoothAdapter.getState());
- } else {
- // Turn on bluetooth if it is disabled
- mBluetoothAdapter.enable();
- }
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mAlwaysDiscoverable = new AlwaysDiscoverable(getContext());
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
}
@Override
public void onStop() {
super.onStop();
- if (mLocalManager == null){
- Log.e(TAG, "Bluetooth is not supported on this device");
- return;
- }
// Make the device only visible to connected devices.
mAlwaysDiscoverable.stop();
- disableScanning();
}
@Override
- void initPreferencesFromPreferenceScreen() {
- mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_AVAIL_DEVICES);
- mFooterPreference = (FooterPreference) findPreference(KEY_FOOTER_PREF);
+ public void initPreferencesFromPreferenceScreen() {
+ super.initPreferencesFromPreferenceScreen();
+ mFooterPreference = findPreference(KEY_FOOTER_PREF);
mFooterPreference.setSelectable(false);
}
@@ -120,23 +89,25 @@
return SettingsEnums.BLUETOOTH_PAIRING;
}
+ /**
+ * {@inheritDoc}
+ *
+ * Will update footer and keep the device discoverable as long as the page is visible.
+ */
+ @VisibleForTesting
@Override
- void enableScanning() {
- // Clear all device states before first scan
- if (!mInitialScanStarted) {
- if (mAvailableDevicesCategory != null) {
- removeAllDevices();
+ public void updateContent(int bluetoothState) {
+ super.updateContent(bluetoothState);
+ if (bluetoothState == BluetoothAdapter.STATE_ON) {
+ if (mInitialScanStarted) {
+ // Don't show bonded devices when screen turned back on
+ setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
+ addCachedDevices();
}
- mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
- mInitialScanStarted = true;
+ setFilter(BluetoothDeviceFilter.ALL_FILTER);
+ updateFooterPreference(mFooterPreference);
+ mAlwaysDiscoverable.start();
}
- super.enableScanning();
- }
-
- @Override
- void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- disableScanning();
- super.onDevicePreferenceClick(btPreference);
}
@Override
@@ -146,78 +117,6 @@
mAvailableDevicesCategory.setProgress(started);
}
- @VisibleForTesting
- void updateContent(int bluetoothState) {
- switch (bluetoothState) {
- case BluetoothAdapter.STATE_ON:
- mDevicePreferenceMap.clear();
- mBluetoothAdapter.enable();
-
- addDeviceCategory(mAvailableDevicesCategory,
- R.string.bluetooth_preference_found_media_devices,
- BluetoothDeviceFilter.ALL_FILTER, mInitialScanStarted);
- updateFooterPreference(mFooterPreference);
- mAlwaysDiscoverable.start();
- enableScanning();
- break;
-
- case BluetoothAdapter.STATE_OFF:
- finish();
- break;
- }
- }
-
- @Override
- public void onBluetoothStateChanged(int bluetoothState) {
- super.onBluetoothStateChanged(bluetoothState);
- updateContent(bluetoothState);
- if (bluetoothState == BluetoothAdapter.STATE_ON) {
- showBluetoothTurnedOnToast();
- }
- }
-
- @Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
- if (bondState == BluetoothDevice.BOND_BONDED) {
- // If one device is connected(bonded), then close this fragment.
- finish();
- return;
- } else if (bondState == BluetoothDevice.BOND_BONDING) {
- // Set the bond entry where binding process starts for logging hearing aid device info
- final int pageId = FeatureFactory.getFactory(
- getContext()).getMetricsFeatureProvider().getAttribution(getActivity());
- final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
- pageId);
- HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
- }
- if (mSelectedDevice != null && cachedDevice != null) {
- BluetoothDevice device = cachedDevice.getDevice();
- if (device != null && mSelectedDevice.equals(device)
- && bondState == BluetoothDevice.BOND_NONE) {
- // If currently selected device failed to bond, restart scanning
- enableScanning();
- }
- }
- }
-
- @Override
- public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
- int bluetoothProfile) {
- // This callback is used to handle the case that bonded device is connected in pairing list.
- // 1. If user selected multiple bonded devices in pairing list, after connected
- // finish this page.
- // 2. If the bonded devices auto connected in paring list, after connected it will be
- // removed from paring list.
- if (cachedDevice != null && cachedDevice.isConnected()) {
- final BluetoothDevice device = cachedDevice.getDevice();
- if (device != null && mSelectedList.contains(device)) {
- finish();
- } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
- onDeviceDeleted(cachedDevice);
- }
- }
- }
-
@Override
public int getHelpResource() {
return R.string.help_url_bluetooth;
@@ -237,10 +136,4 @@
public String getDeviceListKey() {
return KEY_AVAIL_DEVICES;
}
-
- @VisibleForTesting
- void showBluetoothTurnedOnToast() {
- Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
- Toast.LENGTH_SHORT).show();
- }
}
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index 46c4fc3..7bb2696 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -24,7 +24,6 @@
import androidx.preference.Preference;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -39,9 +38,9 @@
private final AudioManager mAudioManager;
- public ConnectedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
- DevicePreferenceCallback devicePreferenceCallback) {
- super(context, fragment, devicePreferenceCallback);
+ public ConnectedBluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+ super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 2181309..522b5cb 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -18,6 +18,11 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
import android.os.Bundle;
import android.os.SystemProperties;
import android.text.BidiFormatter;
@@ -33,6 +38,7 @@
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.ArrayList;
@@ -59,37 +65,51 @@
"persist.bluetooth.showdeviceswithoutnames";
private BluetoothDeviceFilter.Filter mFilter;
+ private List<ScanFilter> mLeScanFilters;
+ private ScanCallback mScanCallback;
@VisibleForTesting
- boolean mScanEnabled;
+ protected boolean mScanEnabled;
- BluetoothDevice mSelectedDevice;
+ protected BluetoothDevice mSelectedDevice;
- BluetoothAdapter mBluetoothAdapter;
- LocalBluetoothManager mLocalManager;
+ protected BluetoothAdapter mBluetoothAdapter;
+ protected LocalBluetoothManager mLocalManager;
+ protected CachedBluetoothDeviceManager mCachedDeviceManager;
@VisibleForTesting
- PreferenceGroup mDeviceListGroup;
+ protected PreferenceGroup mDeviceListGroup;
- final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
+ protected final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
new HashMap<>();
- final List<BluetoothDevice> mSelectedList = new ArrayList<>();
+ protected final List<BluetoothDevice> mSelectedList = new ArrayList<>();
- boolean mShowDevicesWithoutNames;
+ protected boolean mShowDevicesWithoutNames;
- DeviceListPreferenceFragment(String restrictedKey) {
+ public DeviceListPreferenceFragment(String restrictedKey) {
super(restrictedKey);
mFilter = BluetoothDeviceFilter.ALL_FILTER;
}
- final void setFilter(BluetoothDeviceFilter.Filter filter) {
+ protected final void setFilter(BluetoothDeviceFilter.Filter filter) {
mFilter = filter;
}
- final void setFilter(int filterType) {
+ protected final void setFilter(int filterType) {
mFilter = BluetoothDeviceFilter.getFilter(filterType);
}
+ /**
+ * Sets the bluetooth device scanning filter with {@link ScanFilter}s. It will change to start
+ * {@link BluetoothLeScanner} which will scan BLE device only.
+ *
+ * @param leScanFilters list of settings to filter scan result
+ */
+ protected void setFilter(List<ScanFilter> leScanFilters) {
+ mFilter = null;
+ mLeScanFilters = leScanFilters;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -100,6 +120,7 @@
return;
}
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
mShowDevicesWithoutNames = SystemProperties.getBoolean(
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
@@ -109,7 +130,7 @@
}
/** find and update preference that already existed in preference screen */
- abstract void initPreferencesFromPreferenceScreen();
+ protected abstract void initPreferencesFromPreferenceScreen();
@Override
public void onStart() {
@@ -139,7 +160,7 @@
void addCachedDevices() {
Collection<CachedBluetoothDevice> cachedDevices =
- mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
+ mCachedDeviceManager.getCachedDevicesCopy();
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
onDeviceAdded(cachedDevice);
}
@@ -164,7 +185,7 @@
return super.onPreferenceTreeClick(preference);
}
- void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+ protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
btPreference.onClicked();
}
@@ -177,7 +198,8 @@
// Prevent updates while the list shows one of the state messages
if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) return;
- if (mFilter.matches(cachedDevice.getDevice())) {
+ if (mLeScanFilters != null
+ || (mFilter != null && mFilter.matches(cachedDevice.getDevice()))) {
createDevicePreference(cachedDevice);
}
}
@@ -227,7 +249,7 @@
}
@VisibleForTesting
- void enableScanning() {
+ protected void enableScanning() {
// BluetoothAdapter already handles repeated scan requests
if (!mScanEnabled) {
startScanning();
@@ -236,7 +258,7 @@
}
@VisibleForTesting
- void disableScanning() {
+ protected void disableScanning() {
if (mScanEnabled) {
stopScanning();
mScanEnabled = false;
@@ -251,31 +273,6 @@
}
/**
- * Add bluetooth device preferences to {@code preferenceGroup} which satisfy the {@code filter}
- *
- * This method will also (1) set the title for {@code preferenceGroup} and (2) change the
- * default preferenceGroup and filter
- * @param preferenceGroup
- * @param titleId
- * @param filter
- * @param addCachedDevices
- */
- public void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
- BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) {
- cacheRemoveAllPrefs(preferenceGroup);
- preferenceGroup.setTitle(titleId);
- mDeviceListGroup = preferenceGroup;
- if (addCachedDevices) {
- // Don't show bonded devices when screen turned back on
- setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
- addCachedDevices();
- }
- setFilter(filter);
- preferenceGroup.setEnabled(true);
- removeCachedPrefs(preferenceGroup);
- }
-
- /**
* Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
*/
public abstract String getDeviceListKey();
@@ -284,15 +281,65 @@
return mShowDevicesWithoutNames;
}
+ @VisibleForTesting
void startScanning() {
+ if (mFilter != null) {
+ startClassicScanning();
+ } else if (mLeScanFilters != null) {
+ startLeScanning();
+ }
+
+ }
+
+ @VisibleForTesting
+ void stopScanning() {
+ if (mFilter != null) {
+ stopClassicScanning();
+ } else if (mLeScanFilters != null) {
+ stopLeScanning();
+ }
+ }
+
+ private void startClassicScanning() {
if (!mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.startDiscovery();
}
}
- void stopScanning() {
+ private void stopClassicScanning() {
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
}
+
+ private void startLeScanning() {
+ final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+ final ScanSettings settings = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+ .build();
+ mScanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ final BluetoothDevice device = result.getDevice();
+ CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mCachedDeviceManager.addDevice(device);
+ }
+ onDeviceAdded(cachedDevice);
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ Log.w(TAG, "BLE Scan failed with error code " + errorCode);
+ }
+ };
+ scanner.startScan(mLeScanFilters, settings, mScanCallback);
+ }
+
+ private void stopLeScanning() {
+ final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+ if (scanner != null) {
+ scanner.stopScan(mScanCallback);
+ }
+ }
}
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index e8adac0..2e81062 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -27,8 +27,8 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserManager;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -68,7 +68,7 @@
}
@Override
- void initPreferencesFromPreferenceScreen() {
+ public void initPreferencesFromPreferenceScreen() {
Intent intent = getActivity().getIntent();
mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
@@ -136,7 +136,7 @@
}
@Override
- void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+ public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
disableScanning();
LocalBluetoothPreferences.persistSelectedDeviceInPicker(
getActivity(), mSelectedDevice.getAddress());
diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
index e7a8317..a4a9451 100644
--- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
@@ -25,8 +25,6 @@
import androidx.preference.Preference;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -44,15 +42,16 @@
private static final String PREF_KEY = "saved_bt";
- private final boolean mDisplayConnected;
+ private final boolean mShowConnectedDevice;
@VisibleForTesting
BluetoothAdapter mBluetoothAdapter;
- public SavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
- DevicePreferenceCallback devicePreferenceCallback) {
- super(context, fragment, devicePreferenceCallback);
- mDisplayConnected = (fragment instanceof PreviouslyConnectedDeviceDashboardFragment);
+ public SavedBluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback, boolean showConnectedDevice,
+ int metricsCategory) {
+ super(context, devicePreferenceCallback, metricsCategory);
+ mShowConnectedDevice = showConnectedDevice;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@@ -106,13 +105,13 @@
+ cachedDevice.isConnected());
}
return device.getBondState() == BluetoothDevice.BOND_BONDED
- && (mDisplayConnected || (!device.isConnected() && isDeviceInCachedDevicesList(
+ && (mShowConnectedDevice || (!device.isConnected() && isDeviceInCachedDevicesList(
cachedDevice)));
}
@Override
public boolean onPreferenceClick(Preference preference) {
- mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+ mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
.getBluetoothDevice();
if (device.isConnected()) {
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index 37c826f..46d8e70 100644
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -42,6 +42,9 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
/**
* Utils is a helper class that contains constants for various
* Android resource IDs, debug logging flags, and static methods
@@ -132,6 +135,24 @@
return LocalBluetoothManager.getInstance(context, mOnInitCallback);
}
+ /**
+ * Obtains a {@link LocalBluetoothManager}.
+ *
+ * To avoid StrictMode ThreadPolicy violation, will get it in another thread.
+ */
+ public static LocalBluetoothManager getLocalBluetoothManager(Context context) {
+ final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
+ // Avoid StrictMode ThreadPolicy violation
+ () -> getLocalBtManager(context));
+ try {
+ localBtManagerFutureTask.run();
+ return localBtManagerFutureTask.get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "Error getting LocalBluetoothManager.", e);
+ return null;
+ }
+ }
+
public static String createRemoteName(Context context, BluetoothDevice device) {
String mRemoteName = device != null ? device.getAlias() : null;
diff --git a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
index 6623b97..a340015 100644
--- a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
@@ -131,7 +131,7 @@
public void init(DashboardFragment fragment) {
mFragmentManager = fragment.getParentFragmentManager();
mBluetoothDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(fragment.getContext(),
- fragment, AvailableMediaDeviceGroupController.this);
+ AvailableMediaDeviceGroupController.this, fragment.getMetricsCategory());
}
@VisibleForTesting
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index 0d51ebe..f7517b4 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -184,7 +184,8 @@
final DockUpdater connectedDockUpdater =
dockUpdaterFeatureProvider.getConnectedDockUpdater(context, this);
init(hasBluetoothFeature()
- ? new ConnectedBluetoothDeviceUpdater(context, fragment, this)
+ ? new ConnectedBluetoothDeviceUpdater(context, this,
+ fragment.getMetricsCategory())
: null,
hasUsbFeature()
? new ConnectedUsbDeviceUpdater(context, fragment, this)
diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
index afeca51..5c906fd 100644
--- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
@@ -125,7 +125,8 @@
public void init(DashboardFragment fragment) {
mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
- fragment, PreviouslyConnectedDevicePreferenceController.this);
+ PreviouslyConnectedDevicePreferenceController.this, /* showConnectedDevice= */
+ false, fragment.getMetricsCategory());
}
@Override
diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
index df721f1..3034e2f 100644
--- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
@@ -117,7 +117,8 @@
public void init(DashboardFragment fragment) {
mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
- fragment, SavedDeviceGroupController.this);
+ SavedDeviceGroupController.this, /* showConnectedDevice= */true,
+ fragment.getMetricsCategory());
}
@VisibleForTesting
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
index 4e276c1..f1f9521 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
@@ -332,12 +332,13 @@
private void lazyInitUpdaters() {
if (mAvailableMediaBtDeviceUpdater == null) {
mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext,
- null /* fragment */, null /* devicePreferenceCallback */);
+ /* devicePreferenceCallback= */ null, /* metricsCategory= */ 0);
}
if (mSavedBtDeviceUpdater == null) {
mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext,
- null /* fragment */, null /* devicePreferenceCallback */);
+ /* devicePreferenceCallback= */ null, /* showConnectedDevice= */
+ false, /* metricsCategory= */ 0);
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java
new file mode 100644
index 0000000..6305014
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 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.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for {@link AvailableHearingDeviceUpdater}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class AvailableHearingDeviceUpdaterTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private DevicePreferenceCallback mDevicePreferenceCallback;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private AvailableHearingDeviceUpdater mUpdater;
+
+ @Before
+ public void setUp() {
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+ mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ mUpdater = new AvailableHearingDeviceUpdater(mContext,
+ mDevicePreferenceCallback, /* metricsCategory= */ 0);
+ }
+
+ @Test
+ public void isFilterMatch_connectedHearingDevice_returnTrue() {
+ CachedBluetoothDevice connectedHearingDevice = mCachedBluetoothDevice;
+ when(connectedHearingDevice.isConnectedHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(connectedHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(connectedHearingDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isFilterMatch_nonConnectedHearingDevice_returnFalse() {
+ CachedBluetoothDevice nonConnectedHearingDevice = mCachedBluetoothDevice;
+ when(nonConnectedHearingDevice.isConnectedHearingAidDevice()).thenReturn(false);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(nonConnectedHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(nonConnectedHearingDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isFilterMatch_connectedBondingHearingDevice_returnFalse() {
+ CachedBluetoothDevice connectedBondingHearingDevice = mCachedBluetoothDevice;
+ when(connectedBondingHearingDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDING).when(mBluetoothDevice).getBondState();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(connectedBondingHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(connectedBondingHearingDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isFilterMatch_hearingDeviceNotInCachedDevicesList_returnFalse() {
+ CachedBluetoothDevice notInCachedDevicesListDevice = mCachedBluetoothDevice;
+ when(notInCachedDevicesListDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ doReturn(false).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(new ArrayList<>());
+
+ assertThat(mUpdater.isFilterMatched(notInCachedDevicesListDevice)).isEqualTo(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java
new file mode 100644
index 0000000..7e064c4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link BaseBluetoothDevicePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class BaseBluetoothDevicePreferenceControllerTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private static final String FAKE_KEY = "fake_key";
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private PackageManager mPackageManager;
+ private PreferenceCategory mPreferenceCategory;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceScreen mScreen;
+ private TestBaseBluetoothDevicePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ FakeFeatureFactory.setupForTest();
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(FAKE_KEY);
+ mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mScreen.addPreference(mPreferenceCategory);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ mController = new TestBaseBluetoothDevicePreferenceController(mContext, FAKE_KEY);
+ }
+
+ @Test
+ public void getAvailabilityStatus_hasBluetoothFeature_available() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noBluetoothFeature_conditionallyUnavailalbe() {
+ doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void displayPreference_preferenceCategoryInVisible() {
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreferenceCategory.isVisible()).isFalse();
+ }
+
+ @Test
+ public void onDeviceAdded_preferenceCategoryVisible() {
+ Preference preference = new Preference(mContext);
+ mController.displayPreference(mScreen);
+
+ mController.onDeviceAdded(preference);
+
+ assertThat(mPreferenceCategory.isVisible()).isTrue();
+ }
+
+ @Test
+ public void onDeviceRemoved_addedPreferenceFirst_preferenceCategoryInVisible() {
+ Preference preference = new Preference(mContext);
+ mController.displayPreference(mScreen);
+
+ mController.onDeviceAdded(preference);
+ mController.onDeviceRemoved(preference);
+
+ assertThat(mPreferenceCategory.isVisible()).isFalse();
+ }
+
+ public static class TestBaseBluetoothDevicePreferenceController extends
+ BaseBluetoothDevicePreferenceController {
+
+ public TestBaseBluetoothDevicePreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java
new file mode 100644
index 0000000..e1651d9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 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.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.BluetoothProgressCategory;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+/** Tests for {@link HearingDevicePairingDetail}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HearingDevicePairingDetailTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ private BluetoothProgressCategory mProgressCategory;
+ private TestHearingDevicePairingDetail mFragment;
+
+ @Before
+ public void setUp() {
+ final BluetoothAdapter bluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
+ final ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
+ BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setEnabled(true);
+
+ mProgressCategory = spy(new BluetoothProgressCategory(mContext));
+ mFragment = spy(new TestHearingDevicePairingDetail());
+ when(mFragment.getContext()).thenReturn(mContext);
+ when(mFragment.findPreference(
+ HearingDevicePairingDetail.KEY_AVAILABLE_HEARING_DEVICES)).thenReturn(
+ mProgressCategory);
+ mFragment.setBluetoothAdapter(bluetoothAdapter);
+
+ }
+
+ @Test
+ public void getDeviceListKey_expectedKey() {
+ assertThat(mFragment.getDeviceListKey()).isEqualTo(
+ HearingDevicePairingDetail.KEY_AVAILABLE_HEARING_DEVICES);
+ }
+
+ @Test
+ public void onDeviceBondStateChanged_bondNone_setProgressFalse() {
+ mFragment.initPreferencesFromPreferenceScreen();
+
+ mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
+
+ verify(mProgressCategory).setProgress(true);
+ }
+
+ @Test
+ public void onDeviceBondStateChanged_bonding_setProgressTrue() {
+ mFragment.initPreferencesFromPreferenceScreen();
+
+ mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
+
+ verify(mProgressCategory).setProgress(false);
+ }
+
+ private static class TestHearingDevicePairingDetail extends HearingDevicePairingDetail {
+ TestHearingDevicePairingDetail() {
+ super();
+ }
+
+ public void setBluetoothAdapter(BluetoothAdapter bluetoothAdapter) {
+ this.mBluetoothAdapter = bluetoothAdapter;
+ }
+
+ public void enableScanning() {
+ super.enableScanning();
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java
new file mode 100644
index 0000000..9946b9e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 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.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for {@link SavedHearingDeviceUpdater}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class SavedHearingDeviceUpdaterTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private DevicePreferenceCallback mDevicePreferenceCallback;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private SavedHearingDeviceUpdater mUpdater;
+
+ @Before
+ public void setUp() {
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+ mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ mUpdater = new SavedHearingDeviceUpdater(mContext,
+ mDevicePreferenceCallback, /* metricsCategory= */ 0);
+ }
+
+ @Test
+ public void isFilterMatch_savedHearingDevice_returnTrue() {
+ CachedBluetoothDevice savedHearingDevice = mCachedBluetoothDevice;
+ when(savedHearingDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ doReturn(false).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(savedHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(savedHearingDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isFilterMatch_savedNonHearingDevice_returnFalse() {
+ CachedBluetoothDevice savedNonHearingDevice = mCachedBluetoothDevice;
+ when(savedNonHearingDevice.isHearingAidDevice()).thenReturn(false);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ doReturn(false).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(savedNonHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(savedNonHearingDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isFilterMatch_savedBondingHearingDevice_returnFalse() {
+ CachedBluetoothDevice savedBondingHearingDevice = mCachedBluetoothDevice;
+ when(savedBondingHearingDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDING).when(mBluetoothDevice).getBondState();
+ doReturn(false).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(savedBondingHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(savedBondingHearingDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isFilterMatch_connectedHearingDevice_returnFalse() {
+ CachedBluetoothDevice connectdHearingDevice = mCachedBluetoothDevice;
+ when(connectdHearingDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ doReturn(true).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ new ArrayList<>(List.of(connectdHearingDevice)));
+
+ assertThat(mUpdater.isFilterMatched(connectdHearingDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isFilterMatch_hearingDeviceNotInCachedDevicesList_returnFalse() {
+ CachedBluetoothDevice notInCachedDevicesListDevice = mCachedBluetoothDevice;
+ when(notInCachedDevicesListDevice.isHearingAidDevice()).thenReturn(true);
+ doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+ doReturn(false).when(mBluetoothDevice).isConnected();
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(new ArrayList<>());
+
+ assertThat(mUpdater.isFilterMatched(notInCachedDevicesListDevice)).isEqualTo(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index 0b06f3e..d69b5d4 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -96,7 +96,7 @@
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
mBluetoothDeviceUpdater = spy(new AvailableMediaBluetoothDeviceUpdater(mContext,
- mDashboardFragment, mDevicePreferenceCallback));
+ mDevicePreferenceCallback, /* metricsCategory= */ 0));
mBluetoothDeviceUpdater.setPrefContext(mContext);
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false,
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
index 92174f3..ea65856 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
@@ -16,12 +16,17 @@
package com.android.settings.bluetooth;
+import static com.android.settings.bluetooth.BluetoothDetailsAudioRoutingController.KEY_AUDIO_ROUTING;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.util.FeatureFlagUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +41,8 @@
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
private BluetoothDetailsAudioRoutingController mController;
@Override
@@ -44,7 +51,9 @@
mController = new BluetoothDetailsAudioRoutingController(mContext, mFragment, mCachedDevice,
mLifecycle);
- mController.init(mScreen);
+ final PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
+ preferenceCategory.setKey(mController.getPreferenceKey());
+ mScreen.addPreference(preferenceCategory);
}
@Test
@@ -64,4 +73,20 @@
assertThat(mController.isAvailable()).isFalse();
}
+
+ @Test
+ public void init_isHearingAidDevice_expectedAudioRoutingPreference() {
+ when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+
+ mController.init(mScreen);
+ final Preference preference = mScreen.findPreference(KEY_AUDIO_ROUTING);
+ final String address = preference.getExtras().getString(
+ BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS);
+ final String fragment = preference.getFragment();
+
+ assertThat(address).isEqualTo(TEST_ADDRESS);
+ assertThat(fragment).isEqualTo(BluetoothDetailsAudioRoutingFragment.class.getName());
+
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
new file mode 100644
index 0000000..b2da579
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+/** Tests for {@link BluetoothDetailsAudioRoutingFragment}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class BluetoothDetailsAudioRoutingFragmentTest {
+
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ private BluetoothDetailsAudioRoutingFragment mFragment;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private LocalBluetoothAdapter mLocalBluetoothAdapter;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice;
+
+ @Before
+ public void setUp() {
+ setupEnvironment();
+
+ when(mLocalBluetoothAdapter.getRemoteDevice(TEST_ADDRESS)).thenReturn(mBluetoothDevice);
+ when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
+
+ mFragment = new BluetoothDetailsAudioRoutingFragment();
+ }
+
+ @Test
+ public void onAttach_setArgumentsWithAddress_expectedCachedDeviceWithAddress() {
+ final Bundle args = new Bundle();
+ args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
+ mFragment.setArguments(args);
+
+ mFragment.onAttach(mContext);
+
+ assertThat(mFragment.mCachedDevice.getAddress()).isEqualTo(TEST_ADDRESS);
+ }
+
+ @Test
+ public void getNonIndexableKeys_existInXmlLayout() {
+ final List<String> niks = BluetoothDetailsAudioRoutingFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List<String> keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.bluetooth_audio_routing_fragment);
+
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+
+ private void setupEnvironment() {
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+ when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
new file mode 100644
index 0000000..184f521
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.BluetoothProfile;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Pair;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+/** Tests for {@link BluetoothDevicePairingDetailBase}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class BluetoothDevicePairingDetailBaseTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ public static final String KEY_DEVICE_LIST_GROUP = "test_key";
+
+ private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+ private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private Resources mResource;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private LocalBluetoothManager mLocalManager;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private Drawable mDrawable;
+ private BluetoothAdapter mBluetoothAdapter;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private BluetoothProgressCategory mAvailableDevicesCategory;
+ private BluetoothDevice mBluetoothDevice;
+ private TestBluetoothDevicePairingDetailBase mFragment;
+
+ @Before
+ public void setUp() {
+ mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+ final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
+ when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
+ mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+
+ mFragment = spy(new TestBluetoothDevicePairingDetailBase());
+ when(mFragment.findPreference(KEY_DEVICE_LIST_GROUP)).thenReturn(mAvailableDevicesCategory);
+ doReturn(mContext).when(mFragment).getContext();
+ doReturn(mResource).when(mFragment).getResources();
+ mFragment.mDeviceListGroup = mAvailableDevicesCategory;
+ mFragment.mLocalManager = mLocalManager;
+ mFragment.mBluetoothAdapter = mBluetoothAdapter;
+ mFragment.initPreferencesFromPreferenceScreen();
+
+ }
+
+ @Test
+ public void startScanning_startScanAndRemoveDevices() {
+ mFragment.enableScanning();
+
+ verify(mFragment).startScanning();
+ verify(mAvailableDevicesCategory).removeAll();
+ }
+
+ @Test
+ public void updateContent_stateOn() {
+ mFragment.updateContent(BluetoothAdapter.STATE_ON);
+
+ assertThat(mBluetoothAdapter.isEnabled()).isTrue();
+ verify(mFragment).enableScanning();
+ }
+
+ @Test
+ public void updateContent_stateOff_finish() {
+ mFragment.updateContent(BluetoothAdapter.STATE_OFF);
+
+ verify(mFragment).finish();
+ }
+
+ @Test
+ public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
+ mShadowBluetoothAdapter.setEnabled(false);
+
+ mFragment.updateBluetooth();
+
+ assertThat(mBluetoothAdapter.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateBluetooth_bluetoothOn_updateState() {
+ mShadowBluetoothAdapter.setEnabled(true);
+ doNothing().when(mFragment).updateContent(anyInt());
+
+ mFragment.updateBluetooth();
+
+ verify(mFragment).updateContent(anyInt());
+ }
+
+ @Test
+ public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
+ doNothing().when(mFragment).updateContent(anyInt());
+
+ mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+ verify(mFragment).showBluetoothTurnedOnToast();
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
+ final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+ mFragment.mSelectedList.add(mBluetoothDevice);
+ mFragment.mSelectedList.add(device);
+
+ when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+ mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+ verify(mFragment).finish();
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
+ final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+ mFragment.mSelectedList.add(device);
+
+ when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+ // not crash
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
+ final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+ mFragment.mSelectedList.add(mBluetoothDevice);
+ mFragment.mSelectedList.add(device);
+
+ when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+ mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
+
+ // not crash
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
+ final BluetoothDevicePreference preference =
+ new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+ true, BluetoothDevicePreference.SortType.TYPE_FIFO);
+ final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+ mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+
+ when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+ mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+ assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
+ final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+ final BluetoothDevicePreference preference =
+ new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+ true, BluetoothDevicePreference.SortType.TYPE_FIFO);
+ final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+ final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+ mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+
+ when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+ when(cachedDevice.isConnected()).thenReturn(true);
+ when(cachedDevice.getDevice()).thenReturn(device2);
+ when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
+ when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
+
+ mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
+ BluetoothAdapter.STATE_CONNECTED);
+
+ // not crash
+ }
+
+ private static class TestBluetoothDevicePairingDetailBase extends
+ BluetoothDevicePairingDetailBase {
+
+ TestBluetoothDevicePairingDetailBase() {
+ super();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public String getDeviceListKey() {
+ return KEY_DEVICE_LIST_GROUP;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return 0;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return "test_tag";
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
index 6afa56c..d165aa5 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
@@ -34,7 +34,6 @@
import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -63,8 +62,6 @@
private static final String TEST_NAME = "test_name";
@Mock
- private DashboardFragment mDashboardFragment;
- @Mock
private DevicePreferenceCallback mDevicePreferenceCallback;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@@ -84,7 +81,7 @@
private Drawable mDrawable;
private Context mContext;
- private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+ private TestBluetoothDeviceUpdater mBluetoothDeviceUpdater;
private BluetoothDevicePreference mPreference;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
@@ -97,7 +94,6 @@
mContext = RuntimeEnvironment.application;
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mCachedDevices.add(mCachedBluetoothDevice);
- doReturn(mContext).when(mDashboardFragment).getContext();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
@@ -107,20 +103,10 @@
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
- false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
- mBluetoothDeviceUpdater =
- new BluetoothDeviceUpdater(mContext, mDashboardFragment, mDevicePreferenceCallback,
- mLocalManager) {
- @Override
- public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
- return true;
- }
-
- @Override
- protected String getPreferenceKey() {
- return "test_bt";
- }
- };
+ /* showDeviceWithoutNames= */ false,
+ BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+ mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mContext,
+ mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */ 0);
mBluetoothDeviceUpdater.setPrefContext(mContext);
}
@@ -185,7 +171,8 @@
@Test
public void testDeviceProfilesListener_click_startBluetoothDeviceDetailPage() {
- doReturn(mSettingsActivity).when(mDashboardFragment).getContext();
+ mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mSettingsActivity,
+ mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */ 0);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mBluetoothDeviceUpdater.mDeviceProfilesListener.onGearClick(mPreference);
@@ -274,4 +261,22 @@
assertThat(mPreference.getTitle()).isEqualTo(TEST_NAME);
}
+
+ public static class TestBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
+ public TestBluetoothDeviceUpdater(Context context,
+ DevicePreferenceCallback devicePreferenceCallback,
+ LocalBluetoothManager localManager, int metricsCategory) {
+ super(context, devicePreferenceCallback, localManager, metricsCategory);
+ }
+
+ @Override
+ public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
+ return true;
+ }
+
+ @Override
+ protected String getPreferenceKey() {
+ return "test_bt";
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
index bac868f..865ed81 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
@@ -18,102 +18,66 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.Pair;
+import android.os.Bundle;
-import androidx.preference.PreferenceGroup;
+import androidx.test.core.app.ApplicationProvider;
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.widget.FooterPreference;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothPairingDetailTest {
- private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
- private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private Resources mResource;
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private LocalBluetoothManager mLocalManager;
- @Mock
- private PreferenceGroup mPreferenceGroup;
- @Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
- private Drawable mDrawable;
-
private BluetoothPairingDetail mFragment;
- private Context mContext;
private BluetoothProgressCategory mAvailableDevicesCategory;
private FooterPreference mFooterPreference;
private BluetoothAdapter mBluetoothAdapter;
- private ShadowBluetoothAdapter mShadowBluetoothAdapter;
- private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
- mContext = RuntimeEnvironment.application;
mFragment = spy(new BluetoothPairingDetail());
doReturn(mContext).when(mFragment).getContext();
- doReturn(mResource).when(mFragment).getResources();
- when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
- when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
-
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
mFooterPreference = new FooterPreference(mContext);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
- mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+
+ doReturn(mAvailableDevicesCategory).when(mFragment)
+ .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
+ doReturn(mFooterPreference).when(mFragment)
+ .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
mFragment.mBluetoothAdapter = mBluetoothAdapter;
mFragment.mLocalManager = mLocalManager;
- mFragment.mDeviceListGroup = mPreferenceGroup;
- mFragment.mAlwaysDiscoverable = new AlwaysDiscoverable(mContext);
+ mFragment.mDeviceListGroup = mAvailableDevicesCategory;
+ mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
}
-
+//
@Test
public void initPreferencesFromPreferenceScreen_findPreferences() {
- doReturn(mAvailableDevicesCategory).when(mFragment)
- .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
- doReturn(mFooterPreference).when(mFragment)
- .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
-
mFragment.initPreferencesFromPreferenceScreen();
assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory);
@@ -121,63 +85,19 @@
}
@Test
- public void startScanning_startScanAndRemoveDevices() {
- mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
- mFragment.mDeviceListGroup = mAvailableDevicesCategory;
-
- mFragment.enableScanning();
-
- verify(mFragment).startScanning();
- verify(mAvailableDevicesCategory).removeAll();
- }
-
- @Test
public void updateContent_stateOn_addDevices() {
- mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
- mFragment.mFooterPreference = mFooterPreference;
- doNothing().when(mFragment).addDeviceCategory(any(), anyInt(), any(), anyBoolean());
+ mFragment.initPreferencesFromPreferenceScreen();
mFragment.updateContent(BluetoothAdapter.STATE_ON);
- verify(mFragment).addDeviceCategory(mAvailableDevicesCategory,
- R.string.bluetooth_preference_found_media_devices,
- BluetoothDeviceFilter.ALL_FILTER, false);
+ assertThat(mFragment.mAlwaysDiscoverable.mStarted).isEqualTo(true);
assertThat(mBluetoothAdapter.getScanMode())
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
}
@Test
- public void updateContent_stateOff_finish() {
- mFragment.updateContent(BluetoothAdapter.STATE_OFF);
-
- verify(mFragment).finish();
- }
-
- @Test
- public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
- mShadowBluetoothAdapter.setEnabled(false);
-
- mFragment.updateBluetooth();
-
- assertThat(mBluetoothAdapter.isEnabled()).isTrue();
- }
-
- @Test
- public void updateBluetooth_bluetoothOn_updateState() {
- mShadowBluetoothAdapter.setEnabled(true);
- doNothing().when(mFragment).updateContent(anyInt());
-
- mFragment.updateBluetooth();
-
- verify(mFragment).updateContent(anyInt());
- }
-
- @Test
public void onScanningStateChanged_restartScanAfterInitialScanning() {
- mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
- mFragment.mFooterPreference = mFooterPreference;
- mFragment.mDeviceListGroup = mAvailableDevicesCategory;
- doNothing().when(mFragment).addDeviceCategory(any(), anyInt(), any(), anyBoolean());
+ mFragment.initPreferencesFromPreferenceScreen();
// Initial Bluetooth ON will trigger scan enable, list clear and scan start
mFragment.updateContent(BluetoothAdapter.STATE_ON);
@@ -219,97 +139,4 @@
// Verify that clean up only happen once at initialization
verify(mAvailableDevicesCategory, times(1)).removeAll();
}
-
- @Test
- public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
- doNothing().when(mFragment).updateContent(anyInt());
-
- mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
-
- verify(mFragment).showBluetoothTurnedOnToast();
- }
-
- @Test
- public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
- mFragment.mSelectedList.add(mBluetoothDevice);
- mFragment.mSelectedList.add(device);
-
- when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
- mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
- BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
- verify(mFragment).finish();
- }
-
- @Test
- public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
- mFragment.mSelectedList.add(device);
-
- when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
-
- mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
- BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
- // not crash
- }
-
- @Test
- public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
- mFragment.mSelectedList.add(mBluetoothDevice);
- mFragment.mSelectedList.add(device);
-
- when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
- mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
- BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
-
- // not crash
- }
-
- @Test
- public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
- final BluetoothDevicePreference preference =
- new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
- true, BluetoothDevicePreference.SortType.TYPE_FIFO);
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
- mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
-
- when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
- mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
- BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
- assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
- }
-
- @Test
- public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
- final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
- final BluetoothDevicePreference preference =
- new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
- true, BluetoothDevicePreference.SortType.TYPE_FIFO);
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
- final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
- mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
-
- when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
- when(cachedDevice.isConnected()).thenReturn(true);
- when(cachedDevice.getDevice()).thenReturn(device2);
- when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
- when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
-
- mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
- BluetoothAdapter.STATE_CONNECTED);
-
- // not crash
- }
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index 1f90981..00115d7 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -97,7 +97,7 @@
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
- mDashboardFragment, mDevicePreferenceCallback));
+ mDevicePreferenceCallback, /* metricsCategory= */ 0));
mBluetoothDeviceUpdater.setPrefContext(mContext);
doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
diff --git a/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
index 19de2e4..4f46ce9 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
@@ -26,6 +26,11 @@
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.res.Resources;
@@ -45,6 +50,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -57,10 +63,14 @@
private Resources mResource;
@Mock
private Context mContext;
+ @Mock
+ private BluetoothLeScanner mBluetoothLeScanner;
private TestFragment mFragment;
private Preference mMyDevicePreference;
+
+ private BluetoothAdapter mBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -68,7 +78,8 @@
mFragment = spy(new TestFragment());
doReturn(mContext).when(mFragment).getContext();
doReturn(mResource).when(mFragment).getResources();
- mFragment.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
+ mFragment.mBluetoothAdapter = mBluetoothAdapter;
mMyDevicePreference = new Preference(RuntimeEnvironment.application);
}
@@ -169,6 +180,20 @@
verify(mFragment, times(1)).startScanning();
}
+ @Test
+ public void startScanning_setLeScanFilter_shouldStartLeScan() {
+ final ScanFilter leScanFilter = new ScanFilter.Builder()
+ .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}, new byte[]{0})
+ .build();
+ doReturn(mBluetoothLeScanner).when(mBluetoothAdapter).getBluetoothLeScanner();
+
+ mFragment.setFilter(Collections.singletonList(leScanFilter));
+ mFragment.startScanning();
+
+ verify(mBluetoothLeScanner).startScan(eq(Collections.singletonList(leScanFilter)),
+ any(ScanSettings.class), any(ScanCallback.class));
+ }
+
/**
* Fragment to test since {@code DeviceListPreferenceFragment} is abstract
*/
@@ -187,7 +212,7 @@
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
@Override
- void initPreferencesFromPreferenceScreen() {}
+ protected void initPreferencesFromPreferenceScreen() {}
@Override
public String getDeviceListKey() {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
index 255a0be..c229449 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
@@ -91,8 +91,8 @@
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
- mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext, mDashboardFragment,
- mDevicePreferenceCallback));
+ mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext,
+ mDevicePreferenceCallback, false, /* metricsCategory= */ 0));
mBluetoothDeviceUpdater.setPrefContext(mContext);
mBluetoothDeviceUpdater.mBluetoothAdapter = mBluetoothAdapter;
mBluetoothDeviceUpdater.mLocalManager = mBluetoothManager;