Merge "Move rename button next to the device name" into main
diff --git a/res/layout/advanced_bt_entity_header.xml b/res/layout/advanced_bt_entity_header.xml
index 833f6bd..37ae843 100644
--- a/res/layout/advanced_bt_entity_header.xml
+++ b/res/layout/advanced_bt_entity_header.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/entity_header"
style="@style/EntityHeader"
android:layout_width="match_parent"
@@ -25,15 +26,41 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical">
- <TextView
- android:id="@+id/entity_header_title"
- style="@style/TextAppearance.EntityHeaderTitle"
- android:layout_width="wrap_content"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:gravity="center"
- android:ellipsize="marquee"
- android:textDirection="locale"/>
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginStart="48dp"
+ android:layout_marginEnd="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constrainedWidth="true" />
+ <ImageButton
+ android:id="@+id/rename_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/min_tap_target_size"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:background="@android:color/transparent"
+ android:src="@drawable/ic_mode_edit"
+ android:contentDescription="@string/bluetooth_rename_button"
+ android:tint="@color/settingslib_materialColorOnSurface"
+ app:layout_constraintStart_toEndOf="@+id/entity_header_title"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:visibility="gone" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/entity_header_summary"
diff --git a/res/layout/general_bt_entity_header.xml b/res/layout/general_bt_entity_header.xml
new file mode 100644
index 0000000..ec15b12
--- /dev/null
+++ b/res/layout/general_bt_entity_header.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/bt_header_icon"
+ android:importantForAccessibility="no"
+ style="@style/SettingsLibEntityHeaderIcon"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:gravity="center">
+ <TextView
+ android:id="@+id/bt_header_device_name"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginStart="48dp"
+ android:layout_marginEnd="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constrainedWidth="true" />
+ <ImageButton
+ android:id="@+id/rename_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/min_tap_target_size"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:background="@android:color/transparent"
+ android:src="@drawable/ic_mode_edit"
+ android:contentDescription="@string/bluetooth_rename_button"
+ android:tint="@color/settingslib_materialColorOnSurface"
+ app:layout_constraintStart_toEndOf="@+id/bt_header_device_name"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <TextView
+ android:id="@+id/bt_header_connection_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:singleLine="false"
+ android:textAlignment="center"/>
+</LinearLayout>
diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml
index 460ae69..81911e9 100644
--- a/res/layout/le_audio_bt_entity_header.xml
+++ b/res/layout/le_audio_bt_entity_header.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/entity_header"
style="@style/EntityHeader"
android:layout_width="match_parent"
@@ -27,15 +28,42 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical">
- <TextView
- android:id="@+id/entity_header_title"
- style="@style/TextAppearance.EntityHeaderTitle"
- android:layout_width="wrap_content"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:gravity="center"
- android:ellipsize="marquee"
- android:textDirection="locale"/>
+ android:gravity="center">
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginStart="48dp"
+ android:layout_marginEnd="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constrainedWidth="true" />
+ <ImageButton
+ android:id="@+id/rename_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/min_tap_target_size"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:background="@android:color/transparent"
+ android:src="@drawable/ic_mode_edit"
+ android:contentDescription="@string/bluetooth_rename_button"
+ android:tint="@color/settingslib_materialColorOnSurface"
+ app:layout_constraintStart_toEndOf="@+id/entity_header_title"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:visibility="gone" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/entity_header_summary"
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index 91f73a7..f2e9e73 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -27,6 +27,13 @@
settings:searchable="false"/>
<com.android.settingslib.widget.LayoutPreference
+ android:key="general_bluetooth_device_header"
+ android:layout="@layout/general_bt_entity_header"
+ android:selectable="false"
+ settings:allowDividerBelow="true"
+ settings:searchable="false"/>
+
+ <com.android.settingslib.widget.LayoutPreference
android:key="advanced_bluetooth_device_header"
android:layout="@layout/advanced_bt_entity_header"
android:selectable="false"
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index 11ecf8b..02b8813 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -37,12 +37,14 @@
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -97,6 +99,7 @@
private static final int MAIN_DEVICE_ID = 4;
private static final float HALF_ALPHA = 0.5f;
+ PreferenceFragmentCompat mFragment;
@VisibleForTesting
LayoutPreference mLayoutPreference;
@VisibleForTesting
@@ -170,8 +173,11 @@
mIconCache.clear();
}
- public void init(CachedBluetoothDevice cachedBluetoothDevice) {
+ /** Initializes the controller. */
+ public void init(
+ CachedBluetoothDevice cachedBluetoothDevice, PreferenceFragmentCompat fragment) {
mCachedDevice = cachedBluetoothDevice;
+ mFragment = fragment;
}
private void registerBluetoothDevice() {
@@ -325,6 +331,14 @@
MAIN_DEVICE_ID);
}
});
+ if (Flags.enableBluetoothDeviceDetailsPolish()) {
+ ImageButton renameButton = mLayoutPreference.findViewById(R.id.rename_button);
+ renameButton.setVisibility(View.VISIBLE);
+ renameButton.setOnClickListener(view -> {
+ RemoteDeviceNameDialogFragment.newInstance(mCachedDevice).show(
+ mFragment.getFragmentManager(), RemoteDeviceNameDialogFragment.TAG);
+ });
+ }
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index 462f422..3fbd445 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -26,6 +26,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.flags.Flags;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -47,6 +48,9 @@
@Override
public boolean isAvailable() {
+ if (Flags.enableBluetoothDeviceDetailsPolish()) {
+ return false;
+ }
boolean hasLeAudio = mCachedDevice.getUiAccessibleProfiles()
.stream()
.anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 5f9957b..ccf38ed 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -50,6 +50,7 @@
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.flags.Flags;
import com.android.settings.inputmethod.KeyboardSettingsPreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.SlicePreferenceController;
@@ -213,8 +214,8 @@
finish();
return;
}
- use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice);
- use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager);
+ use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice, this);
+ use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager, this);
use(KeyboardSettingsPreferenceController.class).init(mCachedDevice);
final BluetoothFeatureProvider featureProvider =
@@ -338,7 +339,7 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (!mUserManager.isGuestUser()) {
+ if (!Flags.enableBluetoothDeviceDetailsPolish() && !mUserManager.isGuestUser()) {
MenuItem item = menu.add(0, EDIT_DEVICE_NAME_ITEM_ID, 0,
R.string.bluetooth_rename_button);
item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
@@ -365,6 +366,9 @@
Lifecycle lifecycle = getSettingsLifecycle();
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
lifecycle));
+ controllers.add(
+ new GeneralBluetoothDetailsHeaderController(
+ context, this, mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
lifecycle));
controllers.add(new BluetoothDetailsCompanionAppsController(context, this,
diff --git a/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java
new file mode 100644
index 0000000..57a1027
--- /dev/null
+++ b/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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.BluetoothProfile;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.Pair;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.flags.Flags;
+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;
+
+/** This class adds a header with device name and status (connected/disconnected, etc.). */
+public class GeneralBluetoothDetailsHeaderController extends BluetoothDetailsController {
+ private static final String KEY_GENERAL_DEVICE_HEADER = "general_bluetooth_device_header";
+
+ @Nullable
+ private LayoutPreference mLayoutPreference;
+
+ public GeneralBluetoothDetailsHeaderController(
+ Context context,
+ PreferenceFragmentCompat fragment,
+ CachedBluetoothDevice device,
+ Lifecycle lifecycle) {
+ super(context, fragment, device, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!Flags.enableBluetoothDeviceDetailsPolish()) {
+ return false;
+ }
+ boolean hasLeAudio =
+ mCachedDevice.getUiAccessibleProfiles().stream()
+ .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
+ return !BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && !hasLeAudio;
+ }
+
+ @Override
+ protected void init(PreferenceScreen screen) {
+ mLayoutPreference = screen.findPreference(KEY_GENERAL_DEVICE_HEADER);
+ }
+
+ @Override
+ protected void refresh() {
+ if (!isAvailable() || mLayoutPreference == null) {
+ return;
+ }
+ ImageView imageView = mLayoutPreference.findViewById(R.id.bt_header_icon);
+ if (imageView != null) {
+ final Pair<Drawable, String> pair =
+ BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+ imageView.setImageDrawable(pair.first);
+ imageView.setContentDescription(pair.second);
+ }
+
+ TextView title = mLayoutPreference.findViewById(R.id.bt_header_device_name);
+ if (title != null) {
+ title.setText(mCachedDevice.getName());
+ }
+ TextView summary = mLayoutPreference.findViewById(R.id.bt_header_connection_summary);
+ if (summary != null) {
+ summary.setText(mCachedDevice.getConnectionSummary());
+ }
+ ImageButton renameButton = mLayoutPreference.findViewById(R.id.rename_button);
+ renameButton.setVisibility(View.VISIBLE);
+ renameButton.setOnClickListener(
+ view -> {
+ RemoteDeviceNameDialogFragment.newInstance(mCachedDevice)
+ .show(
+ mFragment.getFragmentManager(),
+ RemoteDeviceNameDialogFragment.TAG);
+ });
+ }
+
+ @Override
+ @NonNull
+ public String getPreferenceKey() {
+ return KEY_GENERAL_DEVICE_HEADER;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
index a5e9cde..2524894 100644
--- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -27,14 +27,17 @@
import android.util.Log;
import android.util.Pair;
import android.view.View;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
import com.android.settings.fuelgauge.BatteryMeterView;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -86,6 +89,7 @@
@VisibleForTesting
static final int INVALID_RESOURCE_ID = -1;
+ PreferenceFragmentCompat mFragment;
@VisibleForTesting
LayoutPreference mLayoutPreference;
LocalBluetoothManager mManager;
@@ -151,11 +155,12 @@
}
public void init(CachedBluetoothDevice cachedBluetoothDevice,
- LocalBluetoothManager bluetoothManager) {
+ LocalBluetoothManager bluetoothManager, PreferenceFragmentCompat fragment) {
mCachedDevice = cachedBluetoothDevice;
mManager = bluetoothManager;
mProfileManager = bluetoothManager.getProfileManager();
mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice);
+ mFragment = fragment;
}
@VisibleForTesting
@@ -163,6 +168,14 @@
if (mLayoutPreference == null || mCachedDevice == null) {
return;
}
+ if (Flags.enableBluetoothDeviceDetailsPolish()) {
+ ImageButton renameButton = mLayoutPreference.findViewById(R.id.rename_button);
+ renameButton.setVisibility(View.VISIBLE);
+ renameButton.setOnClickListener(view -> {
+ RemoteDeviceNameDialogFragment.newInstance(mCachedDevice).show(
+ mFragment.getFragmentManager(), RemoteDeviceNameDialogFragment.TAG);
+ });
+ }
final ImageView imageView = mLayoutPreference.findViewById(R.id.entity_header_icon);
if (imageView != null) {
final Pair<Drawable, String> pair =
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
index 8d96f21..af4888b 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
@@ -28,15 +28,19 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.preference.PreferenceFragmentCompat;
+
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.BasePreferenceController;
@@ -93,6 +97,8 @@
private CachedBluetoothDevice mCachedDevice;
@Mock
private BluetoothAdapter mBluetoothAdapter;
+ @Mock
+ private PreferenceFragmentCompat mFragment;
private AdvancedBluetoothDetailsHeaderController mController;
private LayoutPreference mLayoutPreference;
@@ -103,7 +109,7 @@
mContext = Robolectric.buildActivity(SettingsActivity.class).get();
mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key");
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
- mController.init(mCachedDevice);
+ mController.init(mCachedDevice, mFragment);
mLayoutPreference = new LayoutPreference(mContext,
LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null));
mController.mLayoutPreference = mLayoutPreference;
@@ -540,6 +546,22 @@
rightBatteryPrediction);
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void enablePolishFlag_renameButtonShown() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
+ SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("true".getBytes());
+ Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
+ when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
+
+ mController.onStart();
+
+ ImageButton button = mLayoutPreference.findViewById(R.id.rename_button);
+ assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) {
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
assertThat(textView.getVisibility()).isEqualTo(visible);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java
new file mode 100644
index 0000000..d608f3f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.core.SettingsUIDeviceConfig;
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowDeviceConfig;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settingslib.bluetooth.LeAudioProfile;
+import com.android.settingslib.widget.LayoutPreference;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowDeviceConfig.class})
+public class GeneralBluetoothDetailsHeaderControllerTest
+ extends BluetoothDetailsControllerTestBase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private GeneralBluetoothDetailsHeaderController mController;
+ private LayoutPreference mPreference;
+
+ @Mock private BluetoothDevice mBluetoothDevice;
+ @Mock private LeAudioProfile mLeAudioProfile;
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ FakeFeatureFactory.setupForTest();
+ android.provider.DeviceConfig.setProperty(
+ android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
+ SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED,
+ "true",
+ true);
+ mController =
+ new GeneralBluetoothDetailsHeaderController(
+ mContext, mFragment, mCachedDevice, mLifecycle);
+ mPreference = new LayoutPreference(mContext, R.layout.general_bt_entity_header);
+ mPreference.setKey(mController.getPreferenceKey());
+ mScreen.addPreference(mPreference);
+ setupDevice(mDeviceConfig);
+ when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowEntityHeaderController.reset();
+ }
+
+ /**
+ * Test to verify the current test context object works so that we are not checking null against
+ * null
+ */
+ @Test
+ public void testContextMock() {
+ assertThat(mContext.getString(com.android.settingslib.R.string.bluetooth_connected))
+ .isNotNull();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void header() {
+ when(mCachedDevice.getName()).thenReturn("device name");
+ when(mCachedDevice.getConnectionSummary()).thenReturn("Active");
+
+ showScreen(mController);
+
+ TextView deviceName = mPreference.findViewById(R.id.bt_header_device_name);
+ TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary);
+ assertThat(deviceName.getText().toString()).isEqualTo("device name");
+ assertThat(summary.getText().toString()).isEqualTo("Active");
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void connectionStatusChangesWhileScreenOpen() {
+ TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary);
+ when(mCachedDevice.getConnectionSummary())
+ .thenReturn(
+ mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
+
+ showScreen(mController);
+ String summaryText1 = summary.getText().toString();
+ when(mCachedDevice.getConnectionSummary()).thenReturn(null);
+ mController.onDeviceAttributesChanged();
+ String summaryText2 = summary.getText().toString();
+ when(mCachedDevice.getConnectionSummary())
+ .thenReturn(
+ mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
+ mController.onDeviceAttributesChanged();
+ String summaryText3 = summary.getText().toString();
+
+ assertThat(summaryText1)
+ .isEqualTo(
+ mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
+ assertThat(summaryText2).isEqualTo("");
+ assertThat(summaryText3)
+ .isEqualTo(
+ mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void isAvailable_untetheredHeadset_returnFalse() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("true".getBytes());
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void isAvailable_notUntetheredHeadset_returnTrue() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("false".getBytes());
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void isAvailable_leAudioDevice_returnFalse() {
+ when(mCachedDevice.getUiAccessibleProfiles())
+ .thenReturn(List.of(mLeAudioProfile));
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void isAvailable_flagEnabled_returnTrue() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("false".getBytes());
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
+ public void iaAvailable_flagDisabled_returnFalse() {
+ assertThat(mController.isAvailable()).isFalse();
+ }
+}