Show bond loss UI in device details
Bug: 380801155 Test: atest BluetoothDetailsFragmentTest Flag: EXEMPT minor change Change-Id: I458778e1a3adde4ec1ddd8b84b8dc7f1d91621f5
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user