1 /*
<lambda>null2  * Copyright (C) 2024 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 package com.android.systemui.statusbar.notification.row
17 
18 import android.R
19 import android.app.AppOpsManager
20 import android.app.INotificationManager
21 import android.app.Notification
22 import android.app.NotificationChannel
23 import android.app.NotificationManager
24 import android.content.Intent
25 import android.content.pm.PackageManager
26 import android.content.pm.ShortcutManager
27 import android.content.pm.launcherApps
28 import android.graphics.Color
29 import android.os.Binder
30 import android.os.fakeExecutorHandler
31 import android.os.userManager
32 import android.provider.Settings
33 import android.service.notification.NotificationListenerService.Ranking
34 import android.testing.TestableLooper.RunWithLooper
35 import android.util.ArraySet
36 import android.view.View
37 import android.view.accessibility.accessibilityManager
38 import androidx.test.ext.junit.runners.AndroidJUnit4
39 import androidx.test.filters.SmallTest
40 import com.android.compose.animation.scene.ObservableTransitionState
41 import com.android.internal.logging.MetricsLogger
42 import com.android.internal.logging.UiEventLogger
43 import com.android.internal.logging.metricsLogger
44 import com.android.internal.logging.testing.UiEventLoggerFake
45 import com.android.internal.statusbar.statusBarService
46 import com.android.systemui.SysuiTestCase
47 import com.android.systemui.concurrency.fakeExecutor
48 import com.android.systemui.flags.EnableSceneContainer
49 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
50 import com.android.systemui.kosmos.testScope
51 import com.android.systemui.people.widget.PeopleSpaceWidgetManager
52 import com.android.systemui.plugins.activityStarter
53 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
54 import com.android.systemui.plugins.statusbar.statusBarStateController
55 import com.android.systemui.power.domain.interactor.PowerInteractorFactory.create
56 import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
57 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
58 import com.android.systemui.scene.domain.interactor.sceneInteractor
59 import com.android.systemui.scene.shared.model.Scenes
60 import com.android.systemui.settings.UserContextProvider
61 import com.android.systemui.shade.shadeControllerSceneImpl
62 import com.android.systemui.statusbar.NotificationEntryHelper
63 import com.android.systemui.statusbar.NotificationPresenter
64 import com.android.systemui.statusbar.notification.AssistantFeedbackController
65 import com.android.systemui.statusbar.notification.NotificationActivityStarter
66 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
67 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
68 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
69 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
70 import com.android.systemui.statusbar.notificationLockscreenUserManager
71 import com.android.systemui.statusbar.policy.deviceProvisionedController
72 import com.android.systemui.statusbar.policy.headsUpManager
73 import com.android.systemui.testKosmos
74 import com.android.systemui.util.kotlin.JavaAdapter
75 import com.android.systemui.wmshell.BubblesManager
76 import java.util.Optional
77 import junit.framework.Assert
78 import kotlin.test.assertEquals
79 import kotlinx.coroutines.flow.MutableStateFlow
80 import kotlinx.coroutines.test.runCurrent
81 import org.junit.Before
82 import org.junit.Test
83 import org.junit.runner.RunWith
84 import org.mockito.ArgumentCaptor
85 import org.mockito.ArgumentMatchers
86 import org.mockito.Mock
87 import org.mockito.Mockito
88 import org.mockito.Mockito.verify
89 import org.mockito.MockitoAnnotations
90 import org.mockito.invocation.InvocationOnMock
91 
92 /** Tests for [NotificationGutsManager] with the scene container enabled. */
93 @SmallTest
94 @RunWith(AndroidJUnit4::class)
95 @RunWithLooper
96 @EnableSceneContainer
97 class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
98     private val testNotificationChannel =
99         NotificationChannel(
100             TEST_CHANNEL_ID,
101             TEST_CHANNEL_ID,
102             NotificationManager.IMPORTANCE_DEFAULT
103         )
104 
105     private val kosmos = testKosmos()
106     private val testScope = kosmos.testScope
107     private val javaAdapter = JavaAdapter(testScope.backgroundScope)
108     private val executor = kosmos.fakeExecutor
109     private val handler = kosmos.fakeExecutorHandler
110     private lateinit var helper: NotificationTestHelper
111     private lateinit var gutsManager: NotificationGutsManager
112     private lateinit var windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
113 
114     private val metricsLogger = kosmos.metricsLogger
115     private val deviceProvisionedController = kosmos.deviceProvisionedController
116     private val accessibilityManager = kosmos.accessibilityManager
117     private val mBarService = kosmos.statusBarService
118     private val launcherApps = kosmos.launcherApps
119     private val shadeController = kosmos.shadeControllerSceneImpl
120     private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
121     private val statusBarStateController = kosmos.statusBarStateController
122     private val headsUpManager = kosmos.headsUpManager
123     private val activityStarter = kosmos.activityStarter
124     private val userManager = kosmos.userManager
125     private val activeNotificationsInteractor = kosmos.activeNotificationsInteractor
126     private val sceneInteractor = kosmos.sceneInteractor
127 
128     @Mock private lateinit var onUserInteractionCallback: OnUserInteractionCallback
129     @Mock private lateinit var presenter: NotificationPresenter
130     @Mock private lateinit var notificationActivityStarter: NotificationActivityStarter
131     @Mock private lateinit var notificationListContainer: NotificationListContainer
132     @Mock
133     private lateinit var onSettingsClickListener: NotificationGutsManager.OnSettingsClickListener
134     @Mock private lateinit var highPriorityProvider: HighPriorityProvider
135     @Mock private lateinit var notificationManager: INotificationManager
136     @Mock private lateinit var shortcutManager: ShortcutManager
137     @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
138     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
139     @Mock private lateinit var contextTracker: UserContextProvider
140     @Mock private lateinit var bubblesManager: BubblesManager
141     @Mock private lateinit var peopleSpaceWidgetManager: PeopleSpaceWidgetManager
142     @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
143 
144     @Before
145     fun setUp() {
146         MockitoAnnotations.initMocks(this)
147         allowTestableLooperAsMainThread()
148         helper = NotificationTestHelper(mContext, mDependency)
149         Mockito.`when`(accessibilityManager.isTouchExplorationEnabled).thenReturn(false)
150         windowRootViewVisibilityInteractor =
151             WindowRootViewVisibilityInteractor(
152                 testScope.backgroundScope,
153                 WindowRootViewVisibilityRepository(mBarService, executor),
154                 FakeKeyguardRepository(),
155                 headsUpManager,
156                 create().powerInteractor,
157                 activeNotificationsInteractor,
158             ) {
159                 sceneInteractor
160             }
161         gutsManager =
162             NotificationGutsManager(
163                 mContext,
164                 handler,
165                 handler,
166                 javaAdapter,
167                 accessibilityManager,
168                 highPriorityProvider,
169                 notificationManager,
170                 userManager,
171                 peopleSpaceWidgetManager,
172                 launcherApps,
173                 shortcutManager,
174                 channelEditorDialogController,
175                 contextTracker,
176                 assistantFeedbackController,
177                 Optional.of(bubblesManager),
178                 UiEventLoggerFake(),
179                 onUserInteractionCallback,
180                 shadeController,
181                 windowRootViewVisibilityInteractor,
182                 notificationLockscreenUserManager,
183                 statusBarStateController,
184                 mBarService,
185                 deviceProvisionedController,
186                 metricsLogger,
187                 headsUpManager,
188                 activityStarter
189             )
190         gutsManager.setUpWithPresenter(
191             presenter,
192             notificationListContainer,
193             onSettingsClickListener
194         )
195         gutsManager.setNotificationActivityStarter(notificationActivityStarter)
196         gutsManager.start()
197     }
198 
199     @Test
200     fun testOpenAndCloseGuts() {
201         val guts = Mockito.spy(NotificationGuts(mContext))
202         Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock
203             ->
204             handler.post((invocation.arguments[0] as Runnable))
205             null
206         }
207 
208         // Test doesn't support animation since the guts view is not attached.
209         Mockito.doNothing()
210             .`when`(guts)
211             .openControls(
212                 ArgumentMatchers.anyInt(),
213                 ArgumentMatchers.anyInt(),
214                 ArgumentMatchers.anyBoolean(),
215                 ArgumentMatchers.any(Runnable::class.java)
216             )
217         val realRow = createTestNotificationRow()
218         val menuItem = createTestMenuItem(realRow)
219         val row = Mockito.spy(realRow)
220         Mockito.`when`(row!!.windowToken).thenReturn(Binder())
221         Mockito.`when`(row.guts).thenReturn(guts)
222         Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
223         assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
224         executor.runAllReady()
225         verify(guts)
226             .openControls(
227                 ArgumentMatchers.anyInt(),
228                 ArgumentMatchers.anyInt(),
229                 ArgumentMatchers.anyBoolean(),
230                 ArgumentMatchers.any(Runnable::class.java)
231             )
232         verify(headsUpManager).setGutsShown(realRow!!.entry, true)
233         assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong())
234         gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false)
235         verify(guts)
236             .closeControls(
237                 ArgumentMatchers.anyBoolean(),
238                 ArgumentMatchers.anyBoolean(),
239                 ArgumentMatchers.anyInt(),
240                 ArgumentMatchers.anyInt(),
241                 ArgumentMatchers.anyBoolean()
242             )
243         verify(row, Mockito.times(1)).setGutsView(ArgumentMatchers.any())
244         executor.runAllReady()
245         verify(headsUpManager).setGutsShown(realRow.entry, false)
246     }
247 
248     @Test
249     fun testLockscreenShadeVisible_visible_gutsNotClosed() {
250         // First, start out lockscreen or shade as not visible
251         setIsLockscreenOrShadeVisible(false)
252         testScope.testScheduler.runCurrent()
253         val guts = Mockito.mock(NotificationGuts::class.java)
254         gutsManager.exposedGuts = guts
255 
256         // WHEN the lockscreen or shade becomes visible
257         setIsLockscreenOrShadeVisible(true)
258         testScope.testScheduler.runCurrent()
259 
260         // THEN the guts are not closed
261         verify(guts, Mockito.never()).removeCallbacks(ArgumentMatchers.any())
262         verify(guts, Mockito.never())
263             .closeControls(
264                 ArgumentMatchers.anyBoolean(),
265                 ArgumentMatchers.anyBoolean(),
266                 ArgumentMatchers.anyInt(),
267                 ArgumentMatchers.anyInt(),
268                 ArgumentMatchers.anyBoolean()
269             )
270     }
271 
272     @Test
273     fun testLockscreenShadeVisible_notVisible_gutsClosed() {
274         // First, start out lockscreen or shade as visible
275         setIsLockscreenOrShadeVisible(true)
276         testScope.testScheduler.runCurrent()
277         val guts = Mockito.mock(NotificationGuts::class.java)
278         gutsManager.exposedGuts = guts
279 
280         // WHEN the lockscreen or shade is no longer visible
281         setIsLockscreenOrShadeVisible(false)
282         testScope.testScheduler.runCurrent()
283 
284         // THEN the guts are closed
285         verify(guts).removeCallbacks(ArgumentMatchers.any())
286         verify(guts)
287             .closeControls(
288                 /* leavebehinds= */ ArgumentMatchers.eq(true),
289                 /* controls= */ ArgumentMatchers.eq(true),
290                 /* x= */ ArgumentMatchers.anyInt(),
291                 /* y= */ ArgumentMatchers.anyInt(),
292                 /* force= */ ArgumentMatchers.eq(true)
293             )
294     }
295 
296     @Test
297     fun testLockscreenShadeVisible_notVisible_listContainerReset() {
298         // First, start out lockscreen or shade as visible
299         setIsLockscreenOrShadeVisible(true)
300         testScope.testScheduler.runCurrent()
301 
302         // WHEN the lockscreen or shade is no longer visible
303         setIsLockscreenOrShadeVisible(false)
304         testScope.testScheduler.runCurrent()
305 
306         // THEN the list container is reset
307         verify(notificationListContainer)
308             .resetExposedMenuView(ArgumentMatchers.anyBoolean(), ArgumentMatchers.anyBoolean())
309     }
310 
311     @Test
312     fun testChangeDensityOrFontScale() {
313         val guts = Mockito.spy(NotificationGuts(mContext))
314         Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock
315             ->
316             handler.post((invocation.arguments[0] as Runnable))
317             null
318         }
319 
320         // Test doesn't support animation since the guts view is not attached.
321         Mockito.doNothing()
322             .`when`(guts)
323             .openControls(
324                 ArgumentMatchers.anyInt(),
325                 ArgumentMatchers.anyInt(),
326                 ArgumentMatchers.anyBoolean(),
327                 ArgumentMatchers.any(Runnable::class.java)
328             )
329         val realRow = createTestNotificationRow()
330         val menuItem = createTestMenuItem(realRow)
331         val row = Mockito.spy(realRow)
332         Mockito.`when`(row!!.windowToken).thenReturn(Binder())
333         Mockito.`when`(row.guts).thenReturn(guts)
334         Mockito.doNothing().`when`(row).ensureGutsInflated()
335         val realEntry = realRow!!.entry
336         val entry = Mockito.spy(realEntry)
337         Mockito.`when`(entry.row).thenReturn(row)
338         Mockito.`when`(entry.getGuts()).thenReturn(guts)
339         Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
340         executor.runAllReady()
341         verify(guts)
342             .openControls(
343                 ArgumentMatchers.anyInt(),
344                 ArgumentMatchers.anyInt(),
345                 ArgumentMatchers.anyBoolean(),
346                 ArgumentMatchers.any(Runnable::class.java)
347             )
348 
349         // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
350         verify(row).setGutsView(ArgumentMatchers.any())
351         row.onDensityOrFontScaleChanged()
352         gutsManager.onDensityOrFontScaleChanged(entry)
353         executor.runAllReady()
354         gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
355         verify(guts)
356             .closeControls(
357                 ArgumentMatchers.anyBoolean(),
358                 ArgumentMatchers.anyBoolean(),
359                 ArgumentMatchers.anyInt(),
360                 ArgumentMatchers.anyInt(),
361                 ArgumentMatchers.anyBoolean()
362             )
363 
364         // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
365         verify(row, Mockito.times(2)).setGutsView(ArgumentMatchers.any())
366     }
367 
368     @Test
369     fun testAppOpsSettingsIntent_camera() {
370         val ops = ArraySet<Int>()
371         ops.add(AppOpsManager.OP_CAMERA)
372         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
373         val captor = ArgumentCaptor.forClass(Intent::class.java)
374         verify(notificationActivityStarter, Mockito.times(1))
375             .startNotificationGutsIntent(
376                 captor.capture(),
377                 ArgumentMatchers.anyInt(),
378                 ArgumentMatchers.any()
379             )
380         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action)
381     }
382 
383     @Test
384     fun testAppOpsSettingsIntent_mic() {
385         val ops = ArraySet<Int>()
386         ops.add(AppOpsManager.OP_RECORD_AUDIO)
387         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
388         val captor = ArgumentCaptor.forClass(Intent::class.java)
389         verify(notificationActivityStarter, Mockito.times(1))
390             .startNotificationGutsIntent(
391                 captor.capture(),
392                 ArgumentMatchers.anyInt(),
393                 ArgumentMatchers.any()
394             )
395         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action)
396     }
397 
398     @Test
399     fun testAppOpsSettingsIntent_camera_mic() {
400         val ops = ArraySet<Int>()
401         ops.add(AppOpsManager.OP_CAMERA)
402         ops.add(AppOpsManager.OP_RECORD_AUDIO)
403         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
404         val captor = ArgumentCaptor.forClass(Intent::class.java)
405         verify(notificationActivityStarter, Mockito.times(1))
406             .startNotificationGutsIntent(
407                 captor.capture(),
408                 ArgumentMatchers.anyInt(),
409                 ArgumentMatchers.any()
410             )
411         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action)
412     }
413 
414     @Test
415     fun testAppOpsSettingsIntent_overlay() {
416         val ops = ArraySet<Int>()
417         ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
418         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
419         val captor = ArgumentCaptor.forClass(Intent::class.java)
420         verify(notificationActivityStarter, Mockito.times(1))
421             .startNotificationGutsIntent(
422                 captor.capture(),
423                 ArgumentMatchers.anyInt(),
424                 ArgumentMatchers.any()
425             )
426         assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.value.action)
427     }
428 
429     @Test
430     fun testAppOpsSettingsIntent_camera_mic_overlay() {
431         val ops = ArraySet<Int>()
432         ops.add(AppOpsManager.OP_CAMERA)
433         ops.add(AppOpsManager.OP_RECORD_AUDIO)
434         ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
435         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
436         val captor = ArgumentCaptor.forClass(Intent::class.java)
437         verify(notificationActivityStarter, Mockito.times(1))
438             .startNotificationGutsIntent(
439                 captor.capture(),
440                 ArgumentMatchers.anyInt(),
441                 ArgumentMatchers.any()
442             )
443         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action)
444     }
445 
446     @Test
447     fun testAppOpsSettingsIntent_camera_overlay() {
448         val ops = ArraySet<Int>()
449         ops.add(AppOpsManager.OP_CAMERA)
450         ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
451         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
452         val captor = ArgumentCaptor.forClass(Intent::class.java)
453         verify(notificationActivityStarter, Mockito.times(1))
454             .startNotificationGutsIntent(
455                 captor.capture(),
456                 ArgumentMatchers.anyInt(),
457                 ArgumentMatchers.any()
458             )
459         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action)
460     }
461 
462     @Test
463     fun testAppOpsSettingsIntent_mic_overlay() {
464         val ops = ArraySet<Int>()
465         ops.add(AppOpsManager.OP_RECORD_AUDIO)
466         ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
467         gutsManager.startAppOpsSettingsActivity("", 0, ops, null)
468         val captor = ArgumentCaptor.forClass(Intent::class.java)
469         verify(notificationActivityStarter, Mockito.times(1))
470             .startNotificationGutsIntent(
471                 captor.capture(),
472                 ArgumentMatchers.anyInt(),
473                 ArgumentMatchers.any()
474             )
475         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action)
476     }
477 
478     @Test
479     @Throws(Exception::class)
480     fun testInitializeNotificationInfoView_highPriority() {
481         val notificationInfoView = Mockito.mock(NotificationInfo::class.java)
482         val row = Mockito.spy(helper.createRow())
483         val entry = row.entry
484         NotificationEntryHelper.modifyRanking(entry)
485             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
486             .setImportance(NotificationManager.IMPORTANCE_HIGH)
487             .build()
488         Mockito.`when`(row.getIsNonblockable()).thenReturn(false)
489         Mockito.`when`(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
490         val statusBarNotification = entry.sbn
491         gutsManager.initializeNotificationInfo(row, notificationInfoView)
492         verify(notificationInfoView)
493             .bindNotification(
494                 ArgumentMatchers.any(PackageManager::class.java),
495                 ArgumentMatchers.any(INotificationManager::class.java),
496                 ArgumentMatchers.eq(onUserInteractionCallback),
497                 ArgumentMatchers.eq(channelEditorDialogController),
498                 ArgumentMatchers.eq(statusBarNotification.packageName),
499                 ArgumentMatchers.any(NotificationChannel::class.java),
500                 ArgumentMatchers.eq(entry),
501                 ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java),
502                 ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java),
503                 ArgumentMatchers.any(UiEventLogger::class.java),
504                 ArgumentMatchers.eq(true),
505                 ArgumentMatchers.eq(false),
506                 ArgumentMatchers.eq(true), /* wasShownHighPriority */
507                 ArgumentMatchers.eq(assistantFeedbackController),
508                 ArgumentMatchers.any(MetricsLogger::class.java)
509             )
510     }
511 
512     @Test
513     @Throws(Exception::class)
514     fun testInitializeNotificationInfoView_PassesAlongProvisionedState() {
515         val notificationInfoView = Mockito.mock(NotificationInfo::class.java)
516         val row = Mockito.spy(helper.createRow())
517         NotificationEntryHelper.modifyRanking(row.entry)
518             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
519             .build()
520         Mockito.`when`(row.getIsNonblockable()).thenReturn(false)
521         val statusBarNotification = row.entry.sbn
522         val entry = row.entry
523         gutsManager.initializeNotificationInfo(row, notificationInfoView)
524         verify(notificationInfoView)
525             .bindNotification(
526                 ArgumentMatchers.any(PackageManager::class.java),
527                 ArgumentMatchers.any(INotificationManager::class.java),
528                 ArgumentMatchers.eq(onUserInteractionCallback),
529                 ArgumentMatchers.eq(channelEditorDialogController),
530                 ArgumentMatchers.eq(statusBarNotification.packageName),
531                 ArgumentMatchers.any(NotificationChannel::class.java),
532                 ArgumentMatchers.eq(entry),
533                 ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java),
534                 ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java),
535                 ArgumentMatchers.any(UiEventLogger::class.java),
536                 ArgumentMatchers.eq(true),
537                 ArgumentMatchers.eq(false),
538                 ArgumentMatchers.eq(false), /* wasShownHighPriority */
539                 ArgumentMatchers.eq(assistantFeedbackController),
540                 ArgumentMatchers.any(MetricsLogger::class.java)
541             )
542     }
543 
544     @Test
545     @Throws(Exception::class)
546     fun testInitializeNotificationInfoView_withInitialAction() {
547         val notificationInfoView = Mockito.mock(NotificationInfo::class.java)
548         val row = Mockito.spy(helper.createRow())
549         NotificationEntryHelper.modifyRanking(row.entry)
550             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
551             .build()
552         Mockito.`when`(row.getIsNonblockable()).thenReturn(false)
553         val statusBarNotification = row.entry.sbn
554         val entry = row.entry
555         gutsManager.initializeNotificationInfo(row, notificationInfoView)
556         verify(notificationInfoView)
557             .bindNotification(
558                 ArgumentMatchers.any(PackageManager::class.java),
559                 ArgumentMatchers.any(INotificationManager::class.java),
560                 ArgumentMatchers.eq(onUserInteractionCallback),
561                 ArgumentMatchers.eq(channelEditorDialogController),
562                 ArgumentMatchers.eq(statusBarNotification.packageName),
563                 ArgumentMatchers.any(NotificationChannel::class.java),
564                 ArgumentMatchers.eq(entry),
565                 ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java),
566                 ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java),
567                 ArgumentMatchers.any(UiEventLogger::class.java),
568                 ArgumentMatchers.eq(true),
569                 ArgumentMatchers.eq(false),
570                 ArgumentMatchers.eq(false), /* wasShownHighPriority */
571                 ArgumentMatchers.eq(assistantFeedbackController),
572                 ArgumentMatchers.any(MetricsLogger::class.java)
573             )
574     }
575 
576     private fun createTestNotificationRow(): ExpandableNotificationRow? {
577         val nb =
578             Notification.Builder(mContext, testNotificationChannel.id)
579                 .setContentTitle("foo")
580                 .setColorized(true)
581                 .setColor(Color.RED)
582                 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
583                 .setSmallIcon(R.drawable.sym_def_app_icon)
584         return try {
585             val row = helper.createRow(nb.build())
586             NotificationEntryHelper.modifyRanking(row.entry)
587                 .setChannel(testNotificationChannel)
588                 .build()
589             row
590         } catch (e: Exception) {
591             org.junit.Assert.fail()
592             null
593         }
594     }
595 
596     private fun setIsLockscreenOrShadeVisible(isVisible: Boolean) {
597         val key =
598             if (isVisible) {
599                 Scenes.Lockscreen
600             } else {
601                 Scenes.Bouncer
602             }
603         sceneInteractor.changeScene(key, "test")
604         sceneInteractor.setTransitionState(
605             MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
606         )
607         testScope.runCurrent()
608     }
609 
610     private fun createTestMenuItem(
611         row: ExpandableNotificationRow?
612     ): NotificationMenuRowPlugin.MenuItem {
613         val menuRow: NotificationMenuRowPlugin =
614             NotificationMenuRow(mContext, peopleNotificationIdentifier)
615         menuRow.createMenu(row, row!!.entry.sbn)
616         val menuItem = menuRow.getLongpressMenuItem(mContext)
617         Assert.assertNotNull(menuItem)
618         return menuItem
619     }
620 
621     companion object {
622         private const val TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"
623     }
624 }
625