2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.android.settings.dashboard.suggestions;
18 import static com.google.common.truth.Truth.assertThat;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.Mockito.doReturn;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.never;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
29 import android.app.PendingIntent;
30 import android.content.Context;
31 import android.content.res.Resources;
32 import android.content.res.TypedArray;
33 import android.graphics.drawable.Drawable;
34 import android.graphics.drawable.Icon;
35 import android.service.settings.suggestions.Suggestion;
36 import android.view.Display;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.WindowManager;
40 import android.widget.FrameLayout;
41 import android.widget.LinearLayout;
43 import com.android.internal.logging.nano.MetricsProto;
44 import com.android.settings.R;
45 import com.android.settings.SettingsActivity;
46 import com.android.settings.TestConfig;
47 import com.android.settings.dashboard.DashboardAdapter;
48 import com.android.settings.testutils.FakeFeatureFactory;
49 import com.android.settings.testutils.SettingsRobolectricTestRunner;
50 import com.android.settingslib.suggestions.SuggestionControllerMixin;
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.mockito.Answers;
56 import org.mockito.Mock;
57 import org.mockito.MockitoAnnotations;
58 import org.robolectric.RuntimeEnvironment;
59 import org.robolectric.annotation.Config;
60 import org.robolectric.util.ReflectionHelpers;
62 import java.util.ArrayList;
63 import java.util.List;
65 @RunWith(SettingsRobolectricTestRunner.class)
66 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
67 public class SuggestionAdapterTest {
69 @Mock(answer = Answers.RETURNS_DEEP_STUBS)
70 private SettingsActivity mActivity;
72 private SuggestionControllerMixin mSuggestionControllerMixin;
74 private Resources mResources;
76 private WindowManager mWindowManager;
78 private FakeFeatureFactory mFeatureFactory;
79 private Context mContext;
80 private SuggestionAdapter mSuggestionAdapter;
81 private DashboardAdapter.DashboardItemHolder mSuggestionHolder;
82 private List<Suggestion> mOneSuggestion;
83 private List<Suggestion> mTwoSuggestions;
84 private SuggestionAdapter.CardConfig mConfig;
88 MockitoAnnotations.initMocks(this);
89 mContext = spy(RuntimeEnvironment.application);
90 when(mActivity.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
91 when(mActivity.getResources()).thenReturn(mResources);
92 when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin))
94 when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin))
96 when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards))
98 mConfig = spy(SuggestionAdapter.CardConfig.get(mActivity));
100 mFeatureFactory = FakeFeatureFactory.setupForTest();
102 final Suggestion suggestion1 = new Suggestion.Builder("id1")
103 .setTitle("Test suggestion 1")
105 final Suggestion suggestion2 = new Suggestion.Builder("id2")
106 .setTitle("Test suggestion 2")
108 mOneSuggestion = new ArrayList<>();
109 mOneSuggestion.add(suggestion1);
110 mTwoSuggestions = new ArrayList<>();
111 mTwoSuggestions.add(suggestion1);
112 mTwoSuggestions.add(suggestion2);
116 public void getItemCount_shouldReturnListSize() {
117 mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
118 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
119 mSuggestionAdapter.setSuggestions(mOneSuggestion);
120 assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
122 mSuggestionAdapter.setSuggestions(mTwoSuggestions);
123 assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
127 public void getItemViewType_shouldReturnSuggestionTile() {
128 mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
129 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
130 mSuggestionAdapter.setSuggestions(mOneSuggestion);
131 assertThat(mSuggestionAdapter.getItemViewType(0))
132 .isEqualTo(R.layout.suggestion_tile);
136 public void getItemType_hasButton_shouldReturnSuggestionWithButton() {
137 final List<Suggestion> suggestions = new ArrayList<>();
138 suggestions.add(new Suggestion.Builder("id")
139 .setFlags(Suggestion.FLAG_HAS_BUTTON)
143 mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
144 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
145 mSuggestionAdapter.setSuggestions(suggestions);
147 assertThat(mSuggestionAdapter.getItemViewType(0))
148 .isEqualTo(R.layout.suggestion_tile_with_button);
152 public void onBindViewHolder_shouldLog() {
153 final View view = spy(LayoutInflater.from(mContext).inflate(
154 R.layout.suggestion_tile, new LinearLayout(mContext), true));
155 mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
156 mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
157 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
158 mSuggestionAdapter.setSuggestions(mOneSuggestion);
159 doReturn("sans").when(mContext).getString(anyInt());
162 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
163 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
166 verify(mFeatureFactory.metricsFeatureProvider).action(
167 mContext, MetricsProto.MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
168 mOneSuggestion.get(0).getId());
172 public void onBindViewHolder_itemViewShouldHandleClick()
173 throws PendingIntent.CanceledException {
174 final List<Suggestion> suggestions = makeSuggestions("pkg1");
175 setupSuggestions(mActivity, suggestions);
177 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
178 mSuggestionHolder.itemView.performClick();
180 verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
181 verify(suggestions.get(0).getPendingIntent()).send();
185 public void onBindViewHolder_hasButton_buttonShouldHandleClick()
186 throws PendingIntent.CanceledException {
187 final List<Suggestion> suggestions = new ArrayList<>();
188 final PendingIntent pendingIntent = mock(PendingIntent.class);
189 suggestions.add(new Suggestion.Builder("id")
190 .setFlags(Suggestion.FLAG_HAS_BUTTON)
193 .setPendingIntent(pendingIntent)
195 mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
196 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
197 mSuggestionAdapter.setSuggestions(suggestions);
198 mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
199 new FrameLayout(RuntimeEnvironment.application),
200 mSuggestionAdapter.getItemViewType(0));
201 doReturn("sans").when(mContext).getString(anyInt());
203 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
204 mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
206 verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
207 verify(pendingIntent).send();
211 public void getSuggestions_shouldReturnSuggestionWhenMatch() {
212 final List<Suggestion> suggestions = makeSuggestions("pkg1");
213 setupSuggestions(mActivity, suggestions);
215 assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
219 public void onBindViewHolder_closeButtonShouldHandleClick()
220 throws PendingIntent.CanceledException {
221 final List<Suggestion> suggestions = makeSuggestions("pkg1");
222 final SuggestionAdapter.Callback callback = mock(SuggestionAdapter.Callback.class);
223 mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
224 null /* savedInstanceState */, callback, null /* lifecycle */);
225 mSuggestionAdapter.setSuggestions(suggestions);
226 mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
227 new FrameLayout(RuntimeEnvironment.application),
228 mSuggestionAdapter.getItemViewType(0));
230 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
231 mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
233 final Suggestion suggestion = suggestions.get(0);
234 verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
235 mActivity, mSuggestionControllerMixin, suggestion);
236 verify(callback).onSuggestionClosed(suggestion);
240 public void onBindViewHolder_iconNotTintable_shouldNotTintIcon()
241 throws PendingIntent.CanceledException {
242 final Icon icon = mock(Icon.class);
243 final Suggestion suggestion = new Suggestion.Builder("pkg1")
244 .setPendingIntent(mock(PendingIntent.class))
247 final List<Suggestion> suggestions = new ArrayList<>();
248 suggestions.add(suggestion);
249 mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
250 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
251 mSuggestionAdapter.setSuggestions(suggestions);
252 mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
253 new FrameLayout(RuntimeEnvironment.application),
254 mSuggestionAdapter.getItemViewType(0));
255 DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
256 final Drawable drawable = mock(Drawable.class);
257 when(cache.getIcon(icon)).thenReturn(drawable);
258 ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
260 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
262 verify(drawable, never()).setTint(anyInt());
266 public void onBindViewHolder_iconTintable_shouldTintIcon()
267 throws PendingIntent.CanceledException {
268 final Icon icon = mock(Icon.class);
269 final int FLAG_ICON_TINTABLE = 1 << 1;
270 final Suggestion suggestion = new Suggestion.Builder("pkg1")
271 .setPendingIntent(mock(PendingIntent.class))
273 .setFlags(FLAG_ICON_TINTABLE)
275 final List<Suggestion> suggestions = new ArrayList<>();
276 suggestions.add(suggestion);
277 mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
278 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
279 mSuggestionAdapter.setSuggestions(suggestions);
280 mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
281 new FrameLayout(RuntimeEnvironment.application),
282 mSuggestionAdapter.getItemViewType(0));
283 DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
284 final Drawable drawable = mock(Drawable.class);
285 when(cache.getIcon(icon)).thenReturn(drawable);
286 ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
287 TypedArray typedArray = mock(TypedArray.class);
288 final int colorAccent = 1234;
289 when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
290 when(typedArray.getColor(anyInt(), anyInt())).thenReturn(colorAccent);
292 mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
294 verify(drawable).setTint(colorAccent);
298 public void setCardLayout_oneCard_shouldSetCardWidthToMatchParent() {
299 final List<Suggestion> suggestions = makeSuggestions("pkg1");
300 setupSuggestions(mContext, suggestions);
302 mConfig.setCardLayout(mSuggestionHolder, 1, 0);
304 assertThat(mSuggestionHolder.itemView.getLayoutParams().width)
305 .isEqualTo(LinearLayout.LayoutParams.MATCH_PARENT);
309 public void setCardLayout_twoCards_shouldSetCardWidthToHalfScreenMinusPadding() {
310 final List<Suggestion> suggestions = makeSuggestions("pkg1");
311 setupSuggestions(mContext, suggestions);
312 doReturn(200).when(mConfig).getScreenWidth();
314 mConfig.setCardLayout(mSuggestionHolder, 2, 0);
317 * card width = (screen width - left margin - inner margin - right margin) / 2
318 * = (200 - 20 - 10 - 20) / 2
321 assertThat(mSuggestionHolder.itemView.getLayoutParams().width).isEqualTo(75);
326 public void setCardLayout_multipleCards_shouldSetCardWidthFromResource() {
327 final List<Suggestion> suggestions = makeSuggestions("pkg1");
328 setupSuggestions(mContext, suggestions);
330 mConfig.setCardLayout(mSuggestionHolder, 3, 0);
332 assertThat(mSuggestionHolder.itemView.getLayoutParams().width).isEqualTo(120);
335 private void setupSuggestions(Context context, List<Suggestion> suggestions) {
336 mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
337 null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
338 mSuggestionAdapter.setSuggestions(suggestions);
339 mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
340 new FrameLayout(RuntimeEnvironment.application),
341 mSuggestionAdapter.getItemViewType(0));
344 private List<Suggestion> makeSuggestions(String... pkgNames) {
345 final List<Suggestion> suggestions = new ArrayList<>();
346 for (String pkgName : pkgNames) {
347 final Suggestion suggestion = new Suggestion.Builder(pkgName)
348 .setPendingIntent(mock(PendingIntent.class))
350 suggestions.add(suggestion);