• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
setUp()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
isAlwaysAvailable()115     public void isAlwaysAvailable() {
116         assertThat(mController.isAvailable()).isTrue();
117     }
118 
119     @Test
doNotIndexCategory()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
onDisplayAndUpdateState_shouldRefreshUi()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")
display_shouldNotShowRecents_showAppInfoPreference()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
display_showRecents()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
display_showRecentsWithInstantApp()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
display_showRecentsWithNullAppEntryOrInfo()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
display_hasRecentButNoneDisplayable_showAppInfo()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
display_showRecents_formatSummary()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 
summaryMatches(String expected)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.
intentMatcher(String packageName)323     private static ArgumentMatcher<Intent> intentMatcher(String packageName) {
324         return intent -> packageName.equals(intent.getPackage());
325     }
326 }
327