[Auto Pin Confirm]: Changes to allow PIN auto confirm feature to be controllable by the user
- Add SwitchPreference to allow user to control the pin auto confirm feature - Add Checkbox option during the PIN setup in Security app - Disable the opt-in checkbox during SUW entry point for PIN setup - Update SwitchPreference availability appropriately according to current PIN length - Update the pin_auto_confirm setting appropriately according to state of switchPreference or checkbox state (in PIN setup) - Update the error-message when PIN Too short to let user know six digit is recommended Bug: 262926000 Bug: 262936383 Bug: 262934702 Bug: 262935305 Test: Manual Test Test: atest SettingsRoboTests Change-Id: Ib9e09bd5ce44652158e77f80e8be19c4dd50f3bf
This commit is contained in:
24
res/drawable/checkbox_circle_shape.xml
Normal file
24
res/drawable/checkbox_circle_shape.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_checked="true"
|
||||
android:drawable="@drawable/ic_check_circle_filled_24dp" />
|
||||
<item
|
||||
android:state_checked="false"
|
||||
android:drawable="@drawable/ic_circle_outline_24dp" />
|
||||
</selector>
|
27
res/drawable/ic_check_circle_filled_24dp.xml
Normal file
27
res/drawable/ic_check_circle_filled_24dp.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?androidprv:attr/colorAccentPrimaryVariant">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10c5.52,0 10,-4.48 10,-10S17.52,2 12,2zM10.59,16.6l-4.24,-4.24l1.41,-1.41l2.83,2.83l5.66,-5.66l1.41,1.41L10.59,16.6z"/>
|
||||
</vector>
|
26
res/drawable/ic_circle_outline_24dp.xml
Normal file
26
res/drawable/ic_circle_outline_24dp.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
</vector>
|
@@ -67,8 +67,31 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/auto_pin_confirm_enabler"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="14dp"
|
||||
android:text="@string/auto_pin_confirm_user_message"
|
||||
android:textSize="16sp"
|
||||
android:button="@drawable/checkbox_circle_shape"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/auto_pin_confirm_security_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/auto_pin_confirm_opt_in_security_message"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/screen_lock_options"
|
||||
style="@style/SudGlifButton.Tertiary"
|
||||
|
@@ -1090,6 +1090,15 @@
|
||||
<!-- Title for dialog in screen lock settings, allowing users to choose other types of screen locks. [CHAR LIMIT=40] -->
|
||||
<string name="setup_lock_settings_options_dialog_title">Screen lock options</string>
|
||||
|
||||
<!-- Title of the lock screen auto pin confirm setting. [CHAR LIMIT=NONE] -->
|
||||
<string name="lock_screen_auto_pin_confirm_title">Auto-confirm unlock</string>
|
||||
<!-- Summary of the lock screen auto pin confirm setting. [CHAR LIMIT=NONE] -->
|
||||
<string name="lock_screen_auto_pin_confirm_summary">Unlock automatically if you input a correct PIN of 6 digits or more. This is slightly less secure than tapping Enter to confirm.</string>
|
||||
<!-- Message shown to check auto pin confirmation feature when the user is updating the PIN. [CHAR LIMIT=NONE] -->
|
||||
<string name="auto_pin_confirm_user_message">Auto-confirm correct PIN</string>
|
||||
<!-- Message shown to explain the security concern if a user opts-in to the auto-pin feature. [CHAR LIMIT=NONE] -->
|
||||
<string name="auto_pin_confirm_opt_in_security_message">Confirming your PIN by tapping Enter is more secure than using auto-confirm</string>
|
||||
|
||||
<!-- Main Security lock settings --><skip />
|
||||
<!-- Title for PreferenceScreen to launch picker for security method when there is none [CHAR LIMIT=22] -->
|
||||
<string name="unlock_set_unlock_launch_picker_title">Screen lock</string>
|
||||
@@ -1250,6 +1259,12 @@
|
||||
other {PIN must be at least # digits}
|
||||
}</string>
|
||||
|
||||
<!-- Hint shown in dialog screen when PIN is too short with Additional text indicating minAutoConfirmLen(eg: 6) digits PIN offer additional security -->
|
||||
<string name="lockpassword_pin_too_short_autoConfirm_extra_message">{count, plural,
|
||||
=1 {PIN must contain at least # digit, but a {minAutoConfirmLen}-digit PIN is recommended for added security}
|
||||
other {PIN must be at least # digits, but a {minAutoConfirmLen}-digit PIN is recommended for added security}
|
||||
}</string>
|
||||
|
||||
<!-- Error shown in popup when password is too long -->
|
||||
<string name="lockpassword_password_too_long">{count, plural,
|
||||
=1 {Must be fewer than # character}
|
||||
|
@@ -27,6 +27,12 @@
|
||||
android:key="visiblepattern"
|
||||
android:title="@string/lockpattern_settings_enable_visible_pattern_title" />
|
||||
|
||||
<!-- available in pin -->
|
||||
<SwitchPreference
|
||||
android:key="auto_pin_confirm"
|
||||
android:title="@string/lock_screen_auto_pin_confirm_title"
|
||||
android:summary="@string/lock_screen_auto_pin_confirm_summary" />
|
||||
|
||||
<!-- available in pin/pattern/password -->
|
||||
<com.android.settings.display.TimeoutListPreference
|
||||
android:key="lock_after_timeout"
|
||||
|
@@ -71,6 +71,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImeAwareEditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
@@ -101,7 +102,9 @@ import com.google.android.setupdesign.util.ThemeHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChooseLockPassword extends SettingsActivity {
|
||||
private static final String TAG = "ChooseLockPassword";
|
||||
@@ -223,6 +226,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private static final String KEY_CURRENT_CREDENTIAL = "current_credential";
|
||||
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
|
||||
|
||||
private static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 6;
|
||||
|
||||
private LockscreenCredential mCurrentCredential;
|
||||
private LockscreenCredential mChosenPassword;
|
||||
private boolean mRequestGatekeeperPassword;
|
||||
@@ -255,6 +260,9 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
protected FooterButton mSkipOrClearButton;
|
||||
private FooterButton mNextButton;
|
||||
private TextView mMessage;
|
||||
protected CheckBox mAutoPinConfirmOption;
|
||||
protected TextView mAutoConfirmSecurityMessage;
|
||||
protected boolean mIsAutoPinConfirmOptionSetManually;
|
||||
|
||||
private TextChangedHandler mTextChangedHandler;
|
||||
|
||||
@@ -515,6 +523,16 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mPasswordEntry.requestFocus();
|
||||
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
|
||||
|
||||
// Fetch the AutoPinConfirmOption
|
||||
mAutoPinConfirmOption = view.findViewById(R.id.auto_pin_confirm_enabler);
|
||||
mAutoConfirmSecurityMessage = view.findViewById(R.id.auto_pin_confirm_security_message);
|
||||
mIsAutoPinConfirmOptionSetManually = false;
|
||||
setOnAutoConfirmOptionClickListener();
|
||||
if (mAutoPinConfirmOption != null) {
|
||||
mAutoPinConfirmOption.setVisibility(View.GONE);
|
||||
mAutoPinConfirmOption.setChecked(false);
|
||||
}
|
||||
|
||||
final Activity activity = getActivity();
|
||||
|
||||
int currentType = mPasswordEntry.getInputType();
|
||||
@@ -808,10 +826,22 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
R.string.lockpassword_password_requires_nonnumerical));
|
||||
break;
|
||||
case TOO_SHORT:
|
||||
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
|
||||
String message = StringUtil.getIcuPluralsString(getContext(),
|
||||
error.requirement,
|
||||
mIsAlphaMode
|
||||
? R.string.lockpassword_password_too_short
|
||||
: R.string.lockpassword_pin_too_short));
|
||||
: R.string.lockpassword_pin_too_short);
|
||||
if (mLockPatternUtils.isAutoPinConfirmFeatureAvailable()
|
||||
&& !mIsAlphaMode
|
||||
&& error.requirement < MIN_AUTO_PIN_REQUIREMENT_LENGTH) {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("count", error.requirement);
|
||||
arguments.put("minAutoConfirmLen", MIN_AUTO_PIN_REQUIREMENT_LENGTH);
|
||||
message = StringUtil.getIcuPluralsString(getContext(),
|
||||
arguments,
|
||||
R.string.lockpassword_pin_too_short_autoConfirm_extra_message);
|
||||
}
|
||||
messages.add(message);
|
||||
break;
|
||||
case TOO_SHORT_WHEN_ALL_NUMERIC:
|
||||
messages.add(
|
||||
@@ -864,6 +894,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
String[] messages = convertErrorCodeToMessages();
|
||||
// Update the fulfillment of requirements.
|
||||
mPasswordRequirementAdapter.setRequirements(messages);
|
||||
// set the visibility of pin_auto_confirm option accordingly
|
||||
setAutoPinConfirmOption(passwordCompliant, length);
|
||||
// Enable/Disable the next button accordingly.
|
||||
setNextEnabled(passwordCompliant);
|
||||
} else {
|
||||
@@ -896,6 +928,36 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return visibleOrGone ? View.VISIBLE : View.GONE;
|
||||
}
|
||||
|
||||
private void setAutoPinConfirmOption(boolean enabled, int length) {
|
||||
if (!mLockPatternUtils.isAutoPinConfirmFeatureAvailable()
|
||||
|| mAutoPinConfirmOption == null) {
|
||||
return;
|
||||
}
|
||||
if (enabled && !mIsAlphaMode && isAutoPinConfirmPossible(length)) {
|
||||
mAutoPinConfirmOption.setVisibility(View.VISIBLE);
|
||||
mAutoConfirmSecurityMessage.setVisibility(View.VISIBLE);
|
||||
if (!mIsAutoPinConfirmOptionSetManually) {
|
||||
mAutoPinConfirmOption.setChecked(length == MIN_AUTO_PIN_REQUIREMENT_LENGTH);
|
||||
}
|
||||
} else {
|
||||
mAutoPinConfirmOption.setVisibility(View.GONE);
|
||||
mAutoConfirmSecurityMessage.setVisibility(View.GONE);
|
||||
mAutoPinConfirmOption.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAutoPinConfirmPossible(int currentPinLength) {
|
||||
return currentPinLength >= MIN_AUTO_PIN_REQUIREMENT_LENGTH;
|
||||
}
|
||||
|
||||
private void setOnAutoConfirmOptionClickListener() {
|
||||
if (mAutoPinConfirmOption != null) {
|
||||
mAutoPinConfirmOption.setOnClickListener((v) -> {
|
||||
mIsAutoPinConfirmOptionSetManually = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setHeaderText(String text) {
|
||||
// Only set the text if it is different than the existing one to avoid announcing again.
|
||||
if (!TextUtils.isEmpty(mLayout.getHeaderText())
|
||||
@@ -951,6 +1013,10 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
|
||||
mChosenPassword, mCurrentCredential, mUserId);
|
||||
// update the pin_auto_confirm setting accordingly.
|
||||
mLockPatternUtils.setAutoPinConfirm(
|
||||
(mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()),
|
||||
mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -170,6 +170,12 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
|
||||
mOptionsButton.setVisibility(
|
||||
mUiStage == Stage.Introduction ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
// Visibility of auto pin confirm opt-in/out option should always be invisible.
|
||||
if (mAutoPinConfirmOption != null) {
|
||||
mAutoPinConfirmOption.setVisibility(View.GONE);
|
||||
mAutoConfirmSecurityMessage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.security.screenlock;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
/**
|
||||
* Preference controller for the pin_auto_confirm setting.
|
||||
*/
|
||||
public class AutoPinConfirmPreferenceController extends AbstractPreferenceController implements
|
||||
PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String PREF_KEY_PIN_AUTO_CONFIRM = "auto_pin_confirm";
|
||||
private static final long MIN_AUTO_PIN_REQUIREMENT_LENGTH = 6L;
|
||||
|
||||
private final int mUserId;
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
|
||||
public AutoPinConfirmPreferenceController(Context context, int userId,
|
||||
LockPatternUtils lockPatternUtils) {
|
||||
super(context);
|
||||
mUserId = userId;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
setPinAutoConfirmSettingState((boolean) newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
((TwoStatePreference) preference).setChecked(getPinAutoConfirmSettingState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mLockPatternUtils.isAutoPinConfirmFeatureAvailable() && isPinLock()
|
||||
&& isPinLengthEligibleForAutoConfirmation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return PREF_KEY_PIN_AUTO_CONFIRM;
|
||||
}
|
||||
|
||||
private boolean isPinLock() {
|
||||
return mLockPatternUtils.getCredentialTypeForUser(mUserId)
|
||||
== LockPatternUtils.CREDENTIAL_TYPE_PIN;
|
||||
}
|
||||
|
||||
private boolean isPinLengthEligibleForAutoConfirmation() {
|
||||
return mLockPatternUtils.getPinLength(mUserId) >= MIN_AUTO_PIN_REQUIREMENT_LENGTH;
|
||||
}
|
||||
|
||||
private boolean getPinAutoConfirmSettingState() {
|
||||
return mLockPatternUtils.isAutoPinConfirmEnabled(mUserId);
|
||||
}
|
||||
|
||||
private void setPinAutoConfirmSettingState(boolean state) {
|
||||
mLockPatternUtils.setAutoPinConfirm(state, mUserId);
|
||||
}
|
||||
}
|
@@ -75,6 +75,8 @@ public class ScreenLockSettings extends DashboardFragment
|
||||
context, MY_USER_ID, lockPatternUtils));
|
||||
controllers.add(new LockAfterTimeoutPreferenceController(
|
||||
context, MY_USER_ID, lockPatternUtils));
|
||||
controllers.add(new AutoPinConfirmPreferenceController(
|
||||
context, MY_USER_ID, lockPatternUtils));
|
||||
controllers.add(new OwnerInfoPreferenceController(context, parent));
|
||||
return controllers;
|
||||
}
|
||||
|
@@ -27,8 +27,10 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
|
||||
import static com.android.internal.widget.LockPatternUtils.FLAG_ENABLE_AUTO_PIN_CONFIRMATION;
|
||||
import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
|
||||
import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
|
||||
|
||||
@@ -43,15 +45,21 @@ import android.app.admin.PasswordMetrics;
|
||||
import android.app.admin.PasswordPolicy;
|
||||
import android.content.Intent;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
|
||||
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
import com.android.settings.widget.ScrollToParentEditText;
|
||||
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
@@ -71,9 +79,9 @@ import org.robolectric.shadows.ShadowDrawable;
|
||||
ShadowLockPatternUtils.class,
|
||||
ShadowUtils.class,
|
||||
ShadowDevicePolicyManager.class,
|
||||
ShadowDeviceConfig.class,
|
||||
})
|
||||
public class ChooseLockPasswordTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SettingsShadowResources.overrideResource(
|
||||
@@ -387,7 +395,9 @@ public class ChooseLockPasswordTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_defaultPinMinimumLength() {
|
||||
public void processAndValidatePasswordRequirements_autoPinDisabled_defaultPinMinimumLength() {
|
||||
DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
|
||||
/* value= */ "false", /* makeDefault= */ false);
|
||||
PasswordPolicy policy = new PasswordPolicy();
|
||||
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
@@ -399,6 +409,22 @@ public class ChooseLockPasswordTest {
|
||||
"PIN must be at least 4 digits");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_autoPinEnabled_defaultPinMinimumLength() {
|
||||
DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
|
||||
/* value= */ "true", /* makeDefault= */ false);
|
||||
PasswordPolicy policy = new PasswordPolicy();
|
||||
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minMetrics */ policy.getMinMetrics(),
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ LockscreenCredential.createPassword("11"),
|
||||
"PIN must be at least 4 digits"
|
||||
+ ", but a 6-digit PIN is recommended for added security");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_maximumLength() {
|
||||
PasswordPolicy policy = new PasswordPolicy();
|
||||
@@ -424,6 +450,123 @@ public class ChooseLockPasswordTest {
|
||||
"PIN must be at least 8 digits");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoPinConfirmOption_featureEnabledAndUntouchedByUser_changeStateAsPerRules() {
|
||||
DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
|
||||
/* value= */ "true", /* makeDefault= */ false);
|
||||
ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
|
||||
|
||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
|
||||
ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
|
||||
CheckBox pinAutoConfirmOption = passwordActivity
|
||||
.findViewById(R.id.auto_pin_confirm_enabler);
|
||||
TextView securityMessage =
|
||||
passwordActivity.findViewById(R.id.auto_pin_confirm_security_message);
|
||||
|
||||
passwordEntry.setText("1234");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("123456");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isTrue();
|
||||
|
||||
passwordEntry.setText("12345678");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("123456");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoPinConfirmOption_featureEnabledAndModifiedByUser_shouldChangeStateAsPerRules() {
|
||||
DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
|
||||
/* value= */ "true", /* makeDefault= */ false);
|
||||
ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
|
||||
|
||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
|
||||
ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
|
||||
CheckBox pinAutoConfirmOption = passwordActivity
|
||||
.findViewById(R.id.auto_pin_confirm_enabler);
|
||||
TextView securityMessage =
|
||||
passwordActivity.findViewById(R.id.auto_pin_confirm_security_message);
|
||||
|
||||
passwordEntry.setText("123456");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isTrue();
|
||||
|
||||
pinAutoConfirmOption.performClick();
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("12345678");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("123456");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoPinConfirmOption_featureDisabled_shouldRemainInvisibleAndUnchecked() {
|
||||
DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
|
||||
/* value= */ "false", /* makeDefault= */ false);
|
||||
ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
|
||||
|
||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
|
||||
ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
|
||||
CheckBox pinAutoConfirmOption = passwordActivity
|
||||
.findViewById(R.id.auto_pin_confirm_enabler);
|
||||
TextView securityMessage =
|
||||
passwordActivity.findViewById(R.id.auto_pin_confirm_security_message);
|
||||
|
||||
passwordEntry.setText("1234");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("123456");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
|
||||
passwordEntry.setText("12345678");
|
||||
fragment.updateUi();
|
||||
assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
private ChooseLockPassword setupActivityWithPinTypeAndDefaultPolicy() {
|
||||
PasswordPolicy policy = new PasswordPolicy();
|
||||
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
return buildChooseLockPasswordActivity(
|
||||
new IntentBuilder(application)
|
||||
.setUserId(UserHandle.myUserId())
|
||||
.setPasswordType(PASSWORD_QUALITY_NUMERIC)
|
||||
.setPasswordRequirement(PASSWORD_COMPLEXITY_NONE, policy.getMinMetrics())
|
||||
.build());
|
||||
}
|
||||
|
||||
private ChooseLockPassword buildChooseLockPasswordActivity(Intent intent) {
|
||||
return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get();
|
||||
}
|
||||
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.security.screenlock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AutoPinConfirmPreferenceControllerTest {
|
||||
private static final Integer TEST_USER_ID = 1;
|
||||
@Mock
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private AutoPinConfirmPreferenceController mController;
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
mController =
|
||||
new AutoPinConfirmPreferenceController(context, TEST_USER_ID, mLockPatternUtils);
|
||||
mPreference = new SwitchPreference(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_featureEnabledAndLockSetToNone_shouldReturnFalse() {
|
||||
when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_featureEnabledAndLockSetToPassword_shouldReturnFalse() {
|
||||
when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
|
||||
.thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_featureEnabledAndLockSetToPIN_lengthLessThanSix_shouldReturnFalse() {
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
|
||||
.thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
|
||||
when(mLockPatternUtils.getPinLength(TEST_USER_ID)).thenReturn(5L);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_featureEnabledAndLockSetToPIN_lengthMoreThanEqSix_shouldReturnTrue() {
|
||||
when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
|
||||
.thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
|
||||
when(mLockPatternUtils.getPinLength(TEST_USER_ID)).thenReturn(6L);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_featureDisabledAndLockSetToPIN_shouldReturnFalse() {
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(false);
|
||||
when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
|
||||
when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
|
||||
.thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_ChangingSettingState_shouldSetPreferenceToAppropriateCheckedState() {
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
// When auto_pin_confirm setting is disabled, switchPreference is unchecked
|
||||
when(mLockPatternUtils.isAutoPinConfirmEnabled(TEST_USER_ID)).thenReturn(false);
|
||||
mController.updateState(mPreference);
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
|
||||
// When auto_pin_confirm setting is enabled, switchPreference is checked
|
||||
when(mLockPatternUtils.isAutoPinConfirmEnabled(TEST_USER_ID)).thenReturn(true);
|
||||
mController.updateState(mPreference);
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_shouldUpdatePinAutoConfirmSetting() {
|
||||
when(mLockPatternUtils.isAutoPinConfirmFeatureAvailable()).thenReturn(true);
|
||||
mController.onPreferenceChange(mPreference, /* newValue= */ true);
|
||||
verify(mLockPatternUtils).setAutoPinConfirm(true, TEST_USER_ID);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user