1 /*
2  * Copyright (C) 2016 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.notification;
18 
19 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.Mockito.doReturn;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.when;
27 
28 import android.app.PendingIntent;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.media.AudioManager;
32 import android.media.session.MediaController;
33 import android.net.Uri;
34 import android.platform.test.annotations.RequiresFlagsDisabled;
35 import android.platform.test.annotations.RequiresFlagsEnabled;
36 import android.platform.test.flag.junit.CheckFlagsRule;
37 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
38 
39 import androidx.slice.builders.SliceAction;
40 
41 import com.android.settings.media.MediaOutputIndicatorWorker;
42 import com.android.settings.slices.SliceBackgroundWorker;
43 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
44 import com.android.settingslib.flags.Flags;
45 import com.android.settingslib.media.BluetoothMediaDevice;
46 import com.android.settingslib.media.MediaDevice;
47 import com.android.settingslib.media.MediaOutputConstants;
48 
49 import org.junit.Before;
50 import org.junit.Rule;
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 import org.robolectric.annotation.Implementation;
59 import org.robolectric.annotation.Implements;
60 
61 @RunWith(RobolectricTestRunner.class)
62 @Config(shadows = MediaVolumePreferenceControllerTest.ShadowSliceBackgroundWorker.class)
63 public class MediaVolumePreferenceControllerTest {
64 
65     private static final String ACTION_LAUNCH_BROADCAST_DIALOG =
66             "android.settings.MEDIA_BROADCAST_DIALOG";
67     private static MediaOutputIndicatorWorker sMediaOutputIndicatorWorker;
68 
69     @Rule
70     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
71 
72     private MediaVolumePreferenceController mController;
73 
74     private Context mContext;
75 
76     @Mock private MediaController mMediaController;
77     @Mock private MediaDevice mDevice1;
78     @Mock private MediaDevice mDevice2;
79 
80     @Before
setUp()81     public void setUp() {
82         MockitoAnnotations.initMocks(this);
83 
84         mContext = RuntimeEnvironment.application;
85         mController = new MediaVolumePreferenceController(mContext);
86         sMediaOutputIndicatorWorker =
87                 spy(new MediaOutputIndicatorWorker(mContext, VOLUME_MEDIA_URI));
88         when(mDevice1.isBLEDevice()).thenReturn(true);
89         when(mDevice2.isBLEDevice()).thenReturn(false);
90     }
91 
92     @Test
isAvailable_byDefault_isTrue()93     public void isAvailable_byDefault_isTrue() {
94         assertThat(mController.isAvailable()).isTrue();
95     }
96 
97     @Test
98     @Config(qualifiers = "mcc999")
isAvailable_whenNotVisible_isFalse()99     public void isAvailable_whenNotVisible_isFalse() {
100         assertThat(mController.isAvailable()).isFalse();
101     }
102 
103     @Test
getAudioStream_shouldReturnMusic()104     public void getAudioStream_shouldReturnMusic() {
105         assertThat(mController.getAudioStream()).isEqualTo(AudioManager.STREAM_MUSIC);
106     }
107 
108     @Test
isSliceableCorrectKey_returnsTrue()109     public void isSliceableCorrectKey_returnsTrue() {
110         final MediaVolumePreferenceController controller =
111                 new MediaVolumePreferenceController(mContext);
112         assertThat(controller.isSliceable()).isTrue();
113     }
114 
115     @Test
isPublicSlice_returnTrue()116     public void isPublicSlice_returnTrue() {
117         assertThat(mController.isPublicSlice()).isTrue();
118     }
119 
120     @Test
121     @RequiresFlagsDisabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_flagOff_returnsFalse()122     public void isSupportEndItem_flagOff_returnsFalse() {
123         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
124         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
125         doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
126 
127         assertThat(mController.isSupportEndItem()).isFalse();
128     }
129 
130     @Test
131     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_withBleDevice_returnsTrue()132     public void isSupportEndItem_withBleDevice_returnsTrue() {
133         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
134         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
135         doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
136 
137         assertThat(mController.isSupportEndItem()).isTrue();
138     }
139 
140     @Test
141     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_notSupportedBroadcast_returnsFalse()142     public void isSupportEndItem_notSupportedBroadcast_returnsFalse() {
143         doReturn(false).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
144         doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
145 
146         assertThat(mController.isSupportEndItem()).isFalse();
147     }
148 
149     @Test
150     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_withNonBleDevice_returnsFalse()151     public void isSupportEndItem_withNonBleDevice_returnsFalse() {
152         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
153         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
154         doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
155 
156         assertThat(mController.isSupportEndItem()).isFalse();
157     }
158 
159     @Test
160     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_deviceIsBroadcastingAndConnectedToNonBleDevice_returnsTrue()161     public void isSupportEndItem_deviceIsBroadcastingAndConnectedToNonBleDevice_returnsTrue() {
162         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
163         doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
164         doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
165 
166         assertThat(mController.isSupportEndItem()).isTrue();
167     }
168 
169     @Test
170     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
isSupportEndItem_deviceIsNotBroadcastingAndConnectedToNonBleDevice_returnsFalse()171     public void isSupportEndItem_deviceIsNotBroadcastingAndConnectedToNonBleDevice_returnsFalse() {
172         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
173         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
174         doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
175 
176         assertThat(mController.isSupportEndItem()).isFalse();
177     }
178 
179     @Test
180     @RequiresFlagsDisabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
getSliceEndItem_flagOff_getsNullSliceAction()181     public void getSliceEndItem_flagOff_getsNullSliceAction() {
182         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
183         doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
184         doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
185 
186         final SliceAction sliceAction = mController.getSliceEndItem(mContext);
187 
188         assertThat(sliceAction).isNull();
189     }
190 
191     @Test
192     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
getSliceEndItem_NotSupportEndItem_getsNullSliceAction()193     public void getSliceEndItem_NotSupportEndItem_getsNullSliceAction() {
194         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
195         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
196         doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
197 
198         final SliceAction sliceAction = mController.getSliceEndItem(mContext);
199 
200         assertThat(sliceAction).isNull();
201     }
202 
203     @Test
204     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
getSliceEndItem_deviceIsBroadcasting_getsBroadcastIntent()205     public void getSliceEndItem_deviceIsBroadcasting_getsBroadcastIntent() {
206         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
207         doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
208         doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
209         doReturn(mMediaController)
210                 .when(sMediaOutputIndicatorWorker)
211                 .getActiveLocalMediaController();
212 
213         final SliceAction sliceAction = mController.getSliceEndItem(mContext);
214 
215         final PendingIntent endItemPendingIntent = sliceAction.getAction();
216         final PendingIntent expectedToggleIntent =
217                 getBroadcastIntent(
218                         MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
219         assertThat(endItemPendingIntent).isEqualTo(expectedToggleIntent);
220     }
221 
222     @Test
223     @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
getSliceEndItem_deviceIsNotBroadcasting_getsActivityIntent()224     public void getSliceEndItem_deviceIsNotBroadcasting_getsActivityIntent() {
225         final MediaDevice device = mock(BluetoothMediaDevice.class);
226         final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
227         when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
228         when(device.isBLEDevice()).thenReturn(true);
229         doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
230         doReturn(device).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
231         doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
232         doReturn(mMediaController)
233                 .when(sMediaOutputIndicatorWorker)
234                 .getActiveLocalMediaController();
235 
236         final SliceAction sliceAction = mController.getSliceEndItem(mContext);
237 
238         final PendingIntent endItemPendingIntent = sliceAction.getAction();
239         final PendingIntent expectedPendingIntent =
240                 getActivityIntent(ACTION_LAUNCH_BROADCAST_DIALOG);
241         assertThat(endItemPendingIntent).isEqualTo(expectedPendingIntent);
242     }
243 
244     @Implements(SliceBackgroundWorker.class)
245     public static class ShadowSliceBackgroundWorker {
246 
247         @Implementation
getInstance(Uri uri)248         public static SliceBackgroundWorker getInstance(Uri uri) {
249             return sMediaOutputIndicatorWorker;
250         }
251     }
252 
getBroadcastIntent(String action)253     private PendingIntent getBroadcastIntent(String action) {
254         final Intent intent = new Intent(action);
255         intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME);
256         return PendingIntent.getBroadcast(
257                 mContext,
258                 0 /* requestCode */,
259                 intent,
260                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
261     }
262 
getActivityIntent(String action)263     private PendingIntent getActivityIntent(String action) {
264         final Intent intent = new Intent(action);
265         return PendingIntent.getActivity(
266                 mContext,
267                 0 /* requestCode */,
268                 intent,
269                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
270     }
271 }
272