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