1 /* 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 17 package com.android.internal.app; 18 19 import com.android.internal.R; 20 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; 21 22 import org.junit.Before; 23 import org.junit.Rule; 24 import org.junit.Test; 25 import org.junit.runner.RunWith; 26 import org.mockito.Mockito; 27 28 import android.app.usage.UsageStats; 29 import android.app.usage.UsageStatsManager; 30 import android.content.Intent; 31 import android.content.pm.ResolveInfo; 32 import android.os.UserHandle; 33 import android.support.test.InstrumentationRegistry; 34 import android.support.test.rule.ActivityTestRule; 35 import android.support.test.runner.AndroidJUnit4; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Map; 40 41 import static android.support.test.espresso.Espresso.onView; 42 import static android.support.test.espresso.action.ViewActions.click; 43 import static android.support.test.espresso.assertion.ViewAssertions.matches; 44 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 45 import static android.support.test.espresso.matcher.ViewMatchers.withId; 46 import static android.support.test.espresso.matcher.ViewMatchers.withText; 47 import static com.android.internal.app.ChooserWrapperActivity.sOverrides; 48 import static org.hamcrest.CoreMatchers.is; 49 import static org.hamcrest.CoreMatchers.not; 50 import static org.hamcrest.MatcherAssert.assertThat; 51 import static org.mockito.Mockito.when; 52 import static org.mockito.Mockito.verify; 53 import static org.mockito.Mockito.times; 54 55 /** 56 * Chooser activity instrumentation tests 57 */ 58 @RunWith(AndroidJUnit4.class) 59 public 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(); 73 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 74 75 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), 76 Mockito.anyBoolean(), 77 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 78 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 { 85 Intent sendIntent = createSendImageIntent(); 86 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 87 88 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), 89 Mockito.anyBoolean(), 90 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 91 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 127 public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException { 128 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 }; 145 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 146 onView(withText(toChoose.activityInfo.name)) 147 .perform(click()); 148 waitForIdle(); 149 verify(sOverrides.resolverListController, times(1)) 150 .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString()); 151 verify(sOverrides.resolverListController, times(1)) 152 .updateModel(toChoose.activityInfo.getComponentName()); 153 assertThat(activity.getIsSelected(), is(true)); 154 } 155 156 @Test 157 public void noResultsFromPackageManager() { 158 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), 159 Mockito.anyBoolean(), 160 Mockito.isA(List.class))).thenReturn(null); 161 Intent sendIntent = createSendImageIntent(); 162 final ChooserWrapperActivity activity = mActivityRule 163 .launchActivity(Intent.createChooser(sendIntent, null)); 164 waitForIdle(); 165 assertThat(activity.isFinishing(), is(false)); 166 167 onView(withId(R.id.empty)).check(matches(isDisplayed())); 168 onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed()))); 169 InstrumentationRegistry.getInstrumentation().runOnMainSync( 170 () -> activity.getAdapter().handlePackagesChanged() 171 ); 172 // backward compatibility. looks like we finish when data is empty after package change 173 assertThat(activity.isFinishing(), is(true)); 174 } 175 176 @Test 177 public void autoLaunchSingleResult() throws InterruptedException { 178 ResolveInfo[] chosen = new ResolveInfo[1]; 179 sOverrides.onSafelyStartCallback = targetInfo -> { 180 chosen[0] = targetInfo.getResolveInfo(); 181 return true; 182 }; 183 184 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1); 185 when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), 186 Mockito.anyBoolean(), 187 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 188 189 Intent sendIntent = createSendImageIntent(); 190 final ChooserWrapperActivity activity = mActivityRule 191 .launchActivity(Intent.createChooser(sendIntent, null)); 192 waitForIdle(); 193 194 assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 195 assertThat(activity.isFinishing(), is(true)); 196 } 197 198 @Test 199 public void hasOtherProfileOneOption() throws Exception { 200 Intent sendIntent = createSendImageIntent(); 201 List<ResolvedComponentInfo> resolvedComponentInfos = 202 createResolvedComponentsForTestWithOtherProfile(2); 203 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 204 205 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( 206 Mockito.anyBoolean(), 207 Mockito.anyBoolean(), 208 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 209 210 final ChooserWrapperActivity activity = mActivityRule 211 .launchActivity(Intent.createChooser(sendIntent, null)); 212 waitForIdle(); 213 214 // The other entry is filtered to the other profile slot 215 assertThat(activity.getAdapter().getCount(), is(1)); 216 217 ResolveInfo[] chosen = new ResolveInfo[1]; 218 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> { 219 chosen[0] = targetInfo.getResolveInfo(); 220 return true; 221 }; 222 223 // Make a stable copy of the components as the original list may be modified 224 List<ResolvedComponentInfo> stableCopy = 225 createResolvedComponentsForTestWithOtherProfile(2); 226 // Check that the "Other Profile" activity is put in the right spot 227 onView(withId(R.id.profile_button)).check(matches( 228 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name))); 229 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 230 .perform(click()); 231 waitForIdle(); 232 assertThat(chosen[0], is(toChoose)); 233 } 234 235 @Test 236 public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception { 237 Intent sendIntent = createSendImageIntent(); 238 List<ResolvedComponentInfo> resolvedComponentInfos = 239 createResolvedComponentsForTestWithOtherProfile(3); 240 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 241 242 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( 243 Mockito.anyBoolean(), 244 Mockito.anyBoolean(), 245 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 246 when(ChooserWrapperActivity.sOverrides.resolverListController.getLastChosen()) 247 .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0)); 248 249 final ChooserWrapperActivity activity = mActivityRule 250 .launchActivity(Intent.createChooser(sendIntent, null)); 251 waitForIdle(); 252 253 // The other entry is filtered to the other profile slot 254 assertThat(activity.getAdapter().getCount(), is(2)); 255 256 ResolveInfo[] chosen = new ResolveInfo[1]; 257 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> { 258 chosen[0] = targetInfo.getResolveInfo(); 259 return true; 260 }; 261 262 // Make a stable copy of the components as the original list may be modified 263 List<ResolvedComponentInfo> stableCopy = 264 createResolvedComponentsForTestWithOtherProfile(3); 265 // Check that the "Other Profile" activity is put in the right spot 266 onView(withId(R.id.profile_button)).check(matches( 267 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name))); 268 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 269 .perform(click()); 270 waitForIdle(); 271 assertThat(chosen[0], is(toChoose)); 272 } 273 274 @Test 275 public void hasLastChosenActivityAndOtherProfile() throws Exception { 276 Intent sendIntent = createSendImageIntent(); 277 List<ResolvedComponentInfo> resolvedComponentInfos = 278 createResolvedComponentsForTestWithOtherProfile(3); 279 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 280 281 when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( 282 Mockito.anyBoolean(), 283 Mockito.anyBoolean(), 284 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); 285 286 final ChooserWrapperActivity activity = mActivityRule 287 .launchActivity(Intent.createChooser(sendIntent, null)); 288 waitForIdle(); 289 290 // The other entry is filtered to the last used slot 291 assertThat(activity.getAdapter().getCount(), is(2)); 292 293 ResolveInfo[] chosen = new ResolveInfo[1]; 294 ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> { 295 chosen[0] = targetInfo.getResolveInfo(); 296 return true; 297 }; 298 299 // Make a stable copy of the components as the original list may be modified 300 List<ResolvedComponentInfo> stableCopy = 301 createResolvedComponentsForTestWithOtherProfile(3); 302 // Check that the "Other Profile" activity is put in the right spot 303 onView(withId(R.id.profile_button)).check(matches( 304 withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name))); 305 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 306 .perform(click()); 307 waitForIdle(); 308 assertThat(chosen[0], is(toChoose)); 309 } 310 311 private Intent createSendImageIntent() { 312 Intent sendIntent = new Intent(); 313 sendIntent.setAction(Intent.ACTION_SEND); 314 sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); 315 sendIntent.setType("image/jpeg"); 316 return sendIntent; 317 } 318 319 private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) { 320 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 321 for (int i = 0; i < numberOfResults; i++) { 322 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); 323 } 324 return infoList; 325 } 326 327 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( 328 int numberOfResults) { 329 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 330 for (int i = 0; i < numberOfResults; i++) { 331 if (i == 0) { 332 infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i)); 333 } else { 334 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); 335 } 336 } 337 return infoList; 338 } 339 340 private void waitForIdle() { 341 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 342 } 343 }