1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.never; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 import static org.robolectric.Shadows.shadowOf; 25 26 import android.bluetooth.BluetoothAdapter; 27 import android.bluetooth.BluetoothDevice; 28 import android.content.Context; 29 import android.graphics.Bitmap; 30 import android.graphics.drawable.Drawable; 31 import android.platform.test.annotations.DisableFlags; 32 import android.platform.test.annotations.EnableFlags; 33 import android.platform.test.flag.junit.SetFlagsRule; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.widget.ImageButton; 37 import android.widget.ImageView; 38 import android.widget.LinearLayout; 39 import android.widget.ProgressBar; 40 import android.widget.TextView; 41 42 import androidx.preference.PreferenceFragmentCompat; 43 44 import com.android.settings.R; 45 import com.android.settings.SettingsActivity; 46 import com.android.settings.core.BasePreferenceController; 47 import com.android.settings.flags.Flags; 48 import com.android.settings.fuelgauge.BatteryMeterView; 49 import com.android.settings.testutils.shadow.ShadowDeviceConfig; 50 import com.android.settings.testutils.shadow.ShadowEntityHeaderController; 51 import com.android.settingslib.bluetooth.BluetoothUtils; 52 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 53 import com.android.settingslib.utils.StringUtil; 54 import com.android.settingslib.widget.LayoutPreference; 55 56 import org.junit.Before; 57 import org.junit.Ignore; 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.mockito.Mock; 62 import org.mockito.MockitoAnnotations; 63 import org.robolectric.Robolectric; 64 import org.robolectric.RobolectricTestRunner; 65 import org.robolectric.annotation.Config; 66 67 import java.util.HashSet; 68 import java.util.Set; 69 70 @RunWith(RobolectricTestRunner.class) 71 @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class}) 72 public class AdvancedBluetoothDetailsHeaderControllerTest { 73 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 74 75 private static final int BATTERY_LEVEL_MAIN = 30; 76 private static final int BATTERY_LEVEL_LEFT = 25; 77 private static final int BATTERY_LEVEL_RIGHT = 45; 78 private static final int LOW_BATTERY_LEVEL = 15; 79 private static final int CASE_LOW_BATTERY_LEVEL = 19; 80 private static final int LOW_BATTERY_LEVEL_THRESHOLD = 15; 81 private static final int BATTERY_LEVEL_5 = 5; 82 private static final int BATTERY_LEVEL_50 = 50; 83 private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; 84 private static final String ICON_URI = "content://test.provider/icon.png"; 85 private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; 86 private static final String DEVICE_SUMMARY = "test summary"; 87 private static final String TEMP_BOND_METADATA = 88 "<TEMP_BOND_TYPE>le_audio_sharing</TEMP_BOND_TYPE>"; 89 90 private Context mContext; 91 92 @Mock 93 private BluetoothDevice mBluetoothDevice; 94 @Mock 95 private Bitmap mBitmap; 96 @Mock 97 private ImageView mImageView; 98 @Mock 99 private CachedBluetoothDevice mCachedDevice; 100 @Mock 101 private BluetoothAdapter mBluetoothAdapter; 102 @Mock 103 private PreferenceFragmentCompat mFragment; 104 private AdvancedBluetoothDetailsHeaderController mController; 105 private LayoutPreference mLayoutPreference; 106 107 @Before setUp()108 public void setUp() { 109 MockitoAnnotations.initMocks(this); 110 111 mContext = Robolectric.buildActivity(SettingsActivity.class).get(); 112 mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key"); 113 when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); 114 mController.init(mCachedDevice, mFragment); 115 mLayoutPreference = new LayoutPreference(mContext, 116 LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null)); 117 mController.mLayoutPreference = mLayoutPreference; 118 mController.mBluetoothAdapter = mBluetoothAdapter; 119 when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); 120 when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS); 121 when(mCachedDevice.getIdentityAddress()).thenReturn(MAC_ADDRESS); 122 } 123 124 @Test createBatteryIcon_hasCorrectInfo()125 public void createBatteryIcon_hasCorrectInfo() { 126 final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN, 127 true /* charging */); 128 assertThat(drawable).isInstanceOf(BatteryMeterView.BatteryMeterDrawable.class); 129 130 final BatteryMeterView.BatteryMeterDrawable iconDrawable = 131 (BatteryMeterView.BatteryMeterDrawable) drawable; 132 assertThat(iconDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL_MAIN); 133 assertThat(iconDrawable.getCharging()).isTrue(); 134 } 135 136 @Test refresh_connectedWatch_behaveAsExpected()137 public void refresh_connectedWatch_behaveAsExpected() { 138 when(mBluetoothDevice.getMetadata( 139 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 140 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); 141 when(mBluetoothDevice.getMetadata( 142 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 143 String.valueOf(false).getBytes()); 144 when(mCachedDevice.isConnected()).thenReturn(true); 145 146 mController.refresh(); 147 148 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo( 149 View.GONE); 150 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo( 151 View.GONE); 152 assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo( 153 View.VISIBLE); 154 // TODO (b/188954766) : clarify settings design 155 } 156 157 @Test refresh_connectedWatch_unknownBatteryLevel_shouldNotShowBatteryLevel()158 public void refresh_connectedWatch_unknownBatteryLevel_shouldNotShowBatteryLevel() { 159 when(mBluetoothDevice.getMetadata( 160 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 161 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); 162 when(mBluetoothDevice.getMetadata( 163 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 164 String.valueOf(false).getBytes()); 165 when(mBluetoothDevice.getMetadata( 166 BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn( 167 String.valueOf(BluetoothUtils.META_INT_ERROR).getBytes()); 168 when(mBluetoothDevice.getBatteryLevel()).thenReturn(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 169 when(mCachedDevice.isConnected()).thenReturn(true); 170 171 mController.refresh(); 172 173 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo( 174 View.GONE); 175 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo( 176 View.GONE); 177 assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo( 178 View.VISIBLE); 179 assertThat(mLayoutPreference.findViewById(R.id.layout_middle) 180 .requireViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(View.GONE); 181 } 182 183 @Test refresh_connectedStylus_behaveAsExpected()184 public void refresh_connectedStylus_behaveAsExpected() { 185 when(mBluetoothDevice.getMetadata( 186 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 187 BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes()); 188 when(mBluetoothDevice.getMetadata( 189 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 190 String.valueOf(false).getBytes()); 191 when(mCachedDevice.isConnected()).thenReturn(true); 192 193 mController.refresh(); 194 195 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo( 196 View.GONE); 197 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo( 198 View.GONE); 199 assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo( 200 View.VISIBLE); 201 } 202 203 @Test refresh_connectedUnknownType_behaveAsExpected()204 public void refresh_connectedUnknownType_behaveAsExpected() { 205 when(mBluetoothDevice.getMetadata( 206 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 207 "UNKNOWN_TYPE".getBytes()); 208 when(mBluetoothDevice.getMetadata( 209 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 210 String.valueOf(false).getBytes()); 211 when(mCachedDevice.isConnected()).thenReturn(true); 212 213 mController.refresh(); 214 215 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo( 216 View.GONE); 217 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo( 218 View.GONE); 219 assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo( 220 View.VISIBLE); 221 } 222 223 @Test refresh_connectedUntetheredHeadset_behaveAsExpected()224 public void refresh_connectedUntetheredHeadset_behaveAsExpected() { 225 when(mBluetoothDevice.getMetadata( 226 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 227 BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); 228 when(mBluetoothDevice.getMetadata( 229 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 230 String.valueOf(false).getBytes()); 231 when(mBluetoothDevice.getMetadata( 232 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( 233 String.valueOf(BATTERY_LEVEL_LEFT).getBytes()); 234 when(mBluetoothDevice.getMetadata( 235 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( 236 String.valueOf(BATTERY_LEVEL_RIGHT).getBytes()); 237 when(mBluetoothDevice.getMetadata( 238 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn( 239 String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); 240 when(mCachedDevice.isConnected()).thenReturn(true); 241 242 mController.refresh(); 243 244 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT); 245 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT); 246 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN); 247 } 248 249 @Test refresh_connected_updateCorrectInfo()250 public void refresh_connected_updateCorrectInfo() { 251 when(mBluetoothDevice.getMetadata( 252 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 253 String.valueOf(true).getBytes()); 254 when(mBluetoothDevice.getMetadata( 255 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( 256 String.valueOf(BATTERY_LEVEL_LEFT).getBytes()); 257 when(mBluetoothDevice.getMetadata( 258 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( 259 String.valueOf(BATTERY_LEVEL_RIGHT).getBytes()); 260 when(mBluetoothDevice.getMetadata( 261 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn( 262 String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); 263 264 when(mCachedDevice.isConnected()).thenReturn(true); 265 mController.refresh(); 266 267 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT); 268 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT); 269 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN); 270 } 271 272 @Test refresh_disconnected_updateCorrectInfo()273 public void refresh_disconnected_updateCorrectInfo() { 274 when(mCachedDevice.isConnected()).thenReturn(false); 275 276 mController.refresh(); 277 278 final LinearLayout layout = mLayoutPreference.findViewById(R.id.layout_middle); 279 280 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo( 281 View.GONE); 282 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo( 283 View.GONE); 284 assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE); 285 assertThat(layout.findViewById(R.id.header_title).getVisibility()).isEqualTo(View.GONE); 286 assertThat(layout.findViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo( 287 View.GONE); 288 assertThat(layout.findViewById(R.id.bt_battery_icon).getVisibility()).isEqualTo(View.GONE); 289 assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE); 290 assertThat(layout.findViewById(R.id.battery_ring).getVisibility()).isEqualTo(View.GONE); 291 } 292 293 @Ignore 294 @Test refresh_connectedWatch_checkSummary()295 public void refresh_connectedWatch_checkSummary() { 296 when(mBluetoothDevice.getMetadata( 297 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( 298 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); 299 when(mCachedDevice.isConnected()).thenReturn(true); 300 when(mCachedDevice.getConnectionSummary(/* shortSummary= */ true)) 301 .thenReturn(DEVICE_SUMMARY); 302 303 mController.refresh(); 304 305 assertThat(((TextView) (mLayoutPreference.findViewById(R.id.entity_header_summary))) 306 .getText()).isEqualTo(DEVICE_SUMMARY); 307 } 308 309 @Test refresh_withLowBatteryAndUncharged_showAlertIcon()310 public void refresh_withLowBatteryAndUncharged_showAlertIcon() { 311 when(mBluetoothDevice.getMetadata( 312 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( 313 String.valueOf(true).getBytes()); 314 when(mBluetoothDevice.getMetadata( 315 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( 316 String.valueOf(LOW_BATTERY_LEVEL).getBytes()); 317 when(mBluetoothDevice.getMetadata( 318 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( 319 String.valueOf(LOW_BATTERY_LEVEL).getBytes()); 320 when(mBluetoothDevice.getMetadata( 321 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn( 322 String.valueOf(CASE_LOW_BATTERY_LEVEL).getBytes()); 323 when(mBluetoothDevice.getMetadata( 324 BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING)).thenReturn( 325 String.valueOf(false).getBytes()); 326 when(mBluetoothDevice.getMetadata( 327 BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING)).thenReturn( 328 String.valueOf(true).getBytes()); 329 when(mBluetoothDevice.getMetadata( 330 BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING)).thenReturn( 331 String.valueOf(false).getBytes()); 332 when(mCachedDevice.isConnected()).thenReturn(true); 333 334 mController.refresh(); 335 336 assertBatteryIcon( 337 mLayoutPreference.findViewById(R.id.layout_left), 338 R.drawable.ic_battery_alert_24dp, 339 false); 340 assertBatteryIcon( 341 mLayoutPreference.findViewById(R.id.layout_right), /* resId= */ -1, true); 342 assertBatteryIcon( 343 mLayoutPreference.findViewById(R.id.layout_middle), 344 R.drawable.ic_battery_alert_24dp, 345 false); 346 } 347 348 @Test getAvailabilityStatus_untetheredHeadset_returnAvailable()349 public void getAvailabilityStatus_untetheredHeadset_returnAvailable() { 350 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 351 .thenReturn("true".getBytes()); 352 353 assertThat(mController.getAvailabilityStatus()).isEqualTo( 354 BasePreferenceController.AVAILABLE); 355 } 356 357 @Test getAvailabilityStatus_notUntetheredHeadset_returnUnavailable()358 public void getAvailabilityStatus_notUntetheredHeadset_returnUnavailable() { 359 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 360 .thenReturn("false".getBytes()); 361 362 assertThat(mController.getAvailabilityStatus()).isEqualTo( 363 BasePreferenceController.CONDITIONALLY_UNAVAILABLE); 364 } 365 366 @Test updateIcon_existInCache_setImageBitmap()367 public void updateIcon_existInCache_setImageBitmap() { 368 mController.mIconCache.put(ICON_URI, mBitmap); 369 370 mController.updateIcon(mImageView, ICON_URI); 371 372 verify(mImageView).setImageBitmap(mBitmap); 373 } 374 375 @Test onStart_isAvailable_registerCallback()376 public void onStart_isAvailable_registerCallback() { 377 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 378 .thenReturn("true".getBytes()); 379 Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>(); 380 when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); 381 when(mBluetoothAdapter.addOnMetadataChangedListener( 382 mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener)) 383 .thenReturn(true); 384 385 mController.onStart(); 386 387 verify(mCachedDevice).registerCallback(mController); 388 verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice, 389 mContext.getMainExecutor(), mController.mMetadataListener); 390 } 391 392 @Test onStart_notAvailable_notNeedToRegisterCallback()393 public void onStart_notAvailable_notNeedToRegisterCallback() { 394 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 395 .thenReturn("false".getBytes()); 396 397 mController.onStart(); 398 399 verify(mCachedDevice, never()).registerCallback(mController); 400 verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, 401 mContext.getMainExecutor(), mController.mMetadataListener); 402 } 403 404 @Test onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback()405 public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() { 406 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 407 .thenReturn("true".getBytes()); 408 when(mCachedDevice.getDevice()).thenReturn(null); 409 Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>(); 410 when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); 411 412 mController.onStart(); 413 414 verify(mCachedDevice, never()).registerCallback(mController); 415 verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, 416 mContext.getMainExecutor(), mController.mMetadataListener); 417 } 418 419 @Test onStop_availableAndHasBluetoothDevice_unregisterCallback()420 public void onStop_availableAndHasBluetoothDevice_unregisterCallback() { 421 onStart_isAvailable_registerCallback(); 422 423 mController.onStop(); 424 425 verify(mCachedDevice).unregisterCallback(mController); 426 verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice, 427 mController.mMetadataListener); 428 } 429 430 @Test onStop_noBluetoothDevice_noNeedToUnregisterCallback()431 public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() { 432 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 433 .thenReturn("true".getBytes()); 434 when(mCachedDevice.getDevice()).thenReturn(null); 435 436 mController.onStart(); 437 mController.onStop(); 438 439 verify(mCachedDevice, never()).unregisterCallback(mController); 440 verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice, 441 mController.mMetadataListener); 442 } 443 444 @Test onDestroy_recycleBitmap()445 public void onDestroy_recycleBitmap() { 446 mController.mIconCache.put(ICON_URI, mBitmap); 447 448 mController.onDestroy(); 449 450 assertThat(mController.mIconCache).isEmpty(); 451 verify(mBitmap).recycle(); 452 } 453 454 @Test estimateReadyIsBothAvailable_showsView()455 public void estimateReadyIsBothAvailable_showsView() { 456 mController.mIsLeftDeviceEstimateReady = true; 457 mController.mIsRightDeviceEstimateReady = true; 458 459 mController.showBothDevicesBatteryPredictionIfNecessary(); 460 461 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left), 462 View.VISIBLE); 463 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right), 464 View.VISIBLE); 465 } 466 467 @Test leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView()468 public void leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView() { 469 mController.mIsLeftDeviceEstimateReady = true; 470 mController.mIsRightDeviceEstimateReady = false; 471 472 mController.showBothDevicesBatteryPredictionIfNecessary(); 473 474 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left), 475 View.GONE); 476 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right), 477 View.GONE); 478 } 479 480 @Test leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView()481 public void leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView() { 482 mController.mIsLeftDeviceEstimateReady = false; 483 mController.mIsRightDeviceEstimateReady = true; 484 485 mController.showBothDevicesBatteryPredictionIfNecessary(); 486 487 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left), 488 View.GONE); 489 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right), 490 View.GONE); 491 } 492 493 @Test bothDevicesEstimateIsNotReady_notShowView()494 public void bothDevicesEstimateIsNotReady_notShowView() { 495 mController.mIsLeftDeviceEstimateReady = false; 496 mController.mIsRightDeviceEstimateReady = false; 497 498 mController.showBothDevicesBatteryPredictionIfNecessary(); 499 500 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left), 501 View.GONE); 502 assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right), 503 View.GONE); 504 } 505 506 @Test showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showCorrectValue()507 public void showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showCorrectValue() { 508 final String leftBatteryPrediction = 509 StringUtil.formatElapsedTime(mContext, 12000000, false, false).toString(); 510 final String rightBatteryPrediction = 511 StringUtil.formatElapsedTime(mContext, 1200000, false, false).toString(); 512 513 mController.showBatteryPredictionIfNecessary(1, 12000000, 514 mLayoutPreference.findViewById(R.id.layout_left)); 515 mController.showBatteryPredictionIfNecessary(1, 1200000, 516 mLayoutPreference.findViewById(R.id.layout_right)); 517 518 assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_left), 519 leftBatteryPrediction); 520 assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_right), 521 rightBatteryPrediction); 522 } 523 524 @Test 525 @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) enablePolishFlag_renameButtonShown()526 public void enablePolishFlag_renameButtonShown() { 527 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 528 .thenReturn("true".getBytes()); 529 Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>(); 530 when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); 531 532 mController.onStart(); 533 534 ImageButton button = mLayoutPreference.findViewById(R.id.rename_button); 535 assertThat(button.getVisibility()).isEqualTo(View.VISIBLE); 536 } 537 538 @Test 539 @EnableFlags({Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH, 540 com.android.settingslib.flags.Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) temporaryBondDevice_renameButtonNotShown()541 public void temporaryBondDevice_renameButtonNotShown() { 542 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 543 .thenReturn("true".getBytes()); 544 when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) 545 .thenReturn(TEMP_BOND_METADATA.getBytes()); 546 Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>(); 547 when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); 548 549 mController.onStart(); 550 551 ImageButton button = mLayoutPreference.findViewById(R.id.rename_button); 552 assertThat(button.getVisibility()).isEqualTo(View.GONE); 553 } 554 555 @Test 556 @EnableFlags(Flags.FLAG_ENABLE_BATTERY_LEVEL_DISPLAY) enableBatt_budsDisconnected_batteryLevelShown()557 public void enableBatt_budsDisconnected_batteryLevelShown() { 558 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)) 559 .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); 560 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 561 .thenReturn(String.valueOf(false).getBytes()); 562 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)) 563 .thenReturn(String.valueOf(BATTERY_LEVEL_LEFT).getBytes()); 564 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)) 565 .thenReturn(String.valueOf(BATTERY_LEVEL_RIGHT).getBytes()); 566 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)) 567 .thenReturn(String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); 568 when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) 569 .thenReturn("<BATT>true</BATT>".getBytes()); 570 when(mCachedDevice.isConnected()).thenReturn(false); 571 572 mController.refresh(); 573 574 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT); 575 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT); 576 assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN); 577 } 578 579 @Test 580 @DisableFlags(Flags.FLAG_ENABLE_BATTERY_LEVEL_DISPLAY) disableBatt_budsDisconnected_batteryLevelNotShown()581 public void disableBatt_budsDisconnected_batteryLevelNotShown() { 582 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)) 583 .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); 584 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 585 .thenReturn(String.valueOf(false).getBytes()); 586 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)) 587 .thenReturn(String.valueOf(BATTERY_LEVEL_LEFT).getBytes()); 588 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)) 589 .thenReturn(String.valueOf(BATTERY_LEVEL_RIGHT).getBytes()); 590 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)) 591 .thenReturn(String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); 592 when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) 593 .thenReturn("<BATT>true</BATT>".getBytes()); 594 when(mCachedDevice.isConnected()).thenReturn(false); 595 596 mController.refresh(); 597 598 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()) 599 .isNotEqualTo(View.VISIBLE); 600 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()) 601 .isNotEqualTo(View.VISIBLE); 602 assertThat( 603 mLayoutPreference 604 .findViewById(R.id.layout_middle) 605 .findViewById(R.id.bt_battery_summary) 606 .getVisibility()) 607 .isNotEqualTo(View.VISIBLE); 608 assertThat( 609 mLayoutPreference 610 .findViewById(R.id.layout_middle) 611 .findViewById(R.id.bt_battery_icon) 612 .getVisibility()) 613 .isNotEqualTo(View.VISIBLE); 614 } 615 616 @Test 617 @EnableFlags(Flags.FLAG_ENABLE_BATTERY_LEVEL_DISPLAY) disableFastPairBatt_budsDisconnected_batteryLevelNotShown()618 public void disableFastPairBatt_budsDisconnected_batteryLevelNotShown() { 619 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)) 620 .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); 621 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) 622 .thenReturn(String.valueOf(false).getBytes()); 623 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)) 624 .thenReturn(String.valueOf(BATTERY_LEVEL_LEFT).getBytes()); 625 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)) 626 .thenReturn(String.valueOf(BATTERY_LEVEL_RIGHT).getBytes()); 627 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)) 628 .thenReturn(String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); 629 when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) 630 .thenReturn("<BATT>false</BATT>".getBytes()); 631 when(mCachedDevice.isConnected()).thenReturn(false); 632 633 mController.refresh(); 634 635 assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()) 636 .isNotEqualTo(View.VISIBLE); 637 assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()) 638 .isNotEqualTo(View.VISIBLE); 639 assertThat( 640 mLayoutPreference 641 .findViewById(R.id.layout_middle) 642 .findViewById(R.id.bt_battery_summary) 643 .getVisibility()) 644 .isNotEqualTo(View.VISIBLE); 645 assertThat( 646 mLayoutPreference 647 .findViewById(R.id.layout_middle) 648 .findViewById(R.id.bt_battery_icon) 649 .getVisibility()) 650 .isNotEqualTo(View.VISIBLE); 651 } 652 assertBatteryPredictionVisible(LinearLayout linearLayout, int visible)653 private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) { 654 final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction); 655 assertThat(textView.getVisibility()).isEqualTo(visible); 656 } 657 assertBatteryPrediction(LinearLayout linearLayout, String prediction)658 private void assertBatteryPrediction(LinearLayout linearLayout, String prediction) { 659 final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction); 660 assertThat(textView.getText().toString()).isEqualTo(prediction); 661 } 662 assertBatteryLevel(LinearLayout linearLayout, int batteryLevel)663 private void assertBatteryLevel(LinearLayout linearLayout, int batteryLevel) { 664 final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary); 665 assertThat(textView.getText().toString()).isEqualTo( 666 com.android.settings.Utils.formatPercentage(batteryLevel)); 667 if (Flags.enableBluetoothDeviceDetailsPolish()) { 668 final ProgressBar bar = linearLayout.findViewById(R.id.battery_ring); 669 assertThat(bar.getProgress()).isEqualTo(batteryLevel); 670 } 671 } 672 assertBatteryIcon(LinearLayout linearLayout, int resId, boolean charging)673 private void assertBatteryIcon(LinearLayout linearLayout, int resId, boolean charging) { 674 final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon); 675 if (charging) { 676 assertThat(imageView.getContentDescription().toString()) 677 .isEqualTo(mContext.getString(R.string.device_details_battery_charging)); 678 } else { 679 assertThat(imageView.getContentDescription().toString()) 680 .isEqualTo(mContext.getString(R.string.device_details_battery)); 681 } 682 assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId()).isEqualTo(resId); 683 } 684 } 685