1 /* 2 * Copyright (C) 2020 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.slices; 18 19 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SLICES_URI; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.content.BroadcastReceiver; 29 import android.content.ContentResolver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.media.AudioManager; 34 import android.net.Uri; 35 36 import com.android.settings.notification.MediaVolumePreferenceController; 37 import com.android.settings.notification.NotificationVolumePreferenceController; 38 import com.android.settings.notification.RingVolumePreferenceController; 39 import com.android.settings.notification.SeparateRingVolumePreferenceController; 40 import com.android.settings.notification.VolumeSeekBarPreferenceController; 41 import com.android.settingslib.SliceBroadcastRelay; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.mockito.Mock; 48 import org.mockito.MockitoAnnotations; 49 import org.robolectric.RobolectricTestRunner; 50 import org.robolectric.RuntimeEnvironment; 51 import org.robolectric.annotation.Config; 52 import org.robolectric.annotation.Implementation; 53 import org.robolectric.annotation.Implements; 54 import org.robolectric.annotation.Resetter; 55 56 @RunWith(RobolectricTestRunner.class) 57 @Config(shadows = VolumeSliceHelperTest.ShadowSliceBroadcastRelay.class) 58 public class VolumeSliceHelperTest { 59 60 @Mock 61 private ContentResolver mResolver; 62 63 private Context mContext; 64 private Intent mIntent; 65 private VolumeSeekBarPreferenceController mMediaController; 66 private VolumeSeekBarPreferenceController mRingController; 67 private VolumeSeekBarPreferenceController mSeparateRingController; 68 private VolumeSeekBarPreferenceController mNotificationController; 69 70 @Before setUp()71 public void setUp() { 72 MockitoAnnotations.initMocks(this); 73 mContext = spy(RuntimeEnvironment.application); 74 when(mContext.getContentResolver()).thenReturn(mResolver); 75 76 mMediaController = new MediaVolumePreferenceController(mContext); 77 mRingController = new RingVolumePreferenceController(mContext); 78 mSeparateRingController = new SeparateRingVolumePreferenceController(mContext); 79 mNotificationController = new NotificationVolumePreferenceController(mContext); 80 81 mIntent = createIntent(AudioManager.VOLUME_CHANGED_ACTION) 82 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1) 83 .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 2) 84 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream()); 85 } 86 87 @After cleanUp()88 public void cleanUp() { 89 ShadowSliceBroadcastRelay.reset(); 90 VolumeSliceHelper.sRegisteredUri.clear(); 91 VolumeSliceHelper.sIntentFilter = null; 92 } 93 94 @Test registerIntentToUri_volumeController_shouldRegisterReceiver()95 public void registerIntentToUri_volumeController_shouldRegisterReceiver() { 96 registerIntentToUri(mMediaController); 97 98 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); 99 assertThat(VolumeSliceHelper.sRegisteredUri) 100 .containsKey((mMediaController.getSliceUri())); 101 } 102 103 @Test registerIntentToUri_doubleVolumeControllers_shouldRegisterReceiverOnce()104 public void registerIntentToUri_doubleVolumeControllers_shouldRegisterReceiverOnce() { 105 registerIntentToUri(mMediaController); 106 107 registerIntentToUri(mRingController); 108 109 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); 110 assertThat(VolumeSliceHelper.sRegisteredUri) 111 .containsKey((mRingController.getSliceUri())); 112 } 113 114 @Test unregisterUri_notFinalUri_shouldNotUnregisterReceiver()115 public void unregisterUri_notFinalUri_shouldNotUnregisterReceiver() { 116 registerIntentToUri(mMediaController); 117 registerIntentToUri(mRingController); 118 119 VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); 120 121 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); 122 assertThat(VolumeSliceHelper.sRegisteredUri) 123 .doesNotContainKey((mMediaController.getSliceUri())); 124 } 125 126 @Test unregisterUri_finalUri_shouldUnregisterReceiver()127 public void unregisterUri_finalUri_shouldUnregisterReceiver() { 128 registerIntentToUri(mMediaController); 129 130 VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); 131 132 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0); 133 assertThat(VolumeSliceHelper.sRegisteredUri) 134 .doesNotContainKey((mMediaController.getSliceUri())); 135 } 136 137 @Test unregisterUri_unregisterTwice_shouldUnregisterReceiverOnce()138 public void unregisterUri_unregisterTwice_shouldUnregisterReceiverOnce() { 139 registerIntentToUri(mMediaController); 140 141 VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); 142 VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); 143 144 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0); 145 } 146 147 @Test unregisterUri_notRegistered_shouldNotUnregisterReceiver()148 public void unregisterUri_notRegistered_shouldNotUnregisterReceiver() { 149 registerIntentToUri(mMediaController); 150 151 VolumeSliceHelper.unregisterUri(mContext, mRingController.getSliceUri()); 152 153 assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); 154 assertThat(VolumeSliceHelper.sRegisteredUri) 155 .containsKey((mMediaController.getSliceUri())); 156 } 157 158 @Test onReceive_audioStreamRegistered_shouldNotifyChange()159 public void onReceive_audioStreamRegistered_shouldNotifyChange() { 160 registerIntentToUri(mMediaController); 161 162 VolumeSliceHelper.onReceive(mContext, mIntent); 163 164 verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); 165 } 166 167 @Test onReceive_audioStreamNotRegistered_shouldNotNotifyChange()168 public void onReceive_audioStreamNotRegistered_shouldNotNotifyChange() { 169 VolumeSliceHelper.onReceive(mContext, mIntent); 170 171 verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); 172 } 173 174 @Test onReceive_audioStreamNotMatched_shouldNotNotifyChange()175 public void onReceive_audioStreamNotMatched_shouldNotNotifyChange() { 176 registerIntentToUri(mMediaController); 177 mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_DTMF); 178 179 VolumeSliceHelper.onReceive(mContext, mIntent); 180 181 verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); 182 } 183 184 @Test onReceive_mediaVolumeNotChanged_shouldNotNotifyChange()185 public void onReceive_mediaVolumeNotChanged_shouldNotNotifyChange() { 186 mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1) 187 .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 1); 188 registerIntentToUri(mMediaController); 189 190 VolumeSliceHelper.onReceive(mContext, mIntent); 191 192 verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); 193 } 194 195 @Test onReceive_ringStreamVolumeMuted_shouldNotifySeparateRing()196 public void onReceive_ringStreamVolumeMuted_shouldNotifySeparateRing() { 197 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 198 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 199 registerIntentToUri(mRingController); 200 registerIntentToUri(mSeparateRingController); 201 202 VolumeSliceHelper.onReceive(mContext, intent); 203 204 verify(mResolver).notifyChange(mSeparateRingController.getSliceUri(), null); 205 } 206 207 @Test onReceive_ringStreamVolumeMuted_shouldNotifyRing()208 public void onReceive_ringStreamVolumeMuted_shouldNotifyRing() { 209 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 210 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 211 registerIntentToUri(mRingController); 212 registerIntentToUri(mSeparateRingController); 213 214 VolumeSliceHelper.onReceive(mContext, intent); 215 216 verify(mResolver).notifyChange(mRingController.getSliceUri(), null); 217 } 218 219 @Test onReceive_ringStreamVolumeMuted_shouldNotifyBothRings()220 public void onReceive_ringStreamVolumeMuted_shouldNotifyBothRings() { 221 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 222 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 223 registerIntentToUri(mRingController); 224 registerIntentToUri(mSeparateRingController); 225 226 VolumeSliceHelper.onReceive(mContext, intent); 227 228 verify(mResolver).notifyChange(mSeparateRingController.getSliceUri(), null); 229 verify(mResolver).notifyChange(mRingController.getSliceUri(), null); 230 } 231 232 @Test onReceive_streamVolumeMuted_shouldNotifyChange()233 public void onReceive_streamVolumeMuted_shouldNotifyChange() { 234 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 235 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream()); 236 registerIntentToUri(mMediaController); 237 registerIntentToUri(mRingController); 238 239 VolumeSliceHelper.onReceive(mContext, intent); 240 241 verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); 242 } 243 244 /** 245 * Without this test passing, when notification is separated from ring and its value is already 246 * zero, setting ringermode to silent would not disable notification slider. 247 * Note: the above scenario happens only in volume panel where controllers do not get to 248 * register for events such as RINGER_MODE_CHANGE. 249 */ 250 @Test onReceive_ringVolumeMuted_shouldNotifyChangeNotificationSlice()251 public void onReceive_ringVolumeMuted_shouldNotifyChangeNotificationSlice() { 252 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 253 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 254 registerIntentToUri(mRingController); 255 registerIntentToUri(mNotificationController); 256 257 VolumeSliceHelper.onReceive(mContext, intent); 258 259 verify(mResolver).notifyChange(mNotificationController.getSliceUri(), null); 260 } 261 262 /** 263 * Notifying notification slice on ring mute does not mean it should not notify ring slice. 264 * Rather, it should notify both slices. 265 */ 266 @Test onReceive_ringVolumeMuted_shouldNotifyChangeRingSlice()267 public void onReceive_ringVolumeMuted_shouldNotifyChangeRingSlice() { 268 final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) 269 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 270 registerIntentToUri(mRingController); 271 registerIntentToUri(mNotificationController); 272 273 VolumeSliceHelper.onReceive(mContext, intent); 274 275 verify(mResolver).notifyChange(mRingController.getSliceUri(), null); 276 } 277 278 @Test onReceive_streamDevicesChanged_shouldNotifyChange()279 public void onReceive_streamDevicesChanged_shouldNotifyChange() { 280 final Intent intent = createIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION) 281 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); 282 registerIntentToUri(mMediaController); 283 registerIntentToUri(mRingController); 284 285 VolumeSliceHelper.onReceive(mContext, intent); 286 287 verify(mResolver).notifyChange(mRingController.getSliceUri(), null); 288 } 289 290 @Test onReceive_primaryMutedChanged_shouldNotifyChangeAll()291 public void onReceive_primaryMutedChanged_shouldNotifyChangeAll() { 292 final Intent intent = createIntent(AudioManager.MASTER_MUTE_CHANGED_ACTION); 293 registerIntentToUri(mMediaController); 294 registerIntentToUri(mRingController); 295 296 VolumeSliceHelper.onReceive(mContext, intent); 297 298 verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); 299 verify(mResolver).notifyChange(mRingController.getSliceUri(), null); 300 } 301 registerIntentToUri(VolumeSeekBarPreferenceController controller)302 private void registerIntentToUri(VolumeSeekBarPreferenceController controller) { 303 VolumeSliceHelper.registerIntentToUri(mContext, controller.getIntentFilter(), 304 controller.getSliceUri(), controller.getAudioStream()); 305 } 306 createIntent(String action)307 private Intent createIntent(String action) { 308 return new Intent(action) 309 .putExtra(SliceBroadcastRelay.EXTRA_URI, VOLUME_SLICES_URI.toString()); 310 } 311 312 @Implements(SliceBroadcastRelay.class) 313 public static class ShadowSliceBroadcastRelay { 314 315 private static int sRegisteredCount; 316 317 @Implementation registerReceiver(Context context, Uri sliceUri, Class<? extends BroadcastReceiver> receiver, IntentFilter filter)318 public static void registerReceiver(Context context, Uri sliceUri, 319 Class<? extends BroadcastReceiver> receiver, IntentFilter filter) { 320 sRegisteredCount++; 321 } 322 323 @Implementation unregisterReceivers(Context context, Uri sliceUri)324 public static void unregisterReceivers(Context context, Uri sliceUri) { 325 sRegisteredCount--; 326 } 327 328 @Resetter reset()329 static void reset() { 330 sRegisteredCount = 0; 331 } 332 getRegisteredCount()333 static int getRegisteredCount() { 334 return sRegisteredCount; 335 } 336 } 337 } 338