1 /* 2 * Copyright (C) 2019 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.systemui.statusbar.notification.row 18 19 import android.app.INotificationManager 20 import android.app.NotificationChannel 21 import android.app.NotificationChannelGroup 22 import android.app.NotificationManager.IMPORTANCE_DEFAULT 23 import android.app.NotificationManager.IMPORTANCE_NONE 24 import android.content.pm.ParceledListSlice 25 import android.graphics.Color 26 import android.graphics.drawable.ColorDrawable 27 import android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME 28 import androidx.test.ext.junit.runners.AndroidJUnit4 29 import androidx.test.filters.SmallTest 30 import android.testing.TestableLooper 31 import android.view.View 32 33 import com.android.systemui.SysuiTestCase 34 35 import org.junit.Assert.assertEquals 36 import org.junit.Before 37 import org.junit.runner.RunWith 38 import org.junit.Test 39 import org.mockito.Answers 40 import org.mockito.ArgumentMatchers.anyBoolean 41 import org.mockito.ArgumentMatchers.eq 42 import org.mockito.Mock 43 import org.mockito.Mockito 44 import org.mockito.Mockito.`when` 45 import org.mockito.Mockito.times 46 import org.mockito.Mockito.verify 47 import org.mockito.MockitoAnnotations 48 49 @SmallTest 50 @RunWith(AndroidJUnit4::class) 51 @TestableLooper.RunWithLooper 52 class ChannelEditorDialogControllerTest : SysuiTestCase() { 53 54 private lateinit var controller: ChannelEditorDialogController 55 private lateinit var channel1: NotificationChannel 56 private lateinit var channel2: NotificationChannel 57 private lateinit var channelDefault: NotificationChannel 58 private lateinit var group: NotificationChannelGroup 59 60 private val appIcon = ColorDrawable(Color.MAGENTA) 61 62 @Mock 63 private lateinit var mockNoMan: INotificationManager 64 @Mock(answer = Answers.RETURNS_SELF) 65 private lateinit var dialogBuilder: ChannelEditorDialog.Builder 66 @Mock 67 private lateinit var dialog: ChannelEditorDialog 68 69 @Before setupnull70 fun setup() { 71 MockitoAnnotations.initMocks(this) 72 `when`(dialogBuilder.build()).thenReturn(dialog) 73 controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder) 74 75 channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT) 76 channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE) 77 channelDefault = NotificationChannel( 78 NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT) 79 80 group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME) 81 82 `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage( 83 eq(TEST_PACKAGE_NAME), eq(TEST_UID))) 84 .thenReturn(ParceledListSlice(listOf(group))) 85 86 `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID))) 87 .thenReturn(true) 88 } 89 90 @Test testPrepareDialogForApp_noExtraChannelsnull91 fun testPrepareDialogForApp_noExtraChannels() { 92 channel1.group = group.id 93 channel2.group = group.id 94 group.channels = listOf(channel1, channel2) 95 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 96 channel1, appIcon, clickListener) 97 98 assertEquals(2, controller.channelList.size) 99 } 100 101 @Test testPrepareDialogForApp_onlyDefaultChannelnull102 fun testPrepareDialogForApp_onlyDefaultChannel() { 103 group.addChannel(channelDefault) 104 105 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 106 channelDefault, appIcon, clickListener) 107 108 assertEquals("No channels should be shown when there is only the miscellaneous channel", 109 0, controller.channelList.size) 110 } 111 112 @Test testPrepareDialogForApp_AddsAllChannelsAllGroupsnull113 fun testPrepareDialogForApp_AddsAllChannelsAllGroups() { 114 val group2 = NotificationChannelGroup("two", "group two") 115 val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT) 116 channel3.group = group2.id 117 val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT) 118 channel4.group = group.id 119 val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT) 120 channel5.group = group.id 121 122 `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage( 123 eq(TEST_PACKAGE_NAME), eq(TEST_UID))) 124 .thenReturn(ParceledListSlice(listOf(group, group2))) 125 126 group.channels = listOf(channel1, channel2, channel4, channel5) 127 group2.channels = listOf(channel3) 128 129 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 130 channel1, appIcon, clickListener) 131 132 assertEquals("ChannelEditorDialog should show all channels", 133 5, controller.channelList.size) 134 } 135 136 @Test testApply_demoteChannelnull137 fun testApply_demoteChannel() { 138 group.channels = listOf(channel1, channel2) 139 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 140 channel1, appIcon, clickListener) 141 142 // propose an adjustment of channel1 143 controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) 144 145 controller.apply() 146 147 assertEquals("Proposed edits should take effect after apply", 148 IMPORTANCE_NONE, channel1.importance) 149 150 // Channel 2 shouldn't have changed 151 assertEquals("Proposed edits should take effect after apply", 152 IMPORTANCE_NONE, channel2.importance) 153 } 154 155 @Test testApply_promoteChannelnull156 fun testApply_promoteChannel() { 157 group.channels = listOf(channel1, channel2) 158 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 159 channel1, appIcon, clickListener) 160 161 // propose an adjustment of channel1 162 controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT) 163 164 controller.apply() 165 166 assertEquals("Proposed edits should take effect after apply", 167 IMPORTANCE_DEFAULT, channel2.importance) 168 169 // Channel 1 shouldn't have changed 170 assertEquals("Proposed edits should take effect after apply", 171 IMPORTANCE_DEFAULT, channel1.importance) 172 } 173 174 @Test testApply_demoteAppnull175 fun testApply_demoteApp() { 176 group.channels = listOf(channel1, channel2) 177 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 178 channel1, appIcon, clickListener) 179 180 controller.proposeSetAppNotificationsEnabled(false) 181 controller.apply() 182 183 verify(mockNoMan, times(1)).setNotificationsEnabledForPackage( 184 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(false)) 185 } 186 187 @Test testApply_promoteAppnull188 fun testApply_promoteApp() { 189 // App starts out disabled 190 `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID))) 191 .thenReturn(false) 192 193 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 194 channel1, appIcon, clickListener) 195 controller.proposeSetAppNotificationsEnabled(true) 196 controller.apply() 197 198 verify(mockNoMan, times(1)).setNotificationsEnabledForPackage( 199 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(true)) 200 } 201 202 @Test testSettingsClickListenerNull_noCrashnull203 fun testSettingsClickListenerNull_noCrash() { 204 // GIVEN editor dialog 205 group.channels = listOf(channel1, channel2) 206 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 207 channel1, appIcon, null) 208 209 // WHEN user taps settings 210 // Pass in any old view, it should never actually be used 211 controller.launchSettings(View(context)) 212 213 // THEN no crash 214 } 215 216 @Test testDoneButtonSaysDone_noChangesnull217 fun testDoneButtonSaysDone_noChanges() { 218 // GIVEN the editor dialog with no changes 219 `when`(dialogBuilder.build()).thenReturn(dialog) 220 221 group.channels = listOf(channel1, channel2) 222 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 223 channel1, appIcon, null) 224 225 // WHEN the user proposes a change 226 controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) 227 228 // THEN the "done" button has been updated to "apply" 229 verify(dialog).updateDoneButtonText(true /* hasChanges */) 230 } 231 232 @Test testDoneButtonGoesBackToNormal_changeThenNoChangenull233 fun testDoneButtonGoesBackToNormal_changeThenNoChange() { 234 val inOrderDialog = Mockito.inOrder(dialog) 235 // GIVEN the editor dialog with no changes 236 `when`(dialogBuilder.build()).thenReturn(dialog) 237 238 group.channels = listOf(channel1, channel2) 239 controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, 240 channel1, appIcon, null) 241 242 // WHEN the user proposes a change 243 controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) 244 // and WHEN the user sets the importance back to its original value 245 controller.proposeEditForChannel(channel1, channel1.importance) 246 247 // THEN the "done" button has been changed back to done 248 inOrderDialog.verify(dialog, times(1)).updateDoneButtonText(eq(true)) 249 inOrderDialog.verify(dialog, times(1)).updateDoneButtonText(eq(false)) 250 } 251 252 private val clickListener = object : NotificationInfo.OnSettingsClickListener { onClicknull253 override fun onClick(v: View, c: NotificationChannel, appUid: Int) { 254 } 255 } 256 257 companion object { 258 const val TEST_APP_NAME = "Test App Name" 259 const val TEST_PACKAGE_NAME = "test_package" 260 const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME 261 const val TEST_UID = 1 262 const val TEST_CHANNEL = "test_channel" 263 const val TEST_CHANNEL_NAME = "Test Channel Name" 264 const val TEST_CHANNEL2 = "test_channel2" 265 const val TEST_CHANNEL_NAME2 = "Test Channel Name2" 266 const val TEST_GROUP_ID = "test_group_id" 267 const val TEST_GROUP_NAME = "Test Group Name" 268 } 269 } 270