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.settingslib.drawer; 18 19 import android.app.ActivityManager; 20 import static org.mockito.Mockito.verify; 21 import android.content.IContentProvider; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ActivityInfo; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.content.pm.ResolveInfo; 30 import android.content.res.Resources; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.provider.Settings.Global; 37 import android.text.TextUtils; 38 import android.util.ArrayMap; 39 import android.util.Pair; 40 41 import com.android.settingslib.SuggestionParser; 42 import com.android.settingslib.TestConfig; 43 import com.android.settingslib.drawer.TileUtilsTest; 44 import static org.mockito.Mockito.atLeastOnce; 45 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.mockito.ArgumentMatcher; 50 import org.mockito.Mock; 51 import org.mockito.MockitoAnnotations; 52 import org.robolectric.shadows.ShadowApplication; 53 import org.robolectric.RobolectricTestRunner; 54 import org.robolectric.RuntimeEnvironment; 55 import org.robolectric.annotation.Config; 56 57 import java.util.ArrayList; 58 import java.util.List; 59 import java.util.Map; 60 61 import static com.google.common.truth.Truth.assertThat; 62 import static org.mockito.Matchers.any; 63 import static org.mockito.Matchers.anyInt; 64 import static org.mockito.Matchers.anyString; 65 import static org.mockito.Matchers.argThat; 66 import static org.mockito.Matchers.eq; 67 import static org.mockito.Mockito.spy; 68 import static org.mockito.Mockito.when; 69 import org.mockito.ArgumentCaptor; 70 71 72 @RunWith(RobolectricTestRunner.class) 73 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 74 public class TileUtilsTest { 75 76 @Mock 77 private Context mContext; 78 @Mock 79 private PackageManager mPackageManager; 80 @Mock 81 private Resources mResources; 82 @Mock 83 private UserManager mUserManager; 84 @Mock 85 private IContentProvider mIContentProvider; 86 @Mock 87 private ContentResolver mContentResolver; 88 89 private static final String URI_GET_SUMMARY = "content://authority/text/summary"; 90 private static final String URI_GET_ICON = "content://authority/icon/my_icon"; 91 92 @Before 93 public void setUp() throws NameNotFoundException { 94 MockitoAnnotations.initMocks(this); 95 when(mContext.getPackageManager()).thenReturn(mPackageManager); 96 when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources); 97 mContentResolver = spy(RuntimeEnvironment.application.getContentResolver()); 98 when(mContext.getContentResolver()).thenReturn(mContentResolver); 99 } 100 101 @Test 102 public void getTilesForIntent_shouldParseCategory() { 103 final String testCategory = "category1"; 104 Intent intent = new Intent(); 105 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 106 List<Tile> outTiles = new ArrayList<>(); 107 List<ResolveInfo> info = new ArrayList<>(); 108 info.add(newInfo(true, testCategory)); 109 Map<Pair<String, String>, Tile> cache = new ArrayMap<>(); 110 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 111 .thenReturn(info); 112 113 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 114 null /* defaultCategory */, outTiles, false /* usePriority */, 115 false /* checkCategory */); 116 117 assertThat(outTiles.size()).isEqualTo(1); 118 assertThat(outTiles.get(0).category).isEqualTo(testCategory); 119 } 120 121 @Test 122 public void getTilesForIntent_shouldParseKeyHintForSystemApp() { 123 String keyHint = "key"; 124 Intent intent = new Intent(); 125 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 126 List<Tile> outTiles = new ArrayList<>(); 127 List<ResolveInfo> info = new ArrayList<>(); 128 ResolveInfo resolveInfo = newInfo(true, null /* category */, keyHint); 129 info.add(resolveInfo); 130 131 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 132 .thenReturn(info); 133 134 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 135 null /* defaultCategory */, outTiles, false /* usePriority */, 136 false /* checkCategory */); 137 138 assertThat(outTiles.size()).isEqualTo(1); 139 assertThat(outTiles.get(0).key).isEqualTo(keyHint); 140 } 141 142 @Test 143 public void getTilesForIntent_shouldSkipNonSystemApp() { 144 final String testCategory = "category1"; 145 Intent intent = new Intent(); 146 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 147 List<Tile> outTiles = new ArrayList<>(); 148 List<ResolveInfo> info = new ArrayList<>(); 149 info.add(newInfo(false, testCategory)); 150 151 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 152 .thenReturn(info); 153 154 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 155 null /* defaultCategory */, outTiles, false /* usePriority */, 156 false /* checkCategory */); 157 158 assertThat(outTiles.isEmpty()).isTrue(); 159 } 160 161 @Test 162 public void getTilesForIntent_shouldSkipFilteredApps() { 163 final String testCategory = "category1"; 164 Intent intent = new Intent(); 165 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 166 List<Tile> outTiles = new ArrayList<>(); 167 List<ResolveInfo> info = new ArrayList<>(); 168 ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, 169 URI_GET_SUMMARY); 170 addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google"); 171 addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true"); 172 info.add(resolveInfo); 173 174 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 175 .thenReturn(info); 176 177 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 178 null /* defaultCategory */, outTiles, false /* usePriority */, 179 false /* checkCategory */); 180 181 assertThat(outTiles.size()).isEqualTo(1); 182 SuggestionParser parser = new SuggestionParser(mContext, null); 183 parser.filterSuggestions(outTiles, 0, false); 184 assertThat(outTiles.size()).isEqualTo(0); 185 } 186 187 @Test 188 public void getCategories_shouldHandleExtraIntentAction() { 189 final String testCategory = "category1"; 190 final String testAction = "action1"; 191 Map<Pair<String, String>, Tile> cache = new ArrayMap<>(); 192 List<ResolveInfo> info = new ArrayList<>(); 193 info.add(newInfo(true, testCategory)); 194 Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1); 195 when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); 196 List<UserHandle> userHandleList = new ArrayList<>(); 197 userHandleList.add(UserHandle.CURRENT); 198 when(mUserManager.getUserProfiles()).thenReturn(userHandleList); 199 200 when(mPackageManager.queryIntentActivitiesAsUser(argThat(new ArgumentMatcher<Intent>() { 201 public boolean matches(Object event) { 202 return testAction.equals(((Intent) event).getAction()); 203 } 204 }), anyInt(), anyInt())).thenReturn(info); 205 206 List<DashboardCategory> categoryList = TileUtils.getCategories( 207 mContext, cache, false /* categoryDefinedInManifest */, testAction, 208 TileUtils.SETTING_PKG); 209 assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory); 210 } 211 212 @Test 213 public void getCategories_withPackageName() throws Exception { 214 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 215 Map<Pair<String, String>, Tile> cache = new ArrayMap<>(); 216 Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1); 217 when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); 218 List<UserHandle> userHandleList = new ArrayList<>(); 219 220 userHandleList.add(new UserHandle(ActivityManager.getCurrentUser())); 221 when(mUserManager.getUserProfiles()).thenReturn(userHandleList); 222 223 TileUtils.getCategories( 224 mContext, cache, false /* categoryDefinedInManifest */, null /* action */, 225 TileUtils.SETTING_PKG); 226 verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser( 227 intentCaptor.capture(), anyInt(), anyInt()); 228 229 assertThat(intentCaptor.getAllValues().get(0).getPackage()) 230 .isEqualTo(TileUtils.SETTING_PKG); 231 } 232 233 @Test 234 public void getTilesForIntent_shouldReadMetadataTitleAsString() throws RemoteException { 235 Intent intent = new Intent(); 236 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 237 List<Tile> outTiles = new ArrayList<>(); 238 List<ResolveInfo> info = new ArrayList<>(); 239 ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, 240 URI_GET_SUMMARY, "my title", 0); 241 info.add(resolveInfo); 242 243 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 244 .thenReturn(info); 245 246 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 247 null /* defaultCategory */, outTiles, false /* usePriority */, 248 false /* checkCategory */); 249 250 assertThat(outTiles.size()).isEqualTo(1); 251 assertThat(outTiles.get(0).title).isEqualTo("my title"); 252 } 253 254 @Test 255 public void getTilesForIntent_shouldReadMetadataTitleFromResource() throws RemoteException { 256 Intent intent = new Intent(); 257 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 258 List<Tile> outTiles = new ArrayList<>(); 259 List<ResolveInfo> info = new ArrayList<>(); 260 ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, 261 URI_GET_SUMMARY, null, 123); 262 info.add(resolveInfo); 263 264 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 265 .thenReturn(info); 266 267 when(mResources.getString(eq(123))) 268 .thenReturn("my localized title"); 269 270 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 271 null /* defaultCategory */, outTiles, false /* usePriority */, 272 false /* checkCategory */); 273 274 assertThat(outTiles.size()).isEqualTo(1); 275 assertThat(outTiles.get(0).title).isEqualTo("my localized title"); 276 } 277 278 @Test 279 public void getTilesForIntent_shouldNotProcessInvalidUriContentSystemApp() 280 throws RemoteException { 281 Intent intent = new Intent(); 282 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 283 List<Tile> outTiles = new ArrayList<>(); 284 List<ResolveInfo> info = new ArrayList<>(); 285 ResolveInfo resolveInfo = newInfo(true, null /* category */, null, null, URI_GET_SUMMARY); 286 info.add(resolveInfo); 287 288 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 289 .thenReturn(info); 290 291 // Case 1: No provider associated with the uri specified. 292 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 293 null /* defaultCategory */, outTiles, false /* usePriority */, 294 false /* checkCategory */); 295 296 assertThat(outTiles.size()).isEqualTo(1); 297 assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159); 298 assertThat(outTiles.get(0).summary).isEqualTo("static-summary"); 299 300 // Case 2: Empty bundle. 301 Bundle bundle = new Bundle(); 302 when(mIContentProvider.call(anyString(), 303 eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY), 304 any())).thenReturn(bundle); 305 when(mContentResolver.acquireUnstableProvider(anyString())) 306 .thenReturn(mIContentProvider); 307 when(mContentResolver.acquireUnstableProvider(any(Uri.class))) 308 .thenReturn(mIContentProvider); 309 310 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 311 null /* defaultCategory */, outTiles, false /* usePriority */, 312 false /* checkCategory */); 313 314 assertThat(outTiles.size()).isEqualTo(1); 315 assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159); 316 assertThat(outTiles.get(0).summary).isEqualTo("static-summary"); 317 } 318 319 @Test 320 public void getTilesForIntent_shouldProcessUriContentForSystemApp() throws RemoteException { 321 Intent intent = new Intent(); 322 Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); 323 List<Tile> outTiles = new ArrayList<>(); 324 List<ResolveInfo> info = new ArrayList<>(); 325 ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, 326 URI_GET_SUMMARY); 327 info.add(resolveInfo); 328 329 when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) 330 .thenReturn(info); 331 332 TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, 333 null /* defaultCategory */, outTiles, false /* usePriority */, 334 false /* checkCategory */); 335 336 assertThat(outTiles.size()).isEqualTo(1); 337 } 338 339 public static ResolveInfo newInfo(boolean systemApp, String category) { 340 return newInfo(systemApp, category, null); 341 } 342 343 private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint) { 344 return newInfo(systemApp, category, keyHint, null, null); 345 } 346 347 private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, 348 String iconUri, String summaryUri) { 349 return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0); 350 } 351 352 private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, 353 String iconUri, String summaryUri, String title, int titleResId) { 354 355 ResolveInfo info = new ResolveInfo(); 356 info.system = systemApp; 357 info.activityInfo = new ActivityInfo(); 358 info.activityInfo.packageName = "abc"; 359 info.activityInfo.name = "123"; 360 info.activityInfo.metaData = new Bundle(); 361 info.activityInfo.metaData.putString("com.android.settings.category", category); 362 info.activityInfo.metaData.putInt("com.android.settings.icon", 314159); 363 info.activityInfo.metaData.putString("com.android.settings.summary", "static-summary"); 364 if (keyHint != null) { 365 info.activityInfo.metaData.putString("com.android.settings.keyhint", keyHint); 366 } 367 if (iconUri != null) { 368 info.activityInfo.metaData.putString("com.android.settings.icon_uri", iconUri); 369 } 370 if (summaryUri != null) { 371 info.activityInfo.metaData.putString("com.android.settings.summary_uri", summaryUri); 372 } 373 if (title != null) { 374 info.activityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title); 375 } 376 if (titleResId != 0) { 377 info.activityInfo.metaData.putInt( 378 TileUtils.META_DATA_PREFERENCE_TITLE_RES_ID, titleResId); 379 } 380 info.activityInfo.applicationInfo = new ApplicationInfo(); 381 if (systemApp) { 382 info.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; 383 } 384 return info; 385 } 386 387 private void addMetadataToInfo(ResolveInfo info, String key, String value) { 388 if (!TextUtils.isEmpty(key)) { 389 if (info.activityInfo == null) { 390 info.activityInfo = new ActivityInfo(); 391 } 392 if (info.activityInfo.metaData == null) { 393 info.activityInfo.metaData = new Bundle(); 394 } 395 info.activityInfo.metaData.putString(key, value); 396 } 397 } 398 } 399