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.settings.homepage;
18 
19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotEquals;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.anyString;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.doReturn;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.spy;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.app.ActivityManager;
39 import android.content.Intent;
40 import android.content.pm.PackageManager;
41 import android.net.Uri;
42 import android.os.Build;
43 import android.os.UserHandle;
44 import android.platform.test.annotations.DisableFlags;
45 import android.platform.test.flag.junit.SetFlagsRule;
46 import android.provider.Settings;
47 import android.util.ArraySet;
48 import android.view.View;
49 import android.view.Window;
50 import android.view.WindowManager;
51 import android.widget.FrameLayout;
52 
53 import androidx.fragment.app.Fragment;
54 import androidx.test.core.app.ApplicationProvider;
55 
56 import com.android.settings.R;
57 import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
58 import com.android.settings.flags.Flags;
59 import com.android.settings.testutils.shadow.ShadowActivityEmbeddingUtils;
60 import com.android.settings.testutils.shadow.ShadowPasswordUtils;
61 import com.android.settings.testutils.shadow.ShadowUserManager;
62 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
63 
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Rule;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.ArgumentCaptor;
70 import org.mockito.junit.MockitoJUnit;
71 import org.mockito.junit.MockitoRule;
72 import org.robolectric.Robolectric;
73 import org.robolectric.RobolectricTestRunner;
74 import org.robolectric.android.controller.ActivityController;
75 import org.robolectric.annotation.Config;
76 import org.robolectric.annotation.Implementation;
77 import org.robolectric.annotation.Implements;
78 import org.robolectric.shadow.api.Shadow;
79 import org.robolectric.shadows.ShadowActivityManager;
80 import org.robolectric.util.ReflectionHelpers;
81 
82 import java.util.Set;
83 
84 @RunWith(RobolectricTestRunner.class)
85 @Config(shadows = {
86         ShadowUserManager.class,
87         SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class,
88         ShadowActivityManager.class,
89 })
90 public class SettingsHomepageActivityTest {
91     @Rule
92     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
93     @Rule
94     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
95 
96     @Before
setup()97     public void setup() {
98         Settings.Global.putInt(ApplicationProvider.getApplicationContext().getContentResolver(),
99                 Settings.Global.DEVICE_PROVISIONED, 1);
100     }
101 
102     @After
tearDown()103     public void tearDown() {
104         ShadowPasswordUtils.reset();
105     }
106 
107     @Test
launch_deviceUnprovisioned_finish()108     public void launch_deviceUnprovisioned_finish() {
109         Settings.Global.putInt(ApplicationProvider.getApplicationContext().getContentResolver(),
110                 Settings.Global.DEVICE_PROVISIONED, 0);
111 
112         SettingsHomepageActivity activity = Robolectric.buildActivity(
113                 SettingsHomepageActivity.class).create().get();
114 
115         assertThat(activity.isFinishing()).isTrue();
116     }
117 
118     @Test
launch_shouldHaveAnimationForIaFragment()119     public void launch_shouldHaveAnimationForIaFragment() {
120         final SettingsHomepageActivity activity = Robolectric.buildActivity(
121                 SettingsHomepageActivity.class).create().get();
122         final FrameLayout frameLayout = activity.findViewById(R.id.main_content);
123 
124         assertThat(frameLayout.getLayoutTransition()).isNotNull();
125     }
126 
127     @Test
128     @DisableFlags(Flags.FLAG_HOMEPAGE_REVAMP)
launch_configDisabled_shouldHideAvatar()129     public void launch_configDisabled_shouldHideAvatar() {
130         final SettingsHomepageActivity activity = Robolectric.buildActivity(
131                 SettingsHomepageActivity.class).create().get();
132 
133         final View avatarView = activity.findViewById(R.id.account_avatar);
134         assertThat(avatarView.getVisibility()).isNotEqualTo(View.VISIBLE);
135     }
136 
137     @Test
138     @Config(qualifiers = "mcc999")
139     @DisableFlags(Flags.FLAG_HOMEPAGE_REVAMP)
launch_configEnabled_shouldShowAvatar()140     public void launch_configEnabled_shouldShowAvatar() {
141         final SettingsHomepageActivity activity = Robolectric.buildActivity(
142                 SettingsHomepageActivity.class).create().get();
143 
144         final View avatarView = activity.findViewById(R.id.account_avatar);
145         assertThat(avatarView.getVisibility()).isEqualTo(View.VISIBLE);
146     }
147 
148     @Test
149     @Config(qualifiers = "mcc999")
150     @DisableFlags(Flags.FLAG_HOMEPAGE_REVAMP)
launch_LowRamDevice_shouldHideAvatar()151     public void launch_LowRamDevice_shouldHideAvatar() {
152         final ShadowActivityManager activityManager = Shadow.extract(
153                 ApplicationProvider.getApplicationContext().getSystemService(
154                         ActivityManager.class));
155         activityManager.setIsLowRamDevice(true);
156 
157         final SettingsHomepageActivity activity = Robolectric.buildActivity(
158                 SettingsHomepageActivity.class).create().get();
159 
160         final View avatarView = activity.findViewById(R.id.account_avatar);
161         assertThat(avatarView.getVisibility()).isNotEqualTo(View.VISIBLE);
162     }
163 
164     @Test
showHomepageWithSuggestion_showSuggestion()165     public void showHomepageWithSuggestion_showSuggestion() {
166         final SettingsHomepageActivity activity = Robolectric.buildActivity(
167                 SettingsHomepageActivity.class).create().get();
168         final View viewRoot = activity.findViewById(R.id.settings_homepage_container);
169         final View suggestionTile = activity.findViewById(R.id.suggestion_content);
170 
171         activity.showHomepageWithSuggestion(true);
172 
173         assertThat(viewRoot.getVisibility()).isEqualTo(View.VISIBLE);
174         assertThat(suggestionTile.getVisibility()).isEqualTo(View.VISIBLE);
175     }
176 
177     @Test
showHomepageWithSuggestion_hideSuggestion()178     public void showHomepageWithSuggestion_hideSuggestion() {
179         final SettingsHomepageActivity activity = Robolectric.buildActivity(
180                 SettingsHomepageActivity.class).create().get();
181         final View viewRoot = activity.findViewById(R.id.settings_homepage_container);
182         final View suggestionTile = activity.findViewById(R.id.suggestion_content);
183 
184         activity.showHomepageWithSuggestion(false);
185 
186         assertThat(viewRoot.getVisibility()).isEqualTo(View.VISIBLE);
187         assertThat(suggestionTile.getVisibility()).isEqualTo(View.GONE);
188     }
189 
190     @Test
showHomepageWithSuggestion_callTwice_shouldKeepPreviousVisibility()191     public void showHomepageWithSuggestion_callTwice_shouldKeepPreviousVisibility() {
192         final SettingsHomepageActivity activity = Robolectric.buildActivity(
193                 SettingsHomepageActivity.class).create().get();
194         final View suggestionTile = activity.findViewById(R.id.suggestion_content);
195 
196         activity.showHomepageWithSuggestion(false);
197         activity.showHomepageWithSuggestion(true);
198 
199         assertThat(suggestionTile.getVisibility()).isEqualTo(View.GONE);
200     }
201 
202     @Test
showHomepageWithSuggestion_callAfterOnStop_shouldUpdateVisibility()203     public void showHomepageWithSuggestion_callAfterOnStop_shouldUpdateVisibility() {
204         final SettingsHomepageActivity activity = Robolectric.buildActivity(
205                 SettingsHomepageActivity.class).create().get();
206         final View suggestionTile = activity.findViewById(R.id.suggestion_content);
207 
208         activity.showHomepageWithSuggestion(true);
209         activity.onStop();
210         activity.showHomepageWithSuggestion(false);
211 
212         assertThat(suggestionTile.getVisibility()).isEqualTo(View.GONE);
213     }
214 
215     @Test
onStart_isNotDebuggable_shouldHideSystemOverlay()216     public void onStart_isNotDebuggable_shouldHideSystemOverlay() {
217         ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
218 
219         final ActivityController<SettingsHomepageActivity> activityController =
220                 Robolectric.buildActivity(SettingsHomepageActivity.class).create();
221         final SettingsHomepageActivity activity = spy(activityController.get());
222         final Window window = mock(Window.class);
223         when(activity.getWindow()).thenReturn(window);
224         activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
225 
226         activityController.start();
227 
228         verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
229     }
230 
231     @Test
onStop_isNotDebuggable_shouldRemoveHideSystemOverlay()232     public void onStop_isNotDebuggable_shouldRemoveHideSystemOverlay() {
233         ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
234 
235         final ActivityController<SettingsHomepageActivity> activityController =
236                 Robolectric.buildActivity(SettingsHomepageActivity.class).create();
237         final SettingsHomepageActivity activity = spy(activityController.get());
238         final Window window = mock(Window.class);
239         when(activity.getWindow()).thenReturn(window);
240         activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
241 
242         activityController.start();
243 
244         verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
245 
246         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
247         when(window.getAttributes()).thenReturn(layoutParams);
248 
249         activityController.stop();
250         final ArgumentCaptor<WindowManager.LayoutParams> paramCaptor = ArgumentCaptor.forClass(
251                 WindowManager.LayoutParams.class);
252 
253         verify(window).setAttributes(paramCaptor.capture());
254         assertThat(paramCaptor.getValue().privateFlags
255                 & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
256     }
257 
258     @Test
onCreate_TaskRoot_shouldNotFinish()259     public void onCreate_TaskRoot_shouldNotFinish() {
260         SettingsHomepageActivity activity =
261                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
262         doReturn(true).when(activity).isTaskRoot();
263 
264         activity.onCreate(/* savedInstanceState= */ null);
265 
266         verify(activity, never()).finish();
267     }
268 
269     @Test
onCreate_notTaskRoot_shouldRestartActivity()270     public void onCreate_notTaskRoot_shouldRestartActivity() {
271         SettingsHomepageActivity activity =
272                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
273         doReturn(false).when(activity).isTaskRoot();
274 
275         activity.onCreate(/* savedInstanceState= */ null);
276 
277         verify(activity).finish();
278         verify(activity).startActivity(any(Intent.class));
279     }
280 
281     @Test
onCreate_notTaskRoot_flagNewTask_shouldOnlyFinish()282     public void onCreate_notTaskRoot_flagNewTask_shouldOnlyFinish() {
283         SettingsHomepageActivity activity =
284                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class,
285                         new Intent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)).get());
286         doReturn(false).when(activity).isTaskRoot();
287 
288         activity.onCreate(/* savedInstanceState= */ null);
289 
290         verify(activity).finish();
291         verify(activity, never()).startActivity(any(Intent.class));
292     }
293 
294     @Test
295     @Config(shadows = ShadowActivityEmbeddingUtils.class)
onCreate_eligibleProfile_shouldNotRestartActivity()296     public void onCreate_eligibleProfile_shouldNotRestartActivity() {
297         ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true);
298         SettingsHomepageActivity activity =
299                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
300         doReturn(0).when(activity).getUserId();
301         doReturn(true).when(activity).isTaskRoot();
302 
303         activity.onCreate(/* savedInstanceState= */ null);
304 
305         verify(activity, never()).finish();
306         verify(activity, never()).startActivityAsUser(any(Intent.class), any(UserHandle.class));
307     }
308 
309     @Test
310     @Config(shadows = ShadowActivityEmbeddingUtils.class)
onCreate_managedProfile_shouldRestartActivity()311     public void onCreate_managedProfile_shouldRestartActivity() {
312         ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true);
313         SettingsHomepageActivity activity =
314                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
315         final Set<Integer> profileIds = new ArraySet<>();
316         profileIds.add(10);
317         ShadowUserManager.getShadow().setManagedProfiles(profileIds);
318         doReturn(10).when(activity).getUserId();
319         doReturn(true).when(activity).isTaskRoot();
320 
321         activity.onCreate(/* savedInstanceState= */ null);
322 
323         verify(activity).finish();
324         verify(activity).startActivityAsUser(any(Intent.class), any(UserHandle.class));
325     }
326 
327     @Test
328     @Config(shadows = ShadowActivityEmbeddingUtils.class)
onCreate_privateProfile_shouldRestartActivity()329     public void onCreate_privateProfile_shouldRestartActivity() {
330         ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true);
331         SettingsHomepageActivity activity =
332                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
333         ShadowUserManager.getShadow().setPrivateProfile(11, "private", 0);
334         doReturn(11).when(activity).getUserId();
335         doReturn(true).when(activity).isTaskRoot();
336 
337         activity.onCreate(/* savedInstanceState= */ null);
338 
339         verify(activity).finish();
340         verify(activity).startActivityAsUser(any(Intent.class), any(UserHandle.class));
341     }
342 
343     /** This test is for large screen devices Activity embedding. */
344     @Test
345     @Config(shadows = ShadowActivityEmbeddingUtils.class)
onCreate_flagClearTop_shouldInitRules()346     public void onCreate_flagClearTop_shouldInitRules() {
347         ShadowActivityEmbeddingUtils.setIsEmbeddingActivityEnabled(true);
348         SettingsHomepageActivity activity =
349                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class,
350                         new Intent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).get());
351 
352         activity.onCreate(/* savedInstanceState= */ null);
353 
354         verify(activity).initSplitPairRules();
355     }
356 
357     @Test
getInitialReferrer_differentPackage_returnCurrentReferrer()358     public void getInitialReferrer_differentPackage_returnCurrentReferrer() {
359         SettingsHomepageActivity activity =
360                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
361         String referrer = "com.abc";
362         doReturn(referrer).when(activity).getCurrentReferrer();
363 
364         assertEquals(activity.getInitialReferrer(), referrer);
365     }
366 
367     @Test
getInitialReferrer_noReferrerExtra_returnCurrentReferrer()368     public void getInitialReferrer_noReferrerExtra_returnCurrentReferrer() {
369         SettingsHomepageActivity activity =
370                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
371         String referrer = activity.getPackageName();
372         doReturn(referrer).when(activity).getCurrentReferrer();
373 
374         assertEquals(activity.getInitialReferrer(), referrer);
375     }
376 
377     @Test
getInitialReferrer_hasReferrerExtra_returnGivenReferrer()378     public void getInitialReferrer_hasReferrerExtra_returnGivenReferrer() {
379         String referrer = "com.abc";
380         SettingsHomepageActivity activity =
381                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class,
382                         new Intent().putExtra(SettingsHomepageActivity.EXTRA_INITIAL_REFERRER,
383                                 referrer)).get());
384         doReturn(activity.getPackageName()).when(activity).getCurrentReferrer();
385 
386         assertEquals(activity.getInitialReferrer(), referrer);
387     }
388 
389     @Test
getCurrentReferrer_hasReferrerExtra_shouldNotEqual()390     public void getCurrentReferrer_hasReferrerExtra_shouldNotEqual() {
391         String referrer = "com.abc";
392         Uri uri = new Uri.Builder().scheme("android-app").authority(referrer).build();
393         SettingsHomepageActivity activity =
394                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class,
395                         new Intent().putExtra(Intent.EXTRA_REFERRER, uri)).get());
396 
397         assertNotEquals(activity.getCurrentReferrer(), referrer);
398     }
399 
400     @Test
getCurrentReferrer_hasReferrerNameExtra_shouldNotEqual()401     public void getCurrentReferrer_hasReferrerNameExtra_shouldNotEqual() {
402         String referrer = "com.abc";
403         SettingsHomepageActivity activity =
404                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class,
405                         new Intent().putExtra(Intent.EXTRA_REFERRER_NAME, referrer)).get());
406 
407         assertNotEquals(activity.getCurrentReferrer(), referrer);
408     }
409 
410     @Test
isCallingAppPermitted_emptyPermission_returnTrue()411     public void isCallingAppPermitted_emptyPermission_returnTrue() {
412         SettingsHomepageActivity activity =
413                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
414         doReturn(PackageManager.PERMISSION_DENIED).when(activity)
415                 .checkPermission(anyString(), anyInt(), anyInt());
416 
417         assertTrue(activity.isCallingAppPermitted("", 1000));
418     }
419 
420     @Test
isCallingAppPermitted_notGrantedPermission_returnFalse()421     public void isCallingAppPermitted_notGrantedPermission_returnFalse() {
422         SettingsHomepageActivity activity =
423                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
424         doReturn(PackageManager.PERMISSION_DENIED).when(activity)
425                 .checkPermission(anyString(), anyInt(), anyInt());
426 
427         assertFalse(activity.isCallingAppPermitted("android.permission.TEST", 1000));
428     }
429 
430     @Test
isCallingAppPermitted_grantedPermission_returnTrue()431     public void isCallingAppPermitted_grantedPermission_returnTrue() {
432         SettingsHomepageActivity activity =
433                 spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
434         String permission = "android.permission.TEST";
435         doReturn(PackageManager.PERMISSION_DENIED).when(activity)
436                 .checkPermission(anyString(), anyInt(), anyInt());
437         doReturn(PackageManager.PERMISSION_GRANTED).when(activity)
438                 .checkPermission(eq(permission), anyInt(), eq(1000));
439 
440         assertTrue(activity.isCallingAppPermitted(permission, 1000));
441     }
442 
443     @Implements(SuggestionFeatureProviderImpl.class)
444     public static class ShadowSuggestionFeatureProviderImpl {
445 
446         @Implementation
getContextualSuggestionFragment()447         public Class<? extends Fragment> getContextualSuggestionFragment() {
448             return Fragment.class;
449         }
450     }
451 }
452