Home | History | Annotate | Download | only in dashboard
      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
     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
    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
    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
    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
    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
    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
    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
    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 
    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