diff --git a/res/layout/advanced_bt_entity_header.xml b/res/layout/advanced_bt_entity_header.xml
new file mode 100644
index 00000000000..4aadad1fc34
--- /dev/null
+++ b/res/layout/advanced_bt_entity_header.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml
new file mode 100644
index 00000000000..07ea8146fad
--- /dev/null
+++ b/res/layout/advanced_bt_entity_sub.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b773fd97ebf..f0809db9c04 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10513,6 +10513,13 @@
No Bluetooth devices
+
+ Left
+
+ Right
+
+ Case
+
Settings Panel
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index cf9fbf936c7..f485c6a8709 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -23,7 +23,16 @@
android:key="bluetooth_device_header"
android:layout="@layout/settings_entity_header"
android:selectable="false"
- settings:allowDividerBelow="true"/>
+ settings:allowDividerBelow="true"
+ settings:searchable="false"/>
+
+
pair = BluetoothUtils
.getBtClassDrawableWithDescription(mContext, mCachedDevice,
- mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1, 1));
+ mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1,
+ 1));
String summaryText = mCachedDevice.getConnectionSummary();
// If both the hearing aids are connected, two device status should be shown.
// If Second Summary is unavailable, to set it to null.
@@ -84,4 +85,4 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
public String getPreferenceKey() {
return KEY_DEVICE_HEADER;
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java b/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
index 987dbe4f050..835961dee5e 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
@@ -62,4 +62,4 @@ public class BluetoothDetailsMacAddressController extends BluetoothDetailsContro
}
return mFooterPreference.getKey();
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 88cebcb331f..5c609e69c44 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -264,4 +264,4 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
public String getPreferenceKey() {
return KEY_PROFILES_GROUP;
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 7cff772c96b..98455f2f0aa 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -108,6 +108,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
mManager = getLocalBluetoothManager(context);
mCachedDevice = getCachedDevice(mDeviceAddress);
super.onAttach(context);
+ use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice);
final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(
context).getBluetoothFeatureProvider(context);
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index d6e395e0c31..ff4a98fc5ce 100755
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -47,6 +47,8 @@ public final class Utils {
static final boolean V = BluetoothUtils.V; // verbose logging
static final boolean D = BluetoothUtils.D; // regular logging
+ public static final int META_INT_ERROR = -1;
+
private Utils() {
}
@@ -152,4 +154,29 @@ public final class Utils {
return Settings.Global.getInt(context.getContentResolver(),
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
}
+
+ public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) {
+ if (bluetoothDevice == null) {
+ return false;
+ }
+ return Boolean.parseBoolean(bluetoothDevice.getMetadata(key));
+ }
+
+ public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) {
+ if (bluetoothDevice == null) {
+ return null;
+ }
+ return bluetoothDevice.getMetadata(key);
+ }
+
+ public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) {
+ if (bluetoothDevice == null) {
+ return META_INT_ERROR;
+ }
+ try {
+ return Integer.parseInt(bluetoothDevice.getMetadata(key));
+ } catch (NumberFormatException e) {
+ return META_INT_ERROR;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
new file mode 100644
index 00000000000..a74610e1a3d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 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.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatteryMeterView;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.widget.LayoutPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowEntityHeaderController.class)
+public class AdvancedBluetoothDetailsHeaderControllerTest{
+ private static final int BATTERY_LEVEL_MAIN = 30;
+ private static final int BATTERY_LEVEL_LEFT = 25;
+ private static final int BATTERY_LEVEL_RIGHT = 45;
+
+ private Context mContext;
+
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice;
+ private AdvancedBluetoothDetailsHeaderController mController;
+ private LayoutPreference mLayoutPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key");
+ mController.init(mCachedDevice);
+ mLayoutPreference = new LayoutPreference(mContext,
+ LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null));
+ mController.mLayoutPreference = mLayoutPreference;
+ when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
+ }
+
+ @Test
+ public void createBatteryIcon_hasCorrectInfo() {
+ final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN);
+ assertThat(drawable).isInstanceOf(BatteryMeterView.BatteryMeterDrawable.class);
+
+ final BatteryMeterView.BatteryMeterDrawable iconDrawable =
+ (BatteryMeterView.BatteryMeterDrawable) drawable;
+ assertThat(iconDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL_MAIN);
+ }
+
+ @Test
+ public void refresh_updateCorrectInfo() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_LEFT));
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_RIGHT));
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_CASE_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_MAIN));
+ mController.refresh();
+
+ assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
+ assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT);
+ assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
+ }
+
+ @Test
+ public void getAvailabilityStatus_unthetheredHeadset_returnAvailable() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+ .thenReturn("true");
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_notUnthetheredHeadset_returnUnavailable() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+ .thenReturn("false");
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+ }
+
+ private void assertBatteryLevel(LinearLayout linearLayout, int batteryLevel) {
+ final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
+ assertThat(textView.getText().toString()).isEqualTo(
+ com.android.settings.Utils.formatPercentage(batteryLevel));
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
index 1e780753156..930d9cbe087 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
@@ -50,7 +50,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-
@Config(shadows = ShadowEntityHeaderController.class)
public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsControllerTestBase {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
index 1337f6640e1..51e559f8900 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settings.bluetooth;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -22,6 +24,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -40,23 +43,68 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class UtilsTest {
+ private static final String STRING_METADATA = "string_metadata";
+ private static final String BOOL_METADATA = "true";
+ private static final String INT_METADATA = "25";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
private MetricsFeatureProvider mMetricsFeatureProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider();
}
@Test
- public void testShowConnectingError_shouldLogBluetoothConnectError() {
+ public void showConnectingError_shouldLogBluetoothConnectError() {
when(mContext.getString(anyInt(), anyString())).thenReturn("testMessage");
Utils.showConnectingError(mContext, "testName", mock(LocalBluetoothManager.class));
verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(),
eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR));
}
+
+ @Test
+ public void getStringMetaData_hasMetaData_getCorrectMetaData() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_ICON)).thenReturn(STRING_METADATA);
+
+ assertThat(Utils.getStringMetaData(mBluetoothDevice,
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA);
+ }
+
+ @Test
+ public void getIntMetaData_hasMetaData_getCorrectMetaData() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(INT_METADATA);
+
+ assertThat(Utils.getIntMetaData(mBluetoothDevice,
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY))
+ .isEqualTo(Integer.parseInt(INT_METADATA));
+ }
+
+ @Test
+ public void getIntMetaData_invalidMetaData_getErrorCode() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(STRING_METADATA);
+
+ assertThat(Utils.getIntMetaData(mBluetoothDevice,
+ BluetoothDevice.METADATA_UNTHETHERED_LEFT_ICON)).isEqualTo(Utils.META_INT_ERROR);
+ }
+
+ @Test
+ public void getBooleanMetaData_hasMetaData_getCorrectMetaData() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(BOOL_METADATA);
+
+ assertThat(Utils.getBooleanMetaData(mBluetoothDevice,
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).isEqualTo(true);
+ }
+
}