• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.settings.dashboard;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.eq;
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.never;
24 import static org.mockito.Mockito.reset;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.app.PendingIntent;
30 import android.content.Context;
31 import android.content.res.Resources;
32 import android.graphics.drawable.Drawable;
33 import android.graphics.drawable.Icon;
34 import android.os.Bundle;
35 import android.service.settings.suggestions.Suggestion;
36 import android.support.v7.widget.RecyclerView;
37 import android.util.DisplayMetrics;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.WindowManager;
41 import android.widget.TextView;
42 
43 import com.android.settings.R;
44 import com.android.settings.SettingsActivity;
45 import com.android.settings.dashboard.conditional.Condition;
46 import com.android.settings.dashboard.suggestions.SuggestionAdapter;
47 import com.android.settings.testutils.FakeFeatureFactory;
48 import com.android.settings.testutils.SettingsRobolectricTestRunner;
49 import com.android.settings.testutils.shadow.SettingsShadowResources;
50 import com.android.settingslib.drawer.Tile;
51 import com.android.settingslib.drawer.TileUtils;
52 import com.android.settingslib.utils.IconCache;
53 
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.Answers;
58 import org.mockito.Mock;
59 import org.mockito.MockitoAnnotations;
60 import org.robolectric.RuntimeEnvironment;
61 import org.robolectric.annotation.Config;
62 import org.robolectric.util.ReflectionHelpers;
63 
64 import java.util.ArrayList;
65 import java.util.List;
66 
67 @RunWith(SettingsRobolectricTestRunner.class)
68 @Config(shadows = SettingsShadowResources.SettingsShadowTheme.class)
69 public class DashboardAdapterTest {
70 
71     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
72     private SettingsActivity mContext;
73     @Mock
74     private View mView;
75     @Mock
76     private Condition mCondition;
77     @Mock
78     private Resources mResources;
79     @Mock
80     private WindowManager mWindowManager;
81     private FakeFeatureFactory mFactory;
82     private DashboardAdapter mDashboardAdapter;
83     private List<Condition> mConditionList;
84 
85     @Before
setUp()86     public void setUp() {
87         MockitoAnnotations.initMocks(this);
88         mFactory = FakeFeatureFactory.setupForTest();
89         when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
90 
91         when(mContext.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
92         when(mContext.getResources()).thenReturn(mResources);
93         when(mResources.getQuantityString(any(int.class), any(int.class), any())).thenReturn("");
94 
95         mConditionList = new ArrayList<>();
96         mConditionList.add(mCondition);
97         when(mCondition.shouldShow()).thenReturn(true);
98         mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
99                 mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
100         when(mView.getTag()).thenReturn(mCondition);
101     }
102 
103     @Test
testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly()104     public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
105         final DashboardAdapter adapter =
106                 spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
107                         null /* conditions */, null /* suggestionControllerMixin */,
108                         null /* lifecycle */));
109         final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
110         adapter.setSuggestions(suggestions);
111 
112         final RecyclerView data = mock(RecyclerView.class);
113         when(data.getResources()).thenReturn(mResources);
114         when(data.getContext()).thenReturn(mContext);
115         when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
116         final View itemView = mock(View.class);
117         when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
118         when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
119         when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
120         final DashboardAdapter.SuggestionContainerHolder holder =
121                 new DashboardAdapter.SuggestionContainerHolder(itemView);
122 
123         adapter.onBindSuggestion(holder, 0);
124 
125         final DashboardData dashboardData = adapter.mDashboardData;
126         reset(adapter); // clear interactions tracking
127 
128         final Suggestion suggestionToRemove = suggestions.get(1);
129         adapter.onSuggestionClosed(suggestionToRemove);
130 
131         assertThat(suggestions.size()).isEqualTo(2);
132         assertThat(suggestions.contains(suggestionToRemove)).isFalse();
133         verify(adapter).notifyDashboardDataChanged(any());
134     }
135 
136     @Test
testSuggestionDismissed_onlySuggestion_updateDashboardData()137     public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
138         DashboardAdapter adapter =
139                 spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
140                         null /* conditions */, null /* suggestionControllerMixin */,
141                         null /* lifecycle */));
142         final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
143         adapter.setSuggestions(suggestions);
144         final DashboardData dashboardData = adapter.mDashboardData;
145         reset(adapter); // clear interactions tracking
146 
147         adapter.onSuggestionClosed(suggestions.get(0));
148 
149         assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
150         verify(adapter).notifyDashboardDataChanged(any());
151     }
152 
153     @Test
testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash()154     public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
155         mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
156                 null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
157         final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
158 
159         mDashboardAdapter.setSuggestions(suggestions);
160 
161         final RecyclerView data = mock(RecyclerView.class);
162         when(data.getResources()).thenReturn(mResources);
163         when(data.getContext()).thenReturn(mContext);
164         when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
165         final View itemView = mock(View.class);
166         when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
167         when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
168         when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
169         final DashboardAdapter.SuggestionContainerHolder holder =
170                 new DashboardAdapter.SuggestionContainerHolder(itemView);
171 
172         mDashboardAdapter.onBindSuggestion(holder, 0);
173 
174         verify(data).setAdapter(any(SuggestionAdapter.class));
175         // should not crash
176     }
177 
178     @Test
onBindTile_internalTile_shouldNotUseGenericBackgroundIcon()179     public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
180         final Context context = RuntimeEnvironment.application;
181         final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
182         final DashboardAdapter.DashboardItemHolder holder =
183                 new DashboardAdapter.DashboardItemHolder(view);
184         final Tile tile = new Tile();
185         tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
186         final IconCache iconCache = mock(IconCache.class);
187         when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
188 
189         mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
190                 null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
191         ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
192         mDashboardAdapter.onBindTile(holder, tile);
193 
194         verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
195     }
196 
197     @Test
onBindTile_externalTile_shouldUpdateIcon()198     public void onBindTile_externalTile_shouldUpdateIcon() {
199         final Context context = spy(RuntimeEnvironment.application);
200         final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
201         final DashboardAdapter.DashboardItemHolder holder =
202                 new DashboardAdapter.DashboardItemHolder(view);
203         final Tile tile = new Tile();
204         tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
205         when(tile.icon.getResPackage()).thenReturn("another.package");
206 
207         final IconCache iconCache = new IconCache(context);
208 
209         mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
210                 null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
211         ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
212 
213         doReturn("another.package").when(context).getPackageName();
214         mDashboardAdapter.onBindTile(holder, tile);
215 
216         assertThat(iconCache.getIcon(tile.icon)).isInstanceOf(RoundedHomepageIcon.class);
217     }
218 
219     @Test
onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon()220     public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
221         final Context context = spy(RuntimeEnvironment.application);
222         final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
223         final DashboardAdapter.DashboardItemHolder holder =
224                 new DashboardAdapter.DashboardItemHolder(view);
225         final Tile tile = new Tile();
226         tile.metaData = new Bundle();
227         tile.metaData.putInt(TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
228                 R.color.memory_critical);
229         tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
230         final IconCache iconCache = new IconCache(context);
231         mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
232                 null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
233         ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
234 
235         doReturn("another.package").when(context).getPackageName();
236         mDashboardAdapter.onBindTile(holder, tile);
237 
238         final RoundedHomepageIcon homepageIcon = (RoundedHomepageIcon) iconCache.getIcon(tile.icon);
239         assertThat(homepageIcon.mBackgroundColor)
240                 .isEqualTo(RuntimeEnvironment.application.getColor(R.color.memory_critical));
241     }
242 
243     @Test
onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon()244     public void onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon() {
245         final Context context = RuntimeEnvironment.application;
246         final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
247         final DashboardAdapter.DashboardItemHolder holder =
248                 new DashboardAdapter.DashboardItemHolder(view);
249         final Tile tile = new Tile();
250         tile.icon = mock(Icon.class);
251         when(tile.icon.getResPackage()).thenReturn("another.package");
252 
253         final IconCache iconCache = mock(IconCache.class);
254         when(iconCache.getIcon(tile.icon)).thenReturn(mock(RoundedHomepageIcon.class));
255 
256         mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
257                 null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
258         ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
259 
260         mDashboardAdapter.onBindTile(holder, tile);
261 
262         verify(iconCache, never()).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
263     }
264 
makeSuggestionsV2(String... pkgNames)265     private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
266         final List<Suggestion> suggestions = new ArrayList<>();
267         for (String pkgName : pkgNames) {
268             final Suggestion suggestion = new Suggestion.Builder(pkgName)
269                     .setPendingIntent(mock(PendingIntent.class))
270                     .build();
271             suggestions.add(suggestion);
272         }
273         return suggestions;
274     }
275 }
276