• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.sound;
18 
19 import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
20 import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
21 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
22 import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.spy;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.bluetooth.BluetoothAdapter;
33 import android.bluetooth.BluetoothDevice;
34 import android.bluetooth.BluetoothManager;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.media.AudioManager;
38 
39 import androidx.preference.Preference;
40 import androidx.preference.PreferenceManager;
41 import androidx.preference.PreferenceScreen;
42 
43 import com.android.settings.R;
44 import com.android.settings.bluetooth.Utils;
45 import com.android.settings.testutils.shadow.ShadowAudioManager;
46 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
47 import com.android.settingslib.bluetooth.A2dpProfile;
48 import com.android.settingslib.bluetooth.BluetoothEventManager;
49 import com.android.settingslib.bluetooth.HearingAidProfile;
50 import com.android.settingslib.bluetooth.LocalBluetoothManager;
51 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
52 import com.android.settingslib.media.MediaOutputSliceConstants;
53 
54 import org.junit.After;
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 import org.mockito.ArgumentCaptor;
59 import org.mockito.Mock;
60 import org.mockito.MockitoAnnotations;
61 import org.robolectric.RobolectricTestRunner;
62 import org.robolectric.RuntimeEnvironment;
63 import org.robolectric.annotation.Config;
64 import org.robolectric.shadows.ShadowBluetoothDevice;
65 
66 import java.util.ArrayList;
67 import java.util.List;
68 
69 @RunWith(RobolectricTestRunner.class)
70 @Config(shadows = {
71         ShadowAudioManager.class,
72         ShadowBluetoothUtils.class,
73         ShadowBluetoothDevice.class}
74 )
75 public class MediaOutputPreferenceControllerTest {
76     private static final String TEST_KEY = "Test_Key";
77     private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
78     private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
79     private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
80     private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
81     private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
82     private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
83     private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
84     private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
85 
86     @Mock
87     private LocalBluetoothManager mLocalManager;
88     @Mock
89     private BluetoothEventManager mBluetoothEventManager;
90     @Mock
91     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
92     @Mock
93     private A2dpProfile mA2dpProfile;
94     @Mock
95     private HearingAidProfile mHearingAidProfile;
96     @Mock
97     private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;
98 
99     private Context mContext;
100     private PreferenceScreen mScreen;
101     private Preference mPreference;
102     private AudioManager mAudioManager;
103     private ShadowAudioManager mShadowAudioManager;
104     private BluetoothManager mBluetoothManager;
105     private BluetoothAdapter mBluetoothAdapter;
106     private BluetoothDevice mBluetoothDevice;
107     private BluetoothDevice mSecondBluetoothDevice;
108     private BluetoothDevice mLeftBluetoothHapDevice;
109     private BluetoothDevice mRightBluetoothHapDevice;
110     private LocalBluetoothManager mLocalBluetoothManager;
111     private MediaOutputPreferenceController mController;
112     private List<BluetoothDevice> mProfileConnectedDevices;
113     private List<BluetoothDevice> mHearingAidActiveDevices;
114 
115     @Before
setUp()116     public void setUp() {
117         MockitoAnnotations.initMocks(this);
118         mContext = spy(RuntimeEnvironment.application);
119 
120         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
121         mShadowAudioManager = ShadowAudioManager.getShadow();
122 
123         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager;
124         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
125 
126         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
127         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
128         when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
129         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
130 
131         mBluetoothManager = new BluetoothManager(mContext);
132         mBluetoothAdapter = mBluetoothManager.getAdapter();
133 
134         mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
135         when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
136         when(mBluetoothDevice.isConnected()).thenReturn(true);
137 
138         mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
139         when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
140         when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
141 
142         mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
143         when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
144         when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
145 
146         mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
147         when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
148         when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
149 
150         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
151         mScreen = spy(new PreferenceScreen(mContext, null));
152         mPreference = new Preference(mContext);
153         mProfileConnectedDevices = new ArrayList<>();
154         mHearingAidActiveDevices = new ArrayList<>(2);
155 
156         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
157         when(mScreen.getContext()).thenReturn(mContext);
158         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
159         mScreen.addPreference(mPreference);
160         mController.displayPreference(mScreen);
161         mController.setCallback(mAudioSwitchPreferenceCallback);
162     }
163 
164     @After
tearDown()165     public void tearDown() {
166         ShadowBluetoothUtils.reset();
167     }
168 
169 
170     /**
171      * A2DP Bluetooth device(s) are not connected nor previously connected
172      * Preference should be invisible
173      */
174     @Test
updateState_withoutConnectedBtDevice_preferenceInvisible()175     public void updateState_withoutConnectedBtDevice_preferenceInvisible() {
176         mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
177         mAudioManager.setMode(AudioManager.MODE_NORMAL);
178         mProfileConnectedDevices.clear();
179         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
180         mPreference.setVisible(true);
181 
182         assertThat(mPreference.isVisible()).isTrue();
183         mController.updateState(mPreference);
184         assertThat(mPreference.isVisible()).isFalse();
185     }
186 
187     /**
188      * A2DP Bluetooth device(s) are connected, no matter active or inactive
189      * Preference should be visible
190      */
191     @Test
updateState_withConnectedBtDevice_preferenceVisible()192     public void updateState_withConnectedBtDevice_preferenceVisible() {
193         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
194         mAudioManager.setMode(AudioManager.MODE_NORMAL);
195         mProfileConnectedDevices.clear();
196         mProfileConnectedDevices.add(mBluetoothDevice);
197         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
198         assertThat(mPreference.isVisible()).isFalse();
199 
200         // Without Active Bluetooth Device
201         mController.updateState(mPreference);
202         assertThat(mPreference.isVisible()).isTrue();
203 
204         // With Active Bluetooth Device
205         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
206         mController.updateState(mPreference);
207         assertThat(mPreference.isVisible()).isTrue();
208     }
209 
210     /**
211      * A2DP Bluetooth device(s) are connected, but no device is set as activated
212      * Preference summary should be "This device"
213      */
214     @Test
updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary()215     public void updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary() {
216         mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
217         mAudioManager.setMode(AudioManager.MODE_NORMAL);
218         mProfileConnectedDevices.clear();
219         mProfileConnectedDevices.add(mBluetoothDevice);
220         mProfileConnectedDevices.add(mSecondBluetoothDevice);
221         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
222         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
223 
224         assertThat(mPreference.getSummary()).isNull();
225         mController.updateState(mPreference);
226         assertThat(mPreference.getSummary()).isEqualTo(
227                 mContext.getText(R.string.media_output_default_summary));
228     }
229 
230     /**
231      * A2DP Bluetooth device(s) are connected and active
232      * Preference summary should be device's name
233      */
234     @Test
updateState_withActiveBtDevice_setActivatedDeviceName()235     public void updateState_withActiveBtDevice_setActivatedDeviceName() {
236         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
237         mAudioManager.setMode(AudioManager.MODE_NORMAL);
238         mProfileConnectedDevices.clear();
239         mProfileConnectedDevices.add(mBluetoothDevice);
240         mProfileConnectedDevices.add(mSecondBluetoothDevice);
241         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
242         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
243 
244         assertThat(mPreference.getSummary()).isNull();
245         mController.updateState(mPreference);
246         assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
247     }
248 
249 
250     /**
251      * Hearing Aid device(s) are connected, no matter active or inactive
252      * Preference should be visible
253      */
254     @Test
updateState_withConnectedHADevice_preferenceVisible()255     public void updateState_withConnectedHADevice_preferenceVisible() {
256         mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
257         mAudioManager.setMode(AudioManager.MODE_NORMAL);
258         mHearingAidActiveDevices.clear();
259         mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
260         when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
261         assertThat(mPreference.isVisible()).isFalse();
262 
263         // Without Active Hearing Aid Device
264         mController.updateState(mPreference);
265         assertThat(mPreference.isVisible()).isTrue();
266 
267         // With Active Hearing Aid Device
268         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
269         mController.updateState(mPreference);
270         assertThat(mPreference.isVisible()).isTrue();
271     }
272 
273     /**
274      * Hearing Aid device(s) are connected and active
275      * Preference summary should be device's name
276      */
277     @Test
updateState_withActiveHADevice_setActivatedDeviceName()278     public void updateState_withActiveHADevice_setActivatedDeviceName() {
279         mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
280         mAudioManager.setMode(AudioManager.MODE_NORMAL);
281         mHearingAidActiveDevices.clear();
282         mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
283         when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
284         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
285 
286         assertThat(mPreference.getSummary()).isNull();
287         mController.updateState(mPreference);
288         assertThat(mPreference.getSummary()).isEqualTo(TEST_HAP_DEVICE_NAME_1);
289 
290     }
291 
292     @Test
click_launch_outputSwitcherSlice()293     public void click_launch_outputSwitcherSlice() {
294         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
295         mController.handlePreferenceTreeClick(mPreference);
296         verify(mContext, never()).startActivity(intentCaptor.capture());
297 
298         mPreference.setKey(TEST_KEY);
299         mController.handlePreferenceTreeClick(mPreference);
300         verify(mContext).startActivity(intentCaptor.capture());
301         assertThat(intentCaptor.getValue().getAction())
302                 .isEqualTo(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
303     }
304 
305     /**
306      * Default status
307      * Preference should be invisible
308      * Summary should be default summary
309      */
310     @Test
updateState_shouldSetSummary()311     public void updateState_shouldSetSummary() {
312         mController.updateState(mPreference);
313 
314         assertThat(mPreference.isVisible()).isFalse();
315         assertThat(mPreference.getSummary()).isEqualTo(
316                 mContext.getText(R.string.media_output_default_summary));
317     }
318 
319     /**
320      * During a call
321      * Preference should be invisible
322      * Default string should be "Unavailable during calls"
323      */
324     @Test
updateState_duringACall_shouldSetDefaultSummary()325     public void updateState_duringACall_shouldSetDefaultSummary() {
326         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
327 
328         mController.updateState(mPreference);
329 
330         assertThat(mPreference.isVisible()).isFalse();
331         assertThat(mPreference.getSummary()).isEqualTo(
332                 mContext.getText(R.string.media_out_summary_ongoing_call_state));
333     }
334 
335     @Test
findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice()336     public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
337         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
338         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
339 
340         assertThat(mController.findActiveDevice()).isEqualTo(mBluetoothDevice);
341     }
342 
343     @Test
findActiveDevice_allDevicesNotActive_returnNull()344     public void findActiveDevice_allDevicesNotActive_returnNull() {
345         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
346         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
347 
348         assertThat(mController.findActiveDevice()).isNull();
349     }
350 }
351