• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.provider.DeviceConfig;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.widget.ImageView;
35 import android.widget.LinearLayout;
36 import android.widget.TextView;
37 
38 import com.android.settings.R;
39 import com.android.settings.core.BasePreferenceController;
40 import com.android.settings.core.SettingsUIDeviceConfig;
41 import com.android.settings.fuelgauge.BatteryMeterView;
42 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
43 import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
44 import com.android.settingslib.bluetooth.BluetoothUtils;
45 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
46 import com.android.settingslib.utils.StringUtil;
47 import com.android.settingslib.widget.LayoutPreference;
48 
49 import org.junit.Before;
50 import org.junit.Ignore;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 import org.robolectric.RobolectricTestRunner;
56 import org.robolectric.RuntimeEnvironment;
57 import org.robolectric.annotation.Config;
58 
59 import java.util.HashSet;
60 import java.util.Set;
61 
62 @RunWith(RobolectricTestRunner.class)
63 @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class})
64 public class AdvancedBluetoothDetailsHeaderControllerTest {
65     private static final int BATTERY_LEVEL_MAIN = 30;
66     private static final int BATTERY_LEVEL_LEFT = 25;
67     private static final int BATTERY_LEVEL_RIGHT = 45;
68     private static final int LOW_BATTERY_LEVEL = 15;
69     private static final int CASE_LOW_BATTERY_LEVEL = 19;
70     private static final int LOW_BATTERY_LEVEL_THRESHOLD = 15;
71     private static final int BATTERY_LEVEL_5 = 5;
72     private static final int BATTERY_LEVEL_50 = 50;
73     private static final String ICON_URI = "content://test.provider/icon.png";
74     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
75     private static final String DEVICE_SUMMARY = "test summary";
76 
77     private Context mContext;
78 
79     @Mock
80     private BluetoothDevice mBluetoothDevice;
81     @Mock
82     private Bitmap mBitmap;
83     @Mock
84     private ImageView mImageView;
85     @Mock
86     private CachedBluetoothDevice mCachedDevice;
87     @Mock
88     private BluetoothAdapter mBluetoothAdapter;
89     private AdvancedBluetoothDetailsHeaderController mController;
90     private LayoutPreference mLayoutPreference;
91 
92     @Before
setUp()93     public void setUp() {
94         MockitoAnnotations.initMocks(this);
95 
96         mContext = RuntimeEnvironment.application;
97         mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key");
98         when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
99         mController.init(mCachedDevice);
100         mLayoutPreference = new LayoutPreference(mContext,
101                 LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null));
102         mController.mLayoutPreference = mLayoutPreference;
103         mController.mBluetoothAdapter = mBluetoothAdapter;
104         when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
105         when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
106         when(mCachedDevice.getIdentityAddress()).thenReturn(MAC_ADDRESS);
107     }
108 
109     @Test
createBatteryIcon_hasCorrectInfo()110     public void createBatteryIcon_hasCorrectInfo() {
111         final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN,
112                 true /* charging */);
113         assertThat(drawable).isInstanceOf(BatteryMeterView.BatteryMeterDrawable.class);
114 
115         final BatteryMeterView.BatteryMeterDrawable iconDrawable =
116                 (BatteryMeterView.BatteryMeterDrawable) drawable;
117         assertThat(iconDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL_MAIN);
118         assertThat(iconDrawable.getCharging()).isTrue();
119     }
120 
121     @Test
refresh_connectedWatch_behaveAsExpected()122     public void refresh_connectedWatch_behaveAsExpected() {
123         when(mBluetoothDevice.getMetadata(
124                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
125                 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
126         when(mBluetoothDevice.getMetadata(
127                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
128                 String.valueOf(false).getBytes());
129         when(mCachedDevice.isConnected()).thenReturn(true);
130 
131         mController.refresh();
132 
133         assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
134                 View.GONE);
135         assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
136                 View.GONE);
137         assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
138                 View.VISIBLE);
139         // TODO (b/188954766) : clarify settings design
140     }
141 
142     @Test
refresh_connectedWatch_unknownBatteryLevel_shouldNotShowBatteryLevel()143     public void refresh_connectedWatch_unknownBatteryLevel_shouldNotShowBatteryLevel() {
144         when(mBluetoothDevice.getMetadata(
145                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
146                 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
147         when(mBluetoothDevice.getMetadata(
148                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
149                 String.valueOf(false).getBytes());
150         when(mBluetoothDevice.getMetadata(
151                 BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
152                 String.valueOf(BluetoothUtils.META_INT_ERROR).getBytes());
153         when(mBluetoothDevice.getBatteryLevel()).thenReturn(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
154         when(mCachedDevice.isConnected()).thenReturn(true);
155 
156         mController.refresh();
157 
158         assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
159                 View.GONE);
160         assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
161                 View.GONE);
162         assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
163                 View.VISIBLE);
164         assertThat(mLayoutPreference.findViewById(R.id.layout_middle)
165                 .requireViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(View.GONE);
166     }
167 
168     @Test
refresh_connectedStylus_behaveAsExpected()169     public void refresh_connectedStylus_behaveAsExpected() {
170         when(mBluetoothDevice.getMetadata(
171                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
172                 BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
173         when(mBluetoothDevice.getMetadata(
174                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
175                 String.valueOf(false).getBytes());
176         when(mCachedDevice.isConnected()).thenReturn(true);
177 
178         mController.refresh();
179 
180         assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
181                 View.GONE);
182         assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
183                 View.GONE);
184         assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
185                 View.VISIBLE);
186     }
187 
188     @Test
refresh_connectedUnknownType_behaveAsExpected()189     public void refresh_connectedUnknownType_behaveAsExpected() {
190         when(mBluetoothDevice.getMetadata(
191                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
192                 "UNKNOWN_TYPE".getBytes());
193         when(mBluetoothDevice.getMetadata(
194                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
195                 String.valueOf(false).getBytes());
196         when(mCachedDevice.isConnected()).thenReturn(true);
197 
198         mController.refresh();
199 
200         assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
201                 View.GONE);
202         assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
203                 View.GONE);
204         assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
205                 View.VISIBLE);
206     }
207 
208     @Test
refresh_connectedUntetheredHeadset_behaveAsExpected()209     public void refresh_connectedUntetheredHeadset_behaveAsExpected() {
210         when(mBluetoothDevice.getMetadata(
211                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
212                 BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
213         when(mBluetoothDevice.getMetadata(
214                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
215                 String.valueOf(false).getBytes());
216         when(mBluetoothDevice.getMetadata(
217                 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
218                 String.valueOf(BATTERY_LEVEL_LEFT).getBytes());
219         when(mBluetoothDevice.getMetadata(
220                 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
221                 String.valueOf(BATTERY_LEVEL_RIGHT).getBytes());
222         when(mBluetoothDevice.getMetadata(
223                 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
224                 String.valueOf(BATTERY_LEVEL_MAIN).getBytes());
225         when(mCachedDevice.isConnected()).thenReturn(true);
226 
227         mController.refresh();
228 
229         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
230         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT);
231         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
232     }
233 
234     @Test
refresh_connected_updateCorrectInfo()235     public void refresh_connected_updateCorrectInfo() {
236         when(mBluetoothDevice.getMetadata(
237                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
238                 String.valueOf(true).getBytes());
239         when(mBluetoothDevice.getMetadata(
240                 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
241                 String.valueOf(BATTERY_LEVEL_LEFT).getBytes());
242         when(mBluetoothDevice.getMetadata(
243                 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
244                 String.valueOf(BATTERY_LEVEL_RIGHT).getBytes());
245         when(mBluetoothDevice.getMetadata(
246                 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
247                 String.valueOf(BATTERY_LEVEL_MAIN).getBytes());
248 
249         when(mCachedDevice.isConnected()).thenReturn(true);
250         mController.refresh();
251 
252         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
253         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT);
254         assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
255     }
256 
257     @Test
refresh_disconnected_updateCorrectInfo()258     public void refresh_disconnected_updateCorrectInfo() {
259         when(mCachedDevice.isConnected()).thenReturn(false);
260 
261         mController.refresh();
262 
263         final LinearLayout layout = mLayoutPreference.findViewById(R.id.layout_middle);
264 
265         assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
266                 View.GONE);
267         assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
268                 View.GONE);
269         assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE);
270         assertThat(layout.findViewById(R.id.header_title).getVisibility()).isEqualTo(View.GONE);
271         assertThat(layout.findViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(
272                 View.GONE);
273         assertThat(layout.findViewById(R.id.bt_battery_icon).getVisibility()).isEqualTo(View.GONE);
274         assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE);
275     }
276 
277     @Ignore
278     @Test
refresh_connectedWatch_checkSummary()279     public void refresh_connectedWatch_checkSummary() {
280         when(mBluetoothDevice.getMetadata(
281                 BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
282                 BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
283         when(mCachedDevice.isConnected()).thenReturn(true);
284         when(mCachedDevice.getConnectionSummary(/* shortSummary= */ true))
285                 .thenReturn(DEVICE_SUMMARY);
286 
287         mController.refresh();
288 
289         assertThat(((TextView) (mLayoutPreference.findViewById(R.id.entity_header_summary)))
290                 .getText()).isEqualTo(DEVICE_SUMMARY);
291     }
292 
293     @Test
refresh_withLowBatteryAndUncharged_showAlertIcon()294     public void refresh_withLowBatteryAndUncharged_showAlertIcon() {
295         when(mBluetoothDevice.getMetadata(
296                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
297                 String.valueOf(true).getBytes());
298         when(mBluetoothDevice.getMetadata(
299                 BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
300                 String.valueOf(LOW_BATTERY_LEVEL).getBytes());
301         when(mBluetoothDevice.getMetadata(
302                 BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
303                 String.valueOf(LOW_BATTERY_LEVEL).getBytes());
304         when(mBluetoothDevice.getMetadata(
305                 BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
306                 String.valueOf(CASE_LOW_BATTERY_LEVEL).getBytes());
307         when(mBluetoothDevice.getMetadata(
308                 BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING)).thenReturn(
309                 String.valueOf(false).getBytes());
310         when(mBluetoothDevice.getMetadata(
311                 BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING)).thenReturn(
312                 String.valueOf(true).getBytes());
313         when(mBluetoothDevice.getMetadata(
314                 BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING)).thenReturn(
315                 String.valueOf(false).getBytes());
316         when(mCachedDevice.isConnected()).thenReturn(true);
317 
318         mController.refresh();
319 
320         assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_left),
321                 R.drawable.ic_battery_alert_24dp);
322         assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_right), /* resId= */-1);
323         assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle),
324                 R.drawable.ic_battery_alert_24dp);
325     }
326 
327     @Test
getAvailabilityStatus_untetheredHeadsetWithConfigOn_returnAvailable()328     public void getAvailabilityStatus_untetheredHeadsetWithConfigOn_returnAvailable() {
329         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
330                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
331         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
332                 .thenReturn("true".getBytes());
333 
334         assertThat(mController.getAvailabilityStatus()).isEqualTo(
335                 BasePreferenceController.AVAILABLE);
336     }
337 
338     @Test
getAvailabilityStatus_untetheredHeadsetWithConfigOff_returnUnavailable()339     public void getAvailabilityStatus_untetheredHeadsetWithConfigOff_returnUnavailable() {
340         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
341                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
342         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
343                 .thenReturn("true".getBytes());
344 
345         assertThat(mController.getAvailabilityStatus()).isEqualTo(
346                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
347     }
348 
349     @Test
getAvailabilityStatus_notUntetheredHeadsetWithConfigOn_returnUnavailable()350     public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOn_returnUnavailable() {
351         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
352                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
353         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
354                 .thenReturn("false".getBytes());
355 
356         assertThat(mController.getAvailabilityStatus()).isEqualTo(
357                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
358     }
359 
360     @Test
getAvailabilityStatus_notUntetheredHeadsetWithConfigOff_returnUnavailable()361     public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOff_returnUnavailable() {
362         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
363                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
364         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
365                 .thenReturn("false".getBytes());
366 
367         assertThat(mController.getAvailabilityStatus()).isEqualTo(
368                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
369     }
370 
371     @Test
updateIcon_existInCache_setImageBitmap()372     public void updateIcon_existInCache_setImageBitmap() {
373         mController.mIconCache.put(ICON_URI, mBitmap);
374 
375         mController.updateIcon(mImageView, ICON_URI);
376 
377         verify(mImageView).setImageBitmap(mBitmap);
378     }
379 
380     @Test
onStart_isAvailable_registerCallback()381     public void onStart_isAvailable_registerCallback() {
382         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
383                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
384         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
385                 .thenReturn("true".getBytes());
386         Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
387         when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
388 
389         mController.onStart();
390 
391         verify(mCachedDevice).registerCallback(mController);
392         verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice,
393                 mContext.getMainExecutor(), mController.mMetadataListener);
394     }
395 
396     @Test
onStart_notAvailable_notNeedToRegisterCallback()397     public void onStart_notAvailable_notNeedToRegisterCallback() {
398         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
399                 .thenReturn("false".getBytes());
400 
401         mController.onStart();
402 
403         verify(mCachedDevice, never()).registerCallback(mController);
404         verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice,
405                 mContext.getMainExecutor(), mController.mMetadataListener);
406     }
407 
408     @Test
onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback()409     public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() {
410         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
411                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
412         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
413                 .thenReturn("true".getBytes());
414         when(mCachedDevice.getDevice()).thenReturn(null);
415         Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
416         when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
417 
418         mController.onStart();
419 
420         verify(mCachedDevice, never()).registerCallback(mController);
421         verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice,
422                 mContext.getMainExecutor(), mController.mMetadataListener);
423     }
424 
425     @Test
onStop_availableAndHasBluetoothDevice_unregisterCallback()426     public void onStop_availableAndHasBluetoothDevice_unregisterCallback() {
427         onStart_isAvailable_registerCallback();
428 
429         mController.onStop();
430 
431         verify(mCachedDevice).unregisterCallback(mController);
432         verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice,
433                 mController.mMetadataListener);
434     }
435 
436     @Test
onStop_noBluetoothDevice_noNeedToUnregisterCallback()437     public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() {
438         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
439                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
440         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
441                 .thenReturn("true".getBytes());
442         when(mCachedDevice.getDevice()).thenReturn(null);
443 
444         mController.onStart();
445         mController.onStop();
446 
447         verify(mCachedDevice, never()).unregisterCallback(mController);
448         verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice,
449                 mController.mMetadataListener);
450     }
451 
452     @Test
onDestroy_recycleBitmap()453     public void onDestroy_recycleBitmap() {
454         mController.mIconCache.put(ICON_URI, mBitmap);
455 
456         mController.onDestroy();
457 
458         assertThat(mController.mIconCache).isEmpty();
459         verify(mBitmap).recycle();
460     }
461 
462     @Test
estimateReadyIsBothAvailable_showsView()463     public void estimateReadyIsBothAvailable_showsView() {
464         mController.mIsLeftDeviceEstimateReady = true;
465         mController.mIsRightDeviceEstimateReady = true;
466 
467         mController.showBothDevicesBatteryPredictionIfNecessary();
468 
469         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
470                 View.VISIBLE);
471         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
472                 View.VISIBLE);
473     }
474 
475     @Test
leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView()476     public void leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView() {
477         mController.mIsLeftDeviceEstimateReady = true;
478         mController.mIsRightDeviceEstimateReady = false;
479 
480         mController.showBothDevicesBatteryPredictionIfNecessary();
481 
482         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
483                 View.GONE);
484         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
485                 View.GONE);
486     }
487 
488     @Test
leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView()489     public void leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView() {
490         mController.mIsLeftDeviceEstimateReady = false;
491         mController.mIsRightDeviceEstimateReady = true;
492 
493         mController.showBothDevicesBatteryPredictionIfNecessary();
494 
495         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
496                 View.GONE);
497         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
498                 View.GONE);
499     }
500 
501     @Test
bothDevicesEstimateIsNotReady_notShowView()502     public void bothDevicesEstimateIsNotReady_notShowView() {
503         mController.mIsLeftDeviceEstimateReady = false;
504         mController.mIsRightDeviceEstimateReady = false;
505 
506         mController.showBothDevicesBatteryPredictionIfNecessary();
507 
508         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
509                 View.GONE);
510         assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
511                 View.GONE);
512     }
513 
514     @Test
showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showCorrectValue()515     public void showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showCorrectValue() {
516         final String leftBatteryPrediction =
517                 StringUtil.formatElapsedTime(mContext, 12000000, false, false).toString();
518         final String rightBatteryPrediction =
519                 StringUtil.formatElapsedTime(mContext, 1200000, false, false).toString();
520 
521         mController.showBatteryPredictionIfNecessary(1, 12000000,
522                 mLayoutPreference.findViewById(R.id.layout_left));
523         mController.showBatteryPredictionIfNecessary(1, 1200000,
524                 mLayoutPreference.findViewById(R.id.layout_right));
525 
526         assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_left),
527                 leftBatteryPrediction);
528         assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_right),
529                 rightBatteryPrediction);
530     }
531 
assertBatteryPredictionVisible(LinearLayout linearLayout, int visible)532     private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) {
533         final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
534         assertThat(textView.getVisibility()).isEqualTo(visible);
535     }
536 
assertBatteryPrediction(LinearLayout linearLayout, String prediction)537     private void assertBatteryPrediction(LinearLayout linearLayout, String prediction) {
538         final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
539         assertThat(textView.getText().toString()).isEqualTo(prediction);
540     }
541 
assertBatteryLevel(LinearLayout linearLayout, int batteryLevel)542     private void assertBatteryLevel(LinearLayout linearLayout, int batteryLevel) {
543         final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
544         assertThat(textView.getText().toString()).isEqualTo(
545                 com.android.settings.Utils.formatPercentage(batteryLevel));
546     }
547 
assertBatteryIcon(LinearLayout linearLayout, int resId)548     private void assertBatteryIcon(LinearLayout linearLayout, int resId) {
549         final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon);
550         assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId())
551                 .isEqualTo(resId);
552     }
553 
554 }
555