Show bond loss UI in device details
Bug: 380801155 Test: atest BluetoothDetailsFragmentTest Flag: EXEMPT minor change Change-Id: I458778e1a3adde4ec1ddd8b84b8dc7f1d91621f5
This commit is contained in:
32
res/drawable/bluetooth_details_banner_background.xml
Normal file
32
res/drawable/bluetooth_details_banner_background.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?android:colorControlHighlight">
|
||||||
|
<item
|
||||||
|
android:start="16dp"
|
||||||
|
android:end="16dp"
|
||||||
|
android:top="16dp"
|
||||||
|
android:bottom="16dp">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid
|
||||||
|
android:color="@color/settingslib_materialColorSurfaceVariant" />
|
||||||
|
<corners
|
||||||
|
android:radius="28dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
50
res/layout/bluetooth_details_banner.xml
Normal file
50
res/layout/bluetooth_details_banner.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="36dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/bluetooth_details_banner_background">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/settingslib_ic_info_outline_24"
|
||||||
|
android:tint="@color/settingslib_materialColorOnSurfaceVariant"
|
||||||
|
android:importantForAccessibility="no" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bluetooth_details_banner_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="@color/settingslib_materialColorOnSurfaceVariant"
|
||||||
|
android:hyphenationFrequency="normalFast"
|
||||||
|
android:lineBreakWordStyle="phrase"
|
||||||
|
android:ellipsize="marquee" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -19,6 +19,13 @@
|
|||||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
android:title="@string/device_details_title">
|
android:title="@string/device_details_title">
|
||||||
|
|
||||||
|
<com.android.settingslib.widget.LayoutPreference
|
||||||
|
android:key="bluetooth_details_banner"
|
||||||
|
android:layout="@layout/bluetooth_details_banner"
|
||||||
|
android:selectable="false"
|
||||||
|
settings:allowDividerBelow="true"
|
||||||
|
settings:searchable="false"/>
|
||||||
|
|
||||||
<com.android.settingslib.widget.LayoutPreference
|
<com.android.settingslib.widget.LayoutPreference
|
||||||
android:key="bluetooth_device_header"
|
android:key="bluetooth_device_header"
|
||||||
android:layout="@layout/settings_entity_header"
|
android:layout="@layout/settings_entity_header"
|
||||||
|
@@ -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);
|
final LayoutPreference headerPreference = screen.findPreference(KEY_DEVICE_HEADER);
|
||||||
mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
|
mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
|
||||||
headerPreference.findViewById(R.id.entity_header));
|
headerPreference.findViewById(R.id.entity_header));
|
||||||
screen.addPreference(headerPreference);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setHeaderProperties() {
|
protected void setHeaderProperties() {
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothDevice.BOND_NONE;
|
import static android.bluetooth.BluetoothDevice.BOND_NONE;
|
||||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.settings.SettingsEnums;
|
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.model.FragmentTypeModel;
|
||||||
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
|
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
|
||||||
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
|
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
|
||||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
|
||||||
import com.android.settings.flags.Flags;
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.inputmethod.KeyboardSettingsPreferenceController;
|
import com.android.settings.inputmethod.KeyboardSettingsPreferenceController;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
@@ -66,7 +64,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
|
public class BluetoothDeviceDetailsFragment extends BluetoothDetailsConfigurableFragment {
|
||||||
public static final String KEY_DEVICE_ADDRESS = "device_address";
|
public static final String KEY_DEVICE_ADDRESS = "device_address";
|
||||||
private static final String TAG = "BTDeviceDetailsFrg";
|
private static final String TAG = "BTDeviceDetailsFrg";
|
||||||
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
|
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
|
||||||
@@ -102,6 +100,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
BluetoothAdapter mBluetoothAdapter;
|
BluetoothAdapter mBluetoothAdapter;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DeviceDetailsFragmentFormatter mFormatter;
|
DeviceDetailsFragmentFormatter mFormatter;
|
||||||
|
boolean mIsKeyMissingDevice = false;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
InputDevice mInputDevice;
|
InputDevice mInputDevice;
|
||||||
@@ -144,7 +143,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
};
|
};
|
||||||
|
|
||||||
public BluetoothDeviceDetailsFragment() {
|
public BluetoothDeviceDetailsFragment() {
|
||||||
super(DISALLOW_CONFIG_BLUETOOTH);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -212,6 +211,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mCachedDevice.getDevice());
|
||||||
|
mIsKeyMissingDevice = keyMissingCount != null && keyMissingCount > 0;
|
||||||
|
setPreferenceDisplayOrder(generateDisplayedPreferenceKeys(mIsKeyMissingDevice));
|
||||||
getController(
|
getController(
|
||||||
AdvancedBluetoothDetailsHeaderController.class,
|
AdvancedBluetoothDetailsHeaderController.class,
|
||||||
controller -> controller.init(mCachedDevice, this));
|
controller -> controller.init(mCachedDevice, this));
|
||||||
@@ -342,7 +344,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
if (Flags.enableBluetoothDeviceDetailsPolish()) {
|
if (!mIsKeyMissingDevice && Flags.enableBluetoothDeviceDetailsPolish()) {
|
||||||
if (mFormatter == null) {
|
if (mFormatter == null) {
|
||||||
List<AbstractPreferenceController> controllers = getPreferenceControllers().stream()
|
List<AbstractPreferenceController> controllers = getPreferenceControllers().stream()
|
||||||
.flatMap(List::stream)
|
.flatMap(List::stream)
|
||||||
@@ -412,12 +414,29 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
return super.onOptionsItemSelected(menuItem);
|
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
|
@Override
|
||||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||||
ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
|
ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
|
|
||||||
if (mCachedDevice != null) {
|
if (mCachedDevice != null) {
|
||||||
Lifecycle lifecycle = getSettingsLifecycle();
|
Lifecycle lifecycle = getSettingsLifecycle();
|
||||||
|
controllers.add(
|
||||||
|
new BluetoothDetailsBannerController(
|
||||||
|
context, this, mCachedDevice, lifecycle));
|
||||||
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
|
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
|
||||||
lifecycle));
|
lifecycle));
|
||||||
controllers.add(
|
controllers.add(
|
||||||
|
@@ -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.bluetooth.BluetoothDevice
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory
|
||||||
|
import com.android.settingslib.widget.LayoutPreference
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoRule
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
|
class BluetoothDetailsBannerControllerTest : BluetoothDetailsControllerTestBase() {
|
||||||
|
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||||
|
|
||||||
|
private lateinit var controller: BluetoothDetailsBannerController
|
||||||
|
private lateinit var preference: LayoutPreference
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
FakeFeatureFactory.setupForTest()
|
||||||
|
controller =
|
||||||
|
BluetoothDetailsBannerController(mContext, mFragment, mCachedDevice, mLifecycle)
|
||||||
|
preference = LayoutPreference(mContext, R.layout.bluetooth_details_banner)
|
||||||
|
preference.key = controller.getPreferenceKey()
|
||||||
|
mScreen.addPreference(preference)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun iaAvailable_notKeyMissing_false() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig())
|
||||||
|
|
||||||
|
assertThat(controller.isAvailable).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/379729762): add more tests after BluetoothDevice.getKeyMissingCount is available.
|
||||||
|
}
|
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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 androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.testing.EmptyFragmentActivity
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.test.core.app.ActivityScenario
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoRule
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class BluetoothDetailsFragmentTest {
|
||||||
|
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||||
|
|
||||||
|
private lateinit var activity: FragmentActivity
|
||||||
|
private lateinit var fragment: TestConfigurableFragment
|
||||||
|
private lateinit var context: Context
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
context = spy(ApplicationProvider.getApplicationContext<Context>())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setPreferenceDisplayOrder_null_unchanged() = buildFragment {
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||||
|
|
||||||
|
fragment.setPreferenceDisplayOrder(null)
|
||||||
|
|
||||||
|
assertThat(this.displayedKeys).containsExactly("key1", "key2")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setPreferenceDisplayOrder_hideItem() = buildFragment {
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||||
|
|
||||||
|
fragment.setPreferenceDisplayOrder(mutableListOf("key2"))
|
||||||
|
|
||||||
|
assertThat(this.displayedKeys).containsExactly("key2")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setPreferenceDisplayOrder_hideAndReShownItem() = buildFragment {
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||||
|
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||||
|
|
||||||
|
fragment.setPreferenceDisplayOrder(mutableListOf("key2"))
|
||||||
|
fragment.setPreferenceDisplayOrder(mutableListOf("key2", "key1"))
|
||||||
|
|
||||||
|
assertThat(this.displayedKeys).containsExactly("key2", "key1")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFragment(r: (() -> Unit)) {
|
||||||
|
ActivityScenario.launch(EmptyFragmentActivity::class.java).use { activityScenario ->
|
||||||
|
activityScenario.onActivity { activity: EmptyFragmentActivity ->
|
||||||
|
this@BluetoothDetailsFragmentTest.activity = activity
|
||||||
|
fragment = TestConfigurableFragment()
|
||||||
|
activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow()
|
||||||
|
fragment.setPreferenceScreen(
|
||||||
|
fragment.preferenceManager.createPreferenceScreen(context)
|
||||||
|
)
|
||||||
|
r.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val displayedKeys: List<String>
|
||||||
|
get() {
|
||||||
|
val keys: MutableList<String> = mutableListOf()
|
||||||
|
for (i in 0..<fragment.preferenceScreen.preferenceCount) {
|
||||||
|
if (fragment.preferenceScreen.getPreference(i).isVisible) {
|
||||||
|
keys.add(fragment.preferenceScreen.getPreference(i).key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestConfigurableFragment : BluetoothDetailsConfigurableFragment() {
|
||||||
|
protected override fun getPreferenceScreenResId(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLogTag(): String {
|
||||||
|
return "TAG"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMetricsCategory(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user