Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2017 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.applications;
     18 
     19 import static com.google.common.truth.Truth.assertThat;
     20 import static org.mockito.Matchers.any;
     21 import static org.mockito.Matchers.anyInt;
     22 import static org.mockito.Matchers.anyLong;
     23 import static org.mockito.Matchers.anyString;
     24 import static org.mockito.Matchers.argThat;
     25 import static org.mockito.Mockito.doNothing;
     26 import static org.mockito.Mockito.doReturn;
     27 import static org.mockito.Mockito.mock;
     28 import static org.mockito.Mockito.never;
     29 import static org.mockito.Mockito.spy;
     30 import static org.mockito.Mockito.times;
     31 import static org.mockito.Mockito.verify;
     32 import static org.mockito.Mockito.when;
     33 
     34 import android.app.Application;
     35 import android.app.usage.UsageStats;
     36 import android.app.usage.UsageStatsManager;
     37 import android.content.Context;
     38 import android.content.Intent;
     39 import android.content.pm.ApplicationInfo;
     40 import android.content.pm.PackageManager;
     41 import android.content.pm.ResolveInfo;
     42 import android.os.UserHandle;
     43 import android.os.UserManager;
     44 import android.support.v7.preference.Preference;
     45 import android.support.v7.preference.PreferenceCategory;
     46 import android.support.v7.preference.PreferenceScreen;
     47 import android.text.TextUtils;
     48 
     49 import com.android.settings.R;
     50 import com.android.settings.testutils.SettingsRobolectricTestRunner;
     51 import com.android.settingslib.applications.AppUtils;
     52 import com.android.settingslib.applications.ApplicationsState;
     53 import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
     54 
     55 import org.junit.Before;
     56 import org.junit.Test;
     57 import org.junit.runner.RunWith;
     58 import org.mockito.ArgumentCaptor;
     59 import org.mockito.ArgumentMatcher;
     60 import org.mockito.Mock;
     61 import org.mockito.MockitoAnnotations;
     62 import org.robolectric.RuntimeEnvironment;
     63 import org.robolectric.annotation.Config;
     64 import org.robolectric.util.ReflectionHelpers;
     65 
     66 import java.util.ArrayList;
     67 import java.util.List;
     68 
     69 @RunWith(SettingsRobolectricTestRunner.class)
     70 public class RecentAppsPreferenceControllerTest {
     71 
     72     @Mock
     73     private PreferenceScreen mScreen;
     74     @Mock
     75     private PreferenceCategory mCategory;
     76     @Mock
     77     private Preference mSeeAllPref;
     78     @Mock
     79     private PreferenceCategory mDivider;
     80     @Mock
     81     private UsageStatsManager mUsageStatsManager;
     82     @Mock
     83     private UserManager mUserManager;
     84     @Mock
     85     private ApplicationsState mAppState;
     86     @Mock
     87     private PackageManager mPackageManager;
     88     @Mock
     89     private ApplicationsState.AppEntry mAppEntry;
     90     @Mock
     91     private ApplicationInfo mApplicationInfo;
     92 
     93     private Context mContext;
     94     private RecentAppsPreferenceController mController;
     95 
     96     @Before
     97     public void setUp() {
     98         MockitoAnnotations.initMocks(this);
     99         mContext = spy(RuntimeEnvironment.application);
    100         doReturn(mUsageStatsManager).when(mContext).getSystemService(Context.USAGE_STATS_SERVICE);
    101         doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
    102         doReturn(mPackageManager).when(mContext).getPackageManager();
    103 
    104         mController = new RecentAppsPreferenceController(mContext, mAppState, null);
    105         when(mScreen.findPreference(anyString())).thenReturn(mCategory);
    106 
    107         when(mScreen.findPreference(RecentAppsPreferenceController.KEY_SEE_ALL))
    108                 .thenReturn(mSeeAllPref);
    109         when(mScreen.findPreference(RecentAppsPreferenceController.KEY_DIVIDER))
    110                 .thenReturn(mDivider);
    111         when(mCategory.getContext()).thenReturn(mContext);
    112     }
    113 
    114     @Test
    115     public void isAlwaysAvailable() {
    116         assertThat(mController.isAvailable()).isTrue();
    117     }
    118 
    119     @Test
    120     public void doNotIndexCategory() {
    121         final List<String> nonIndexable = new ArrayList<>();
    122 
    123         mController.updateNonIndexableKeys(nonIndexable);
    124 
    125         assertThat(nonIndexable).containsAllOf(mController.getPreferenceKey(),
    126                 RecentAppsPreferenceController.KEY_DIVIDER);
    127     }
    128 
    129     @Test
    130     public void onDisplayAndUpdateState_shouldRefreshUi() {
    131         mController = spy(new RecentAppsPreferenceController(mContext, (Application) null, null));
    132 
    133         doNothing().when(mController).refreshUi(mContext);
    134 
    135         mController.displayPreference(mScreen);
    136         mController.updateState(mCategory);
    137 
    138         verify(mController, times(2)).refreshUi(mContext);
    139     }
    140 
    141     @Test
    142     @Config(qualifiers = "mcc999")
    143     public void display_shouldNotShowRecents_showAppInfoPreference() {
    144         mController.displayPreference(mScreen);
    145 
    146         verify(mCategory, never()).addPreference(any(Preference.class));
    147         verify(mCategory).setTitle(null);
    148         verify(mSeeAllPref).setTitle(R.string.applications_settings);
    149         verify(mSeeAllPref).setIcon(null);
    150         verify(mDivider).setVisible(false);
    151     }
    152 
    153     @Test
    154     public void display_showRecents() {
    155         final List<UsageStats> stats = new ArrayList<>();
    156         final UsageStats stat1 = new UsageStats();
    157         final UsageStats stat2 = new UsageStats();
    158         final UsageStats stat3 = new UsageStats();
    159         stat1.mLastTimeUsed = System.currentTimeMillis();
    160         stat1.mPackageName = "pkg.class";
    161         stats.add(stat1);
    162 
    163         stat2.mLastTimeUsed = System.currentTimeMillis();
    164         stat2.mPackageName = "com.android.settings";
    165         stats.add(stat2);
    166 
    167         stat3.mLastTimeUsed = System.currentTimeMillis();
    168         stat3.mPackageName = "pkg.class2";
    169         stats.add(stat3);
    170 
    171         // stat1, stat2 are valid apps. stat3 is invalid.
    172         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
    173             .thenReturn(mAppEntry);
    174         when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
    175             .thenReturn(mAppEntry);
    176         when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
    177             .thenReturn(null);
    178         when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
    179             .thenReturn(new ResolveInfo());
    180         when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
    181             .thenReturn(stats);
    182         mAppEntry.info = mApplicationInfo;
    183 
    184         mController.displayPreference(mScreen);
    185 
    186         verify(mCategory).setTitle(R.string.recent_app_category_title);
    187         // Only add stat1. stat2 is skipped because of the package name, stat3 skipped because
    188         // it's invalid app.
    189         verify(mCategory, times(1)).addPreference(any(Preference.class));
    190 
    191         verify(mSeeAllPref).setSummary(null);
    192         verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp);
    193         verify(mDivider).setVisible(true);
    194     }
    195 
    196     @Test
    197     public void display_showRecentsWithInstantApp() {
    198         // Regular app.
    199         final List<UsageStats> stats = new ArrayList<>();
    200         final UsageStats stat1 = new UsageStats();
    201         stat1.mLastTimeUsed = System.currentTimeMillis();
    202         stat1.mPackageName = "com.foo.bar";
    203         stats.add(stat1);
    204 
    205         // Instant app.
    206         final UsageStats stat2 = new UsageStats();
    207         stat2.mLastTimeUsed = System.currentTimeMillis() + 200;
    208         stat2.mPackageName = "com.foo.barinstant";
    209         stats.add(stat2);
    210 
    211         ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class);
    212         ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class);
    213         stat1Entry.info = mApplicationInfo;
    214         stat2Entry.info = mApplicationInfo;
    215 
    216         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry);
    217         when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry);
    218 
    219         // Only the regular app stat1 should have its intent resolve.
    220         when(mPackageManager.resolveActivity(argThat(intentMatcher(stat1.mPackageName)), anyInt()))
    221             .thenReturn(new ResolveInfo());
    222 
    223         when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
    224                 .thenReturn(stats);
    225 
    226         // Make sure stat2 is considered an instant app.
    227         ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
    228                 (InstantAppDataProvider) (ApplicationInfo info) -> info == stat2Entry.info);
    229 
    230         mController.displayPreference(mScreen);
    231 
    232         ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
    233         verify(mCategory, times(2)).addPreference(prefCaptor.capture());
    234         List<Preference> prefs = prefCaptor.getAllValues();
    235         assertThat(prefs.get(1).getKey()).isEqualTo(stat1.mPackageName);
    236         assertThat(prefs.get(0).getKey()).isEqualTo(stat2.mPackageName);
    237     }
    238 
    239     @Test
    240     public void display_showRecentsWithNullAppEntryOrInfo() {
    241         final List<UsageStats> stats = new ArrayList<>();
    242         final UsageStats stat1 = new UsageStats();
    243         final UsageStats stat2 = new UsageStats();
    244         stat1.mLastTimeUsed = System.currentTimeMillis();
    245         stat1.mPackageName = "pkg.class";
    246         stats.add(stat1);
    247 
    248         stat2.mLastTimeUsed = System.currentTimeMillis();
    249         stat2.mPackageName = "pkg.class2";
    250         stats.add(stat2);
    251 
    252         // app1 has AppEntry with null info, app2 has null AppEntry.
    253         mAppEntry.info = null;
    254         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
    255                 .thenReturn(mAppEntry);
    256         when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
    257                 .thenReturn(null);
    258 
    259         when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
    260                 .thenReturn(stats);
    261 
    262         // We should not crash here.
    263         mController.displayPreference(mScreen);
    264     }
    265 
    266     @Test
    267     public void display_hasRecentButNoneDisplayable_showAppInfo() {
    268         final List<UsageStats> stats = new ArrayList<>();
    269         final UsageStats stat1 = new UsageStats();
    270         final UsageStats stat2 = new UsageStats();
    271         stat1.mLastTimeUsed = System.currentTimeMillis();
    272         stat1.mPackageName = "com.android.phone";
    273         stats.add(stat1);
    274 
    275         stat2.mLastTimeUsed = System.currentTimeMillis();
    276         stat2.mPackageName = "com.android.settings";
    277         stats.add(stat2);
    278 
    279         // stat1, stat2 are not displayable
    280         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
    281             .thenReturn(mock(ApplicationsState.AppEntry.class));
    282         when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
    283             .thenReturn(mock(ApplicationsState.AppEntry.class));
    284         when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
    285             .thenReturn(new ResolveInfo());
    286         when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
    287             .thenReturn(stats);
    288 
    289         mController.displayPreference(mScreen);
    290 
    291         verify(mCategory, never()).addPreference(any(Preference.class));
    292         verify(mCategory).setTitle(null);
    293         verify(mSeeAllPref).setTitle(R.string.applications_settings);
    294         verify(mSeeAllPref).setIcon(null);
    295     }
    296 
    297     @Test
    298     public void display_showRecents_formatSummary() {
    299         final UsageStats stat1 = new UsageStats();
    300         stat1.mLastTimeUsed = System.currentTimeMillis();
    301         stat1.mPackageName = "pkg.class";
    302         final List<UsageStats> stats = new ArrayList<>();
    303         stats.add(stat1);
    304 
    305         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
    306             .thenReturn(mAppEntry);
    307         when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
    308             .thenReturn(new ResolveInfo());
    309         when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
    310             .thenReturn(stats);
    311         mAppEntry.info = mApplicationInfo;
    312 
    313         mController.displayPreference(mScreen);
    314 
    315         verify(mCategory).addPreference(argThat(summaryMatches("0 minutes ago")));
    316     }
    317 
    318     private static ArgumentMatcher<Preference> summaryMatches(String expected) {
    319         return preference -> TextUtils.equals(expected, preference.getSummary());
    320     }
    321 
    322     // Used for matching an intent with a specific package name.
    323     private static ArgumentMatcher<Intent> intentMatcher(String packageName) {
    324         return intent -> packageName.equals(intent.getPackage());
    325     }
    326 }
    327