1 /* 2 * Copyright (C) 2017 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 android.app.NotificationChannel.DEFAULT_CHANNEL_ID; 20 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 21 import static android.app.NotificationManager.IMPORTANCE_HIGH; 22 import static android.app.NotificationManager.IMPORTANCE_LOW; 23 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.spy; 33 import static org.mockito.Mockito.times; 34 import static org.mockito.Mockito.verify; 35 import static org.mockito.Mockito.when; 36 37 import android.app.Notification; 38 import android.app.NotificationChannel; 39 import android.app.NotificationManager; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.media.AudioAttributes; 43 import android.media.RingtoneManager; 44 import android.net.Uri; 45 import android.os.UserManager; 46 import android.provider.Settings; 47 import android.util.AttributeSet; 48 49 import androidx.preference.Preference; 50 import androidx.preference.PreferenceScreen; 51 52 import com.android.settings.SettingsPreferenceFragment; 53 import com.android.settingslib.RestrictedLockUtils; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.Answers; 59 import org.mockito.ArgumentCaptor; 60 import org.mockito.Mock; 61 import org.mockito.MockitoAnnotations; 62 import org.robolectric.Robolectric; 63 import org.robolectric.RobolectricTestRunner; 64 import org.robolectric.RuntimeEnvironment; 65 import org.robolectric.shadows.ShadowApplication; 66 67 @RunWith(RobolectricTestRunner.class) 68 public class SoundPreferenceControllerTest { 69 70 private Context mContext; 71 @Mock 72 private NotificationBackend mBackend; 73 @Mock 74 private NotificationManager mNm; 75 @Mock 76 private UserManager mUm; 77 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 78 private PreferenceScreen mScreen; 79 @Mock 80 private SettingsPreferenceFragment mFragment; 81 @Mock 82 private NotificationSettingsBase.ImportanceListener mImportanceListener; 83 84 private SoundPreferenceController mController; 85 86 @Before setUp()87 public void setUp() { 88 MockitoAnnotations.initMocks(this); 89 ShadowApplication shadowApplication = ShadowApplication.getInstance(); 90 shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); 91 shadowApplication.setSystemService(Context.USER_SERVICE, mUm); 92 mContext = RuntimeEnvironment.application; 93 mController = spy(new SoundPreferenceController( 94 mContext, mFragment, mImportanceListener, mBackend)); 95 } 96 97 @Test testNoCrashIfNoOnResume()98 public void testNoCrashIfNoOnResume() { 99 mController.isAvailable(); 100 mController.updateState(mock(NotificationSoundPreference.class)); 101 mController.onPreferenceChange(mock(NotificationSoundPreference.class), Uri.EMPTY); 102 mController.handlePreferenceTreeClick(mock(NotificationSoundPreference.class)); 103 mController.onActivityResult(1, 1, null); 104 SoundPreferenceController.hasValidSound(null); 105 } 106 107 @Test testIsAvailable_notIfChannelNull()108 public void testIsAvailable_notIfChannelNull() { 109 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 110 mController.onResume(appRow, null, null, null); 111 assertFalse(mController.isAvailable()); 112 } 113 114 @Test testIsAvailable_notIfNotImportant()115 public void testIsAvailable_notIfNotImportant() { 116 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 117 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW); 118 mController.onResume(appRow, channel, null, null); 119 assertFalse(mController.isAvailable()); 120 } 121 122 @Test testIsAvailable_notIfDefaultChannel()123 public void testIsAvailable_notIfDefaultChannel() { 124 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 125 NotificationChannel channel = 126 new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT); 127 mController.onResume(appRow, channel, null, null); 128 assertFalse(mController.isAvailable()); 129 } 130 131 @Test testIsAvailable()132 public void testIsAvailable() { 133 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 134 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT); 135 mController.onResume(appRow, channel, null, null); 136 assertTrue(mController.isAvailable()); 137 } 138 139 @Test testDisplayPreference_savesPreference()140 public void testDisplayPreference_savesPreference() { 141 NotificationSoundPreference pref = mock(NotificationSoundPreference.class); 142 when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); 143 mController.displayPreference(mScreen); 144 145 mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent()); 146 verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any()); 147 } 148 149 @Test testUpdateState_disabledByAdmin()150 public void testUpdateState_disabledByAdmin() { 151 NotificationChannel channel = mock(NotificationChannel.class); 152 when(channel.getId()).thenReturn("something"); 153 mController.onResume(new NotificationBackend.AppRow(), channel, null, mock( 154 RestrictedLockUtils.EnforcedAdmin.class)); 155 156 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 157 Preference pref = new NotificationSoundPreference(mContext, attributeSet); 158 mController.updateState(pref); 159 160 assertFalse(pref.isEnabled()); 161 } 162 163 @Test testUpdateState_notBlockable()164 public void testUpdateState_notBlockable() { 165 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 166 NotificationChannel channel = mock(NotificationChannel.class); 167 when(channel.isImportanceLockedByOEM()).thenReturn(true); 168 mController.onResume(appRow, channel, null, null); 169 170 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 171 Preference pref = new NotificationSoundPreference(mContext, attributeSet); 172 mController.updateState(pref); 173 174 assertTrue(pref.isEnabled()); 175 } 176 177 @Test testUpdateState_configurable()178 public void testUpdateState_configurable() { 179 Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI; 180 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 181 NotificationChannel channel = mock(NotificationChannel.class); 182 when(channel.getId()).thenReturn("something"); 183 when(channel.getSound()).thenReturn(sound); 184 mController.onResume(appRow, channel, null, null); 185 186 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 187 NotificationSoundPreference pref = new NotificationSoundPreference(mContext, attributeSet); 188 mController.updateState(pref); 189 190 assertEquals(sound, pref.onRestoreRingtone()); 191 assertTrue(pref.isEnabled()); 192 } 193 194 @Test testOnPreferenceChange()195 public void testOnPreferenceChange() { 196 Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI; 197 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 198 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); 199 channel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT); 200 mController.onResume(appRow, channel, null, null); 201 202 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 203 NotificationSoundPreference pref = 204 new NotificationSoundPreference(mContext, attributeSet); 205 mController.updateState(pref); 206 207 mController.onPreferenceChange(pref, Uri.EMPTY); 208 assertEquals(Uri.EMPTY, channel.getSound()); 209 assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, channel.getAudioAttributes()); 210 verify(mBackend, times(1)).updateChannel(any(), anyInt(), any()); 211 } 212 213 @Test testOnPreferenceTreeClick_incorrectPref()214 public void testOnPreferenceTreeClick_incorrectPref() { 215 NotificationSoundPreference pref = mock(NotificationSoundPreference.class); 216 mController.handlePreferenceTreeClick(pref); 217 218 verify(pref, never()).onPrepareRingtonePickerIntent(any()); 219 verify(mFragment, never()).startActivityForResult(any(), anyInt()); 220 } 221 222 @Test testOnPreferenceTreeClick_correctPref()223 public void testOnPreferenceTreeClick_correctPref() { 224 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 225 NotificationSoundPreference pref = 226 spy(new NotificationSoundPreference(mContext, attributeSet)); 227 pref.setKey(mController.getPreferenceKey()); 228 mController.handlePreferenceTreeClick(pref); 229 230 verify(pref, times(1)).onPrepareRingtonePickerIntent(any()); 231 verify(mFragment, times(1)).startActivityForResult(any(), anyInt()); 232 } 233 234 @Test testOnPreferenceTreeClick_alarmSound()235 public void testOnPreferenceTreeClick_alarmSound() { 236 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 237 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); 238 channel.setSound(null, new AudioAttributes.Builder().setUsage( 239 AudioAttributes.USAGE_ALARM).build()); 240 mController.onResume(appRow, channel, null, null); 241 242 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 243 NotificationSoundPreference pref = 244 spy(new NotificationSoundPreference(mContext, attributeSet)); 245 pref.setKey(mController.getPreferenceKey()); 246 mController.handlePreferenceTreeClick(pref); 247 248 ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 249 verify(pref, times(1)).onPrepareRingtonePickerIntent(intentArgumentCaptor.capture()); 250 assertEquals(RingtoneManager.TYPE_ALARM, 251 intentArgumentCaptor.getValue().getIntExtra( 252 RingtoneManager.EXTRA_RINGTONE_TYPE, 0)); 253 } 254 255 @Test testOnPreferenceTreeClick_ringtoneSound()256 public void testOnPreferenceTreeClick_ringtoneSound() { 257 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 258 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); 259 channel.setSound(null, new AudioAttributes.Builder().setUsage( 260 AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build()); 261 mController.onResume(appRow, channel, null, null); 262 263 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 264 NotificationSoundPreference pref = 265 spy(new NotificationSoundPreference(mContext, attributeSet)); 266 pref.setKey(mController.getPreferenceKey()); 267 mController.handlePreferenceTreeClick(pref); 268 269 ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 270 verify(pref, times(1)).onPrepareRingtonePickerIntent(intentArgumentCaptor.capture()); 271 assertEquals(RingtoneManager.TYPE_RINGTONE, 272 intentArgumentCaptor.getValue().getIntExtra( 273 RingtoneManager.EXTRA_RINGTONE_TYPE, 0)); 274 } 275 276 @Test testOnPreferenceTreeClick_otherSound()277 public void testOnPreferenceTreeClick_otherSound() { 278 NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); 279 NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); 280 channel.setSound(null, new AudioAttributes.Builder().setUsage( 281 AudioAttributes.USAGE_UNKNOWN).build()); 282 mController.onResume(appRow, channel, null, null); 283 284 AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); 285 NotificationSoundPreference pref = 286 spy(new NotificationSoundPreference(mContext, attributeSet)); 287 pref.setKey(mController.getPreferenceKey()); 288 mController.handlePreferenceTreeClick(pref); 289 290 ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 291 verify(pref, times(1)).onPrepareRingtonePickerIntent(intentArgumentCaptor.capture()); 292 assertEquals(RingtoneManager.TYPE_NOTIFICATION, 293 intentArgumentCaptor.getValue().getIntExtra( 294 RingtoneManager.EXTRA_RINGTONE_TYPE, 0)); 295 } 296 297 @Test testOnActivityResult()298 public void testOnActivityResult() { 299 NotificationSoundPreference pref = mock(NotificationSoundPreference.class); 300 when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); 301 mController.displayPreference(mScreen); 302 303 mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent("hi")); 304 verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any()); 305 verify(mImportanceListener, times(1)).onImportanceChanged(); 306 } 307 308 @Test testHasValidSound()309 public void testHasValidSound() { 310 NotificationChannel channel = 311 new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH); 312 assertTrue(SoundPreferenceController.hasValidSound(channel)); 313 314 channel.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT); 315 assertFalse(SoundPreferenceController.hasValidSound(channel)); 316 317 channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); 318 assertFalse(SoundPreferenceController.hasValidSound(channel)); 319 } 320 } 321