Default setup wizard to use PIN

- Added "Screen lock options" button in PIN screen, controlled by
  extra EXTRA_SHOW_OPTIONS_BUTTON, which will create a dialog to ask
  the user to choose another screen lock type.
- Extracted ScreenLockType enum and ChooseLockGenericController that
  can be shared by ChooseLockGeneric and the dialog
  ChooseLockTypeDialogFragment.
- The intent extra EXTRA_SHOW_OPTIONS_BUTTON will be set if
  ChooseLockGeneric screen starts ChooseLockPassword /
  ChooseLockPattern without asking the user. (Although the extra is
  ignored by ChooseLockPattern currently)
- Fix layout alignment for the password entry field to remove the
  extra 4dp padding on the sides.

Test: cd tests/robotests && mma
Bug: 35442933
Bug: 38002299
Change-Id: I877fbe08a0c05bb97175e1cbf0260ea6dbda22e2
This commit is contained in:
Maurice Lam
2017-04-27 18:54:33 -07:00
parent 9e8a182c31
commit edb3944984
17 changed files with 1117 additions and 183 deletions

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.password;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.regex.Pattern;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class
})
public class ChooseLockGenericControllerTest {
private ChooseLockGenericController mController;
@Mock
private ManagedLockPasswordProvider mManagedLockPasswordProvider;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new ChooseLockGenericController(
application,
0 /* userId */,
mDevicePolicyManager,
mManagedLockPasswordProvider);
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false);
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false);
}
@After
public void tearDown() {
SettingsShadowResources.reset();
}
@Test
public void isScreenLockVisible_shouldRespectResourceConfig() {
for (ScreenLockType lock : ScreenLockType.values()) {
// All locks except managed defaults to visible
assertThat(mController.isScreenLockVisible(lock)).named(lock + " visible")
.isEqualTo(lock != ScreenLockType.MANAGED);
}
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, true);
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, true);
assertThat(mController.isScreenLockVisible(ScreenLockType.NONE)).named("NONE visible")
.isFalse();
assertThat(mController.isScreenLockVisible(ScreenLockType.SWIPE)).named("SWIPE visible")
.isFalse();
}
@Test
public void isScreenLockVisible_notCurrentUser_shouldHideSwipe() {
mController = new ChooseLockGenericController(application, 1 /* userId */);
assertThat(mController.isScreenLockVisible(ScreenLockType.SWIPE)).named("SWIPE visible")
.isFalse();
}
@Test
public void isScreenLockVisible_managedPasswordChoosable_shouldShowManaged() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
assertThat(mController.isScreenLockVisible(ScreenLockType.MANAGED)).named("MANAGED visible")
.isTrue();
}
@Test
public void isScreenLockEnabled_lowerQuality_shouldReturnFalse() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.maxQuality + 1))
.named(lock + " enabled")
.isFalse();
}
}
@Test
public void isScreenLockEnabled_equalQuality_shouldReturnTrue() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.defaultQuality))
.named(lock + " enabled")
.isTrue();
}
}
@Test
public void isScreenLockEnabled_higherQuality_shouldReturnTrue() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.maxQuality - 1))
.named(lock + " enabled")
.isTrue();
}
}
@Test
public void isScreenLockDisabledByAdmin_lowerQuality_shouldReturnTrue() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality + 1))
.named(lock + " disabledByAdmin")
.isTrue();
}
}
@Test
public void isScreenLockDisabledByAdmin_equalQuality_shouldReturnFalse() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality))
.named(lock + " disabledByAdmin")
.isFalse();
}
}
@Test
public void isScreenLockDisabledByAdmin_higherQuality_shouldReturnFalse() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality - 1))
.named(lock + " disabledByAdmin")
.isFalse();
}
}
@Test
public void isScreenLockDisabledByAdmin_managedNotChoosable_shouldReturnTrue() {
doReturn(false).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
assertThat(mController.isScreenLockDisabledByAdmin(
ScreenLockType.MANAGED, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
.named("MANANGED disabledByAdmin")
.isTrue();
}
@Test
public void getTitle_shouldContainEnumName() {
doReturn("MANAGED").when(mManagedLockPasswordProvider).getPickerOptionTitle(anyBoolean());
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.getTitle(lock).toString())
.containsMatch(Pattern.compile(lock.toString(), Pattern.CASE_INSENSITIVE));
}
}
@Test
public void getVisibleScreenLockTypes_qualitySomething_shouldReturnPatterPinPassword() {
assertThat(mController.getVisibleScreenLockTypes(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false))
.isEqualTo(Arrays.asList(
ScreenLockType.PATTERN,
ScreenLockType.PIN,
ScreenLockType.PASSWORD));
}
@Test
public void getVisibleScreenLockTypes_showDisabled_shouldReturnAllButManaged() {
assertThat(mController.getVisibleScreenLockTypes(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, true))
.isEqualTo(Arrays.asList(
ScreenLockType.NONE,
ScreenLockType.SWIPE,
ScreenLockType.PATTERN,
ScreenLockType.PIN,
ScreenLockType.PASSWORD));
}
@Test
public void upgradeQuality_noDpmRequirement_shouldReturnQuality() {
doReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
.when(mDevicePolicyManager).getPasswordQuality(any(ComponentName.class), anyInt());
int upgradedQuality = mController.upgradeQuality(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertThat(upgradedQuality).named("upgradedQuality")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
}
@Test
public void upgradeQuality_dpmRequirement_shouldReturnRequiredQuality() {
doReturn(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC)
.when(mDevicePolicyManager).getPasswordQuality(any(ComponentName.class), anyInt());
int upgradedQuality = mController.upgradeQuality(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
assertThat(upgradedQuality).named("upgradedQuality")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.password;
import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DevicePolicyManager;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION)
public class ScreenLockTypeTest {
@Test
public void fromQuality_shouldReturnLockWithAssociatedQuality() {
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK))
.isNull();
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_MANAGED))
.isEqualTo(ScreenLockType.MANAGED);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC))
.isEqualTo(ScreenLockType.PIN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX))
.isEqualTo(ScreenLockType.PIN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING))
.isEqualTo(ScreenLockType.PATTERN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
.isEqualTo(ScreenLockType.SWIPE);
}
@Test
public void fromKey_shouldReturnLockWithGivenKey() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(ScreenLockType.fromKey(lock.preferenceKey)).isEqualTo(lock);
}
assertThat(ScreenLockType.fromKey("nonexistent")).isNull();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.password;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
import com.android.settings.password.SetupChooseLockPassword.SetupChooseLockPasswordFragment;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.shadows.ShadowActivity.IntentForResult;
import org.robolectric.shadows.ShadowDialog;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
ShadowDynamicIndexableContentMonitor.class,
ShadowEventLogWriter.class,
ShadowUtils.class
})
public class SetupChooseLockPasswordTest {
@Test
public void createActivity_shouldNotCrash() {
// Basic sanity test for activity created without crashing
Robolectric.buildActivity(SetupChooseLockPassword.class,
SetupChooseLockPassword.modifyIntentForSetup(
application,
new IntentBuilder(application).build()))
.setup().get();
}
@Test
public void createActivity_withShowOptionsButtonExtra_shouldShowButton() {
Intent intent = SetupChooseLockPassword.modifyIntentForSetup(
application,
new IntentBuilder(application).build());
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
SetupChooseLockPassword activity =
Robolectric.buildActivity(SetupChooseLockPassword.class, intent).setup().get();
Button optionsButton = activity.findViewById(R.id.screen_lock_options);
assertThat(optionsButton).isNotNull();
ShadowActivity shadowActivity = shadowOf(activity);
optionsButton.performClick();
assertThat(ShadowDialog.getLatestDialog()).isNotNull();
}
@Test
public void createActivity_clickDifferentOption_extrasShouldBePropagated() {
Bundle bundle = new Bundle();
bundle.putString("foo", "bar");
Intent intent = new IntentBuilder(application).build();
intent.putExtra(ChooseLockGenericFragment.EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, bundle);
intent = SetupChooseLockPassword.modifyIntentForSetup(application, intent);
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
SetupChooseLockPassword activity =
Robolectric.buildActivity(SetupChooseLockPassword.class, intent).setup().get();
SetupChooseLockPasswordFragment fragment =
(SetupChooseLockPasswordFragment) activity.getFragmentManager()
.findFragmentById(R.id.main_content);
fragment.onLockTypeSelected(ScreenLockType.PATTERN);
ShadowActivity shadowActivity = shadowOf(activity);
IntentForResult chooseLockIntent = shadowActivity.getNextStartedActivityForResult();
assertThat(chooseLockIntent).isNotNull();
assertThat(chooseLockIntent.requestCode)
.isEqualTo(SetupChooseLockPasswordFragment.REQUEST_SCREEN_LOCK_OPTIONS);
assertThat(chooseLockIntent.intent.getStringExtra("foo")).named("Foo extra")
.isEqualTo("bar");
}
}

View File

@@ -137,6 +137,16 @@ public class SettingsShadowResources extends ShadowResources {
realResources, Resources.class, "getInteger", ClassParameter.from(int.class, id));
}
@Implementation
public boolean getBoolean(int id) {
final Object override = sResourceOverrides.get(id);
if (override instanceof Boolean) {
return (boolean) override;
}
return Shadow.directlyOn(realResources, Resources.class, "getBoolean",
ClassParameter.from(int.class, id));
}
@Implements(Theme.class)
public static class SettingsShadowTheme extends ShadowTheme {