/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.slices; import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SLICES_URI; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.net.Uri; import com.android.settings.notification.MediaVolumePreferenceController; import com.android.settings.notification.NotificationVolumePreferenceController; import com.android.settings.notification.RingVolumePreferenceController; import com.android.settings.notification.SeparateRingVolumePreferenceController; import com.android.settings.notification.VolumeSeekBarPreferenceController; import com.android.settingslib.SliceBroadcastRelay; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; @RunWith(RobolectricTestRunner.class) @Config(shadows = VolumeSliceHelperTest.ShadowSliceBroadcastRelay.class) public class VolumeSliceHelperTest { @Mock private ContentResolver mResolver; private Context mContext; private Intent mIntent; private VolumeSeekBarPreferenceController mMediaController; private VolumeSeekBarPreferenceController mRingController; private VolumeSeekBarPreferenceController mSeparateRingController; private VolumeSeekBarPreferenceController mNotificationController; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); when(mContext.getContentResolver()).thenReturn(mResolver); mMediaController = new MediaVolumePreferenceController(mContext); mRingController = new RingVolumePreferenceController(mContext); mSeparateRingController = new SeparateRingVolumePreferenceController(mContext); mNotificationController = new NotificationVolumePreferenceController(mContext); mIntent = createIntent(AudioManager.VOLUME_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1) .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 2) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream()); } @After public void cleanUp() { ShadowSliceBroadcastRelay.reset(); VolumeSliceHelper.sRegisteredUri.clear(); VolumeSliceHelper.sIntentFilter = null; } @Test public void registerIntentToUri_volumeController_shouldRegisterReceiver() { registerIntentToUri(mMediaController); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); assertThat(VolumeSliceHelper.sRegisteredUri) .containsKey((mMediaController.getSliceUri())); } @Test public void registerIntentToUri_doubleVolumeControllers_shouldRegisterReceiverOnce() { registerIntentToUri(mMediaController); registerIntentToUri(mRingController); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); assertThat(VolumeSliceHelper.sRegisteredUri) .containsKey((mRingController.getSliceUri())); } @Test public void unregisterUri_notFinalUri_shouldNotUnregisterReceiver() { registerIntentToUri(mMediaController); registerIntentToUri(mRingController); VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); assertThat(VolumeSliceHelper.sRegisteredUri) .doesNotContainKey((mMediaController.getSliceUri())); } @Test public void unregisterUri_finalUri_shouldUnregisterReceiver() { registerIntentToUri(mMediaController); VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0); assertThat(VolumeSliceHelper.sRegisteredUri) .doesNotContainKey((mMediaController.getSliceUri())); } @Test public void unregisterUri_unregisterTwice_shouldUnregisterReceiverOnce() { registerIntentToUri(mMediaController); VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri()); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0); } @Test public void unregisterUri_notRegistered_shouldNotUnregisterReceiver() { registerIntentToUri(mMediaController); VolumeSliceHelper.unregisterUri(mContext, mRingController.getSliceUri()); assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1); assertThat(VolumeSliceHelper.sRegisteredUri) .containsKey((mMediaController.getSliceUri())); } @Test public void onReceive_audioStreamRegistered_shouldNotifyChange() { registerIntentToUri(mMediaController); VolumeSliceHelper.onReceive(mContext, mIntent); verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); } @Test public void onReceive_audioStreamNotRegistered_shouldNotNotifyChange() { VolumeSliceHelper.onReceive(mContext, mIntent); verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); } @Test public void onReceive_audioStreamNotMatched_shouldNotNotifyChange() { registerIntentToUri(mMediaController); mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_DTMF); VolumeSliceHelper.onReceive(mContext, mIntent); verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); } @Test public void onReceive_mediaVolumeNotChanged_shouldNotNotifyChange() { mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1) .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 1); registerIntentToUri(mMediaController); VolumeSliceHelper.onReceive(mContext, mIntent); verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null); } @Test public void onReceive_ringStreamVolumeMuted_shouldNotifySeparateRing() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mRingController); registerIntentToUri(mSeparateRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mSeparateRingController.getSliceUri(), null); } @Test public void onReceive_ringStreamVolumeMuted_shouldNotifyRing() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mRingController); registerIntentToUri(mSeparateRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mRingController.getSliceUri(), null); } @Test public void onReceive_ringStreamVolumeMuted_shouldNotifyBothRings() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mRingController); registerIntentToUri(mSeparateRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mSeparateRingController.getSliceUri(), null); verify(mResolver).notifyChange(mRingController.getSliceUri(), null); } @Test public void onReceive_streamVolumeMuted_shouldNotifyChange() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream()); registerIntentToUri(mMediaController); registerIntentToUri(mRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); } /** * Without this test passing, when notification is separated from ring and its value is already * zero, setting ringermode to silent would not disable notification slider. * Note: the above scenario happens only in volume panel where controllers do not get to * register for events such as RINGER_MODE_CHANGE. */ @Test public void onReceive_ringVolumeMuted_shouldNotifyChangeNotificationSlice() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mRingController); registerIntentToUri(mNotificationController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mNotificationController.getSliceUri(), null); } /** * Notifying notification slice on ring mute does not mean it should not notify ring slice. * Rather, it should notify both slices. */ @Test public void onReceive_ringVolumeMuted_shouldNotifyChangeRingSlice() { final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mRingController); registerIntentToUri(mNotificationController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mRingController.getSliceUri(), null); } @Test public void onReceive_streamDevicesChanged_shouldNotifyChange() { final Intent intent = createIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream()); registerIntentToUri(mMediaController); registerIntentToUri(mRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mRingController.getSliceUri(), null); } @Test public void onReceive_primaryMutedChanged_shouldNotifyChangeAll() { final Intent intent = createIntent(AudioManager.MASTER_MUTE_CHANGED_ACTION); registerIntentToUri(mMediaController); registerIntentToUri(mRingController); VolumeSliceHelper.onReceive(mContext, intent); verify(mResolver).notifyChange(mMediaController.getSliceUri(), null); verify(mResolver).notifyChange(mRingController.getSliceUri(), null); } private void registerIntentToUri(VolumeSeekBarPreferenceController controller) { VolumeSliceHelper.registerIntentToUri(mContext, controller.getIntentFilter(), controller.getSliceUri(), controller.getAudioStream()); } private Intent createIntent(String action) { return new Intent(action) .putExtra(SliceBroadcastRelay.EXTRA_URI, VOLUME_SLICES_URI.toString()); } @Implements(SliceBroadcastRelay.class) public static class ShadowSliceBroadcastRelay { private static int sRegisteredCount; @Implementation public static void registerReceiver(Context context, Uri sliceUri, Class<? extends BroadcastReceiver> receiver, IntentFilter filter) { sRegisteredCount++; } @Implementation public static void unregisterReceivers(Context context, Uri sliceUri) { sRegisteredCount--; } @Resetter static void reset() { sRegisteredCount = 0; } static int getRegisteredCount() { return sRegisteredCount; } } }