• 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.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