New hearing device pairing page (1/2)
Rewrite a new hearing device pairing page with update UI for "See more devices". Bug: 307473972 Test: atest HearingDevicePairingFragmentTest Test: flip the flag com.android.settings.flags.new_hearing_device_pairing_page && atest HearingAidPairingDialogFragmentTest AddDevicePreferenceControllerTest Change-Id: Ic60601905e3d0d7d7c5b1ef9733652118a211f1d
This commit is contained in:
@@ -32,7 +32,7 @@ flag {
|
|||||||
flag {
|
flag {
|
||||||
name: "new_hearing_device_pairing_page"
|
name: "new_hearing_device_pairing_page"
|
||||||
namespace: "accessibility"
|
namespace: "accessibility"
|
||||||
description: "New hearing device pairing page with deny list method"
|
description: "New hearing device pairing page with extra MFi+ASHA filtering"
|
||||||
bug: "307473972"
|
bug: "307473972"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
res/layout/arrow_preference.xml
Normal file
56
res/layout/arrow_preference.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingVertical="@dimen/settingslib_switchbar_margin"
|
||||||
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/background"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
|
||||||
|
android:paddingEnd="@dimen/settingslib_switchbar_padding_right"
|
||||||
|
android:background="@drawable/settingslib_switch_bar_bg_on"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingVertical="@dimen/settingslib_switch_title_margin"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:hyphenationFrequency="normalFast"
|
||||||
|
android:lineBreakWordStyle="phrase"
|
||||||
|
style="@style/MainSwitchText.Settingslib"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_arrow_forward"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
@@ -28,11 +28,10 @@
|
|||||||
settings:controller="com.android.settings.accessibility.AvailableHearingDevicePreferenceController"/>
|
settings:controller="com.android.settings.accessibility.AvailableHearingDevicePreferenceController"/>
|
||||||
|
|
||||||
<com.android.settingslib.RestrictedPreference
|
<com.android.settingslib.RestrictedPreference
|
||||||
android:key="add_bt_devices"
|
android:key="hearing_device_add_bt_devices"
|
||||||
android:title="@string/bluetooth_pairing_pref_title"
|
android:title="@string/bluetooth_pairing_pref_title"
|
||||||
android:icon="@drawable/ic_add_24dp"
|
android:icon="@drawable/ic_add_24dp"
|
||||||
android:summary="@string/connected_device_add_device_summary"
|
android:summary="@string/connected_device_add_device_summary"
|
||||||
android:fragment="com.android.settings.accessibility.HearingDevicePairingDetail"
|
|
||||||
settings:userRestriction="no_config_bluetooth"
|
settings:userRestriction="no_config_bluetooth"
|
||||||
settings:useAdminDisabledSummary="true"
|
settings:useAdminDisabledSummary="true"
|
||||||
settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
|
settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
|
||||||
|
45
res/xml/hearing_device_pairing_fragment.xml
Normal file
45
res/xml/hearing_device_pairing_fragment.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?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="more_devices_category"
|
||||||
|
android:title="@string/accessibility_found_all_devices">
|
||||||
|
<com.android.settings.accessibility.ArrowPreference
|
||||||
|
android:key="more_devices"
|
||||||
|
android:title="@string/accessibility_list_all_devices_title"
|
||||||
|
settings:searchable="false"
|
||||||
|
settings:userRestriction="no_config_bluetooth"
|
||||||
|
settings:useAdminDisabledSummary="true"
|
||||||
|
settings:controller="com.android.settings.accessibility.ViewAllBluetoothDevicesPreferenceController"/>
|
||||||
|
</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"
|
||||||
|
settings:controller="com.android.settings.accessibility.PairHearingDeviceFooterPreferenceController"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -36,9 +36,9 @@ import com.android.settingslib.search.SearchIndexable;
|
|||||||
/** Accessibility settings for hearing aids. */
|
/** Accessibility settings for hearing aids. */
|
||||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||||
public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPreferenceFragment {
|
public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPreferenceFragment {
|
||||||
|
|
||||||
private static final String TAG = "AccessibilityHearingAidsFragment";
|
private static final String TAG = "AccessibilityHearingAidsFragment";
|
||||||
private static final String KEY_HEARING_OPTIONS_CATEGORY = "hearing_options_category";
|
private static final String KEY_HEARING_OPTIONS_CATEGORY = "hearing_options_category";
|
||||||
|
public static final String KEY_HEARING_DEVICE_ADD_BT_DEVICES = "hearing_device_add_bt_devices";
|
||||||
private static final int SHORTCUT_PREFERENCE_IN_CATEGORY_INDEX = 20;
|
private static final int SHORTCUT_PREFERENCE_IN_CATEGORY_INDEX = 20;
|
||||||
private String mFeatureName;
|
private String mFeatureName;
|
||||||
|
|
||||||
|
58
src/com/android/settings/accessibility/ArrowPreference.java
Normal file
58
src/com/android/settings/accessibility/ArrowPreference.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.res.TypedArrayUtils;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A settings preference with colored rounded rectangle background and an arrow icon on the right
|
||||||
|
*/
|
||||||
|
public class ArrowPreference extends Preference {
|
||||||
|
|
||||||
|
public ArrowPreference(@NonNull Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
|
this(context, attrs, TypedArrayUtils.getAttr(context,
|
||||||
|
androidx.preference.R.attr.preferenceStyle,
|
||||||
|
android.R.attr.preferenceStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr) {
|
||||||
|
this(context, attrs, defStyleAttr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
|
||||||
|
int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
setLayoutResource(R.layout.arrow_preference);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.app.Activity.RESULT_OK;
|
||||||
|
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothUuid;
|
||||||
|
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.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.bluetooth.BluetoothDevicePreference;
|
||||||
|
import com.android.settings.bluetooth.BluetoothProgressCategory;
|
||||||
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothCallback;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||||
|
import com.android.settingslib.bluetooth.HearingAidInfo;
|
||||||
|
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows all scanned hearing devices through BLE scanning. Users can
|
||||||
|
* pair them in this page.
|
||||||
|
*/
|
||||||
|
public class HearingDevicePairingFragment extends RestrictedDashboardFragment implements
|
||||||
|
BluetoothCallback {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
private static final String TAG = "HearingDevicePairingFragment";
|
||||||
|
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
|
||||||
|
"persist.bluetooth.showdeviceswithoutnames";
|
||||||
|
private static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices";
|
||||||
|
|
||||||
|
LocalBluetoothManager mLocalManager;
|
||||||
|
@Nullable
|
||||||
|
BluetoothAdapter mBluetoothAdapter;
|
||||||
|
@Nullable
|
||||||
|
CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||||
|
|
||||||
|
private boolean mShowDevicesWithoutNames;
|
||||||
|
@Nullable
|
||||||
|
private BluetoothProgressCategory mAvailableHearingDeviceGroup;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
BluetoothDevice mSelectedDevice;
|
||||||
|
final List<BluetoothDevice> mSelectedDeviceList = new ArrayList<>();
|
||||||
|
final Map<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
private List<ScanFilter> mLeScanFilters;
|
||||||
|
|
||||||
|
public HearingDevicePairingFragment() {
|
||||||
|
super(DISALLOW_CONFIG_BLUETOOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mLocalManager = Utils.getLocalBtManager(getActivity());
|
||||||
|
if (mLocalManager == null) {
|
||||||
|
Log.e(TAG, "Bluetooth is not supported on this device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter();
|
||||||
|
mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
|
||||||
|
mShowDevicesWithoutNames = SystemProperties.getBoolean(
|
||||||
|
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
|
||||||
|
|
||||||
|
initPreferencesFromPreferenceScreen();
|
||||||
|
initHearingDeviceLeScanFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
use(ViewAllBluetoothDevicesPreferenceController.class).init(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
if (mLocalManager == null || mBluetoothAdapter == null || isUiRestricted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLocalManager.setForegroundActivity(getActivity());
|
||||||
|
mLocalManager.getEventManager().registerCallback(this);
|
||||||
|
if (mBluetoothAdapter.isEnabled()) {
|
||||||
|
startScanning();
|
||||||
|
} else {
|
||||||
|
// Turn on bluetooth if it is disabled
|
||||||
|
mBluetoothAdapter.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (mLocalManager == null || isUiRestricted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stopScanning();
|
||||||
|
removeAllDevices();
|
||||||
|
mLocalManager.setForegroundActivity(null);
|
||||||
|
mLocalManager.getEventManager().unregisterCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceTreeClick(Preference preference) {
|
||||||
|
if (preference instanceof BluetoothDevicePreference) {
|
||||||
|
stopScanning();
|
||||||
|
BluetoothDevicePreference devicePreference = (BluetoothDevicePreference) preference;
|
||||||
|
mSelectedDevice = devicePreference.getCachedDevice().getDevice();
|
||||||
|
if (mSelectedDevice != null) {
|
||||||
|
mSelectedDeviceList.add(mSelectedDevice);
|
||||||
|
}
|
||||||
|
devicePreference.onClicked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onPreferenceTreeClick(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {
|
||||||
|
removeDevice(cachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBluetoothStateChanged(int bluetoothState) {
|
||||||
|
switch (bluetoothState) {
|
||||||
|
case BluetoothAdapter.STATE_ON:
|
||||||
|
startScanning();
|
||||||
|
showBluetoothTurnedOnToast();
|
||||||
|
break;
|
||||||
|
case BluetoothAdapter.STATE_OFF:
|
||||||
|
finish();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceBondStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
|
||||||
|
int bondState) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice.getName() + ", state = "
|
||||||
|
+ bondState);
|
||||||
|
}
|
||||||
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
|
// If one device is connected(bonded), then close this fragment.
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
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.getFeatureFactory().getMetricsFeatureProvider()
|
||||||
|
.getAttribution(getActivity());
|
||||||
|
final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
|
||||||
|
pageId);
|
||||||
|
HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
|
||||||
|
}
|
||||||
|
if (mSelectedDevice != null) {
|
||||||
|
BluetoothDevice device = cachedDevice.getDevice();
|
||||||
|
if (mSelectedDevice.equals(device) && bondState == BluetoothDevice.BOND_NONE) {
|
||||||
|
// If current selected device failed to bond, restart scanning
|
||||||
|
startScanning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfileConnectionStateChanged(@NonNull 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.isConnected()) {
|
||||||
|
final BluetoothDevice device = cachedDevice.getDevice();
|
||||||
|
if (device != null && mSelectedDeviceList.contains(device)) {
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
removeDevice(cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMetricsCategory() {
|
||||||
|
return SettingsEnums.HEARING_AID_PAIRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPreferenceScreenResId() {
|
||||||
|
return R.xml.hearing_device_pairing_fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getLogTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDevice(CachedBluetoothDevice cachedDevice) {
|
||||||
|
if (mBluetoothAdapter == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do not create new preference while the list shows one of the state messages
|
||||||
|
if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mDevicePreferenceMap.get(cachedDevice) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String key = cachedDevice.getDevice().getAddress();
|
||||||
|
BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
|
||||||
|
if (preference == null) {
|
||||||
|
preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
|
||||||
|
mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||||
|
preference.setKey(key);
|
||||||
|
preference.hideSecondTarget(true);
|
||||||
|
}
|
||||||
|
if (mAvailableHearingDeviceGroup != null) {
|
||||||
|
mAvailableHearingDeviceGroup.addPreference(preference);
|
||||||
|
}
|
||||||
|
mDevicePreferenceMap.put(cachedDevice, preference);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Add device. device: " + cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDevice(CachedBluetoothDevice cachedDevice) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "removeDevice: " + cachedDevice);
|
||||||
|
}
|
||||||
|
BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
|
||||||
|
if (mAvailableHearingDeviceGroup != null && preference != null) {
|
||||||
|
mAvailableHearingDeviceGroup.removePreference(preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startScanning() {
|
||||||
|
if (mCachedDeviceManager != null) {
|
||||||
|
mCachedDeviceManager.clearNonBondedDevices();
|
||||||
|
}
|
||||||
|
removeAllDevices();
|
||||||
|
startLeScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopScanning() {
|
||||||
|
stopLeScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ScanCallback mLeScanCallback = new ScanCallback() {
|
||||||
|
@Override
|
||||||
|
public void onScanResult(int callbackType, ScanResult result) {
|
||||||
|
handleLeScanResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchScanResults(List<ScanResult> results) {
|
||||||
|
for (ScanResult result: results) {
|
||||||
|
handleLeScanResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanFailed(int errorCode) {
|
||||||
|
Log.w(TAG, "BLE Scan failed with error code " + errorCode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void handleLeScanResult(ScanResult result) {
|
||||||
|
if (mCachedDeviceManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final BluetoothDevice device = result.getDevice();
|
||||||
|
CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
cachedDevice = mCachedDeviceManager.addDevice(device);
|
||||||
|
}
|
||||||
|
if (cachedDevice.getHearingAidInfo() == null) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Set hearing aid info on device: " + cachedDevice);
|
||||||
|
}
|
||||||
|
cachedDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
|
||||||
|
}
|
||||||
|
addDevice(cachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startLeScanning() {
|
||||||
|
if (mBluetoothAdapter == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "startLeScanning");
|
||||||
|
}
|
||||||
|
final BluetoothLeScanner leScanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||||
|
if (leScanner == null) {
|
||||||
|
Log.w(TAG, "LE scanner not found, cannot start LE scanning");
|
||||||
|
} else {
|
||||||
|
final ScanSettings settings = new ScanSettings.Builder()
|
||||||
|
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||||
|
.setLegacy(false)
|
||||||
|
.build();
|
||||||
|
leScanner.startScan(mLeScanFilters, settings, mLeScanCallback);
|
||||||
|
if (mAvailableHearingDeviceGroup != null) {
|
||||||
|
mAvailableHearingDeviceGroup.setProgress(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopLeScanning() {
|
||||||
|
if (mBluetoothAdapter == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "stopLeScanning");
|
||||||
|
}
|
||||||
|
final BluetoothLeScanner leScanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||||
|
if (leScanner != null) {
|
||||||
|
leScanner.stopScan(mLeScanCallback);
|
||||||
|
if (mAvailableHearingDeviceGroup != null) {
|
||||||
|
mAvailableHearingDeviceGroup.setProgress(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeAllDevices() {
|
||||||
|
mDevicePreferenceMap.clear();
|
||||||
|
if (mAvailableHearingDeviceGroup != null) {
|
||||||
|
mAvailableHearingDeviceGroup.removeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initPreferencesFromPreferenceScreen() {
|
||||||
|
mAvailableHearingDeviceGroup = findPreference(KEY_AVAILABLE_HEARING_DEVICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initHearingDeviceLeScanFilters() {
|
||||||
|
mLeScanFilters = new ArrayList<>();
|
||||||
|
// Filters for ASHA hearing aids
|
||||||
|
mLeScanFilters.add(
|
||||||
|
new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HEARING_AID).build());
|
||||||
|
mLeScanFilters.add(new ScanFilter.Builder()
|
||||||
|
.setServiceData(BluetoothUuid.HEARING_AID, new byte[0]).build());
|
||||||
|
// Filters for LE audio hearing aids
|
||||||
|
mLeScanFilters.add(new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HAS).build());
|
||||||
|
mLeScanFilters.add(new ScanFilter.Builder()
|
||||||
|
.setServiceData(BluetoothUuid.HAS, new byte[0]).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void showBluetoothTurnedOnToast() {
|
||||||
|
Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
@@ -156,7 +156,7 @@ public final class BluetoothDevicePreference extends GearPreference {
|
|||||||
return R.layout.preference_widget_gear;
|
return R.layout.preference_widget_gear;
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedBluetoothDevice getCachedDevice() {
|
public CachedBluetoothDevice getCachedDevice() {
|
||||||
return mCachedDevice;
|
return mCachedDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +362,11 @@ public final class BluetoothDevicePreference extends GearPreference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onClicked() {
|
/**
|
||||||
|
* Performs different actions according to the device connected and bonded state after
|
||||||
|
* clicking on the preference.
|
||||||
|
*/
|
||||||
|
public void onClicked() {
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
int bondState = mCachedDevice.getBondState();
|
int bondState = mCachedDevice.getBondState();
|
||||||
|
|
||||||
|
@@ -29,8 +29,10 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.accessibility.HearingDevicePairingDetail;
|
import com.android.settings.accessibility.HearingDevicePairingDetail;
|
||||||
|
import com.android.settings.accessibility.HearingDevicePairingFragment;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
import com.android.settingslib.bluetooth.HearingAidInfo;
|
import com.android.settingslib.bluetooth.HearingAidInfo;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
@@ -123,8 +125,11 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment
|
|||||||
final int launchPage = getArguments().getInt(KEY_LAUNCH_PAGE);
|
final int launchPage = getArguments().getInt(KEY_LAUNCH_PAGE);
|
||||||
final boolean launchFromA11y = (launchPage == SettingsEnums.ACCESSIBILITY)
|
final boolean launchFromA11y = (launchPage == SettingsEnums.ACCESSIBILITY)
|
||||||
|| (launchPage == SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS);
|
|| (launchPage == SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS);
|
||||||
|
final String a11yDestination = Flags.newHearingDevicePairingPage()
|
||||||
|
? HearingDevicePairingFragment.class.getName()
|
||||||
|
: HearingDevicePairingDetail.class.getName();
|
||||||
final String destination = launchFromA11y
|
final String destination = launchFromA11y
|
||||||
? HearingDevicePairingDetail.class.getName()
|
? a11yDestination
|
||||||
: BluetoothPairingDetail.class.getName();
|
: BluetoothPairingDetail.class.getName();
|
||||||
new SubSettingLauncher(getActivity())
|
new SubSettingLauncher(getActivity())
|
||||||
.setDestination(destination)
|
.setDestination(destination)
|
||||||
|
@@ -15,18 +15,25 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.connecteddevice;
|
package com.android.settings.connecteddevice;
|
||||||
|
|
||||||
|
import static com.android.settings.accessibility.AccessibilityHearingAidsFragment.KEY_HEARING_DEVICE_ADD_BT_DEVICES;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.HearingDevicePairingDetail;
|
||||||
|
import com.android.settings.accessibility.HearingDevicePairingFragment;
|
||||||
import com.android.settings.core.BasePreferenceController;
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||||
@@ -75,6 +82,21 @@ public class AddDevicePreferenceController extends BasePreferenceController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||||
|
if (TextUtils.equals(preference.getKey(), KEY_HEARING_DEVICE_ADD_BT_DEVICES)) {
|
||||||
|
String destination = Flags.newHearingDevicePairingPage()
|
||||||
|
? HearingDevicePairingFragment.class.getName()
|
||||||
|
: HearingDevicePairingDetail.class.getName();
|
||||||
|
new SubSettingLauncher(preference.getContext())
|
||||||
|
.setDestination(destination)
|
||||||
|
.setSourceMetricsCategory(getMetricsCategory())
|
||||||
|
.launch();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.handlePreferenceTreeClick(preference);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus() {
|
public int getAvailabilityStatus() {
|
||||||
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
|
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
|
||||||
|
@@ -32,6 +32,10 @@ import android.content.Context;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||||
|
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||||
|
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
@@ -43,6 +47,7 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.bluetooth.BluetoothPairingDetail;
|
import com.android.settings.bluetooth.BluetoothPairingDetail;
|
||||||
import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
|
import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
|
||||||
import com.android.settings.bluetooth.Utils;
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
@@ -77,6 +82,9 @@ public class HearingAidPairingDialogFragmentTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||||
|
|
||||||
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||||
private static final int TEST_LAUNCH_PAGE = SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY;
|
private static final int TEST_LAUNCH_PAGE = SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY;
|
||||||
|
|
||||||
@@ -129,7 +137,22 @@ public class HearingAidPairingDialogFragmentTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dialogPositiveButtonClick_intentToA11yPairingPage() {
|
@RequiresFlagsEnabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
|
||||||
|
public void dialogPositiveButtonClick_intentToNewA11yPairingPage() {
|
||||||
|
setupDialog(SettingsEnums.ACCESSIBILITY);
|
||||||
|
final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
|
||||||
|
|
||||||
|
final Intent intent = shadowOf(mActivity).getNextStartedActivity();
|
||||||
|
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(HearingDevicePairingFragment.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresFlagsDisabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
|
||||||
|
public void dialogPositiveButtonClick_intentToOldA11yPairingPage() {
|
||||||
setupDialog(SettingsEnums.ACCESSIBILITY);
|
setupDialog(SettingsEnums.ACCESSIBILITY);
|
||||||
final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
|
final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* 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.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.bluetooth.BluetoothDevicePreference;
|
||||||
|
import com.android.settings.bluetooth.BluetoothProgressCategory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||||
|
import com.android.settingslib.bluetooth.HearingAidInfo;
|
||||||
|
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 HearingDevicePairingFragment}. */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowBluetoothAdapter.class})
|
||||||
|
public class HearingDevicePairingFragmentTest {
|
||||||
|
|
||||||
|
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
@Spy
|
||||||
|
private final HearingDevicePairingFragment mFragment = new TestHearingDevicePairingFragment();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LocalBluetoothManager mLocalManager;
|
||||||
|
@Mock
|
||||||
|
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||||
|
@Mock
|
||||||
|
private CachedBluetoothDevice mCachedDevice;
|
||||||
|
@Mock
|
||||||
|
private BluetoothProgressCategory mAvailableHearingDeviceGroup;
|
||||||
|
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private BluetoothDevice mDevice;
|
||||||
|
private BluetoothDevicePreference mDevicePreference;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mFragment.mLocalManager = mLocalManager;
|
||||||
|
mFragment.mCachedDeviceManager = mCachedDeviceManager;
|
||||||
|
mFragment.mBluetoothAdapter = mBluetoothAdapter;
|
||||||
|
doReturn(mContext).when(mFragment).getContext();
|
||||||
|
doReturn(mAvailableHearingDeviceGroup).when(mFragment).findPreference(
|
||||||
|
"available_hearing_devices");
|
||||||
|
mFragment.initPreferencesFromPreferenceScreen();
|
||||||
|
|
||||||
|
|
||||||
|
mDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
|
||||||
|
doReturn(mDevice).when(mCachedDevice).getDevice();
|
||||||
|
final Pair<Drawable, String> pair = new Pair<>(mock(Drawable.class), "test_device");
|
||||||
|
doReturn(pair).when(mCachedDevice).getDrawableWithDescription();
|
||||||
|
|
||||||
|
mDevicePreference = new BluetoothDevicePreference(mContext, mCachedDevice, true,
|
||||||
|
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startAndStopScanning_stateIsCorrect() {
|
||||||
|
mFragment.startScanning();
|
||||||
|
|
||||||
|
verify(mFragment).startLeScanning();
|
||||||
|
|
||||||
|
mFragment.stopScanning();
|
||||||
|
|
||||||
|
verify(mFragment).stopLeScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDeviceDeleted_stateIsCorrect() {
|
||||||
|
mFragment.mDevicePreferenceMap.put(mCachedDevice, mDevicePreference);
|
||||||
|
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap).isNotEmpty();
|
||||||
|
|
||||||
|
mFragment.onDeviceDeleted(mCachedDevice);
|
||||||
|
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap).isEmpty();
|
||||||
|
verify(mAvailableHearingDeviceGroup).removePreference(mDevicePreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addDevice_bluetoothOff_doNothing() {
|
||||||
|
doReturn(BluetoothAdapter.STATE_OFF).when(mBluetoothAdapter).getState();
|
||||||
|
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
|
||||||
|
|
||||||
|
mFragment.addDevice(mCachedDevice);
|
||||||
|
|
||||||
|
verify(mAvailableHearingDeviceGroup, never()).addPreference(mDevicePreference);
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addDevice_addToAvailableHearingDeviceGroup() {
|
||||||
|
doReturn(BluetoothAdapter.STATE_ON).when(mBluetoothAdapter).getState();
|
||||||
|
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
|
||||||
|
|
||||||
|
mFragment.addDevice(mCachedDevice);
|
||||||
|
|
||||||
|
verify(mAvailableHearingDeviceGroup).addPreference(mDevicePreference);
|
||||||
|
assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleLeScanResult_markDeviceAsHearingAid() {
|
||||||
|
ScanResult scanResult = mock(ScanResult.class);
|
||||||
|
doReturn(mDevice).when(scanResult).getDevice();
|
||||||
|
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
|
||||||
|
|
||||||
|
mFragment.handleLeScanResult(scanResult);
|
||||||
|
|
||||||
|
verify(mCachedDevice).setHearingAidInfo(new HearingAidInfo.Builder().build());
|
||||||
|
verify(mFragment).addDevice(mCachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProfileConnectionStateChanged_deviceConnected_inSelectedList_finish() {
|
||||||
|
doReturn(true).when(mCachedDevice).isConnected();
|
||||||
|
mFragment.mSelectedDeviceList.add(mDevice);
|
||||||
|
|
||||||
|
mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
|
||||||
|
BluetoothProfile.A2DP);
|
||||||
|
|
||||||
|
verify(mFragment).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProfileConnectionStateChanged_deviceConnected_notInSelectedList_deleteDevice() {
|
||||||
|
doReturn(true).when(mCachedDevice).isConnected();
|
||||||
|
|
||||||
|
mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
|
||||||
|
BluetoothProfile.A2DP);
|
||||||
|
|
||||||
|
verify(mFragment).removeDevice(mCachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProfileConnectionStateChanged_deviceNotConnected_doNothing() {
|
||||||
|
doReturn(false).when(mCachedDevice).isConnected();
|
||||||
|
|
||||||
|
mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
|
||||||
|
BluetoothProfile.A2DP);
|
||||||
|
|
||||||
|
verify(mFragment, never()).finish();
|
||||||
|
verify(mFragment, never()).removeDevice(mCachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBluetoothStateChanged_stateOn_startScanningAndShowToast() {
|
||||||
|
mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||||
|
|
||||||
|
verify(mFragment).startScanning();
|
||||||
|
verify(mFragment).showBluetoothTurnedOnToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBluetoothStateChanged_stateOff_finish() {
|
||||||
|
mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
|
||||||
|
|
||||||
|
verify(mFragment).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDeviceBondStateChanged_bonded_finish() {
|
||||||
|
mFragment.onDeviceBondStateChanged(mCachedDevice, BluetoothDevice.BOND_BONDED);
|
||||||
|
|
||||||
|
verify(mFragment).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDeviceBondStateChanged_selectedDeviceNotBonded_startScanning() {
|
||||||
|
mFragment.mSelectedDevice = mDevice;
|
||||||
|
|
||||||
|
mFragment.onDeviceBondStateChanged(mCachedDevice, BluetoothDevice.BOND_NONE);
|
||||||
|
|
||||||
|
verify(mFragment).startScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestHearingDevicePairingFragment extends HearingDevicePairingFragment {
|
||||||
|
@Override
|
||||||
|
protected Preference getCachedPreference(String key) {
|
||||||
|
if (key.equals(TEST_DEVICE_ADDRESS)) {
|
||||||
|
return mDevicePreference;
|
||||||
|
}
|
||||||
|
return super.getCachedPreference(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,34 +15,49 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.connecteddevice;
|
package com.android.settings.connecteddevice;
|
||||||
|
|
||||||
|
import static com.android.settings.accessibility.AccessibilityHearingAidsFragment.KEY_HEARING_DEVICE_ADD_BT_DEVICES;
|
||||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||||
|
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||||
|
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.accessibility.HearingDevicePairingDetail;
|
||||||
|
import com.android.settings.accessibility.HearingDevicePairingFragment;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settingslib.RestrictedPreference;
|
import com.android.settingslib.RestrictedPreference;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.Shadows;
|
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
@@ -51,12 +66,16 @@ import org.robolectric.util.ReflectionHelpers;
|
|||||||
@Config(shadows = ShadowApplicationPackageManager.class)
|
@Config(shadows = ShadowApplicationPackageManager.class)
|
||||||
public class AddDevicePreferenceControllerTest {
|
public class AddDevicePreferenceControllerTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private PreferenceScreen mScreen;
|
private PreferenceScreen mScreen;
|
||||||
@Mock
|
@Mock
|
||||||
private BluetoothAdapter mBluetoothAdapter;
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
|
||||||
private Context mContext;
|
@Spy
|
||||||
|
private Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
private AddDevicePreferenceController mAddDevicePreferenceController;
|
private AddDevicePreferenceController mAddDevicePreferenceController;
|
||||||
private RestrictedPreference mAddDevicePreference;
|
private RestrictedPreference mAddDevicePreference;
|
||||||
private ShadowApplicationPackageManager mPackageManager;
|
private ShadowApplicationPackageManager mPackageManager;
|
||||||
@@ -66,8 +85,7 @@ public class AddDevicePreferenceControllerTest {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
mContext = RuntimeEnvironment.application;
|
mPackageManager = (ShadowApplicationPackageManager) shadowOf(
|
||||||
mPackageManager = (ShadowApplicationPackageManager) Shadows.shadowOf(
|
|
||||||
mContext.getPackageManager());
|
mContext.getPackageManager());
|
||||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
|
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
|
||||||
|
|
||||||
@@ -82,6 +100,8 @@ public class AddDevicePreferenceControllerTest {
|
|||||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||||
when(mScreen.findPreference(key)).thenReturn(mAddDevicePreference);
|
when(mScreen.findPreference(key)).thenReturn(mAddDevicePreference);
|
||||||
mAddDevicePreferenceController.displayPreference(mScreen);
|
mAddDevicePreferenceController.displayPreference(mScreen);
|
||||||
|
|
||||||
|
doNothing().when(mContext).startActivity(any(Intent.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,4 +157,30 @@ public class AddDevicePreferenceControllerTest {
|
|||||||
assertThat(mAddDevicePreferenceController.getAvailabilityStatus())
|
assertThat(mAddDevicePreferenceController.getAvailabilityStatus())
|
||||||
.isEqualTo(UNSUPPORTED_ON_DEVICE);
|
.isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresFlagsEnabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
|
||||||
|
public void handlePreferenceClick_A11yPreference_redirectToNewPairingPage() {
|
||||||
|
mAddDevicePreference.setKey(KEY_HEARING_DEVICE_ADD_BT_DEVICES);
|
||||||
|
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
|
|
||||||
|
mAddDevicePreferenceController.handlePreferenceTreeClick(mAddDevicePreference);
|
||||||
|
|
||||||
|
verify(mContext).startActivity(intentCaptor.capture());
|
||||||
|
assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(HearingDevicePairingFragment.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresFlagsDisabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
|
||||||
|
public void handlePreferenceClick_A11yPreference_redirectToOldPairingPage() {
|
||||||
|
mAddDevicePreference.setKey(KEY_HEARING_DEVICE_ADD_BT_DEVICES);
|
||||||
|
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
|
|
||||||
|
mAddDevicePreferenceController.handlePreferenceTreeClick(mAddDevicePreference);
|
||||||
|
|
||||||
|
verify(mContext).startActivity(intentCaptor.capture());
|
||||||
|
assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(HearingDevicePairingDetail.class.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user