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
This commit is contained in:
Jason Hsu
2023-02-09 04:04:03 +00:00
committed by Android (Google) Code Review
43 changed files with 2079 additions and 470 deletions

View File

@@ -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">

View File

@@ -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] -->

View File

@@ -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"

View File

@@ -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>

View File

@@ -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/>

View File

@@ -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>

View File

@@ -29,7 +29,6 @@ import android.content.IntentFilter;
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 com.android.settingslib.core.lifecycle.events.OnStop;
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 class AccessibilityHearingAidPreferenceController extends BasePreferenceC
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 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
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;

View File

@@ -19,6 +19,7 @@ package com.android.settings.accessibility;
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;
@@ -44,6 +45,13 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
super(DISALLOW_CONFIG_BLUETOOTH);
}
@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);

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -23,7 +23,6 @@ import android.util.Log;
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 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
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 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
@Override
public boolean onPreferenceClick(Preference preference) {
mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
.getBluetoothDevice();
return device.setActive();

View File

@@ -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 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
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 @@ public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsCont
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;
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -27,11 +27,9 @@ import androidx.preference.Preference;
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 @@ import java.util.Map;
* {@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 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
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 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
* {@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 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
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();
}

View File

@@ -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";
@@ -49,35 +43,13 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
@VisibleForTesting
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 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
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 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
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 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
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 class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
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();
}
}

View File

@@ -24,7 +24,6 @@ import android.util.Log;
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 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
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);
}

View File

@@ -18,6 +18,11 @@ package com.android.settings.bluetooth;
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.settings.dashboard.RestrictedDashboardFragment;
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 @@ public abstract class DeviceListPreferenceFragment extends
"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 @@ public abstract class DeviceListPreferenceFragment extends
return;
}
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
mShowDevicesWithoutNames = SystemProperties.getBoolean(
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
@@ -109,7 +130,7 @@ public abstract class DeviceListPreferenceFragment extends
}
/** 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 @@ public abstract class DeviceListPreferenceFragment extends
void addCachedDevices() {
Collection<CachedBluetoothDevice> cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
mCachedDeviceManager.getCachedDevicesCopy();
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
onDeviceAdded(cachedDevice);
}
@@ -164,7 +185,7 @@ public abstract class DeviceListPreferenceFragment extends
return super.onPreferenceTreeClick(preference);
}
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
btPreference.onClicked();
}
@@ -177,7 +198,8 @@ public abstract class DeviceListPreferenceFragment extends
// 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 @@ public abstract class DeviceListPreferenceFragment extends
}
@VisibleForTesting
void enableScanning() {
protected void enableScanning() {
// BluetoothAdapter already handles repeated scan requests
if (!mScanEnabled) {
startScanning();
@@ -236,7 +258,7 @@ public abstract class DeviceListPreferenceFragment extends
}
@VisibleForTesting
void disableScanning() {
protected void disableScanning() {
if (mScanEnabled) {
stopScanning();
mScanEnabled = false;
@@ -250,31 +272,6 @@ public abstract class DeviceListPreferenceFragment extends
}
}
/**
* 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
*/
@@ -284,15 +281,65 @@ public abstract class DeviceListPreferenceFragment extends
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);
}
}
}

View File

@@ -27,8 +27,8 @@ import android.content.Context;
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 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
}
@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 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
}
@Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
disableScanning();
LocalBluetoothPreferences.persistSelectedDeviceInPicker(
getActivity(), mSelectedDevice.getAddress());

View File

@@ -25,8 +25,6 @@ import androidx.annotation.VisibleForTesting;
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 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater
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 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater
+ 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()) {

View File

@@ -42,6 +42,9 @@ import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
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 @@ public final class Utils {
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;

View File

@@ -131,7 +131,7 @@ public class AvailableMediaDeviceGroupController extends BasePreferenceControlle
public void init(DashboardFragment fragment) {
mFragmentManager = fragment.getParentFragmentManager();
mBluetoothDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(fragment.getContext(),
fragment, AvailableMediaDeviceGroupController.this);
AvailableMediaDeviceGroupController.this, fragment.getMetricsCategory());
}
@VisibleForTesting

View File

@@ -184,7 +184,8 @@ public class ConnectedDeviceGroupController extends BasePreferenceController
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)

View File

@@ -125,7 +125,8 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc
public void init(DashboardFragment fragment) {
mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
fragment, PreviouslyConnectedDevicePreferenceController.this);
PreviouslyConnectedDevicePreferenceController.this, /* showConnectedDevice= */
false, fragment.getMetricsCategory());
}
@Override

View File

@@ -117,7 +117,8 @@ public class SavedDeviceGroupController extends BasePreferenceController
public void init(DashboardFragment fragment) {
mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
fragment, SavedDeviceGroupController.this);
SavedDeviceGroupController.this, /* showConnectedDevice= */true,
fragment.getMetricsCategory());
}
@VisibleForTesting

View File

@@ -332,12 +332,13 @@ public class BluetoothDevicesSlice implements CustomSliceable {
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -96,7 +96,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
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);

View File

@@ -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 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
@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 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
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 @@ public class BluetoothDetailsAudioRoutingControllerTest extends
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());
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}
}

View File

@@ -34,7 +34,6 @@ import androidx.preference.Preference;
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;
@@ -62,8 +61,6 @@ public class BluetoothDeviceUpdaterTest {
private static final String SUB_MAC_ADDRESS = "05:52:C7:0B:D8:3C";
private static final String TEST_NAME = "test_name";
@Mock
private DashboardFragment mDashboardFragment;
@Mock
private DevicePreferenceCallback mDevicePreferenceCallback;
@Mock
@@ -84,7 +81,7 @@ public class BluetoothDeviceUpdaterTest {
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 @@ public class BluetoothDeviceUpdaterTest {
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 @@ public class BluetoothDeviceUpdaterTest {
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 @@ public class BluetoothDeviceUpdaterTest {
@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 @@ public class BluetoothDeviceUpdaterTest {
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";
}
}
}

View File

@@ -18,166 +18,86 @@ package com.android.settings.bluetooth;
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();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private Resources mResource;
@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);
mFragment.mBluetoothAdapter = mBluetoothAdapter;
mFragment.mLocalManager = mLocalManager;
mFragment.mDeviceListGroup = mPreferenceGroup;
mFragment.mAlwaysDiscoverable = new AlwaysDiscoverable(mContext);
}
@Test
public void initPreferencesFromPreferenceScreen_findPreferences() {
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 = mAvailableDevicesCategory;
mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
}
//
@Test
public void initPreferencesFromPreferenceScreen_findPreferences() {
mFragment.initPreferencesFromPreferenceScreen();
assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory);
assertThat(mFragment.mFooterPreference).isEqualTo(mFooterPreference);
}
@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 @@ public class BluetoothPairingDetailTest {
// 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
}
}

View File

@@ -97,7 +97,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
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());

View File

@@ -26,6 +26,11 @@ import static org.mockito.Mockito.times;
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.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -57,10 +63,14 @@ public class DeviceListPreferenceFragmentTest {
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 @@ public class DeviceListPreferenceFragmentTest {
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 @@ public class DeviceListPreferenceFragmentTest {
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 class DeviceListPreferenceFragmentTest {
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
@Override
void initPreferencesFromPreferenceScreen() {}
protected void initPreferencesFromPreferenceScreen() {}
@Override
public String getDeviceListKey() {

View File

@@ -91,8 +91,8 @@ public class SavedBluetoothDeviceUpdaterTest {
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;