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.settings.dashboard; 18 19 import static android.content.Intent.EXTRA_USER; 20 21 import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR; 22 import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER; 23 import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; 24 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; 25 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI; 26 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; 27 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; 28 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI; 29 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE; 30 import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; 31 import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; 32 33 import static com.google.common.truth.Truth.assertThat; 34 35 import static org.mockito.ArgumentMatchers.any; 36 import static org.mockito.ArgumentMatchers.anyInt; 37 import static org.mockito.ArgumentMatchers.eq; 38 import static org.mockito.Mockito.doReturn; 39 import static org.mockito.Mockito.mock; 40 import static org.mockito.Mockito.never; 41 import static org.mockito.Mockito.spy; 42 import static org.mockito.Mockito.verify; 43 import static org.mockito.Mockito.verifyNoInteractions; 44 import static org.mockito.Mockito.when; 45 46 import android.app.Application; 47 import android.app.PendingIntent; 48 import android.content.Context; 49 import android.content.Intent; 50 import android.content.pm.ActivityInfo; 51 import android.content.pm.PackageManager; 52 import android.content.pm.ProviderInfo; 53 import android.content.pm.ResolveInfo; 54 import android.graphics.Bitmap; 55 import android.graphics.drawable.Drawable; 56 import android.graphics.drawable.Icon; 57 import android.os.Bundle; 58 import android.os.UserHandle; 59 import android.os.UserManager; 60 import android.util.Pair; 61 62 import androidx.fragment.app.Fragment; 63 import androidx.fragment.app.FragmentActivity; 64 import androidx.preference.Preference; 65 import androidx.preference.SwitchPreference; 66 import androidx.test.core.app.ApplicationProvider; 67 68 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 69 import com.android.settings.R; 70 import com.android.settings.SettingsActivity; 71 import com.android.settings.Utils; 72 import com.android.settings.homepage.TopLevelHighlightMixin; 73 import com.android.settings.homepage.TopLevelSettings; 74 import com.android.settings.search.SearchFeatureProviderImpl; 75 import com.android.settings.testutils.FakeFeatureFactory; 76 import com.android.settings.testutils.shadow.ShadowActivityEmbeddingUtils; 77 import com.android.settings.testutils.shadow.ShadowSplitController; 78 import com.android.settings.testutils.shadow.ShadowTileUtils; 79 import com.android.settings.testutils.shadow.ShadowUserManager; 80 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 81 import com.android.settingslib.drawer.ActivityTile; 82 import com.android.settingslib.drawer.CategoryKey; 83 import com.android.settingslib.drawer.ProviderTile; 84 import com.android.settingslib.drawer.Tile; 85 import com.android.settingslib.drawer.TileUtils; 86 87 import org.junit.Before; 88 import org.junit.Rule; 89 import org.junit.Test; 90 import org.junit.runner.RunWith; 91 import org.mockito.Answers; 92 import org.mockito.ArgumentCaptor; 93 import org.mockito.Mock; 94 import org.mockito.junit.MockitoJUnit; 95 import org.mockito.junit.MockitoRule; 96 import org.robolectric.Robolectric; 97 import org.robolectric.RobolectricTestRunner; 98 import org.robolectric.Shadows; 99 import org.robolectric.annotation.Config; 100 import org.robolectric.shadows.ShadowActivity; 101 import org.robolectric.util.ReflectionHelpers; 102 103 import java.util.ArrayList; 104 import java.util.List; 105 106 @RunWith(RobolectricTestRunner.class) 107 @Config(shadows = ShadowUserManager.class) 108 public class DashboardFeatureProviderImplTest { 109 @Rule 110 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 111 112 private static final String KEY = "key"; 113 private static final String SWITCH_URI = "content://com.android.settings/tile_switch"; 114 115 private final Application mApplication = ApplicationProvider.getApplicationContext(); 116 117 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 118 private FragmentActivity mActivity; 119 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 120 private UserManager mUserManager; 121 @Mock 122 private PackageManager mPackageManager; 123 private FakeFeatureFactory mFeatureFactory; 124 125 private Context mContext; 126 private ActivityInfo mActivityInfo; 127 private ProviderInfo mProviderInfo; 128 private Bundle mSwitchMetaData; 129 private DashboardFeatureProviderImpl mImpl; 130 private boolean mForceRoundedIcon; 131 private DashboardFragment mFragment; 132 133 @Before setUp()134 public void setUp() { 135 mContext = spy(mApplication); 136 doReturn(mApplication).when(mActivity).getApplicationContext(); 137 mForceRoundedIcon = false; 138 mActivityInfo = new ActivityInfo(); 139 mActivityInfo.packageName = mContext.getPackageName(); 140 mActivityInfo.name = "class"; 141 mActivityInfo.metaData = new Bundle(); 142 mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); 143 mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_SUMMARY, 144 R.string.about_settings_summary); 145 146 mProviderInfo = new ProviderInfo(); 147 mProviderInfo.packageName = mContext.getPackageName(); 148 mProviderInfo.name = "provider"; 149 mProviderInfo.authority = "com.android.settings"; 150 mSwitchMetaData = new Bundle(); 151 mSwitchMetaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); 152 mSwitchMetaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary); 153 mSwitchMetaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY); 154 mSwitchMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, SWITCH_URI); 155 156 doReturn(mPackageManager).when(mContext).getPackageManager(); 157 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 158 .thenReturn(new ResolveInfo()); 159 mFeatureFactory = FakeFeatureFactory.setupForTest(); 160 mImpl = new DashboardFeatureProviderImpl(mContext); 161 mFragment = new TestFragment(); 162 } 163 164 @Test shouldHoldAppContext()165 public void shouldHoldAppContext() { 166 assertThat(mImpl.mContext).isEqualTo(mContext.getApplicationContext()); 167 } 168 169 @Test bindPreference_shouldBindAllData()170 public void bindPreference_shouldBindAllData() { 171 final Preference preference = new Preference(mApplication); 172 final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); 173 mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); 174 doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) 175 .when(tile).getIcon(any(Context.class)); 176 mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); 177 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 178 preference, tile, "123", Preference.DEFAULT_ORDER); 179 180 assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); 181 assertThat(preference.getSummary()) 182 .isEqualTo(mContext.getText(R.string.about_settings_summary)); 183 assertThat(preference.getIcon()).isNotNull(); 184 assertThat(preference.getFragment()).isEqualTo( 185 mActivityInfo.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS)); 186 assertThat(preference.getOrder()).isEqualTo(tile.getOrder()); 187 } 188 189 @Test bindPreference_shouldBindAllSwitchData()190 public void bindPreference_shouldBindAllSwitchData() { 191 final Preference preference = new SwitchPreference(mApplication); 192 final Tile tile = spy(new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, 193 mSwitchMetaData)); 194 mSwitchMetaData.putInt(META_DATA_KEY_ORDER, 10); 195 doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) 196 .when(tile).getIcon(any(Context.class)); 197 final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers( 198 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key*/, 199 Preference.DEFAULT_ORDER); 200 201 assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); 202 assertThat(preference.getSummary()) 203 .isEqualTo(mContext.getText(R.string.about_settings_summary)); 204 assertThat(preference.getKey()).isEqualTo(KEY); 205 assertThat(preference.getIcon()).isNotNull(); 206 assertThat(preference.getOrder()).isEqualTo(tile.getOrder()); 207 assertThat(observers.get(0).getUri().toString()).isEqualTo(SWITCH_URI); 208 } 209 210 @Test bindPreference_providerTileWithPendingIntent_shouldBindIntent()211 public void bindPreference_providerTileWithPendingIntent_shouldBindIntent() { 212 final Preference preference = new SwitchPreference(mApplication); 213 Bundle metaData = new Bundle(); 214 metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); 215 metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary); 216 metaData.putInt(META_DATA_KEY_ORDER, 10); 217 metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY); 218 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData); 219 PendingIntent pendingIntent = 220 PendingIntent.getActivity(mApplication, 0, new Intent("test"), 0); 221 tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent); 222 223 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 224 preference, tile, "123", Preference.DEFAULT_ORDER); 225 226 assertThat(preference.getFragment()).isNull(); 227 assertThat(preference.getOnPreferenceClickListener()).isNotNull(); 228 assertThat(preference.getOrder()).isEqualTo(tile.getOrder()); 229 } 230 231 @Test bindPreference_noFragmentMetadata_shouldBindIntent()232 public void bindPreference_noFragmentMetadata_shouldBindIntent() { 233 final Preference preference = new Preference(mApplication); 234 mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); 235 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 236 237 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 238 preference, tile, "123", Preference.DEFAULT_ORDER); 239 240 assertThat(preference.getFragment()).isNull(); 241 assertThat(preference.getOnPreferenceClickListener()).isNotNull(); 242 assertThat(preference.getOrder()).isEqualTo(tile.getOrder()); 243 } 244 245 @Test bindPreference_noFragmentMetadata_shouldBindToProfileSelector()246 public void bindPreference_noFragmentMetadata_shouldBindToProfileSelector() { 247 final Preference preference = new Preference(mApplication); 248 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 249 tile.userHandle = new ArrayList<>(); 250 tile.userHandle.add(mock(UserHandle.class)); 251 tile.userHandle.add(mock(UserHandle.class)); 252 253 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 254 preference, tile, "123", Preference.DEFAULT_ORDER); 255 preference.getOnPreferenceClickListener().onPreferenceClick(null); 256 257 verify(mActivity).getSupportFragmentManager(); 258 } 259 260 @Test bindPreference_noFragmentMetadataSingleUser_shouldBindToDirectLaunchIntent()261 public void bindPreference_noFragmentMetadataSingleUser_shouldBindToDirectLaunchIntent() { 262 final Preference preference = new Preference(mApplication); 263 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 264 tile.userHandle = new ArrayList<>(); 265 tile.userHandle.add(mock(UserHandle.class)); 266 267 when(mActivity.getSystemService(Context.USER_SERVICE)) 268 .thenReturn(mUserManager); 269 270 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 271 preference, tile, "123", Preference.DEFAULT_ORDER); 272 preference.getOnPreferenceClickListener().onPreferenceClick(null); 273 274 verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( 275 any(Intent.class), 276 eq(MetricsEvent.SETTINGS_GESTURES)); 277 verify(mActivity) 278 .startActivityAsUser(any(Intent.class), any(UserHandle.class)); 279 } 280 281 @Test bindPreference_toInternalSettingActivity_shouldBindToDirectLaunchIntentAndNotLog()282 public void bindPreference_toInternalSettingActivity_shouldBindToDirectLaunchIntentAndNotLog() { 283 final Preference preference = new Preference(mApplication); 284 mActivityInfo.packageName = mApplication.getPackageName(); 285 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 286 tile.userHandle = new ArrayList<>(); 287 tile.userHandle.add(mock(UserHandle.class)); 288 289 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 290 preference, tile, "123", Preference.DEFAULT_ORDER); 291 preference.getOnPreferenceClickListener().onPreferenceClick(null); 292 verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( 293 any(Intent.class), 294 anyInt()); 295 verify(mActivity) 296 .startActivityAsUser(any(Intent.class), any(UserHandle.class)); 297 } 298 299 @Test bindPreference_nullPreference_shouldIgnore()300 public void bindPreference_nullPreference_shouldIgnore() { 301 final Tile tile = mock(Tile.class); 302 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 303 null /* keys */, tile, "123", Preference.DEFAULT_ORDER); 304 305 verifyNoInteractions(tile); 306 } 307 308 @Test bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority()309 public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() { 310 final Preference preference = new Preference(mApplication); 311 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 312 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 313 preference, tile, null /* key */, 314 Preference.DEFAULT_ORDER); 315 316 assertThat(preference.getKey()).isNotNull(); 317 assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); 318 } 319 320 @Test bindPreference_noSummary_shouldSetNullSummary()321 public void bindPreference_noSummary_shouldSetNullSummary() { 322 final Preference preference = new Preference(mApplication); 323 mActivityInfo.metaData.remove(META_DATA_PREFERENCE_SUMMARY); 324 325 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 326 327 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 328 preference, tile, null /* key */, Preference.DEFAULT_ORDER); 329 330 assertThat(preference.getSummary()).isNull(); 331 } 332 333 @Test 334 @Config(shadows = {ShadowTileUtils.class}) bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver()335 public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver() { 336 final Preference preference = new Preference(mApplication); 337 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 338 final String uriString = "content://com.android.settings/tile_summary"; 339 mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString); 340 341 final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers( 342 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, 343 Preference.DEFAULT_ORDER); 344 345 assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); 346 assertThat(preference.getSummary()).isNotEqualTo(ShadowTileUtils.MOCK_TEXT); 347 348 observers.get(0).updateUi(); 349 350 assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_TEXT); 351 } 352 353 @Test 354 @Config(shadows = {ShadowTileUtils.class}) bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver()355 public void bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver() { 356 final Preference preference = new Preference(mApplication); 357 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 358 final String uriString = "content://com.android.settings/tile_title"; 359 mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString); 360 361 final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers( 362 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, 363 Preference.DEFAULT_ORDER); 364 365 assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); 366 assertThat(preference.getTitle()).isNotEqualTo(ShadowTileUtils.MOCK_TEXT); 367 368 observers.get(0).updateUi(); 369 370 assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_TEXT); 371 } 372 373 @Test 374 @Config(shadows = {ShadowTileUtils.class}) bindPreference_onCheckedChanged_shouldPutStateToContentProvider()375 public void bindPreference_onCheckedChanged_shouldPutStateToContentProvider() { 376 final SwitchPreference preference = new SwitchPreference(mApplication); 377 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, 378 mSwitchMetaData); 379 final Bundle bundle = new Bundle(); 380 bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false); 381 ShadowTileUtils.setResultBundle(bundle); 382 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 383 preference, tile, null /* key */, Preference.DEFAULT_ORDER); 384 385 preference.callChangeListener(false); 386 387 assertThat(ShadowTileUtils.getProviderChecked()).isFalse(); 388 389 preference.callChangeListener(true); 390 391 assertThat(ShadowTileUtils.getProviderChecked()).isTrue(); 392 } 393 394 @Test 395 @Config(shadows = {ShadowTileUtils.class}) bindPreference_onCheckedChangedError_shouldRevertCheckedState()396 public void bindPreference_onCheckedChangedError_shouldRevertCheckedState() { 397 final SwitchPreference preference = new SwitchPreference(mApplication); 398 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, 399 mSwitchMetaData); 400 final Bundle bundle = new Bundle(); 401 bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true); 402 ShadowTileUtils.setResultBundle(bundle); 403 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 404 preference, tile, null /* key */, Preference.DEFAULT_ORDER); 405 406 preference.callChangeListener(true); 407 408 assertThat(preference.isChecked()).isFalse(); 409 410 preference.callChangeListener(false); 411 412 assertThat(preference.isChecked()).isTrue(); 413 } 414 415 @Test 416 @Config(shadows = {ShadowTileUtils.class}) bindPreference_callbackOnChanged_shouldLoadFromContentProvider()417 public void bindPreference_callbackOnChanged_shouldLoadFromContentProvider() { 418 final SwitchPreference preference = new SwitchPreference(mApplication); 419 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, 420 mSwitchMetaData); 421 final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers( 422 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, 423 Preference.DEFAULT_ORDER); 424 observers.get(0).updateUi(); 425 426 ShadowTileUtils.setProviderChecked(false); 427 observers.get(0).onDataChanged(); 428 429 assertThat(preference.isChecked()).isFalse(); 430 431 ShadowTileUtils.setProviderChecked(true); 432 observers.get(0).onDataChanged(); 433 434 assertThat(preference.isChecked()).isTrue(); 435 } 436 437 @Test bindPreference_withNullKeyTileKey_shouldUseTileKey()438 public void bindPreference_withNullKeyTileKey_shouldUseTileKey() { 439 final Preference preference = new Preference(mApplication); 440 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 441 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 442 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 443 preference, tile, null /* key */, Preference.DEFAULT_ORDER); 444 445 assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); 446 } 447 448 @Test bindIcon_withStaticIcon_shouldLoadStaticIcon()449 public void bindIcon_withStaticIcon_shouldLoadStaticIcon() { 450 final Preference preference = new Preference(mApplication); 451 mActivityInfo.packageName = mApplication.getPackageName(); 452 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 453 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 454 mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_add_40dp); 455 456 mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */); 457 458 final Bitmap preferenceBmp = Utils.createIconWithDrawable(preference.getIcon()).getBitmap(); 459 final Drawable staticIcon = Icon.createWithResource(mActivityInfo.packageName, 460 R.drawable.ic_add_40dp).loadDrawable(preference.getContext()); 461 final Bitmap staticIconBmp = Utils.createIconWithDrawable(staticIcon).getBitmap(); 462 assertThat(preferenceBmp.sameAs(staticIconBmp)).isTrue(); 463 } 464 465 @Test 466 @Config(shadows = {ShadowTileUtils.class}) bindIcon_withIconUri_shouldLoadIconFromContentProvider()467 public void bindIcon_withIconUri_shouldLoadIconFromContentProvider() { 468 final Preference preference = new Preference(mApplication); 469 mActivityInfo.packageName = mApplication.getPackageName(); 470 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 471 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 472 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI, 473 "content://com.android.settings/tile_icon"); 474 475 mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */); 476 477 assertThat(preference.getIcon()).isNotNull(); 478 } 479 480 @Test 481 @Config(shadows = {ShadowTileUtils.class}) bindIcon_withStaticIconAndIconUri_shouldLoadIconFromContentProvider()482 public void bindIcon_withStaticIconAndIconUri_shouldLoadIconFromContentProvider() { 483 final Preference preference = new Preference(mApplication); 484 mActivityInfo.packageName = mApplication.getPackageName(); 485 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 486 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 487 mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_add_40dp); 488 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI, 489 "content://com.android.settings/tile_icon"); 490 491 mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */); 492 493 final Bitmap preferenceBmp = Utils.createIconWithDrawable(preference.getIcon()).getBitmap(); 494 final Drawable staticIcon = Icon.createWithResource(mActivityInfo.packageName, 495 R.drawable.ic_add_40dp).loadDrawable(preference.getContext()); 496 final Bitmap staticIconBmp = Utils.createIconWithDrawable(staticIcon).getBitmap(); 497 assertThat(preferenceBmp.sameAs(staticIconBmp)).isFalse(); 498 499 final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri( 500 mContext, "pkg", null /* uri */, null /* providerMap */); 501 final Drawable iconFromUri = Icon.createWithResource(iconInfo.first, iconInfo.second) 502 .loadDrawable(preference.getContext()); 503 final Bitmap iconBmpFromUri = Utils.createIconWithDrawable(iconFromUri).getBitmap(); 504 assertThat(preferenceBmp.sameAs(iconBmpFromUri)).isTrue(); 505 } 506 507 @Test 508 @Config(shadows = {ShadowTileUtils.class}) bindIcon_noIcon_shouldNotLoadIcon()509 public void bindIcon_noIcon_shouldNotLoadIcon() { 510 final Preference preference = new Preference(mApplication); 511 mActivityInfo.packageName = mApplication.getPackageName(); 512 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 513 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 514 515 mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */); 516 517 assertThat(preference.getIcon()).isNull(); 518 } 519 520 @Test bindPreference_withBaseOrder_shouldOffsetOrder()521 public void bindPreference_withBaseOrder_shouldOffsetOrder() { 522 final int baseOrder = 100; 523 final Preference preference = new Preference(mApplication); 524 mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); 525 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 526 527 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 528 preference, tile, "123", baseOrder); 529 530 assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder); 531 } 532 533 @Test bindPreference_withOrderMetadata_shouldUseOrderInMetadata()534 public void bindPreference_withOrderMetadata_shouldUseOrderInMetadata() { 535 final Preference preference = new Preference(mApplication); 536 final int testOrder = -30; 537 mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); 538 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 539 mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder); 540 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 541 preference, tile, "123", Preference.DEFAULT_ORDER); 542 543 assertThat(preference.getOrder()).isEqualTo(testOrder); 544 } 545 546 @Test bindPreference_invalidOrderMetadata_shouldIgnore()547 public void bindPreference_invalidOrderMetadata_shouldIgnore() { 548 final Preference preference = new Preference(mApplication); 549 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 550 mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello"); 551 552 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 553 preference, tile, "123", Preference.DEFAULT_ORDER); 554 555 assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); 556 } 557 558 @Test bindPreference_withIntentActionMetadata_shouldSetLaunchAction()559 public void bindPreference_withIntentActionMetadata_shouldSetLaunchAction() { 560 FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get(); 561 final Preference preference = new Preference(mApplication); 562 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 563 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 564 mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); 565 tile.userHandle = null; 566 mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, 567 preference, tile, "123", Preference.DEFAULT_ORDER); 568 preference.performClick(); 569 ShadowActivity shadowActivity = Shadows.shadowOf(activity); 570 571 final Intent launchIntent = shadowActivity.getNextStartedActivityForResult().intent; 572 assertThat(launchIntent.getAction()) 573 .isEqualTo("TestAction"); 574 assertThat( 575 launchIntent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0)) 576 .isEqualTo(MetricsEvent.SETTINGS_GESTURES); 577 } 578 579 /** This test is for large screen devices Activity embedding. */ 580 @Test 581 @Config(shadows = {ShadowActivityEmbeddingUtils.class, ShadowSplitController.class}) bindPreference_clickHighlightedPreference_shouldNotStartActivity()582 public void bindPreference_clickHighlightedPreference_shouldNotStartActivity() { 583 ShadowSplitController.setIsActivityEmbedded(true); 584 ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true); 585 mFeatureFactory.searchFeatureProvider = new SearchFeatureProviderImpl(); 586 587 String clickPrefKey = "highlight_pref_key"; 588 String highlightMixinPrefKey = "highlight_pref_key"; 589 FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get(); 590 Preference preference = new Preference(mApplication); 591 Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 592 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 593 mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); 594 tile.userHandle = null; 595 596 TopLevelSettings largeScreenTopLevelSettings = spy(new TopLevelSettings( 597 new TestTopLevelHighlightMixin(highlightMixinPrefKey, 598 true /* activityEmbedded */))); 599 doReturn(true).when(largeScreenTopLevelSettings).isActivityEmbedded(); 600 largeScreenTopLevelSettings.setHighlightPreferenceKey(clickPrefKey); 601 602 mImpl.bindPreferenceToTileAndGetObservers(activity, largeScreenTopLevelSettings, 603 mForceRoundedIcon, preference, tile, clickPrefKey, Preference.DEFAULT_ORDER); 604 preference.performClick(); 605 606 ShadowActivity shadowActivity = Shadows.shadowOf(activity); 607 assertThat(shadowActivity.getNextStartedActivityForResult()).isEqualTo(null); 608 } 609 610 /** This test is for large screen devices Activity embedding. */ 611 @Test 612 @Config(shadows = {ShadowActivityEmbeddingUtils.class, ShadowSplitController.class}) bindPreference_clickNotHighlightedPreference_shouldStartActivity()613 public void bindPreference_clickNotHighlightedPreference_shouldStartActivity() { 614 ShadowSplitController.setIsActivityEmbedded(true); 615 ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true); 616 mFeatureFactory.searchFeatureProvider = new SearchFeatureProviderImpl(); 617 618 String clickPrefKey = "not_highlight_pref_key"; 619 String highlightMixinPrefKey = "highlight_pref_key"; 620 FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get(); 621 Preference preference = new Preference(mApplication); 622 Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 623 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 624 mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); 625 tile.userHandle = null; 626 627 TopLevelSettings largeScreenTopLevelSettings = spy(new TopLevelSettings( 628 new TestTopLevelHighlightMixin(highlightMixinPrefKey, 629 true /* activityEmbedded */))); 630 doReturn(true).when(largeScreenTopLevelSettings).isActivityEmbedded(); 631 largeScreenTopLevelSettings.setHighlightPreferenceKey(clickPrefKey); 632 633 mImpl.bindPreferenceToTileAndGetObservers(activity, largeScreenTopLevelSettings, 634 mForceRoundedIcon, preference, tile, clickPrefKey, Preference.DEFAULT_ORDER); 635 preference.performClick(); 636 637 Intent launchIntent = Shadows.shadowOf(activity).getNextStartedActivityForResult().intent; 638 assertThat(launchIntent.getAction()).isEqualTo("TestAction"); 639 } 640 641 @Test clickPreference_withUnresolvableIntent_shouldNotLaunchAnything()642 public void clickPreference_withUnresolvableIntent_shouldNotLaunchAnything() { 643 ReflectionHelpers.setField( 644 mImpl, "mPackageManager", mApplication.getPackageManager()); 645 FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get(); 646 final Preference preference = new Preference(mApplication); 647 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 648 mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); 649 mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); 650 tile.userHandle = null; 651 652 mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, 653 preference, tile, "123", Preference.DEFAULT_ORDER); 654 preference.performClick(); 655 656 final ShadowActivity.IntentForResult launchIntent = 657 Shadows.shadowOf(activity).getNextStartedActivityForResult(); 658 659 assertThat(launchIntent).isNull(); 660 } 661 662 @Test clickPreference_providerTileWithPendingIntent_singleUser_executesPendingIntent()663 public void clickPreference_providerTileWithPendingIntent_singleUser_executesPendingIntent() { 664 final Preference preference = new SwitchPreference(mApplication); 665 Bundle metaData = new Bundle(); 666 metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); 667 metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary); 668 metaData.putInt(META_DATA_KEY_ORDER, 10); 669 metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY); 670 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData); 671 PendingIntent pendingIntent = 672 PendingIntent.getActivity(mApplication, 0, new Intent("test"), 0); 673 tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent); 674 675 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 676 preference, tile, "123", Preference.DEFAULT_ORDER); 677 preference.performClick(); 678 679 Intent nextStartedActivity = 680 Shadows.shadowOf(mApplication).peekNextStartedActivity(); 681 assertThat(nextStartedActivity).isNotNull(); 682 assertThat(nextStartedActivity.getAction()).isEqualTo("test"); 683 } 684 685 @Test clickPreference_providerTileWithPendingIntent_multiUser_showsProfileDialog()686 public void clickPreference_providerTileWithPendingIntent_multiUser_showsProfileDialog() { 687 final Preference preference = new SwitchPreference(mApplication); 688 Bundle metaData = new Bundle(); 689 metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); 690 metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary); 691 metaData.putInt(META_DATA_KEY_ORDER, 10); 692 metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY); 693 final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData); 694 PendingIntent pendingIntent = 695 PendingIntent.getActivity(mApplication, 0, new Intent("test"), 0); 696 tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent); 697 tile.pendingIntentMap.put(new UserHandle(10), pendingIntent); 698 699 mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, 700 preference, tile, "123", Preference.DEFAULT_ORDER); 701 preference.performClick(); 702 703 Fragment dialogFragment = 704 mActivity.getSupportFragmentManager().findFragmentByTag("select_profile"); 705 assertThat(dialogFragment).isNotNull(); 706 Intent nextStartedActivity = 707 Shadows.shadowOf(mApplication).peekNextStartedActivity(); 708 assertThat(nextStartedActivity).isNull(); 709 } 710 711 @Test openTileIntent_profileSelectionDialog_shouldShow()712 public void openTileIntent_profileSelectionDialog_shouldShow() { 713 ShadowUserManager.getShadow().addUser(10, "Someone", 0); 714 715 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 716 final ArrayList<UserHandle> handles = new ArrayList<>(); 717 handles.add(new UserHandle(0)); 718 handles.add(new UserHandle(10)); 719 tile.userHandle = handles; 720 mImpl.openTileIntent(mActivity, tile); 721 722 verify(mActivity, never()) 723 .startActivity(any(Intent.class)); 724 verify(mActivity).getSupportFragmentManager(); 725 } 726 727 @Test openTileIntent_profileSelectionDialog_explicitMetadataShouldShow()728 public void openTileIntent_profileSelectionDialog_explicitMetadataShouldShow() { 729 ShadowUserManager.getShadow().addUser(10, "Someone", 0); 730 731 mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL); 732 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 733 final ArrayList<UserHandle> handles = new ArrayList<>(); 734 handles.add(new UserHandle(0)); 735 handles.add(new UserHandle(10)); 736 tile.userHandle = handles; 737 mImpl.openTileIntent(mActivity, tile); 738 739 verify(mActivity, never()) 740 .startActivity(any(Intent.class)); 741 verify(mActivity).getSupportFragmentManager(); 742 } 743 744 @Test openTileIntent_profileSelectionDialog_shouldNotShow()745 public void openTileIntent_profileSelectionDialog_shouldNotShow() { 746 ShadowUserManager.getShadow().addUser(10, "Someone", 0); 747 748 mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY); 749 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 750 final ArrayList<UserHandle> handles = new ArrayList<>(); 751 handles.add(new UserHandle(0)); 752 handles.add(new UserHandle(10)); 753 tile.userHandle = handles; 754 mImpl.openTileIntent(mActivity, tile); 755 756 verify(mActivity) 757 .startActivity(any(Intent.class)); 758 verify(mActivity, never()).getSupportFragmentManager(); 759 } 760 761 @Test openTileIntent_profileSelectionDialog_validUserHandleShouldNotShow()762 public void openTileIntent_profileSelectionDialog_validUserHandleShouldNotShow() { 763 final int userId = 10; 764 ShadowUserManager.getShadow().addUser(userId, "Someone", 0); 765 766 final UserHandle userHandle = new UserHandle(userId); 767 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 768 tile.getIntent().putExtra(EXTRA_USER, userHandle); 769 final ArrayList<UserHandle> handles = new ArrayList<>(); 770 handles.add(new UserHandle(0)); 771 handles.add(userHandle); 772 tile.userHandle = handles; 773 774 mImpl.openTileIntent(mActivity, tile); 775 776 final ArgumentCaptor<UserHandle> argument = ArgumentCaptor.forClass(UserHandle.class); 777 verify(mActivity) 778 .startActivityAsUser(any(Intent.class), argument.capture()); 779 assertThat(argument.getValue().getIdentifier()).isEqualTo(userId); 780 verify(mActivity, never()).getSupportFragmentManager(); 781 } 782 783 @Test openTileIntent_profileSelectionDialog_invalidUserHandleShouldShow()784 public void openTileIntent_profileSelectionDialog_invalidUserHandleShouldShow() { 785 ShadowUserManager.getShadow().addUser(10, "Someone", 0); 786 787 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 788 tile.getIntent().putExtra(EXTRA_USER, new UserHandle(30)); 789 final ArrayList<UserHandle> handles = new ArrayList<>(); 790 handles.add(new UserHandle(0)); 791 handles.add(new UserHandle(10)); 792 tile.userHandle = handles; 793 794 mImpl.openTileIntent(mActivity, tile); 795 796 verify(mActivity, never()) 797 .startActivityAsUser(any(Intent.class), any(UserHandle.class)); 798 verify(mActivity).getSupportFragmentManager(); 799 } 800 801 @Test openTileIntent_profileSelectionDialog_unresolvableWorkProfileIntentShouldNotShow()802 public void openTileIntent_profileSelectionDialog_unresolvableWorkProfileIntentShouldNotShow() { 803 final int userId = 10; 804 ShadowUserManager.getShadow().addUser(userId, "Someone", 0); 805 final UserHandle userHandle = new UserHandle(userId); 806 final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); 807 final ArrayList<UserHandle> handles = new ArrayList<>(); 808 handles.add(new UserHandle(0)); 809 handles.add(userHandle); 810 tile.userHandle = handles; 811 when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), eq(0))) 812 .thenReturn(new ResolveInfo()); 813 when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), eq(userId))) 814 .thenReturn(null); 815 816 mImpl.openTileIntent(mActivity, tile); 817 818 final ArgumentCaptor<UserHandle> argument = ArgumentCaptor.forClass(UserHandle.class); 819 verify(mActivity) 820 .startActivityAsUser(any(Intent.class), argument.capture()); 821 assertThat(argument.getValue().getIdentifier()).isEqualTo(0); 822 verify(mActivity, never()).getSupportFragmentManager(); 823 } 824 825 private static class TestFragment extends DashboardFragment { 826 827 @Override getMetricsCategory()828 public int getMetricsCategory() { 829 return MetricsEvent.SETTINGS_GESTURES; 830 } 831 832 @Override getPreferenceScreenResId()833 protected int getPreferenceScreenResId() { 834 return R.xml.gestures; 835 } 836 837 @Override getLogTag()838 protected String getLogTag() { 839 return "TestFragment"; 840 } 841 } 842 843 private static class TestTopLevelHighlightMixin extends TopLevelHighlightMixin { 844 private final String mHighlightPreferenceKey; 845 TestTopLevelHighlightMixin(String highlightPreferenceKey, boolean activityEmbedded)846 TestTopLevelHighlightMixin(String highlightPreferenceKey, 847 boolean activityEmbedded) { 848 super(activityEmbedded); 849 mHighlightPreferenceKey = highlightPreferenceKey; 850 } 851 852 @Override getHighlightPreferenceKey()853 public String getHighlightPreferenceKey() { 854 return mHighlightPreferenceKey; 855 } 856 } 857 } 858