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.password;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.res.Resources;
27 import android.os.UserHandle;
28 import android.util.TypedValue;
29 import android.view.View;
30 import android.widget.Button;
31 import android.widget.TextView;
32 
33 import androidx.appcompat.app.AlertDialog;
34 import androidx.fragment.app.FragmentActivity;
35 import androidx.test.core.app.ApplicationProvider;
36 
37 import com.android.internal.widget.LockPatternUtils;
38 import com.android.internal.widget.LockPatternView;
39 import com.android.internal.widget.LockPatternView.Cell;
40 import com.android.internal.widget.LockPatternView.DisplayMode;
41 import com.android.settings.R;
42 import com.android.settings.SetupRedactionInterstitial;
43 import com.android.settings.password.ChooseLockPattern.ChooseLockPatternFragment;
44 import com.android.settings.password.ChooseLockPattern.IntentBuilder;
45 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
46 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
47 import com.android.settings.testutils.shadow.ShadowUtils;
48 
49 import com.google.android.setupcompat.PartnerCustomizationLayout;
50 import com.google.android.setupcompat.template.FooterBarMixin;
51 import com.google.android.setupcompat.template.FooterButton;
52 
53 import org.junit.Before;
54 import org.junit.Ignore;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.robolectric.RobolectricTestRunner;
58 import org.robolectric.Shadows;
59 import org.robolectric.android.controller.ActivityController;
60 import org.robolectric.annotation.Config;
61 import org.robolectric.shadows.ShadowPackageManager;
62 import org.robolectric.util.ReflectionHelpers;
63 import org.robolectric.util.ReflectionHelpers.ClassParameter;
64 
65 import java.util.Arrays;
66 
67 @RunWith(RobolectricTestRunner.class)
68 @Config(shadows = {ShadowUtils.class, ShadowAlertDialogCompat.class, ShadowLockPatternUtils.class})
69 public class SetupChooseLockPatternTest {
70     private Context mContext;
71 
72     private SetupChooseLockPattern mActivity;
73 
74     @Before
setUp()75     public void setUp() {
76         mContext = ApplicationProvider.getApplicationContext();
77         mContext.getPackageManager().setComponentEnabledSetting(
78                 new ComponentName(mContext, SetupRedactionInterstitial.class),
79                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
80                 PackageManager.DONT_KILL_APP);
81 
82         final Intent intent =
83                 SetupChooseLockPattern.modifyIntentForSetup(
84                         mContext,
85                         new IntentBuilder(mContext)
86                                 .setUserId(UserHandle.myUserId())
87                                 .build());
88         mActivity = ActivityController.of(new SetupChooseLockPattern(), intent).setup().get();
89     }
90 
91     @Test
chooseLockSaved_shouldEnableRedactionInterstitial()92     public void chooseLockSaved_shouldEnableRedactionInterstitial() {
93         findFragment(mActivity).onChosenLockSaveFinished(false, null);
94 
95         ShadowPackageManager spm = Shadows.shadowOf(mContext.getPackageManager());
96         ComponentName cname = new ComponentName(mContext, SetupRedactionInterstitial.class);
97         final int componentEnabled = spm.getComponentEnabledSettingFlags(cname)
98                 & PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
99         assertThat(componentEnabled).isEqualTo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
100     }
101 
102     @Test
103     @Ignore("b/295325503")
optionsButton_whenPatternSelected_shouldBeVisible()104     public void optionsButton_whenPatternSelected_shouldBeVisible() {
105         final Button button = mActivity.findViewById(R.id.screen_lock_options);
106         assertThat(button).isNotNull();
107         assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
108 
109         final LockPatternView lockPatternView = mActivity.findViewById(R.id.lockPattern);
110         ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
111 
112         enterPattern();
113         assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
114     }
115 
verifyScreenLockOptionsShown()116     private void verifyScreenLockOptionsShown() {
117         final Button button = mActivity.findViewById(R.id.screen_lock_options);
118         assertThat(button).isNotNull();
119         assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
120 
121         button.performClick();
122         final AlertDialog chooserDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
123         assertThat(chooserDialog).isNotNull();
124         int count = chooserDialog.getListView().getCount();
125         assertWithMessage("List items shown").that(count).isEqualTo(3);
126     }
127 
128     @Config(qualifiers = "sw400dp")
129     @Test
130     @Ignore("b/295325503")
sw400dp_shouldShowScreenLockOptions()131     public void sw400dp_shouldShowScreenLockOptions() {
132         verifyScreenLockOptionsShown();
133     }
134 
135     @Config(qualifiers = "sw400dp-land")
136     @Test
137     @Ignore("b/295325503")
sw400dpLandscape_shouldShowScreenLockOptions()138     public void sw400dpLandscape_shouldShowScreenLockOptions() {
139         verifyScreenLockOptionsShown();
140     }
141 
verifyScreenLockOptionsHidden()142     private void verifyScreenLockOptionsHidden() {
143         Button button = mActivity.findViewById(R.id.screen_lock_options);
144         assertThat(button).isNotNull();
145         assertThat(button.getVisibility()).isEqualTo(View.GONE);
146     }
147 
148     @Config(qualifiers = "sw300dp")
149     @Test
150     @Ignore("b/295325503")
smallScreens_shouldHideScreenLockOptions()151     public void smallScreens_shouldHideScreenLockOptions() {
152         verifyScreenLockOptionsHidden();
153     }
154 
155     @Config(qualifiers = "sw300dp-land")
156     @Test
157     @Ignore("b/295325503")
smallScreensLandscape_shouldHideScreenLockOptions()158     public void smallScreensLandscape_shouldHideScreenLockOptions() {
159         verifyScreenLockOptionsHidden();
160     }
161 
162     @Test
163     @Ignore("b/295325503")
skipButton_shouldBeVisible_duringNonFingerprintFlow()164     public void skipButton_shouldBeVisible_duringNonFingerprintFlow() {
165         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
166         final Button skipOrClearButton =
167                 layout.getMixin(FooterBarMixin.class).getSecondaryButtonView();
168 
169         assertThat(skipOrClearButton).isNotNull();
170         assertThat(skipOrClearButton.getVisibility()).isEqualTo(View.VISIBLE);
171 
172         skipOrClearButton.performClick();
173         AlertDialog chooserDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
174         assertThat(chooserDialog).isNotNull();
175     }
176 
177     @Test
clearButton_shouldBeVisible_duringRetryStage()178     public void clearButton_shouldBeVisible_duringRetryStage() {
179         enterPattern();
180 
181         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
182         final Button skipOrClearButton =
183                 layout.getMixin(FooterBarMixin.class).getSecondaryButtonView();
184 
185         assertThat(skipOrClearButton.getVisibility()).isEqualTo(View.VISIBLE);
186         assertThat(skipOrClearButton.isEnabled()).isTrue();
187 
188         skipOrClearButton.performClick();
189 
190         assertThat(findFragment(mActivity).mChosenPattern).isNull();
191     }
192 
193     @Test
createActivity_enterPattern_clearButtonShouldBeShown()194     public void createActivity_enterPattern_clearButtonShouldBeShown() {
195         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
196         final Button skipOrClearButton =
197                 layout.getMixin(FooterBarMixin.class).getSecondaryButtonView();
198 
199         assertThat(skipOrClearButton.isEnabled()).isTrue();
200         assertThat(skipOrClearButton.getVisibility()).isEqualTo(View.VISIBLE);
201         assertThat(skipOrClearButton.getText())
202                 .isEqualTo(mContext.getString(R.string.skip_label));
203 
204         enterPattern();
205 
206         assertThat(skipOrClearButton.isEnabled()).isTrue();
207         assertThat(skipOrClearButton.getVisibility()).isEqualTo(View.VISIBLE);
208         assertThat(skipOrClearButton.getText())
209                 .isEqualTo(mContext.getString(R.string.lockpattern_retry_button_text));
210     }
211 
212     @Test
createActivity_patternDescription_shouldBeShown()213     public void createActivity_patternDescription_shouldBeShown() {
214         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
215         final TextView patternDescription =
216                 layout.findViewById(R.id.sud_layout_subtitle);
217 
218         assertThat(patternDescription.getVisibility()).isEqualTo(View.VISIBLE);
219         assertThat(patternDescription.getText()).isEqualTo(
220                 mContext.getString(R.string.lockpassword_choose_your_pattern_description));
221     }
222 
223     @Test
createActivity_patternTitle_shouldShowGenericText()224     public void createActivity_patternTitle_shouldShowGenericText() {
225         final CharSequence headerView = mActivity.getTitle();
226 
227         assertThat(headerView).isEqualTo(
228                 mContext.getString(R.string.lockpassword_choose_your_pattern_header));
229     }
230 
231     @Test
inIntroductionStage_theHeaderHeight_shouldSetMinLinesTwoToPreventFlicker()232     public void inIntroductionStage_theHeaderHeight_shouldSetMinLinesTwoToPreventFlicker() {
233         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
234         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
235 
236         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
237         assertThat(headerView.getText().toString()).isEqualTo(
238                 mContext.getString(R.string.lockpassword_choose_your_pattern_description));
239     }
240 
241     @Test
createActivity_enterPattern_shouldGoToFirstChoiceValidStage()242     public void createActivity_enterPattern_shouldGoToFirstChoiceValidStage() {
243         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
244         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
245 
246         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
247 
248         enterPattern();
249 
250         assertThat(headerView.getText().toString()).isEqualTo(
251                 mContext.getString(R.string.lockpattern_pattern_entered_header));
252     }
253 
254     @Test
createActivity_enterShortPattern_shouldGoToChoiceTooShortStage()255     public void createActivity_enterShortPattern_shouldGoToChoiceTooShortStage() {
256         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
257         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
258 
259         enterShortPattern();
260 
261         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
262         assertThat(headerView.getText().toString()).isEqualTo(
263                 mContext.getResources().getString(
264                         R.string.lockpattern_recording_incorrect_too_short,
265                         LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
266     }
267 
268     @Test
inChoiceTooShortStage_theHeaderColor_shouldTintOnErrorColor()269     public void inChoiceTooShortStage_theHeaderColor_shouldTintOnErrorColor() {
270         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
271         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
272         final TypedValue typedValue = new TypedValue();
273         final Resources.Theme theme = mActivity.getTheme();
274         theme.resolveAttribute(androidx.appcompat.R.attr.colorError, typedValue, true);
275         final int errorColor = typedValue.data;
276 
277         enterShortPattern();
278 
279         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
280         assertThat(headerView.getTextColors().getDefaultColor()).isEqualTo(errorColor);
281     }
282 
283     @Test
inFirstChoiceValidStage_nextButtonState_shouldEnabled()284     public void inFirstChoiceValidStage_nextButtonState_shouldEnabled() {
285         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
286         final FooterBarMixin footerBarMixin = layout.getMixin(FooterBarMixin.class);
287         final FooterButton nextButton = footerBarMixin.getPrimaryButton();
288 
289         assertThat(nextButton.getVisibility()).isEqualTo(View.VISIBLE);
290         assertThat(nextButton.isEnabled()).isFalse();
291 
292         enterPattern();
293 
294         assertThat(nextButton.isEnabled()).isTrue();
295     }
296 
297     @Test
inFirstChoiceValidStage_clickNextButton_shouldGoToNeedToConfirmStage()298     public void inFirstChoiceValidStage_clickNextButton_shouldGoToNeedToConfirmStage() {
299         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
300         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
301         final FooterBarMixin footerBarMixin = layout.getMixin(FooterBarMixin.class);
302         final Button nextButton = footerBarMixin.getPrimaryButtonView();
303 
304         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
305 
306         enterPattern();
307         nextButton.performClick();
308 
309         assertThat(headerView.getText().toString()).isEqualTo(
310                 mContext.getString(R.string.lockpattern_need_to_confirm));
311     }
312 
313     @Test
inNeedToConfirmStage_enterWrongPattern_shouldGoToConfirmWrongStage()314     public void inNeedToConfirmStage_enterWrongPattern_shouldGoToConfirmWrongStage() {
315         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
316         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
317         final FooterBarMixin footerBarMixin = layout.getMixin(FooterBarMixin.class);
318         final Button nextButton = footerBarMixin.getPrimaryButtonView();
319         // IntroductionStage
320         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
321 
322         enterPattern();
323         nextButton.performClick();
324 
325         // NeedToConfirmStage
326         assertThat(headerView.getText().toString()).isEqualTo(
327                 mContext.getString(R.string.lockpattern_need_to_confirm));
328 
329         enterShortPattern();
330 
331         // ConfirmWrongStage
332         assertThat(headerView.getText().toString()).isEqualTo(
333                 mContext.getString(R.string.lockpattern_need_to_unlock_wrong));
334         assertThat(nextButton.getText().toString()).isEqualTo(
335                 mContext.getString(R.string.lockpattern_confirm_button_text));
336         assertThat(nextButton.isEnabled()).isFalse();
337     }
338 
339     @Test
inNeedToConfirmStage_enterCorrectPattern_shouldGoToChoiceConfirmedStage()340     public void inNeedToConfirmStage_enterCorrectPattern_shouldGoToChoiceConfirmedStage() {
341         final PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
342         final TextView headerView = layout.findViewById(R.id.sud_layout_subtitle);
343         final FooterBarMixin footerBarMixin = layout.getMixin(FooterBarMixin.class);
344         final Button nextButton = footerBarMixin.getPrimaryButtonView();
345         // IntroductionStage
346         assertThat(headerView.getVisibility()).isEqualTo(View.VISIBLE);
347 
348         enterPattern();
349         nextButton.performClick();
350 
351         // NeedToConfirmStage
352         assertThat(headerView.getText().toString()).isEqualTo(
353                 mContext.getString(R.string.lockpattern_need_to_confirm));
354 
355         enterPattern();
356 
357         // ChoiceConfirmedStage
358         assertThat(headerView.getText().toString()).isEqualTo(
359                 mContext.getString(R.string.lockpattern_pattern_confirmed_header));
360         assertThat(nextButton.getText().toString()).isEqualTo(
361                 mContext.getString(R.string.lockpattern_confirm_button_text));
362         assertThat(nextButton.isEnabled()).isTrue();
363     }
364 
findFragment(FragmentActivity activity)365     private ChooseLockPatternFragment findFragment(FragmentActivity activity) {
366         return (ChooseLockPatternFragment)
367                 activity.getSupportFragmentManager().findFragmentById(R.id.main_content);
368     }
369 
enterPattern()370     private void enterPattern() {
371         LockPatternView lockPatternView = mActivity.findViewById(R.id.lockPattern);
372         lockPatternView.setPattern(
373                 DisplayMode.Animate,
374                 Arrays.asList(
375                         createCell(0, 0),
376                         createCell(0, 1),
377                         createCell(1, 1),
378                         createCell(1, 0)));
379         ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
380     }
381 
enterShortPattern()382     private void enterShortPattern() {
383         LockPatternView lockPatternView = mActivity.findViewById(R.id.lockPattern);
384         lockPatternView.setPattern(
385                 DisplayMode.Animate,
386                 Arrays.asList(
387                         createCell(0, 0),
388                         createCell(0, 1),
389                         createCell(1, 1)));
390         ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
391     }
392 
createCell(int row, int column)393     private Cell createCell(int row, int column) {
394         return ReflectionHelpers.callConstructor(
395                 Cell.class,
396                 ClassParameter.from(int.class, row),
397                 ClassParameter.from(int.class, column));
398     }
399 }
400