• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.intentresolver.ui
18 
19 import android.app.PendingIntent
20 import android.compat.testing.PlatformCompatChangeRule
21 import android.content.ComponentName
22 import android.content.Intent
23 import android.os.Process
24 import android.service.chooser.ChooserResult
25 import androidx.test.platform.app.InstrumentationRegistry
26 import com.android.intentresolver.ui.model.ShareAction
27 import com.google.common.truth.Truth.assertThat
28 import com.google.common.truth.Truth.assertWithMessage
29 import kotlinx.coroutines.CompletableDeferred
30 import kotlinx.coroutines.ExperimentalCoroutinesApi
31 import kotlinx.coroutines.test.UnconfinedTestDispatcher
32 import kotlinx.coroutines.test.runCurrent
33 import kotlinx.coroutines.test.runTest
34 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges
35 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges
36 import org.junit.Rule
37 import org.junit.Test
38 import org.junit.rules.TestRule
39 
40 @OptIn(ExperimentalCoroutinesApi::class)
41 class ShareResultSenderImplTest {
42 
43     private val context = InstrumentationRegistry.getInstrumentation().context
44 
45     @get:Rule val compatChangeRule: TestRule = PlatformCompatChangeRule()
46 
47     @OptIn(ExperimentalCoroutinesApi::class)
48     @EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
49     @Test
50     fun onComponentSelected_chooserResultEnabled() = runTest {
51         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
52         val deferred = CompletableDeferred<Intent>()
53         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
54 
55         val resultSender =
56             ShareResultSenderImpl(
57                 scope = this,
58                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
59                 callerUid = Process.myUid(),
60                 resultSender = pi.intentSender,
61                 intentDispatcher = intentDispatcher
62             )
63 
64         resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, false)
65         runCurrent()
66 
67         val intentReceived = deferred.await()
68         val chooserResult =
69             intentReceived.getParcelableExtra(
70                 Intent.EXTRA_CHOOSER_RESULT,
71                 ChooserResult::class.java
72             )
73         assertThat(chooserResult).isNotNull()
74         assertThat(chooserResult?.type).isEqualTo(ChooserResult.CHOOSER_RESULT_SELECTED_COMPONENT)
75         assertThat(chooserResult?.selectedComponent).isEqualTo(ComponentName("example.com", "Foo"))
76         assertThat(chooserResult?.isShortcut).isTrue()
77     }
78 
79     @OptIn(ExperimentalCoroutinesApi::class)
80     @EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
81     @Test
82     fun onComponentSelected_crossProfile_chooserResultEnabled() = runTest {
83         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
84         val deferred = CompletableDeferred<Intent>()
85         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
86 
87         val resultSender =
88             ShareResultSenderImpl(
89                 scope = this,
90                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
91                 callerUid = Process.myUid(),
92                 resultSender = pi.intentSender,
93                 intentDispatcher = intentDispatcher
94             )
95 
96         // Invoke as in the previous test, but this time say that the selection was cross-profile.
97         resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, true)
98         runCurrent()
99 
100         val intentReceived = deferred.await()
101         val chooserResult =
102             intentReceived.getParcelableExtra(
103                 Intent.EXTRA_CHOOSER_RESULT,
104                 ChooserResult::class.java
105             )
106         assertThat(chooserResult).isNotNull()
107         assertThat(chooserResult?.type).isEqualTo(ChooserResult.CHOOSER_RESULT_UNKNOWN)
108         assertThat(chooserResult?.selectedComponent).isNull()
109         assertThat(chooserResult?.isShortcut).isTrue()
110         assertThat(intentReceived.hasExtra(Intent.EXTRA_CHOSEN_COMPONENT)).isFalse()
111     }
112 
113     @DisableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
114     @Test
115     fun onComponentSelected_chooserResultDisabled() = runTest {
116         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
117         val deferred = CompletableDeferred<Intent>()
118         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
119 
120         val resultSender =
121             ShareResultSenderImpl(
122                 scope = this,
123                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
124                 callerUid = Process.myUid(),
125                 resultSender = pi.intentSender,
126                 intentDispatcher = intentDispatcher
127             )
128 
129         resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, false)
130         runCurrent()
131 
132         val intentReceived = deferred.await()
133         val componentName =
134             intentReceived.getParcelableExtra(
135                 Intent.EXTRA_CHOSEN_COMPONENT,
136                 ComponentName::class.java
137             )
138 
139         assertWithMessage("EXTRA_CHOSEN_COMPONENT from received intent")
140             .that(componentName)
141             .isEqualTo(ComponentName("example.com", "Foo"))
142 
143         assertWithMessage("received intent has EXTRA_CHOOSER_RESULT")
144             .that(intentReceived.hasExtra(Intent.EXTRA_CHOOSER_RESULT))
145             .isFalse()
146     }
147 
148     @DisableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
149     @Test
150     fun onComponentSelected_crossProfile_chooserResultDisabled() = runTest {
151         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
152         val deferred = CompletableDeferred<Intent>()
153         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
154 
155         val resultSender =
156             ShareResultSenderImpl(
157                 scope = this,
158                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
159                 callerUid = Process.myUid(),
160                 resultSender = pi.intentSender,
161                 intentDispatcher = intentDispatcher
162             )
163 
164         // Invoke as in the previous test, but this time say that the selection was cross-profile.
165         resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, true)
166         runCurrent()
167 
168         // In the pre-ChooserResult API, no callback intent is sent for cross-profile selections.
169         assertWithMessage("deferred result isComplete").that(deferred.isCompleted).isFalse()
170     }
171 
172     @EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
173     @Test
174     fun onActionSelected_chooserResultEnabled() = runTest {
175         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
176         val deferred = CompletableDeferred<Intent>()
177         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
178 
179         val resultSender =
180             ShareResultSenderImpl(
181                 scope = this,
182                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
183                 callerUid = Process.myUid(),
184                 resultSender = pi.intentSender,
185                 intentDispatcher = intentDispatcher
186             )
187 
188         resultSender.onActionSelected(ShareAction.SYSTEM_COPY)
189         runCurrent()
190 
191         val intentReceived = deferred.await()
192         val chosenComponent =
193             intentReceived.getParcelableExtra(
194                 Intent.EXTRA_CHOSEN_COMPONENT,
195                 ChooserResult::class.java
196             )
197         assertThat(chosenComponent).isNull()
198 
199         val chooserResult =
200             intentReceived.getParcelableExtra(
201                 Intent.EXTRA_CHOOSER_RESULT,
202                 ChooserResult::class.java
203             )
204         assertThat(chooserResult).isNotNull()
205         assertThat(chooserResult?.type).isEqualTo(ChooserResult.CHOOSER_RESULT_COPY)
206         assertThat(chooserResult?.selectedComponent).isNull()
207         assertThat(chooserResult?.isShortcut).isFalse()
208     }
209 
210     @DisableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
211     @Test
212     fun onActionSelected_chooserResultDisabled() = runTest {
213         val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
214         val deferred = CompletableDeferred<Intent>()
215         val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
216 
217         val resultSender =
218             ShareResultSenderImpl(
219                 scope = this,
220                 backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
221                 callerUid = Process.myUid(),
222                 resultSender = pi.intentSender,
223                 intentDispatcher = intentDispatcher
224             )
225 
226         resultSender.onActionSelected(ShareAction.SYSTEM_COPY)
227         runCurrent()
228 
229         // No result should have been sent, this should never complete
230         assertWithMessage("deferred result isComplete").that(deferred.isCompleted).isFalse()
231     }
232 }
233