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 */
16package com.android.settings.dashboard;
17
18import static com.google.common.truth.Truth.assertThat;
19import static org.mockito.ArgumentMatchers.anyInt;
20import static org.mockito.ArgumentMatchers.eq;
21import static org.mockito.Mockito.any;
22import static org.mockito.Mockito.doReturn;
23import static org.mockito.Mockito.mock;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.reset;
26import static org.mockito.Mockito.spy;
27import static org.mockito.Mockito.times;
28import static org.mockito.Mockito.verify;
29import static org.mockito.Mockito.when;
30
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.res.Resources;
35import android.content.res.TypedArray;
36import android.graphics.drawable.Icon;
37import android.os.Bundle;
38import android.support.v7.widget.RecyclerView;
39import android.util.DisplayMetrics;
40import android.util.Pair;
41import android.view.LayoutInflater;
42import android.view.View;
43import android.widget.RelativeLayout;
44
45import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
46import com.android.settings.R;
47import com.android.settings.SettingsActivity;
48import com.android.settings.TestConfig;
49import com.android.settings.dashboard.conditional.Condition;
50import com.android.settings.dashboard.conditional.ConditionAdapter;
51import com.android.settings.dashboard.suggestions.SuggestionAdapter;
52import com.android.settings.testutils.FakeFeatureFactory;
53import com.android.settings.testutils.SettingsRobolectricTestRunner;
54import com.android.settings.testutils.shadow.SettingsShadowResources;
55import com.android.settingslib.drawer.DashboardCategory;
56import com.android.settingslib.drawer.Tile;
57
58import org.junit.Before;
59import org.junit.Test;
60import org.junit.runner.RunWith;
61import org.mockito.Answers;
62import org.mockito.ArgumentCaptor;
63import org.mockito.Captor;
64import org.mockito.Mock;
65import org.mockito.MockitoAnnotations;
66import org.robolectric.RuntimeEnvironment;
67import org.robolectric.annotation.Config;
68
69import java.util.ArrayList;
70import java.util.List;
71
72@RunWith(SettingsRobolectricTestRunner.class)
73@Config(manifest = TestConfig.MANIFEST_PATH,
74        sdk = TestConfig.SDK_VERSION,
75        shadows = {
76                SettingsShadowResources.class,
77                SettingsShadowResources.SettingsShadowTheme.class,
78        })
79public class DashboardAdapterTest {
80
81    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
82    private SettingsActivity mContext;
83    @Mock
84    private View mView;
85    @Mock
86    private Condition mCondition;
87    @Mock
88    private Resources mResources;
89    @Captor
90    private ArgumentCaptor<Integer> mActionCategoryCaptor = ArgumentCaptor.forClass(Integer.class);
91    @Captor
92    private ArgumentCaptor<String> mActionPackageCaptor = ArgumentCaptor.forClass(String.class);
93    @Captor
94    private ArgumentCaptor<Pair> mTaggedDataCaptor = ArgumentCaptor.forClass(Pair.class);
95    private FakeFeatureFactory mFactory;
96    private DashboardAdapter mDashboardAdapter;
97    private DashboardAdapter.SuggestionAndConditionHeaderHolder mSuggestionHolder;
98    private DashboardData.SuggestionConditionHeaderData mSuggestionHeaderData;
99    private List<Condition> mConditionList;
100
101    @Before
102    public void setUp() {
103        MockitoAnnotations.initMocks(this);
104        FakeFeatureFactory.setupForTest(mContext);
105        mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
106        when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
107        when(mFactory.suggestionsFeatureProvider
108                .getSuggestionIdentifier(any(Context.class), any(Tile.class)))
109                .thenAnswer(invocation -> {
110                    final Object[] args = invocation.getArguments();
111                    return ((Tile)args[1]).intent.getComponent().getPackageName();
112                });
113
114        when(mContext.getResources()).thenReturn(mResources);
115        when(mResources.getQuantityString(any(int.class), any(int.class), any()))
116                .thenReturn("");
117
118        mConditionList = new ArrayList<>();
119        mConditionList.add(mCondition);
120        when(mCondition.shouldShow()).thenReturn(true);
121        mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null);
122        mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1);
123        when(mView.getTag()).thenReturn(mCondition);
124    }
125
126    @Test
127    public void testSuggestionsLogs_NotExpanded() {
128        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
129
130        verify(mFactory.metricsFeatureProvider, times(2)).action(
131                any(Context.class), mActionCategoryCaptor.capture(),
132                mActionPackageCaptor.capture(),
133                mTaggedDataCaptor.capture());
134        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
135                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
136                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION);
137        assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg2");
138        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
139                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
140                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
141    }
142
143    @Test
144    public void testSuggestionsLogs_NotExpandedAndPaused() {
145        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
146
147        mDashboardAdapter.onPause();
148
149        verify(mFactory.metricsFeatureProvider, times(4)).action(
150                any(Context.class), mActionCategoryCaptor.capture(),
151                mActionPackageCaptor.capture(),
152                mTaggedDataCaptor.capture());
153        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
154                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
155                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
156                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
157                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
158        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
159                "pkg1", "pkg2", "pkg1", "pkg2");
160        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
161                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
162                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
163                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
164                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
165    }
166
167    @Test
168    public void testSuggestionsLogs_Expanded() {
169        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
170
171        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
172        mSuggestionHolder.itemView.callOnClick();
173
174        verify(mFactory.metricsFeatureProvider, times(3)).action(
175                any(Context.class), mActionCategoryCaptor.capture(),
176                mActionPackageCaptor.capture(),
177                mTaggedDataCaptor.capture());
178        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
179                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
180                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
181                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION);
182        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
183                "pkg1", "pkg2", "pkg3");
184        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
185                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
186                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
187                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
188    }
189
190    @Test
191    public void testSuggestionsLogs_ExpandedAndPaused() {
192        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
193
194        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
195        mSuggestionHolder.itemView.callOnClick();
196        mDashboardAdapter.onPause();
197
198        verify(mFactory.metricsFeatureProvider, times(6)).action(
199                any(Context.class), mActionCategoryCaptor.capture(),
200                mActionPackageCaptor.capture(),
201                mTaggedDataCaptor.capture());
202        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
203                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
204                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
205                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
206                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
207                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
208                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
209        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
210                "pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3");
211        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
212                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
213                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
214                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
215                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
216                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
217                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
218    }
219
220    @Test
221    public void testSuggestionsLogs_ExpandedAfterPause() {
222        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
223
224        mDashboardAdapter.onPause();
225        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
226        mSuggestionHolder.itemView.callOnClick();
227
228        verify(mFactory.metricsFeatureProvider, times(7)).action(
229                any(Context.class), mActionCategoryCaptor.capture(),
230                mActionPackageCaptor.capture(),
231                mTaggedDataCaptor.capture());
232        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
233                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
234                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
235                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
236                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
237                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
238                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
239                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION);
240        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
241                "pkg1", "pkg2", "pkg1", "pkg2", "pkg1", "pkg2", "pkg3");
242        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
243                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
244                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
245                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
246                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
247                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
248                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
249                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
250    }
251
252    @Test
253    public void testSuggestionsLogs_ExpandedAfterPauseAndPausedAgain() {
254        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
255
256        mDashboardAdapter.onPause();
257        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
258        mSuggestionHolder.itemView.callOnClick();
259        mDashboardAdapter.onPause();
260
261        verify(mFactory.metricsFeatureProvider, times(10)).action(
262                any(Context.class), mActionCategoryCaptor.capture(),
263                mActionPackageCaptor.capture(),
264                mTaggedDataCaptor.capture());
265        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
266                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
267                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
268                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
269                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
270                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
271                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
272                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
273                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
274                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
275                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
276        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
277                "pkg1", "pkg2", "pkg1", "pkg2", "pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3");
278        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
279                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
280                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
281                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
282                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
283                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
284                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
285                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
286                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
287                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
288                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
289    }
290
291    @Test
292    public void testSuggestionsLogs_ExpandedWithLessThanDefaultShown() {
293        setupSuggestions(makeSuggestions("pkg1"));
294
295        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
296        mSuggestionHolder.itemView.callOnClick();
297
298        verify(mFactory.metricsFeatureProvider, times(1)).action(
299                any(Context.class), mActionCategoryCaptor.capture(),
300                mActionPackageCaptor.capture(),
301                mTaggedDataCaptor.capture());
302        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
303                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION);
304        assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1");
305        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
306                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
307    }
308
309    @Test
310    public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAndPaused() {
311        setupSuggestions(makeSuggestions("pkg1"));
312
313        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
314        mSuggestionHolder.itemView.callOnClick();
315        mDashboardAdapter.onPause();
316
317        verify(mFactory.metricsFeatureProvider, times(2)).action(
318                any(Context.class), mActionCategoryCaptor.capture(),
319                mActionPackageCaptor.capture(),
320                mTaggedDataCaptor.capture());
321        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
322                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
323                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
324        assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1");
325        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
326                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
327                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
328    }
329
330    @Test
331    public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAfterPause() {
332        setupSuggestions(makeSuggestions("pkg1"));
333
334        mDashboardAdapter.onPause();
335        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
336        mSuggestionHolder.itemView.callOnClick();
337
338        verify(mFactory.metricsFeatureProvider, times(3)).action(
339                any(Context.class), mActionCategoryCaptor.capture(),
340                mActionPackageCaptor.capture(),
341                mTaggedDataCaptor.capture());
342        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
343                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
344                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
345                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION);
346        assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1", "pkg1");
347        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
348                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
349                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
350                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
351    }
352
353    @Test
354    public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAfterPauseAndPausedAgain() {
355        setupSuggestions(makeSuggestions("pkg1"));
356        mDashboardAdapter.onPause();
357        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
358        mSuggestionHolder.itemView.callOnClick();
359        mDashboardAdapter.onPause();
360
361        verify(mFactory.metricsFeatureProvider, times(4)).action(
362                any(Context.class), mActionCategoryCaptor.capture(),
363                mActionPackageCaptor.capture(),
364                mTaggedDataCaptor.capture());
365        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
366                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
367                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
368                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
369                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
370        assertThat(mActionPackageCaptor.getAllValues()).containsExactly(
371                "pkg1", "pkg1", "pkg1", "pkg1");
372        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
373                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
374                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
375                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0),
376                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
377    }
378
379    @Test
380    public void testSuggestionsLogs_SmartSuggestionEnabled() {
381        when(mFactory.suggestionsFeatureProvider
382                .isSmartSuggestionEnabled(any(Context.class))).thenReturn(true);
383        setupSuggestions(makeSuggestions("pkg1"));
384
385        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
386        mSuggestionHolder.itemView.callOnClick();
387        mDashboardAdapter.onPause();
388
389        verify(mFactory.metricsFeatureProvider, times(2)).action(
390                any(Context.class), mActionCategoryCaptor.capture(),
391                mActionPackageCaptor.capture(),
392                mTaggedDataCaptor.capture());
393        assertThat(mActionCategoryCaptor.getAllValues()).containsExactly(
394                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
395                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION);
396        assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1");
397        assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
398                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 1),
399                Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 1));
400    }
401
402    @Test
403    public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() {
404        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
405        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
406
407        // set suggestions to null
408        final DashboardData prevData = mDashboardAdapter.mDashboardData;
409        mDashboardAdapter.mDashboardData = new DashboardData.Builder(prevData)
410                .setSuggestions(null)
411                .build();
412
413        mSuggestionHolder.itemView.callOnClick();
414        // no crash
415    }
416
417    @Test
418    public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
419        final DashboardAdapter adapter =
420                spy(new DashboardAdapter(mContext, null, null, null, null));
421        final List<Tile> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
422        adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
423
424        final RecyclerView data = mock(RecyclerView.class);
425        when(data.getResources()).thenReturn(mResources);
426        when(data.getContext()).thenReturn(mContext);
427        when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
428        final View itemView = mock(View.class);
429        when(itemView.findViewById(R.id.data)).thenReturn(data);
430        final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
431                new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
432
433        adapter.onBindConditionAndSuggestion(
434                holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
435
436        final DashboardData dashboardData = adapter.mDashboardData;
437        reset(adapter); // clear interactions tracking
438
439        final Tile suggestionToRemove = suggestions.get(1);
440        adapter.onSuggestionDismissed(suggestionToRemove);
441
442        assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
443        assertThat(suggestions.size()).isEqualTo(2);
444        assertThat(suggestions.contains(suggestionToRemove)).isFalse();
445        verify(adapter, never()).notifyDashboardDataChanged(any());
446    }
447
448    @Test
449    public void testSuggestionDismissed_moreThanTwoSuggestions_defaultMode_shouldNotCrash() {
450        final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
451        final View itemView = mock(View.class);
452        when(itemView.findViewById(R.id.data)).thenReturn(data);
453        final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
454                new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
455        final List<Tile> suggestions =
456                makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4");
457        final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */,
458                null /* conditions */, null /* suggestionParser */, null /* callback */));
459        adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
460        adapter.onBindConditionAndSuggestion(
461                holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
462        // default mode, only displaying 2 suggestions
463
464        adapter.onSuggestionDismissed(suggestions.get(1));
465
466        // verify operations that access the lists will not cause ConcurrentModificationException
467        assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(1);
468        adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
469        // should not crash
470    }
471
472    @Test
473    public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
474        DashboardAdapter adapter =
475                spy(new DashboardAdapter(mContext, null, null, null, null));
476        final List<Tile> suggestions = makeSuggestions("pkg1");
477        adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
478        final DashboardData dashboardData = adapter.mDashboardData;
479        reset(adapter); // clear interactions tracking
480
481        adapter.onSuggestionDismissed(suggestions.get(0));
482
483        assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
484        verify(adapter).notifyDashboardDataChanged(any());
485    }
486
487    @Test
488    public void testSetCategoriesAndSuggestions_iconTinted() {
489        TypedArray mockTypedArray = mock(TypedArray.class);
490        doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class));
491        doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt());
492
493        List<Tile> packages = makeSuggestions("pkg1");
494        Icon mockIcon = mock(Icon.class);
495        packages.get(0).isIconTintable = true;
496        packages.get(0).icon = mockIcon;
497
498        mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages);
499
500        verify(mockIcon).setTint(eq(0x89000000));
501    }
502
503    @Test
504    public void testSetCategories_iconTinted() {
505        TypedArray mockTypedArray = mock(TypedArray.class);
506        doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class));
507        doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt());
508
509        final DashboardCategory category = mock(DashboardCategory.class);
510        final List<Tile> tiles = new ArrayList<>();
511        final Icon mockIcon = mock(Icon.class);
512        final Tile tile = new Tile();
513        tile.isIconTintable = true;
514        tile.icon = mockIcon;
515        tiles.add(tile);
516        category.tiles = tiles;
517
518        mDashboardAdapter.setCategory(category);
519
520        verify(mockIcon).setTint(eq(0x89000000));
521    }
522
523    @Test
524    public void testSetCategoriesAndSuggestions_limitSuggestionSize() {
525        List<Tile> packages =
526                makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5", "pkg6", "pkg7");
527        mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages);
528
529        assertThat(mDashboardAdapter.mDashboardData.getSuggestions().size())
530                .isEqualTo(DashboardAdapter.MAX_SUGGESTION_TO_SHOW);
531    }
532
533    @Test
534    public void testBindConditionAndSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
535        mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null);
536        final List<Tile> suggestions = makeSuggestions("pkg1");
537        final DashboardCategory category = mock(DashboardCategory.class);
538        final List<Tile> tiles = new ArrayList<>();
539        tiles.add(mock(Tile.class));
540        category.tiles = tiles;
541
542        mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions);
543
544        final RecyclerView data = mock(RecyclerView.class);
545        when(data.getResources()).thenReturn(mResources);
546        when(data.getContext()).thenReturn(mContext);
547        when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
548        final View itemView = mock(View.class);
549        when(itemView.findViewById(R.id.data)).thenReturn(data);
550        final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
551                new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
552
553        mDashboardAdapter.onBindConditionAndSuggestion(
554                holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
555
556        verify(data).setAdapter(any(SuggestionAdapter.class));
557        // should not crash
558    }
559
560    @Test
561    public void testBindConditionAndSuggestion_emptySuggestion_shouldSetConditionAdpater() {
562        final Bundle savedInstance = new Bundle();
563        savedInstance.putInt(DashboardAdapter.STATE_SUGGESTION_CONDITION_MODE,
564                DashboardData.HEADER_MODE_FULLY_EXPANDED);
565        mDashboardAdapter = new DashboardAdapter(mContext, savedInstance, mConditionList,
566                null /* SuggestionParser */, null /* SuggestionDismissController.Callback */);
567
568        final List<Tile> suggestions = new ArrayList<>();
569        final DashboardCategory category = mock(DashboardCategory.class);
570        final List<Tile> tiles = new ArrayList<>();
571        tiles.add(mock(Tile.class));
572        category.tiles = tiles;
573        mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions);
574
575        final RecyclerView data = mock(RecyclerView.class);
576        when(data.getResources()).thenReturn(mResources);
577        when(data.getContext()).thenReturn(mContext);
578        when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
579        final View itemView = mock(View.class);
580        when(itemView.findViewById(R.id.data)).thenReturn(data);
581        final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
582                new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
583
584        mDashboardAdapter.onBindConditionAndSuggestion(
585                holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
586
587        verify(data).setAdapter(any(ConditionAdapter.class));
588    }
589
590    private List<Tile> makeSuggestions(String... pkgNames) {
591        final List<Tile> suggestions = new ArrayList<>();
592        for (String pkgName : pkgNames) {
593            Tile suggestion = new Tile();
594            suggestion.intent = new Intent("action");
595            suggestion.intent.setComponent(new ComponentName(pkgName, "cls"));
596            suggestions.add(suggestion);
597        }
598        return suggestions;
599    }
600
601    private void setupSuggestions(List<Tile> suggestions) {
602        mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, suggestions);
603        final Context context = RuntimeEnvironment.application;
604        mSuggestionHolder = new DashboardAdapter.SuggestionAndConditionHeaderHolder(
605                LayoutInflater.from(context).inflate(
606                        R.layout.suggestion_condition_header, new RelativeLayout(context), true));
607    }
608}
609