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