1 /*
2  * Copyright (C) 2023 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.accessibility;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.isNull;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.bluetooth.BluetoothDevice;
30 import android.content.Context;
31 import android.media.AudioAttributes;
32 import android.media.AudioDeviceAttributes;
33 import android.media.AudioDeviceInfo;
34 import android.media.AudioManager;
35 import android.media.audiopolicy.AudioProductStrategy;
36 
37 import androidx.preference.ListPreference;
38 import androidx.test.core.app.ApplicationProvider;
39 
40 import com.android.settings.R;
41 import com.android.settings.bluetooth.Utils;
42 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
43 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
44 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
45 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
46 import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants;
47 import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper;
48 import com.android.settingslib.bluetooth.LocalBluetoothManager;
49 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
50 
51 import org.junit.Before;
52 import org.junit.Rule;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.mockito.Mock;
56 import org.mockito.Spy;
57 import org.mockito.junit.MockitoJUnit;
58 import org.mockito.junit.MockitoRule;
59 import org.robolectric.RobolectricTestRunner;
60 import org.robolectric.annotation.Config;
61 
62 import java.util.List;
63 
64 /** Tests for {@link HearingDeviceAudioRoutingBasePreferenceController}. */
65 @RunWith(RobolectricTestRunner.class)
66 @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
67 public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
68 
69     @Rule
70     public MockitoRule mMockitoRule = MockitoJUnit.rule();
71 
72     @Spy
73     private final Context mContext = ApplicationProvider.getApplicationContext();
74     private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
75     private final ListPreference mListPreference = new ListPreference(mContext);
76 
77     @Mock
78     private LocalBluetoothManager mLocalBluetoothManager;
79     @Mock
80     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
81     @Mock
82     private CachedBluetoothDeviceManager mCachedDeviceManager;
83     @Mock
84     private AudioProductStrategy mAudioProductStrategyMedia;
85     @Mock
86     private CachedBluetoothDevice mCachedBluetoothDevice;
87     @Mock
88     private BluetoothDevice mBluetoothDevice;
89     @Spy
90     private HearingAidAudioRoutingHelper mAudioRoutingHelper =
91             new HearingAidAudioRoutingHelper(mContext);
92     @Mock
93     private HearingAidHelper mHearingAidHelper;
94     private TestHearingDeviceAudioRoutingBasePreferenceController mController;
95 
96     @Before
setUp()97     public void setUp() {
98         final AudioDeviceAttributes hearingDeviceAttribute = new AudioDeviceAttributes(
99                 AudioDeviceAttributes.ROLE_OUTPUT,
100                 AudioDeviceInfo.TYPE_HEARING_AID,
101                 TEST_DEVICE_ADDRESS);
102 
103         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
104         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
105         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
106         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
107         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
108         when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ADDRESS);
109         when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
110         doReturn(hearingDeviceAttribute).when(
111                 mAudioRoutingHelper).getMatchedHearingDeviceAttributes(any());
112         when(mAudioProductStrategyMedia.getAudioAttributesForLegacyStreamType(
113                 AudioManager.STREAM_MUSIC)).thenReturn((new AudioAttributes.Builder()).build());
114         when(mAudioRoutingHelper.getAudioProductStrategies()).thenReturn(
115                 List.of(mAudioProductStrategyMedia));
116 
117         mController = new TestHearingDeviceAudioRoutingBasePreferenceController(mContext,
118                 "test_key",
119                 mAudioRoutingHelper, mHearingAidHelper);
120         mListPreference.setEntries(R.array.bluetooth_audio_routing_titles);
121         mListPreference.setEntryValues(R.array.bluetooth_audio_routing_values);
122         mListPreference.setSummary("%s");
123     }
124 
125     @Test
updateState_routingValueAuto_expectedSummary()126     public void updateState_routingValueAuto_expectedSummary() {
127         mController.saveRoutingValue(mContext, HearingAidAudioRoutingConstants.RoutingValue.AUTO);
128 
129         mController.updateState(mListPreference);
130 
131         assertThat(mListPreference.getSummary().toString()).isEqualTo(
132                 mListPreference.getEntries()[0].toString());
133     }
134 
135     @Test
onPreferenceChange_routingValueHearingDevice_restoreSameValue()136     public void onPreferenceChange_routingValueHearingDevice_restoreSameValue() {
137         mController.onPreferenceChange(mListPreference, String.valueOf(
138                 HearingAidAudioRoutingConstants.RoutingValue.HEARING_DEVICE));
139 
140         assertThat(mController.restoreRoutingValue(mContext)).isEqualTo(
141                 HearingAidAudioRoutingConstants.RoutingValue.HEARING_DEVICE);
142     }
143 
144     @Test
onPreferenceChange_noMatchedDeviceAttributes_notCallSetStrategies()145     public void onPreferenceChange_noMatchedDeviceAttributes_notCallSetStrategies() {
146         when(mAudioRoutingHelper.getMatchedHearingDeviceAttributes(any())).thenReturn(null);
147 
148         verify(mAudioRoutingHelper, never()).setPreferredDeviceRoutingStrategies(any(), isNull(),
149                 anyInt());
150     }
151 
152     private static class TestHearingDeviceAudioRoutingBasePreferenceController extends
153             HearingDeviceAudioRoutingBasePreferenceController {
154 
155         private static int sSavedRoutingValue;
156 
TestHearingDeviceAudioRoutingBasePreferenceController(Context context, String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper, HearingAidHelper hearingAidHelper)157         TestHearingDeviceAudioRoutingBasePreferenceController(Context context,
158                 String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper,
159                 HearingAidHelper hearingAidHelper) {
160             super(context, preferenceKey, audioRoutingHelper, hearingAidHelper);
161         }
162 
163         @Override
getSupportedAttributeList()164         protected int[] getSupportedAttributeList() {
165             return new int[]{AudioAttributes.USAGE_MEDIA};
166         }
167 
168         @Override
saveRoutingValue(Context context, int routingValue)169         protected void saveRoutingValue(Context context, int routingValue) {
170             sSavedRoutingValue = routingValue;
171         }
172 
173         @Override
restoreRoutingValue(Context context)174         protected int restoreRoutingValue(Context context) {
175             return sSavedRoutingValue;
176         }
177     }
178 }
179