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.verifyZeroInteractions;
44 import static org.mockito.Mockito.when;
45 
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.pm.ActivityInfo;
49 import android.content.pm.PackageManager;
50 import android.content.pm.ProviderInfo;
51 import android.content.pm.ResolveInfo;
52 import android.graphics.Bitmap;
53 import android.graphics.drawable.Drawable;
54 import android.graphics.drawable.Icon;
55 import android.os.Bundle;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.util.Pair;
59 
60 import androidx.fragment.app.FragmentActivity;
61 import androidx.preference.Preference;
62 import androidx.preference.SwitchPreference;
63 
64 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
65 import com.android.settings.R;
66 import com.android.settings.SettingsActivity;
67 import com.android.settings.Utils;
68 import com.android.settings.testutils.FakeFeatureFactory;
69 import com.android.settings.testutils.shadow.ShadowTileUtils;
70 import com.android.settings.testutils.shadow.ShadowUserManager;
71 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
72 import com.android.settingslib.drawer.ActivityTile;
73 import com.android.settingslib.drawer.CategoryKey;
74 import com.android.settingslib.drawer.ProviderTile;
75 import com.android.settingslib.drawer.Tile;
76 import com.android.settingslib.drawer.TileUtils;
77 
78 import org.junit.Before;
79 import org.junit.Test;
80 import org.junit.runner.RunWith;
81 import org.mockito.Answers;
82 import org.mockito.ArgumentCaptor;
83 import org.mockito.Mock;
84 import org.mockito.MockitoAnnotations;
85 import org.robolectric.Robolectric;
86 import org.robolectric.RobolectricTestRunner;
87 import org.robolectric.RuntimeEnvironment;
88 import org.robolectric.Shadows;
89 import org.robolectric.annotation.Config;
90 import org.robolectric.shadows.ShadowActivity;
91 import org.robolectric.util.ReflectionHelpers;
92 
93 import java.util.ArrayList;
94 import java.util.List;
95 
96 @RunWith(RobolectricTestRunner.class)
97 @Config(shadows = ShadowUserManager.class)
98 public class DashboardFeatureProviderImplTest {
99 
100     private static final String KEY = "key";
101     private static final String SWITCH_URI = "content://com.android.settings/tile_switch";
102 
103     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
104     private FragmentActivity mActivity;
105     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
106     private UserManager mUserManager;
107     @Mock
108     private PackageManager mPackageManager;
109     private FakeFeatureFactory mFeatureFactory;
110 
111     private Context mContext;
112     private ActivityInfo mActivityInfo;
113     private ProviderInfo mProviderInfo;
114     private Bundle mSwitchMetaData;
115     private DashboardFeatureProviderImpl mImpl;
116     private boolean mForceRoundedIcon;
117     private DashboardFragment mFragment;
118 
119     @Before
setUp()120     public void setUp() {
121         MockitoAnnotations.initMocks(this);
122         mContext = spy(RuntimeEnvironment.application);
123         doReturn(RuntimeEnvironment.application).when(mActivity).getApplicationContext();
124         mForceRoundedIcon = false;
125         mActivityInfo = new ActivityInfo();
126         mActivityInfo.packageName = mContext.getPackageName();
127         mActivityInfo.name = "class";
128         mActivityInfo.metaData = new Bundle();
129         mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
130         mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_SUMMARY,
131                 R.string.about_settings_summary);
132 
133         mProviderInfo = new ProviderInfo();
134         mProviderInfo.packageName = mContext.getPackageName();
135         mProviderInfo.name = "provider";
136         mProviderInfo.authority = "com.android.settings";
137         mSwitchMetaData = new Bundle();
138         mSwitchMetaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
139         mSwitchMetaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
140         mSwitchMetaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
141         mSwitchMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, SWITCH_URI);
142 
143         doReturn(mPackageManager).when(mContext).getPackageManager();
144         when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
145                 .thenReturn(new ResolveInfo());
146         mFeatureFactory = FakeFeatureFactory.setupForTest();
147         mImpl = new DashboardFeatureProviderImpl(mContext);
148         mFragment = new TestFragment();
149     }
150 
151     @Test
shouldHoldAppContext()152     public void shouldHoldAppContext() {
153         assertThat(mImpl.mContext).isEqualTo(mContext.getApplicationContext());
154     }
155 
156     @Test
bindPreference_shouldBindAllData()157     public void bindPreference_shouldBindAllData() {
158         final Preference preference = new Preference(RuntimeEnvironment.application);
159         final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
160         mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
161         doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
162                 .when(tile).getIcon(any(Context.class));
163         mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI");
164         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
165                 preference, tile, "123", Preference.DEFAULT_ORDER);
166 
167         assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
168         assertThat(preference.getSummary())
169                 .isEqualTo(mContext.getText(R.string.about_settings_summary));
170         assertThat(preference.getIcon()).isNotNull();
171         assertThat(preference.getFragment()).isEqualTo(
172                 mActivityInfo.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS));
173         assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
174     }
175 
176     @Test
bindPreference_shouldBindAllSwitchData()177     public void bindPreference_shouldBindAllSwitchData() {
178         final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
179         final Tile tile = spy(new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
180                 mSwitchMetaData));
181         mSwitchMetaData.putInt(META_DATA_KEY_ORDER, 10);
182         doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
183                 .when(tile).getIcon(any(Context.class));
184         final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
185                 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key*/,
186                 Preference.DEFAULT_ORDER);
187 
188         assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
189         assertThat(preference.getSummary())
190                 .isEqualTo(mContext.getText(R.string.about_settings_summary));
191         assertThat(preference.getKey()).isEqualTo(KEY);
192         assertThat(preference.getIcon()).isNotNull();
193         assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
194         assertThat(observers.get(0).getUri().toString()).isEqualTo(SWITCH_URI);
195     }
196 
197     @Test
bindPreference_noFragmentMetadata_shouldBindIntent()198     public void bindPreference_noFragmentMetadata_shouldBindIntent() {
199         final Preference preference = new Preference(RuntimeEnvironment.application);
200         mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
201         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
202 
203         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
204                 preference, tile, "123", Preference.DEFAULT_ORDER);
205 
206         assertThat(preference.getFragment()).isNull();
207         assertThat(preference.getOnPreferenceClickListener()).isNotNull();
208         assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
209     }
210 
211     @Test
bindPreference_noFragmentMetadata_shouldBindToProfileSelector()212     public void bindPreference_noFragmentMetadata_shouldBindToProfileSelector() {
213         final Preference preference = new Preference(RuntimeEnvironment.application);
214         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
215         tile.userHandle = new ArrayList<>();
216         tile.userHandle.add(mock(UserHandle.class));
217         tile.userHandle.add(mock(UserHandle.class));
218 
219         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
220                 preference, tile, "123", Preference.DEFAULT_ORDER);
221         preference.getOnPreferenceClickListener().onPreferenceClick(null);
222 
223         verify(mActivity).getSupportFragmentManager();
224     }
225 
226     @Test
bindPreference_noFragmentMetadataSingleUser_shouldBindToDirectLaunchIntent()227     public void bindPreference_noFragmentMetadataSingleUser_shouldBindToDirectLaunchIntent() {
228         final Preference preference = new Preference(RuntimeEnvironment.application);
229         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
230         tile.userHandle = new ArrayList<>();
231         tile.userHandle.add(mock(UserHandle.class));
232 
233         when(mActivity.getSystemService(Context.USER_SERVICE))
234                 .thenReturn(mUserManager);
235 
236         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
237                 preference, tile, "123", Preference.DEFAULT_ORDER);
238         preference.getOnPreferenceClickListener().onPreferenceClick(null);
239 
240         verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent(
241                 any(Intent.class),
242                 eq(MetricsEvent.SETTINGS_GESTURES));
243         verify(mActivity)
244                 .startActivityAsUser(any(Intent.class), any(UserHandle.class));
245     }
246 
247     @Test
bindPreference_toInternalSettingActivity_shouldBindToDirectLaunchIntentAndNotLog()248     public void bindPreference_toInternalSettingActivity_shouldBindToDirectLaunchIntentAndNotLog() {
249         final Preference preference = new Preference(RuntimeEnvironment.application);
250         mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
251         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
252         tile.userHandle = new ArrayList<>();
253         tile.userHandle.add(mock(UserHandle.class));
254 
255         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
256                 preference, tile, "123", Preference.DEFAULT_ORDER);
257         preference.getOnPreferenceClickListener().onPreferenceClick(null);
258         verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent(
259                 any(Intent.class),
260                 anyInt());
261         verify(mActivity)
262                 .startActivityAsUser(any(Intent.class), any(UserHandle.class));
263     }
264 
265     @Test
bindPreference_nullPreference_shouldIgnore()266     public void bindPreference_nullPreference_shouldIgnore() {
267         final Tile tile = mock(Tile.class);
268         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
269                 null /* keys */, tile, "123", Preference.DEFAULT_ORDER);
270 
271         verifyZeroInteractions(tile);
272     }
273 
274     @Test
bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority()275     public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() {
276         final Preference preference = new Preference(RuntimeEnvironment.application);
277         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
278         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
279                 preference, tile, null /* key */,
280                 Preference.DEFAULT_ORDER);
281 
282         assertThat(preference.getKey()).isNotNull();
283         assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
284     }
285 
286     @Test
bindPreference_noSummary_shouldSetNullSummary()287     public void bindPreference_noSummary_shouldSetNullSummary() {
288         final Preference preference = new Preference(RuntimeEnvironment.application);
289         mActivityInfo.metaData.remove(META_DATA_PREFERENCE_SUMMARY);
290 
291         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
292 
293         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
294                 preference, tile, null /* key */, Preference.DEFAULT_ORDER);
295 
296         assertThat(preference.getSummary()).isNull();
297     }
298 
299     @Test
300     @Config(shadows = {ShadowTileUtils.class})
bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver()301     public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver() {
302         final Preference preference = new Preference(RuntimeEnvironment.application);
303         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
304         final String uriString = "content://com.android.settings/tile_summary";
305         mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString);
306 
307         final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
308                 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */,
309                 Preference.DEFAULT_ORDER);
310 
311         assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
312         assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
313     }
314 
315     @Test
316     @Config(shadows = {ShadowTileUtils.class})
bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver()317     public void bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver() {
318         final Preference preference = new Preference(RuntimeEnvironment.application);
319         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
320         final String uriString =  "content://com.android.settings/tile_title";
321         mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString);
322 
323         final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
324                 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */,
325                 Preference.DEFAULT_ORDER);
326 
327         assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
328         assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
329     }
330 
331     @Test
332     @Config(shadows = {ShadowTileUtils.class})
bindPreference_onCheckedChanged_shouldPutStateToContentProvider()333     public void bindPreference_onCheckedChanged_shouldPutStateToContentProvider() {
334         final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
335         final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
336                 mSwitchMetaData);
337         final Bundle bundle = new Bundle();
338         bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false);
339         ShadowTileUtils.setResultBundle(bundle);
340         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
341                 preference, tile, null /* key */, Preference.DEFAULT_ORDER);
342 
343         preference.callChangeListener(false);
344 
345         assertThat(ShadowTileUtils.getProviderChecked()).isFalse();
346 
347         preference.callChangeListener(true);
348 
349         assertThat(ShadowTileUtils.getProviderChecked()).isTrue();
350     }
351 
352     @Test
353     @Config(shadows = {ShadowTileUtils.class})
bindPreference_onCheckedChangedError_shouldRevertCheckedState()354     public void bindPreference_onCheckedChangedError_shouldRevertCheckedState() {
355         final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
356         final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
357                 mSwitchMetaData);
358         final Bundle bundle = new Bundle();
359         bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true);
360         ShadowTileUtils.setResultBundle(bundle);
361         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
362                 preference, tile, null /* key */, Preference.DEFAULT_ORDER);
363 
364         preference.callChangeListener(true);
365 
366         assertThat(preference.isChecked()).isFalse();
367 
368         preference.callChangeListener(false);
369 
370         assertThat(preference.isChecked()).isTrue();
371     }
372 
373     @Test
374     @Config(shadows = {ShadowTileUtils.class})
bindPreference_callbackOnChanged_shouldLoadFromContentProvider()375     public void bindPreference_callbackOnChanged_shouldLoadFromContentProvider() {
376         final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
377         final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
378                 mSwitchMetaData);
379         final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
380                 mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */,
381                 Preference.DEFAULT_ORDER);
382 
383         ShadowTileUtils.setProviderChecked(false);
384         observers.get(0).onDataChanged();
385 
386         assertThat(preference.isChecked()).isFalse();
387 
388         ShadowTileUtils.setProviderChecked(true);
389         observers.get(0).onDataChanged();
390 
391         assertThat(preference.isChecked()).isTrue();
392     }
393 
394     @Test
bindPreference_withNullKeyTileKey_shouldUseTileKey()395     public void bindPreference_withNullKeyTileKey_shouldUseTileKey() {
396         final Preference preference = new Preference(RuntimeEnvironment.application);
397         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
398         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
399         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
400                 preference, tile, null /* key */, Preference.DEFAULT_ORDER);
401 
402         assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
403     }
404 
405     @Test
bindIcon_withStaticIcon_shouldLoadStaticIcon()406     public void bindIcon_withStaticIcon_shouldLoadStaticIcon() {
407         final Preference preference = new Preference(RuntimeEnvironment.application);
408         mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
409         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
410         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
411         mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_add_40dp);
412 
413         mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */);
414 
415         final Bitmap preferenceBmp = Utils.createIconWithDrawable(preference.getIcon()).getBitmap();
416         final Drawable staticIcon = Icon.createWithResource(mActivityInfo.packageName,
417                 R.drawable.ic_add_40dp).loadDrawable(preference.getContext());
418         final Bitmap staticIconBmp = Utils.createIconWithDrawable(staticIcon).getBitmap();
419         assertThat(preferenceBmp.sameAs(staticIconBmp)).isTrue();
420     }
421 
422     @Test
423     @Config(shadows = {ShadowTileUtils.class})
bindIcon_withIconUri_shouldLoadIconFromContentProvider()424     public void bindIcon_withIconUri_shouldLoadIconFromContentProvider() {
425         final Preference preference = new Preference(RuntimeEnvironment.application);
426         mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
427         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
428         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
429         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI,
430                 "content://com.android.settings/tile_icon");
431 
432         mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */);
433 
434         assertThat(preference.getIcon()).isNotNull();
435     }
436 
437     @Test
438     @Config(shadows = {ShadowTileUtils.class})
bindIcon_withStaticIconAndIconUri_shouldLoadIconFromContentProvider()439     public void bindIcon_withStaticIconAndIconUri_shouldLoadIconFromContentProvider() {
440         final Preference preference = new Preference(RuntimeEnvironment.application);
441         mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
442         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
443         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
444         mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_add_40dp);
445         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI,
446                 "content://com.android.settings/tile_icon");
447 
448         mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */);
449 
450         final Bitmap preferenceBmp = Utils.createIconWithDrawable(preference.getIcon()).getBitmap();
451         final Drawable staticIcon = Icon.createWithResource(mActivityInfo.packageName,
452                 R.drawable.ic_add_40dp).loadDrawable(preference.getContext());
453         final Bitmap staticIconBmp = Utils.createIconWithDrawable(staticIcon).getBitmap();
454         assertThat(preferenceBmp.sameAs(staticIconBmp)).isFalse();
455 
456         final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri(
457                 mContext, "pkg", null /* uri */, null /* providerMap */);
458         final Drawable iconFromUri = Icon.createWithResource(iconInfo.first, iconInfo.second)
459                 .loadDrawable(preference.getContext());
460         final Bitmap iconBmpFromUri = Utils.createIconWithDrawable(iconFromUri).getBitmap();
461         assertThat(preferenceBmp.sameAs(iconBmpFromUri)).isTrue();
462     }
463 
464     @Test
465     @Config(shadows = {ShadowTileUtils.class})
bindIcon_noIcon_shouldNotLoadIcon()466     public void bindIcon_noIcon_shouldNotLoadIcon() {
467         final Preference preference = new Preference(RuntimeEnvironment.application);
468         mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
469         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
470         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
471 
472         mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */);
473 
474         assertThat(preference.getIcon()).isNull();
475     }
476 
477     @Test
bindPreference_withBaseOrder_shouldOffsetOrder()478     public void bindPreference_withBaseOrder_shouldOffsetOrder() {
479         final int baseOrder = 100;
480         final Preference preference = new Preference(RuntimeEnvironment.application);
481         mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
482         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
483 
484         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
485                 preference, tile, "123", baseOrder);
486 
487         assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder);
488     }
489 
490     @Test
bindPreference_withOrderMetadata_shouldUseOrderInMetadata()491     public void bindPreference_withOrderMetadata_shouldUseOrderInMetadata() {
492         final Preference preference = new Preference(RuntimeEnvironment.application);
493         final int testOrder = -30;
494         mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
495         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
496         mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder);
497         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
498                 preference, tile, "123", Preference.DEFAULT_ORDER);
499 
500         assertThat(preference.getOrder()).isEqualTo(testOrder);
501     }
502 
503     @Test
bindPreference_invalidOrderMetadata_shouldIgnore()504     public void bindPreference_invalidOrderMetadata_shouldIgnore() {
505         final Preference preference = new Preference(RuntimeEnvironment.application);
506         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
507         mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello");
508 
509         mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
510                 preference, tile, "123", Preference.DEFAULT_ORDER);
511 
512         assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
513     }
514 
515     @Test
bindPreference_withIntentActionMetadata_shouldSetLaunchAction()516     public void bindPreference_withIntentActionMetadata_shouldSetLaunchAction() {
517         FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get();
518         final Preference preference = new Preference(RuntimeEnvironment.application);
519         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
520         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
521         mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
522         tile.userHandle = null;
523         mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon,
524                 preference, tile, "123", Preference.DEFAULT_ORDER);
525         preference.performClick();
526         ShadowActivity shadowActivity = Shadows.shadowOf(activity);
527 
528         final Intent launchIntent = shadowActivity.getNextStartedActivityForResult().intent;
529         assertThat(launchIntent.getAction())
530                 .isEqualTo("TestAction");
531         assertThat(
532                 launchIntent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0))
533                 .isEqualTo(MetricsEvent.SETTINGS_GESTURES);
534     }
535 
536     @Test
clickPreference_withUnresolvableIntent_shouldNotLaunchAnything()537     public void clickPreference_withUnresolvableIntent_shouldNotLaunchAnything() {
538         ReflectionHelpers.setField(
539                 mImpl, "mPackageManager", RuntimeEnvironment.application.getPackageManager());
540         FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).get();
541         final Preference preference = new Preference(RuntimeEnvironment.application);
542         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
543         mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
544         mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
545         tile.userHandle = null;
546 
547         mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon,
548                 preference, tile, "123", Preference.DEFAULT_ORDER);
549         preference.performClick();
550 
551         final ShadowActivity.IntentForResult launchIntent =
552                 Shadows.shadowOf(activity).getNextStartedActivityForResult();
553 
554         assertThat(launchIntent).isNull();
555     }
556 
557     @Test
openTileIntent_profileSelectionDialog_shouldShow()558     public void openTileIntent_profileSelectionDialog_shouldShow() {
559         ShadowUserManager.getShadow().addUser(10, "Someone", 0);
560 
561         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
562         final ArrayList<UserHandle> handles = new ArrayList<>();
563         handles.add(new UserHandle(0));
564         handles.add(new UserHandle(10));
565         tile.userHandle = handles;
566         mImpl.openTileIntent(mActivity, tile);
567 
568         verify(mActivity, never())
569                 .startActivity(any(Intent.class));
570         verify(mActivity).getSupportFragmentManager();
571     }
572 
573     @Test
openTileIntent_profileSelectionDialog_explicitMetadataShouldShow()574     public void openTileIntent_profileSelectionDialog_explicitMetadataShouldShow() {
575         ShadowUserManager.getShadow().addUser(10, "Someone", 0);
576 
577         mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
578         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
579         final ArrayList<UserHandle> handles = new ArrayList<>();
580         handles.add(new UserHandle(0));
581         handles.add(new UserHandle(10));
582         tile.userHandle = handles;
583         mImpl.openTileIntent(mActivity, tile);
584 
585         verify(mActivity, never())
586                 .startActivity(any(Intent.class));
587         verify(mActivity).getSupportFragmentManager();
588     }
589 
590     @Test
openTileIntent_profileSelectionDialog_shouldNotShow()591     public void openTileIntent_profileSelectionDialog_shouldNotShow() {
592         ShadowUserManager.getShadow().addUser(10, "Someone", 0);
593 
594         mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
595         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
596         final ArrayList<UserHandle> handles = new ArrayList<>();
597         handles.add(new UserHandle(0));
598         handles.add(new UserHandle(10));
599         tile.userHandle = handles;
600         mImpl.openTileIntent(mActivity, tile);
601 
602         verify(mActivity)
603                 .startActivity(any(Intent.class));
604         verify(mActivity, never()).getSupportFragmentManager();
605     }
606 
607     @Test
openTileIntent_profileSelectionDialog_validUserHandleShouldNotShow()608     public void openTileIntent_profileSelectionDialog_validUserHandleShouldNotShow() {
609         final int userId = 10;
610         ShadowUserManager.getShadow().addUser(userId, "Someone", 0);
611 
612         final UserHandle userHandle = new UserHandle(userId);
613         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
614         tile.getIntent().putExtra(EXTRA_USER, userHandle);
615         final ArrayList<UserHandle> handles = new ArrayList<>();
616         handles.add(new UserHandle(0));
617         handles.add(userHandle);
618         tile.userHandle = handles;
619 
620         mImpl.openTileIntent(mActivity, tile);
621 
622         final ArgumentCaptor<UserHandle> argument = ArgumentCaptor.forClass(UserHandle.class);
623         verify(mActivity)
624                 .startActivityAsUser(any(Intent.class), argument.capture());
625         assertThat(argument.getValue().getIdentifier()).isEqualTo(userId);
626         verify(mActivity, never()).getSupportFragmentManager();
627     }
628 
629     @Test
openTileIntent_profileSelectionDialog_invalidUserHandleShouldShow()630     public void openTileIntent_profileSelectionDialog_invalidUserHandleShouldShow() {
631         ShadowUserManager.getShadow().addUser(10, "Someone", 0);
632 
633         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
634         tile.getIntent().putExtra(EXTRA_USER, new UserHandle(30));
635         final ArrayList<UserHandle> handles = new ArrayList<>();
636         handles.add(new UserHandle(0));
637         handles.add(new UserHandle(10));
638         tile.userHandle = handles;
639 
640         mImpl.openTileIntent(mActivity, tile);
641 
642         verify(mActivity, never())
643                 .startActivityAsUser(any(Intent.class), any(UserHandle.class));
644         verify(mActivity).getSupportFragmentManager();
645     }
646 
647     @Test
openTileIntent_profileSelectionDialog_unresolvableWorkProfileIntentShouldNotShow()648     public void openTileIntent_profileSelectionDialog_unresolvableWorkProfileIntentShouldNotShow() {
649         final int userId = 10;
650         ShadowUserManager.getShadow().addUser(userId, "Someone", 0);
651         final UserHandle userHandle = new UserHandle(userId);
652         final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
653         final ArrayList<UserHandle> handles = new ArrayList<>();
654         handles.add(new UserHandle(0));
655         handles.add(userHandle);
656         tile.userHandle = handles;
657         when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), eq(0)))
658                 .thenReturn(new ResolveInfo());
659         when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), eq(userId)))
660                 .thenReturn(null);
661 
662         mImpl.openTileIntent(mActivity, tile);
663 
664         final ArgumentCaptor<UserHandle> argument = ArgumentCaptor.forClass(UserHandle.class);
665         verify(mActivity)
666                 .startActivityAsUser(any(Intent.class), argument.capture());
667         assertThat(argument.getValue().getIdentifier()).isEqualTo(0);
668         verify(mActivity, never()).getSupportFragmentManager();
669     }
670 
671     private static class TestFragment extends DashboardFragment {
672 
673         @Override
getMetricsCategory()674         public int getMetricsCategory() {
675             return MetricsEvent.SETTINGS_GESTURES;
676         }
677 
678         @Override
getPreferenceScreenResId()679         protected int getPreferenceScreenResId() {
680             return R.xml.gestures;
681         }
682 
683         @Override
getLogTag()684         protected String getLogTag() {
685             return "TestFragment";
686         }
687     }
688 }
689