1 /*
2  * Copyright (C) 2023 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.qs.tiles.base.actions
18 
19 import android.app.PendingIntent
20 import android.content.ComponentName
21 import android.content.Intent
22 import android.content.pm.ActivityInfo
23 import android.content.pm.PackageManager
24 import android.content.pm.PackageManager.ResolveInfoFlags
25 import android.content.pm.ResolveInfo
26 import android.os.UserHandle
27 import android.platform.test.annotations.EnabledOnRavenwood
28 import androidx.test.ext.junit.runners.AndroidJUnit4
29 import androidx.test.filters.SmallTest
30 import com.android.systemui.SysuiTestCase
31 import com.android.systemui.plugins.ActivityStarter
32 import com.android.systemui.util.mockito.argThat
33 import com.android.systemui.util.mockito.mock
34 import com.android.systemui.util.mockito.whenever
35 import org.junit.Before
36 import org.junit.Test
37 import org.junit.runner.RunWith
38 import org.mockito.ArgumentMatcher
39 import org.mockito.Mock
40 import org.mockito.Mockito.any
41 import org.mockito.Mockito.eq
42 import org.mockito.Mockito.never
43 import org.mockito.Mockito.verify
44 import org.mockito.Mockito.`when`
45 import org.mockito.MockitoAnnotations
46 
47 @SmallTest
48 @EnabledOnRavenwood
49 @RunWith(AndroidJUnit4::class)
50 class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
51     @Mock private lateinit var packageManager: PackageManager
52     @Mock private lateinit var activityStarter: ActivityStarter
53 
54     lateinit var underTest: QSTileIntentUserInputHandler
55 
56     @Before
setupnull57     fun setup() {
58         MockitoAnnotations.initMocks(this)
59         underTest = QSTileIntentUserInputHandlerImpl(activityStarter, packageManager, user)
60     }
61 
62     @Test
testPassesIntentToStarternull63     fun testPassesIntentToStarter() {
64         val intent = Intent("test.ACTION")
65 
66         underTest.handle(null, intent)
67 
68         verify(activityStarter).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
69     }
70 
71     @Test
testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLockednull72     fun testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLocked() {
73         val intent = Intent("test.ACTION")
74 
75         underTest.handle(null, intent, true)
76 
77         verify(activityStarter).startActivity(eq(intent), eq(true), any(), eq(true))
78     }
79 
80     @Test
testPassesActivityPendingIntentToStarterAsPendingIntentnull81     fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
82         val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
83 
84         underTest.handle(null, pendingIntent, true)
85 
86         verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
87     }
88 
89     @Test
testPassesActivityPendingIntentToStarterAsPendingIntentWhenNotRequestingActivityStartnull90     fun testPassesActivityPendingIntentToStarterAsPendingIntentWhenNotRequestingActivityStart() {
91         val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
92 
93         underTest.handle(null, pendingIntent, false)
94 
95         verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
96     }
97 
98     @Test
testPassNonActivityPendingIntentAndRequestStartingActivity_findsIntentAndStartsnull99     fun testPassNonActivityPendingIntentAndRequestStartingActivity_findsIntentAndStarts() {
100         val pendingIntent =
101             mock<PendingIntent> {
102                 whenever(isActivity).thenReturn(false)
103                 whenever(creatorPackage).thenReturn(ORIGINAL_PACKAGE)
104             }
105         setUpQueryResult(listOf(createActivityInfo(testResolvedComponent, exported = true)))
106 
107         underTest.handle(null, pendingIntent, true)
108 
109         val expectedIntent =
110             Intent(Intent.ACTION_MAIN)
111                 .addCategory(Intent.CATEGORY_LAUNCHER)
112                 .setPackage(null)
113                 .addFlags(
114                     Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
115                 )
116                 .setComponent(testResolvedComponent)
117 
118         verify(activityStarter)
119             .postStartActivityDismissingKeyguard(
120                 argThat(IntentMatcher(expectedIntent)),
121                 eq(0),
122                 any()
123             )
124     }
125 
126     @Test
testPassNonActivityPendingIntentAndDoNotRequestStartingActivity_doesNotStartActivitynull127     fun testPassNonActivityPendingIntentAndDoNotRequestStartingActivity_doesNotStartActivity() {
128         val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
129 
130         underTest.handle(null, pendingIntent, false)
131 
132         verify(activityStarter, never())
133             .postStartActivityDismissingKeyguard(any(Intent::class.java), eq(0), any())
134     }
135 
createActivityInfonull136     private fun createActivityInfo(
137         componentName: ComponentName,
138         exported: Boolean = false,
139     ): ActivityInfo {
140         return ActivityInfo().apply {
141             packageName = componentName.packageName
142             name = componentName.className
143             this.exported = exported
144         }
145     }
146 
setUpQueryResultnull147     private fun setUpQueryResult(infos: List<ActivityInfo>) {
148         `when`(
149                 packageManager.queryIntentActivitiesAsUser(
150                     any(Intent::class.java),
151                     any(ResolveInfoFlags::class.java),
152                     eq(user.identifier)
153                 )
154             )
155             .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
156     }
157 
158     private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> {
159         private val expectedIntent = intent
matchesnull160         override fun matches(argument: Intent?): Boolean {
161             return argument?.action.equals(expectedIntent.action) &&
162                 argument?.`package`.equals(expectedIntent.`package`) &&
163                 argument?.component?.equals(expectedIntent.component)!! &&
164                 argument?.categories?.equals(expectedIntent.categories)!! &&
165                 argument?.flags?.equals(expectedIntent.flags)!!
166         }
167     }
168 
169     companion object {
170         private const val ORIGINAL_PACKAGE = "original_pkg"
171         private const val TEST_PACKAGE = "test_pkg"
172         private const val TEST_COMPONENT_CLASS_NAME = "test_component_class_name"
173         private val testResolvedComponent = ComponentName(TEST_PACKAGE, TEST_COMPONENT_CLASS_NAME)
174         private val user = UserHandle.of(0)
175     }
176 }
177