Show bond loss UI in device details

Bug: 380801155
Test: atest BluetoothDetailsFragmentTest
Flag: EXEMPT minor change
Change-Id: I458778e1a3adde4ec1ddd8b84b8dc7f1d91621f5
This commit is contained in:
Haijie Hong
2025-03-14 14:55:33 +08:00
parent 594062970c
commit 519f1752f0
9 changed files with 432 additions and 6 deletions

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.bluetooth
import android.content.Context
import android.widget.TextView
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.core.lifecycle.Lifecycle
import com.android.settingslib.widget.LayoutPreference
class BluetoothDetailsBannerController(
private val context: Context,
fragment: PreferenceFragmentCompat,
private val cachedDevice: CachedBluetoothDevice,
lifecycle: Lifecycle,
) : BluetoothDetailsController(context, fragment, cachedDevice, lifecycle) {
private lateinit var pref: LayoutPreference
override fun getPreferenceKey(): String = KEY_BLUETOOTH_DETAILS_BANNER
override fun init(screen: PreferenceScreen) {
pref = screen.findPreference(KEY_BLUETOOTH_DETAILS_BANNER) ?: return
}
override fun refresh() {
pref.findViewById<TextView>(R.id.bluetooth_details_banner_message).text =
context.getString(R.string.device_details_key_missing_title, cachedDevice.name)
}
override fun isAvailable(): Boolean =
BluetoothUtils.getKeyMissingCount(cachedDevice.device)?.let { it > 0 } ?: false
private companion object {
const val KEY_BLUETOOTH_DETAILS_BANNER: String = "bluetooth_details_banner"
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.bluetooth
import android.os.Bundle
import android.os.UserManager
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import com.android.settings.dashboard.RestrictedDashboardFragment
/** Base class for bluetooth settings which makes the preference visibility/order configurable. */
abstract class BluetoothDetailsConfigurableFragment :
RestrictedDashboardFragment(UserManager.DISALLOW_CONFIG_BLUETOOTH) {
private var displayOrder: List<String>? = null
fun setPreferenceDisplayOrder(prefKeyOrder: List<String>?) {
if (displayOrder == prefKeyOrder) {
return
}
displayOrder = prefKeyOrder
updatePreferenceOrder()
}
private val invisiblePrefCategory: PreferenceGroup by lazy {
preferenceScreen.findPreference<PreferenceGroup>(INVISIBLE_CATEGORY)
?: run {
PreferenceCategory(requireContext())
.apply {
key = INVISIBLE_CATEGORY
isVisible = false
isOrderingAsAdded = true
}
.also { preferenceScreen.addPreference(it) }
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updatePreferenceOrder()
}
private fun updatePreferenceOrder() {
val order = displayOrder?: return
if (preferenceScreen == null) {
return
}
preferenceScreen.isOrderingAsAdded = true
val allPrefs =
(invisiblePrefCategory.getAndRemoveAll() + preferenceScreen.getAndRemoveAll()).filter {
it != invisiblePrefCategory
}
allPrefs.forEach { it.order = Preference.DEFAULT_ORDER }
val visiblePrefs =
allPrefs.filter { order.contains(it.key) }.sortedBy { order.indexOf(it.key) }
val invisiblePrefs = allPrefs.filter { !order.contains(it.key) }
preferenceScreen.addPreferences(visiblePrefs)
preferenceScreen.addPreference(invisiblePrefCategory)
invisiblePrefCategory.addPreferences(invisiblePrefs)
}
private fun PreferenceGroup.getAndRemoveAll(): List<Preference> {
val prefs = mutableListOf<Preference>()
for (i in 0..<preferenceCount) {
prefs.add(getPreference(i))
}
removeAll()
return prefs
}
private fun PreferenceGroup.addPreferences(prefs: List<Preference>) {
for (pref in prefs) {
addPreference(pref)
}
}
private companion object {
const val INVISIBLE_CATEGORY = "invisible_profile_category"
}
}

View File

@@ -62,7 +62,6 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
final LayoutPreference headerPreference = screen.findPreference(KEY_DEVICE_HEADER);
mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
headerPreference.findViewById(R.id.entity_header));
screen.addPreference(headerPreference);
}
protected void setHeaderProperties() {

View File

@@ -17,7 +17,6 @@
package com.android.settings.bluetooth;
import static android.bluetooth.BluetoothDevice.BOND_NONE;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import android.app.Activity;
import android.app.settings.SettingsEnums;
@@ -49,7 +48,6 @@ import com.android.settings.R;
import com.android.settings.bluetooth.ui.model.FragmentTypeModel;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settings.inputmethod.KeyboardSettingsPreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -66,7 +64,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
public class BluetoothDeviceDetailsFragment extends BluetoothDetailsConfigurableFragment {
public static final String KEY_DEVICE_ADDRESS = "device_address";
private static final String TAG = "BTDeviceDetailsFrg";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
@@ -102,6 +100,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
BluetoothAdapter mBluetoothAdapter;
@VisibleForTesting
DeviceDetailsFragmentFormatter mFormatter;
boolean mIsKeyMissingDevice = false;
@Nullable
InputDevice mInputDevice;
@@ -144,7 +143,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
};
public BluetoothDeviceDetailsFragment() {
super(DISALLOW_CONFIG_BLUETOOTH);
super();
}
@VisibleForTesting
@@ -212,6 +211,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
finish();
return;
}
Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mCachedDevice.getDevice());
mIsKeyMissingDevice = keyMissingCount != null && keyMissingCount > 0;
setPreferenceDisplayOrder(generateDisplayedPreferenceKeys(mIsKeyMissingDevice));
getController(
AdvancedBluetoothDetailsHeaderController.class,
controller -> controller.init(mCachedDevice, this));
@@ -342,7 +344,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (Flags.enableBluetoothDeviceDetailsPolish()) {
if (!mIsKeyMissingDevice && Flags.enableBluetoothDeviceDetailsPolish()) {
if (mFormatter == null) {
List<AbstractPreferenceController> controllers = getPreferenceControllers().stream()
.flatMap(List::stream)
@@ -412,12 +414,29 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
return super.onOptionsItemSelected(menuItem);
}
@Nullable
private List<String> generateDisplayedPreferenceKeys(boolean bondingLoss) {
if (bondingLoss) {
return List.of(
use(BluetoothDetailsBannerController.class).getPreferenceKey(),
use(AdvancedBluetoothDetailsHeaderController.class).getPreferenceKey(),
use(BluetoothDetailsHeaderController.class).getPreferenceKey(),
use(LeAudioBluetoothDetailsHeaderController.class).getPreferenceKey(),
use(BluetoothDetailsButtonsController.class).getPreferenceKey(),
use(BluetoothDetailsMacAddressController.class).getPreferenceKey());
}
return null;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
if (mCachedDevice != null) {
Lifecycle lifecycle = getSettingsLifecycle();
controllers.add(
new BluetoothDetailsBannerController(
context, this, mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
lifecycle));
controllers.add(