Audio output switcher: show low battery in color
Bug: 292067610
Test: atest CachedBluetoothDeviceTest
Change-Id: I820d4529a80e8e1dcfcb0ddc1348edb43a7bab51
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9687674..8b1e93a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -202,10 +202,18 @@
<string name="bluetooth_active_battery_level_untethered">Active, L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
<!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message on TV when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="tv_bluetooth_battery_level">Battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
<!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for untethered headset. [CHAR LIMIT=NONE] -->
<string name="bluetooth_battery_level_untethered">L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the left part of the untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_battery_level_untethered_left">Left <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
+ <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_battery_level_untethered_right">Right <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
<!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
<string name="bluetooth_active_no_battery_level">Active</string>
+ <!-- Connected devices settings. Message shown when bluetooth device is disconnected but is a known, previously connected device [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_saved_device">Saved</string>
<!-- Connected device settings. Message when the left-side hearing aid device is active. [CHAR LIMIT=NONE] -->
<string name="bluetooth_hearing_aid_left_active">Active, left only</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index c67df71..a7537e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -34,7 +34,9 @@
import android.os.Message;
import android.os.ParcelUuid;
import android.os.SystemClock;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
@@ -44,6 +46,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.media.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
@@ -73,6 +76,12 @@
private static final long MAX_LEAUDIO_DELAY_FOR_AUTO_CONNECT = 30000;
private static final long MAX_MEDIA_PROFILE_CONNECT_DELAY = 60000;
+ private static final int DEFAULT_LOW_BATTERY_THRESHOLD = 20;
+
+ // To be used instead of a resource id to indicate that low battery states should not be
+ // changed to a different color.
+ private static final int SUMMARY_NO_COLOR_FOR_LOW_BATTERY = 0;
+
private final Context mContext;
private final BluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
@@ -1150,6 +1159,46 @@
* @param shortSummary {@code true} if need to return short version summary
*/
public String getConnectionSummary(boolean shortSummary) {
+ CharSequence summary = getConnectionSummary(shortSummary, false /* isTvSummary */,
+ SUMMARY_NO_COLOR_FOR_LOW_BATTERY);
+ if (summary != null) {
+ return summary.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Returns android tv string that describes the connection state of this device.
+ */
+ public CharSequence getTvConnectionSummary() {
+ return getTvConnectionSummary(SUMMARY_NO_COLOR_FOR_LOW_BATTERY);
+ }
+
+ /**
+ * Returns android tv string that describes the connection state of this device, with low
+ * battery states highlighted in color.
+ *
+ * @param lowBatteryColorRes - resource id for the color that should be used for the part of the
+ * CharSequence that contains low battery information.
+ */
+ public CharSequence getTvConnectionSummary(int lowBatteryColorRes) {
+ return getConnectionSummary(false /* shortSummary */, true /* isTvSummary */,
+ lowBatteryColorRes);
+ }
+
+ /**
+ * Return summary that describes connection state of this device. Summary depends on:
+ * 1. Whether device has battery info
+ * 2. Whether device is in active usage(or in phone call)
+ *
+ * @param shortSummary {@code true} if need to return short version summary
+ * @param isTvSummary {@code true} if the summary should be TV specific
+ * @param lowBatteryColorRes Resource id of the color to be used for low battery strings. Use
+ * {@link SUMMARY_NO_COLOR_FOR_LOW_BATTERY} if no separate color
+ * should be used.
+ */
+ private CharSequence getConnectionSummary(boolean shortSummary, boolean isTvSummary,
+ int lowBatteryColorRes) {
boolean profileConnected = false; // Updated as long as BluetoothProfile is connected
boolean a2dpConnected = true; // A2DP is connected
boolean hfpConnected = true; // HFP is connected
@@ -1276,17 +1325,82 @@
}
}
- if (stringRes != R.string.bluetooth_pairing
- || getBondState() == BluetoothDevice.BOND_BONDING) {
- if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
- return mContext.getString(stringRes, Utils.formatPercentage(leftBattery),
- Utils.formatPercentage(rightBattery));
- } else {
- return mContext.getString(stringRes, batteryLevelPercentageString);
- }
- } else {
+ if (stringRes == R.string.bluetooth_pairing
+ && getBondState() != BluetoothDevice.BOND_BONDING) {
return null;
}
+
+ boolean summaryIncludesBatteryLevel = stringRes == R.string.bluetooth_battery_level
+ || stringRes == R.string.bluetooth_active_battery_level
+ || stringRes == R.string.bluetooth_active_battery_level_untethered
+ || stringRes == R.string.bluetooth_battery_level_untethered;
+ if (isTvSummary && summaryIncludesBatteryLevel && Flags.enableTvMediaOutputDialog()) {
+ return getTvBatterySummary(batteryLevel, leftBattery, rightBattery, lowBatteryColorRes);
+ }
+
+ if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+ return mContext.getString(stringRes, Utils.formatPercentage(leftBattery),
+ Utils.formatPercentage(rightBattery));
+ } else {
+ return mContext.getString(stringRes, batteryLevelPercentageString);
+ }
+ }
+
+ private CharSequence getTvBatterySummary(int mainBattery, int leftBattery, int rightBattery,
+ int lowBatteryColorRes) {
+ // Since there doesn't seem to be a way to use format strings to add the
+ // percentages and also mark which part of the string is left and right to color
+ // them, we are using one string resource per battery.
+ Resources res = mContext.getResources();
+ SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
+ if (leftBattery >= 0 || rightBattery >= 0) {
+ // Not switching the left and right for RTL to keep the left earbud always on
+ // the left.
+ if (leftBattery >= 0) {
+ String left = res.getString(
+ R.string.bluetooth_battery_level_untethered_left,
+ Utils.formatPercentage(leftBattery));
+ addBatterySpan(spannableBuilder, left, isBatteryLow(leftBattery,
+ BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD),
+ lowBatteryColorRes);
+ }
+ if (rightBattery >= 0) {
+ if (!spannableBuilder.isEmpty()) {
+ spannableBuilder.append(" ");
+ }
+ String right = res.getString(
+ R.string.bluetooth_battery_level_untethered_right,
+ Utils.formatPercentage(rightBattery));
+ addBatterySpan(spannableBuilder, right, isBatteryLow(rightBattery,
+ BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD),
+ lowBatteryColorRes);
+ }
+ } else {
+ addBatterySpan(spannableBuilder, res.getString(R.string.tv_bluetooth_battery_level,
+ Utils.formatPercentage(mainBattery)),
+ isBatteryLow(mainBattery, BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD),
+ lowBatteryColorRes);
+ }
+ return spannableBuilder;
+ }
+
+ private void addBatterySpan(SpannableStringBuilder builder,
+ String batteryString, boolean lowBattery, int lowBatteryColorRes) {
+ if (lowBattery && lowBatteryColorRes != SUMMARY_NO_COLOR_FOR_LOW_BATTERY) {
+ builder.append(batteryString,
+ new ForegroundColorSpan(mContext.getResources().getColor(lowBatteryColorRes)),
+ 0 /* flags */);
+ } else {
+ builder.append(batteryString);
+ }
+ }
+
+ private boolean isBatteryLow(int batteryLevel, int metadataKey) {
+ int lowBatteryThreshold = BluetoothUtils.getIntMetaData(mDevice, metadataKey);
+ if (lowBatteryThreshold <= 0) {
+ lowBatteryThreshold = DEFAULT_LOW_BATTERY_THRESHOLD;
+ }
+ return batteryLevel <= lowBatteryThreshold;
}
private boolean isTwsBatteryAvailable(int leftBattery, int rightBattery) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index ed518f7..9560b8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -72,6 +72,13 @@
}
@Override
+ public CharSequence getSummaryForTv(int lowBatteryColorRes) {
+ return isConnected() || mCachedDevice.isBusy()
+ ? mCachedDevice.getTvConnectionSummary(lowBatteryColorRes)
+ : mContext.getString(R.string.bluetooth_saved_device);
+ }
+
+ @Override
public int getSelectionBehavior() {
// We don't allow apps to override the selection behavior of system routes.
return SELECTION_BEHAVIOR_TRANSFER;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 8085c99..c8e4c0c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -208,6 +208,17 @@
public abstract String getSummary();
/**
+ * Get summary from MediaDevice for TV with low batter states in a different color if
+ * applicable.
+ *
+ * @param lowBatteryColorRes Color resource for the part of the CharSequence that describes a
+ * low battery state.
+ */
+ public CharSequence getSummaryForTv(int lowBatteryColorRes) {
+ return getSummary();
+ }
+
+ /**
* Get icon of MediaDevice.
*
* @return drawable of icon.
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 2d875cf..732c336 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -49,6 +49,8 @@
"androidx.fragment_fragment",
"androidx.test.core",
"androidx.core_core",
+ "flag-junit",
+ "settingslib_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 85efe69..ed545ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -35,13 +35,18 @@
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.media.AudioManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.text.Spannable;
+import android.text.style.ForegroundColorSpan;
import android.util.LruCache;
import com.android.settingslib.R;
+import com.android.settingslib.media.flags.Flags;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -60,10 +65,13 @@
private static final String DEVICE_ALIAS_NEW = "TestAliasNew";
private static final String TWS_BATTERY_LEFT = "15";
private static final String TWS_BATTERY_RIGHT = "25";
+ private static final String TWS_LOW_BATTERY_THRESHOLD_LOW = "10";
+ private static final String TWS_LOW_BATTERY_THRESHOLD_HIGH = "25";
private static final short RSSI_1 = 10;
private static final short RSSI_2 = 11;
private static final boolean JUSTDISCOVERED_1 = true;
private static final boolean JUSTDISCOVERED_2 = false;
+ private static final int LOW_BATTERY_COLOR = android.R.color.holo_red_dark;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
@@ -89,9 +97,13 @@
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
mContext = RuntimeEnvironment.application;
mAudioManager = mContext.getSystemService(AudioManager.class);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -179,6 +191,17 @@
}
@Test
+ public void getTvConnectionSummary_testProfilesInactive_returnPairing() {
+ // Arrange:
+ // Bond State: Bonding
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ // Act & Assert:
+ // Get "Pairing…" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Pairing…");
+ }
+
+ @Test
public void getConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
@@ -212,6 +235,39 @@
}
@Test
+ public void getTvConnectionSummary_testSingleProfileConnectDisconnect() {
+ // Test without battery level
+ // Set PAN profile to be connected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set PAN profile to be disconnected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ // Set PAN profile to be connected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+ // Set PAN profile to be disconnected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+
+ // Set PAN profile to be connected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set PAN profile to be disconnected and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testMultipleProfileConnectDisconnect() {
mBatteryLevel = 10;
@@ -243,6 +299,37 @@
}
@Test
+ public void getTvConnectionSummary_testMultipleProfileConnectDisconnect() {
+ mBatteryLevel = 10;
+
+ // Set HFP, A2DP and PAN profile to be connected and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+ // Disconnect HFP only and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Disconnect A2DP only and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Disconnect both HFP and A2DP and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Disconnect all profiles and test connection state summary
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testSingleProfileActiveDeviceA2dp() {
// Test without battery level
// Set A2DP profile to be connected and test connection state summary
@@ -275,6 +362,37 @@
}
@Test
+ public void getTvConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP profile to be connected, Active and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_shortSummary_returnShortSummary() {
// Test without battery level
// Set A2DP profile to be connected and test connection state summary
@@ -309,6 +427,18 @@
}
@Test
+ public void getTvConnectionSummary_testA2dpBatteryInactive_returnBattery() {
+ // Arrange:
+ // 1. Profile: {A2DP, CONNECTED, Inactive}
+ // 2. Battery Level: 10
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testA2dpInCall_returnNull() {
// Arrange:
// 1. Profile: {A2DP, Connected, Active}
@@ -323,6 +453,20 @@
}
@Test
+ public void getTvConnectionSummary_testA2dpInCall_returnNull() {
+ // Arrange:
+ // 1. Profile: {A2DP, Connected, Active}
+ // 2. Audio Manager: In Call
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ // Act & Assert:
+ // Get null result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testA2dpBatteryInCall_returnBattery() {
// Arrange:
// 1. Profile: {A2DP, Connected, Active}
@@ -339,6 +483,22 @@
}
@Test
+ public void getTvConnectionSummary_testA2dpBatteryInCall_returnBattery() {
+ // Arrange:
+ // 1. Profile: {A2DP, Connected, Active}
+ // 3. Battery Level: 10
+ // 2. Audio Manager: In Call
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mBatteryLevel = 10;
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ // Act & Assert:
+ // Get "10% battery" result with Battery Level 10.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testSingleProfileActiveDeviceHfp() {
// Test without battery level
// Set HFP profile to be connected and test connection state summary
@@ -372,6 +532,39 @@
}
@Test
+ public void getTvConnectionSummary_testSingleProfileActiveDeviceHfp() {
+ // Test without battery level
+ // Set HFP profile to be connected and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set device as Active for HFP and test connection state summary
+ mCachedDevice.onAudioModeChanged();
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set HFP profile to be connected, Active and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testHeadsetBatteryInactive_returnBattery() {
// Arrange:
// 1. Profile: {HEADSET, CONNECTED, Inactive}
@@ -385,6 +578,19 @@
}
@Test
+ public void getTvConnectionSummary_testHeadsetBatteryInactive_returnBattery() {
+ // Arrange:
+ // 1. Profile: {HEADSET, CONNECTED, Inactive}
+ // 2. Battery Level: 10
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ // Get "10% battery" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testHeadsetWithoutInCall_returnNull() {
// Arrange:
// 1. Profile: {HEADSET, Connected, Active}
@@ -398,6 +604,19 @@
}
@Test
+ public void getTvConnectionSummary_testHeadsetWithoutInCall_returnNull() {
+ // Arrange:
+ // 1. Profile: {HEADSET, Connected, Active}
+ // 2. Audio Manager: Normal (Without In Call)
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+
+ // Act & Assert:
+ // Get null result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testHeadsetBatteryWithoutInCall_returnBattery() {
// Arrange:
// 1. Profile: {HEADSET, Connected, Active}
@@ -413,6 +632,22 @@
}
@Test
+ public void getTvConnectionSummary_testHeadsetBatteryWithoutInCall_returnBattery() {
+ // Arrange:
+ // 1. Profile: {HEADSET, Connected, Active}
+ // 2. Battery Level: 10
+ // 3. Audio Manager: Normal (Without In Call)
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ // Get "10% battery" result with Battery Level 10.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+
+ @Test
public void getConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
// Test without battery level
// Set Hearing Aid profile to be connected and test connection state summary
@@ -432,6 +667,26 @@
}
@Test
+ public void getTvConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+ // Test without battery level
+ // Set Hearing Aid profile to be connected and test connection state summary
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set device as Active for Hearing Aid and test connection state summary
+ mCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Active, left only");
+
+ // Set Hearing Aid profile to be disconnected and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+ mCachedDevice.onProfileStateChanged(mHearingAidProfile,
+ BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testHearingAidBatteryInactive_returnBattery() {
// Arrange:
// 1. Profile: {HEARING_AID, CONNECTED, Inactive}
@@ -445,6 +700,19 @@
}
@Test
+ public void getTvConnectionSummary_testHearingAidBatteryInactive_returnBattery() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, CONNECTED, Inactive}
+ // 2. Battery Level: 10
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ // Get "10% battery" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testHearingAidBatteryWithoutInCall_returnActiveBattery() {
// Arrange:
// 1. Profile: {HEARING_AID, Connected, Active}
@@ -460,6 +728,21 @@
}
@Test
+ public void getTvConnectionSummary_testHearingAidBatteryWithoutInCall_returnBattery() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, Connected, Active}
+ // 2. Battery Level: 10
+ // 3. Audio Manager: Normal (Without In Call)
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ // Get "Active, 10% battery" result with Battery Level 10.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
// Arrange:
// 1. Profile: {HEARING_AID, Connected, Active, Right ear}
@@ -475,6 +758,22 @@
}
@Test
+ public void getTvConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, Connected, Active, Right ear}
+ // 2. Audio Manager: In Call
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ // Act & Assert:
+ // Get "Active" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Active, right only");
+ }
+
+ @Test
public void getConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
// Arrange:
// 1. Profile: {HEARING_AID, Connected, Active, Both ear}
@@ -493,6 +792,25 @@
}
@Test
+ public void getTvConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, Connected, Active, Both ear}
+ // 2. Audio Manager: In Call
+ mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mSubCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
+ updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setSubDevice(mSubCachedDevice);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ // Act & Assert:
+ // Get "Active" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString())
+ .isEqualTo("Active, left and right");
+ }
+
+ @Test
public void getConnectionSummary_testHearingAidBatteryInCall_returnActiveBattery() {
// Arrange:
// 1. Profile: {HEARING_AID, Connected, Active}
@@ -509,6 +827,22 @@
}
@Test
+ public void getTvConnectionSummary_testHearingAidBatteryInCall_returnBattery() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, Connected, Active}
+ // 2. Battery Level: 10
+ // 3. Audio Manager: In Call
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mBatteryLevel = 10;
+
+ // Act & Assert:
+ // Get "Active, 10% battery" result with Battery Level 10.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+ }
+
+ @Test
public void getConnectionSummary_testActiveDeviceLeAudioHearingAid() {
// Test without battery level
// Set HAP Client and LE Audio profile to be connected and test connection state summary
@@ -529,6 +863,27 @@
}
@Test
+ public void getTvConnectionSummary_testActiveDeviceLeAudioHearingAid() {
+ // Test without battery level
+ // Set HAP Client and LE Audio profile to be connected and test connection state summary
+ when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+ updateProfileStatus(mHapClientProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set device as Active for LE Audio and test connection state summary
+ mCachedDevice.setHearingAidInfo(getLeftLeAudioHearingAidInfo());
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.LE_AUDIO);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Active, left only");
+
+ // Set LE Audio profile to be disconnected and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.LE_AUDIO);
+ mCachedDevice.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testMemberDevicesExist_returnMinBattery() {
// One device is active with battery level 70.
mBatteryLevel = 70;
@@ -545,6 +900,22 @@
}
@Test
+ public void getTvConnectionSummary_testMemberDevicesExist_returnMinBattery() {
+ // One device is active with battery level 70.
+ mBatteryLevel = 70;
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+
+ // Add a member device with battery level 30.
+ int lowerBatteryLevel = 30;
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ doAnswer((invocation) -> lowerBatteryLevel).when(mSubCachedDevice).getBatteryLevel();
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 30%");
+ }
+
+ @Test
public void getConnectionSummary_testMemberDevicesBatteryUnknown_returnMinBattery() {
// One device is active with battery level 70.
mBatteryLevel = 70;
@@ -560,6 +931,21 @@
}
@Test
+ public void getTvConnectionSummary_testMemberDevicesBatteryUnknown_returnMinBattery() {
+ // One device is active with battery level 70.
+ mBatteryLevel = 70;
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+ // Add a member device with battery level unknown.
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ doAnswer((invocation) -> BluetoothDevice.BATTERY_LEVEL_UNKNOWN).when(
+ mSubCachedDevice).getBatteryLevel();
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 70%");
+ }
+
+ @Test
public void getConnectionSummary_testAllDevicesBatteryUnknown_returnNoBattery() {
// One device is active with battery level unknown.
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -574,6 +960,20 @@
}
@Test
+ public void getTvConnectionSummary_testAllDevicesBatteryUnknown_returnNoBattery() {
+ // One device is active with battery level unknown.
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+ // Add a member device with battery level unknown.
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ doAnswer((invocation) -> BluetoothDevice.BATTERY_LEVEL_UNKNOWN).when(
+ mSubCachedDevice).getBatteryLevel();
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+ }
+
+ @Test
public void getConnectionSummary_testMultipleProfilesActiveDevice() {
// Test without battery level
// Set A2DP and HFP profiles to be connected and test connection state summary
@@ -621,6 +1021,53 @@
}
@Test
+ public void getTvConnectionSummary_testMultipleProfilesActiveDevice() {
+ // Test without battery level
+ // Set A2DP and HFP profiles to be connected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+ // Set device as Active for A2DP and HFP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Battery 10%");
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+ // Set A2DP and HFP profiles to be disconnected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testMultipleProfilesInactive_returnPairing() {
// Arrange:
// 1. Profile 1: {A2DP, CONNECTED, Inactive}
@@ -638,6 +1085,23 @@
}
@Test
+ public void getTvConnectionSummary_testMultipleProfilesInactive_returnPairing() {
+ // Arrange:
+ // 1. Profile 1: {A2DP, CONNECTED, Inactive}
+ // 2. Profile 2: {HEADSET, CONNECTED, Inactive}
+ // 3. Profile 3: {HEARING_AID, CONNECTED, Inactive}
+ // 4. Bond State: Bonding
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ // Act & Assert:
+ // Get "Pairing…" result without Battery Level.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Pairing…");
+ }
+
+ @Test
public void getConnectionSummary_trueWirelessActiveDeviceWithBattery_returnActiveWithBattery() {
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -656,6 +1120,24 @@
}
@Test
+ public void getTvConnectionSummary_trueWirelessActiveDeviceWithBattery_returnBattery() {
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ "true".getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+ TWS_BATTERY_LEFT.getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+ TWS_BATTERY_RIGHT.getBytes());
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Left 15% Right 25%");
+ }
+
+ @Test
public void getConnectionSummary_trueWirelessDeviceWithBattery_returnActiveWithBattery() {
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -673,6 +1155,84 @@
}
@Test
+ public void getTvConnectionSummary_trueWirelessDeviceWithBattery_returnBattery() {
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ "true".getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+ TWS_BATTERY_LEFT.getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+ TWS_BATTERY_RIGHT.getBytes());
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ "Left 15% Right 25%");
+ }
+
+ @Test
+ public void getTvConnectionSummary_trueWirelessDeviceWithLowBattery() {
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ "true".getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+ TWS_BATTERY_LEFT.getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+ TWS_BATTERY_RIGHT.getBytes());
+
+ int lowBatteryColor = mContext.getColor(LOW_BATTERY_COLOR);
+
+ // Default low battery threshold, only left battery is low
+ CharSequence summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+ assertForegroundColorSpan(summary, 0, 0, 8, lowBatteryColor);
+ assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+
+ // Lower threshold, neither battery should be low
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD))
+ .thenReturn(TWS_LOW_BATTERY_THRESHOLD_LOW.getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD))
+ .thenReturn(TWS_LOW_BATTERY_THRESHOLD_LOW.getBytes());
+ summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+ assertNoForegroundColorSpans(summary);
+ assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+
+
+ // Higher Threshold, both batteries are low
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD))
+ .thenReturn(TWS_LOW_BATTERY_THRESHOLD_HIGH.getBytes());
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD))
+ .thenReturn(TWS_LOW_BATTERY_THRESHOLD_HIGH.getBytes());
+ summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+ assertForegroundColorSpan(summary, 0, 0, 8, lowBatteryColor);
+ assertForegroundColorSpan(summary, 1, 9, 18, lowBatteryColor);
+ assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+ }
+
+ private void assertNoForegroundColorSpans(CharSequence charSequence) {
+ if (charSequence instanceof Spannable) {
+ Spannable summarySpan = (Spannable) charSequence;
+ ForegroundColorSpan[] spans = summarySpan.getSpans(0, summarySpan.length(),
+ ForegroundColorSpan.class);
+ assertThat(spans).isEmpty();
+ }
+ }
+
+ private void assertForegroundColorSpan(CharSequence charSequence, int indexInSpannable,
+ int start, int end, int color) {
+ assertThat(charSequence).isInstanceOf(Spannable.class);
+ Spannable summarySpan = (Spannable) charSequence;
+ ForegroundColorSpan[] spans = summarySpan.getSpans(0, summarySpan.length(),
+ ForegroundColorSpan.class);
+ assertThat(spans[indexInSpannable].getForegroundColor()).isEqualTo(color);
+ assertThat(summarySpan.getSpanStart(spans[indexInSpannable])).isEqualTo(start);
+ assertThat(summarySpan.getSpanEnd(spans[indexInSpannable])).isEqualTo(end);
+ }
+
+ @Test
public void getCarConnectionSummary_singleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
@@ -1136,6 +1696,18 @@
}
@Test
+ public void getTvConnectionSummary_profileConnectedFail_showErrorMessage() {
+ final A2dpProfile profile = mock(A2dpProfile.class);
+ mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
+
+ when(profile.getConnectionStatus(mDevice)).thenReturn(BluetoothProfile.STATE_CONNECTED);
+
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+ mContext.getString(R.string.profile_connect_timeout_subtext));
+ }
+
+ @Test
public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() {
mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS);
when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS);