blob: 1080a9fcfe715c198314545ff59bebd185125b75 [file] [log] [blame]
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001/*
2 * Copyright (C) 2016 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
17package com.android.internal.app;
18
19import com.android.internal.R;
20import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
21
22import org.junit.Before;
23import org.junit.Rule;
24import org.junit.Test;
25import org.junit.runner.RunWith;
26import org.mockito.Mockito;
27
Kang Li64b018e2017-01-05 17:30:06 -080028import android.app.usage.UsageStats;
29import android.app.usage.UsageStatsManager;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080030import android.content.Intent;
31import android.content.pm.ResolveInfo;
Kang Li64b018e2017-01-05 17:30:06 -080032import android.os.UserHandle;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080033import android.support.test.InstrumentationRegistry;
34import android.support.test.rule.ActivityTestRule;
35import android.support.test.runner.AndroidJUnit4;
36
37import java.util.ArrayList;
38import java.util.List;
Kang Li64b018e2017-01-05 17:30:06 -080039import java.util.Map;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080040
41import static android.support.test.espresso.Espresso.onView;
42import static android.support.test.espresso.action.ViewActions.click;
43import static android.support.test.espresso.assertion.ViewAssertions.matches;
44import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
45import static android.support.test.espresso.matcher.ViewMatchers.withId;
46import static android.support.test.espresso.matcher.ViewMatchers.withText;
47import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
48import static org.hamcrest.CoreMatchers.is;
49import static org.hamcrest.CoreMatchers.not;
50import static org.hamcrest.MatcherAssert.assertThat;
51import static org.mockito.Mockito.when;
Kang Li64b018e2017-01-05 17:30:06 -080052import static org.mockito.Mockito.verify;
53import static org.mockito.Mockito.times;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080054
55/**
56 * Chooser activity instrumentation tests
57 */
58@RunWith(AndroidJUnit4.class)
59public class ChooserActivityTest {
60 @Rule
61 public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
62 new ActivityTestRule<>(ChooserWrapperActivity.class, false,
63 false);
64
65 @Before
66 public void cleanOverrideData() {
67 sOverrides.reset();
68 }
69
70 @Test
71 public void customTitle() throws InterruptedException {
72 Intent sendIntent = createSendImageIntent();
Hakan Seyalioglu79b69f02017-01-12 17:08:02 -080073 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
74
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080075 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
76 Mockito.anyBoolean(),
Hakan Seyalioglu79b69f02017-01-12 17:08:02 -080077 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080078 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
79 waitForIdle();
80 onView(withId(R.id.title)).check(matches(withText("chooser test")));
81 }
82
83 @Test
84 public void emptyTitle() throws InterruptedException {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080085 Intent sendIntent = createSendImageIntent();
Hakan Seyalioglu79b69f02017-01-12 17:08:02 -080086 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
87
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080088 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
89 Mockito.anyBoolean(),
Hakan Seyalioglu79b69f02017-01-12 17:08:02 -080090 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080091 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
92 waitForIdle();
93 onView(withId(R.id.title))
94 .check(matches(withText(R.string.whichSendApplication)));
95 }
96
97 @Test
98 public void twoOptionsAndUserSelectsOne() throws InterruptedException {
99 Intent sendIntent = createSendImageIntent();
100 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
101
102 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
103 Mockito.anyBoolean(),
104 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
105
106 final ChooserWrapperActivity activity = mActivityRule
107 .launchActivity(Intent.createChooser(sendIntent, null));
108 waitForIdle();
109
110 assertThat(activity.getAdapter().getCount(), is(2));
111 onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
112
113 ResolveInfo[] chosen = new ResolveInfo[1];
114 sOverrides.onSafelyStartCallback = targetInfo -> {
115 chosen[0] = targetInfo.getResolveInfo();
116 return true;
117 };
118
119 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
120 onView(withText(toChoose.activityInfo.name))
121 .perform(click());
122 waitForIdle();
123 assertThat(chosen[0], is(toChoose));
124 }
125
126 @Test
Kang Li9fa2a2c2017-01-06 13:33:24 -0800127 public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
Kang Li64b018e2017-01-05 17:30:06 -0800128 Intent sendIntent = createSendImageIntent();
129 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
130
131 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
132 Mockito.anyBoolean(),
133 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
134
135 final ChooserWrapperActivity activity = mActivityRule
136 .launchActivity(Intent.createChooser(sendIntent, null));
137 waitForIdle();
138 UsageStatsManager usm = activity.getUsageStatsManager();
139 verify(sOverrides.resolverListController, times(1))
140 .sort(Mockito.any(List.class));
141 assertThat(activity.getIsSelected(), is(false));
142 sOverrides.onSafelyStartCallback = targetInfo -> {
143 return true;
144 };
Kang Li64b018e2017-01-05 17:30:06 -0800145 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
Kang Li64b018e2017-01-05 17:30:06 -0800146 onView(withText(toChoose.activityInfo.name))
147 .perform(click());
148 waitForIdle();
149 verify(sOverrides.resolverListController, times(1))
Kang Li9fa2a2c2017-01-06 13:33:24 -0800150 .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString());
151 verify(sOverrides.resolverListController, times(1))
Kang Li64b018e2017-01-05 17:30:06 -0800152 .updateModel(toChoose.activityInfo.getComponentName());
153 assertThat(activity.getIsSelected(), is(true));
Kang Li64b018e2017-01-05 17:30:06 -0800154 }
155
156 @Test
157 public void reportChooserSelection() throws InterruptedException {
158 Intent sendIntent = createSendImageIntent();
159 final ChooserWrapperActivity activity = mActivityRule
160 .launchActivity(Intent.createChooser(sendIntent, null));
161 waitForIdle();
162 UsageStatsManager usm = activity.getUsageStatsManager();
163 String packageName = "test_package";
164 String action = "test_action";
165 String annotation = "test_annotation";
166 long beforeReport = getCount(usm, packageName, action, annotation);
167 usm.reportChooserSelection(packageName, activity.getUserId(), annotation, null, action);
168 long afterReport = getCount(usm, packageName, action, annotation);
169 assertThat(afterReport, is(beforeReport + 1l));
170 }
171
172 @Test
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800173 public void noResultsFromPackageManager() {
174 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
175 Mockito.anyBoolean(),
176 Mockito.isA(List.class))).thenReturn(null);
177 Intent sendIntent = createSendImageIntent();
178 final ChooserWrapperActivity activity = mActivityRule
179 .launchActivity(Intent.createChooser(sendIntent, null));
180 waitForIdle();
181 assertThat(activity.isFinishing(), is(false));
182
183 onView(withId(R.id.empty)).check(matches(isDisplayed()));
184 onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed())));
185 InstrumentationRegistry.getInstrumentation().runOnMainSync(
186 () -> activity.getAdapter().handlePackagesChanged()
187 );
188 // backward compatibility. looks like we finish when data is empty after package change
189 assertThat(activity.isFinishing(), is(true));
190 }
191
192 @Test
193 public void autoLaunchSingleResult() throws InterruptedException {
194 ResolveInfo[] chosen = new ResolveInfo[1];
195 sOverrides.onSafelyStartCallback = targetInfo -> {
196 chosen[0] = targetInfo.getResolveInfo();
197 return true;
198 };
199
200 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
201 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
202 Mockito.anyBoolean(),
203 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
204
205 Intent sendIntent = createSendImageIntent();
206 final ChooserWrapperActivity activity = mActivityRule
207 .launchActivity(Intent.createChooser(sendIntent, null));
208 waitForIdle();
209
210 assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
211 assertThat(activity.isFinishing(), is(true));
212 }
213
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800214 @Test
215 public void hasOtherProfileOneOption() throws Exception {
216 Intent sendIntent = createSendImageIntent();
217 List<ResolvedComponentInfo> resolvedComponentInfos =
218 createResolvedComponentsForTestWithOtherProfile(2);
219 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
220
221 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
222 Mockito.anyBoolean(),
223 Mockito.anyBoolean(),
224 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
225
226 final ChooserWrapperActivity activity = mActivityRule
227 .launchActivity(Intent.createChooser(sendIntent, null));
228 waitForIdle();
229
230 // The other entry is filtered to the other profile slot
231 assertThat(activity.getAdapter().getCount(), is(1));
232
233 ResolveInfo[] chosen = new ResolveInfo[1];
234 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
235 chosen[0] = targetInfo.getResolveInfo();
236 return true;
237 };
238
239 // Make a stable copy of the components as the original list may be modified
240 List<ResolvedComponentInfo> stableCopy =
241 createResolvedComponentsForTestWithOtherProfile(2);
242 // Check that the "Other Profile" activity is put in the right spot
243 onView(withId(R.id.profile_button)).check(matches(
244 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
245 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
246 .perform(click());
247 waitForIdle();
248 assertThat(chosen[0], is(toChoose));
249 }
250
251 @Test
252 public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
253 Intent sendIntent = createSendImageIntent();
254 List<ResolvedComponentInfo> resolvedComponentInfos =
255 createResolvedComponentsForTestWithOtherProfile(3);
256 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
257
258 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
259 Mockito.anyBoolean(),
260 Mockito.anyBoolean(),
261 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
262 when(ChooserWrapperActivity.sOverrides.resolverListController.getLastChosen())
263 .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
264
265 final ChooserWrapperActivity activity = mActivityRule
266 .launchActivity(Intent.createChooser(sendIntent, null));
267 waitForIdle();
268
269 // The other entry is filtered to the other profile slot
270 assertThat(activity.getAdapter().getCount(), is(2));
271
272 ResolveInfo[] chosen = new ResolveInfo[1];
273 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
274 chosen[0] = targetInfo.getResolveInfo();
275 return true;
276 };
277
278 // Make a stable copy of the components as the original list may be modified
279 List<ResolvedComponentInfo> stableCopy =
280 createResolvedComponentsForTestWithOtherProfile(3);
281 // Check that the "Other Profile" activity is put in the right spot
282 onView(withId(R.id.profile_button)).check(matches(
283 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
284 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
285 .perform(click());
286 waitForIdle();
287 assertThat(chosen[0], is(toChoose));
288 }
289
290 @Test
291 public void hasLastChosenActivityAndOtherProfile() throws Exception {
292 Intent sendIntent = createSendImageIntent();
293 List<ResolvedComponentInfo> resolvedComponentInfos =
294 createResolvedComponentsForTestWithOtherProfile(3);
295 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
296
297 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
298 Mockito.anyBoolean(),
299 Mockito.anyBoolean(),
300 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
301
302 final ChooserWrapperActivity activity = mActivityRule
303 .launchActivity(Intent.createChooser(sendIntent, null));
304 waitForIdle();
305
306 // The other entry is filtered to the last used slot
307 assertThat(activity.getAdapter().getCount(), is(2));
308
309 ResolveInfo[] chosen = new ResolveInfo[1];
310 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
311 chosen[0] = targetInfo.getResolveInfo();
312 return true;
313 };
314
315 // Make a stable copy of the components as the original list may be modified
316 List<ResolvedComponentInfo> stableCopy =
317 createResolvedComponentsForTestWithOtherProfile(3);
318 // Check that the "Other Profile" activity is put in the right spot
319 onView(withId(R.id.profile_button)).check(matches(
320 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
321 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
322 .perform(click());
323 waitForIdle();
324 assertThat(chosen[0], is(toChoose));
325 }
326
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800327 private Intent createSendImageIntent() {
328 Intent sendIntent = new Intent();
329 sendIntent.setAction(Intent.ACTION_SEND);
330 sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
331 sendIntent.setType("image/jpeg");
332 return sendIntent;
333 }
334
335 private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
336 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
337 for (int i = 0; i < numberOfResults; i++) {
Hakan Seyalioglu9149dca2017-01-17 12:20:01 -0800338 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800339 }
340 return infoList;
341 }
342
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800343 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
344 int numberOfResults) {
345 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
346 for (int i = 0; i < numberOfResults; i++) {
347 if (i == 0) {
348 infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i));
349 } else {
350 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
351 }
352 }
353 return infoList;
354 }
355
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800356 private void waitForIdle() {
357 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
358 }
Kang Li64b018e2017-01-05 17:30:06 -0800359
360 private Integer getCount(
361 UsageStatsManager usm, String packageName, String action, String annotation) {
362 if (usm == null) {
363 return 0;
364 }
365 Map<String, UsageStats> stats =
366 usm.queryAndAggregateUsageStats(Long.MIN_VALUE, Long.MAX_VALUE);
367 UsageStats packageStats = stats.get(packageName);
368 if (packageStats == null || packageStats.mChooserCounts == null
369 || packageStats.mChooserCounts.get(action) == null) {
370 return 0;
371 }
372 return packageStats.mChooserCounts.get(action).getOrDefault(annotation, 0);
373 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800374}