Merge "Add basic settings suggestion support" into pi-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 433725d..f873183 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -412,6 +412,8 @@
<string name="lockpattern_restart_button_text">Redraw</string>
<!-- Choose security lock screen button. After first valid enter, they will be prompt to confirm page. [CHAR LIMIT=20] -->
<string name="continue_button_text">Continue</string>
+ <!-- Security lock screen button. If error is encountered when saving password, PIN or pattern, the user can click this button to try saving again. [CHAR LIMIT=20] -->
+ <string name="lockscreen_retry_button_text">Retry</string>
<!-- Title string shown in choose screen lock [CHAR LIMIT=40] -->
<string name="set_screen_lock">Set a screen lock</string>
<!-- Message on first screen of choose pattern flow [CHAR LIMIT=40] -->
@@ -436,6 +438,8 @@
<string name="lockpattern_pattern_wrong">Wrong pattern</string>
<!-- Choose unlock pattern screen instruction, the help instructions (an animation) caption. [CHAR LIMIT=40] -->
<string name="lockpattern_settings_help_how_to_record">How to draw an unlock pattern</string>
+ <!-- Message shown when error is encountered when saving pattern [CHAR LIMIT=40] -->
+ <string name="error_saving_lockpattern">Error saving pattern</string>
<!-- Label for button to confirm picker selection [CHAR_LIMIT=20] -->
<string name="okay">OK</string>
@@ -449,6 +453,8 @@
<string name="lockpin_invalid_pin">Pin invalid, must be at least 4 digits</string>
<!-- Header on pin confirm screen if second pin doesn't match the first. [CHAR LIMIT=30]-->
<string name="confirm_pins_dont_match">PINs don\'t match</string>
+ <!-- Message shown when error is encountered when saving PIN [CHAR LIMIT=40] -->
+ <string name="error_saving_lockpin">Error saving PIN</string>
<!-- Description for Choose Lock Password Screen [CHAR_LIMIT=40] -->
<string name="choose_lock_password_message">For security, set a password</string>
<!-- Header on password confirm screen [CHAR LIMIT=40] -->
@@ -530,6 +536,9 @@
<string name="lockpassword_password_recently_used">Device admin doesn\'t allow using a recent
password</string>
+ <!-- Message shown when error is encountered when saving password [CHAR LIMIT=40] -->
+ <string name="error_saving_password">Error saving password</string>
+
<!-- Error shown when a user is choosing a PASSWORD for their work phone, but what they suggest is blocked by their company's IT administrator. The user should try another PASSWORD that's less common and more complicated. [CHAR LIMIT=NONE] -->
<string name="lockpassword_password_blacklisted_by_admin">Common passwords are blocked by your IT admin. Try a different password.</string>
diff --git a/src/com/android/car/settings/accounts/UserDetailsFragment.java b/src/com/android/car/settings/accounts/UserDetailsFragment.java
index e9ef045..50e65d6 100644
--- a/src/com/android/car/settings/accounts/UserDetailsFragment.java
+++ b/src/com/android/car/settings/accounts/UserDetailsFragment.java
@@ -20,6 +20,7 @@
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.View;
import android.widget.Button;
import com.android.car.settings.R;
@@ -71,8 +72,12 @@
super.onActivityCreated(savedInstanceState);
mAddAccountButton = (Button) getActivity().findViewById(R.id.action_button1);
- mAddAccountButton.setText(R.string.user_add_account_menu);
- mAddAccountButton.setOnClickListener(v -> onAddAccountClicked());
+ if(mUserManagerHelper.canModifyAccounts()) {
+ mAddAccountButton.setText(R.string.user_add_account_menu);
+ mAddAccountButton.setOnClickListener(v -> onAddAccountClicked());
+ } else {
+ mAddAccountButton.setVisibility(View.GONE);
+ }
}
@Override
@@ -80,6 +85,9 @@
super.onDestroy();
mUserManagerHelper.unregisterOnUsersUpdateListener();
mAccountManagerHelper.stopListeningToAccountUpdates();
+
+ // The action button may be hidden at some point, so make it visible again
+ mAddAccountButton.setVisibility(View.VISIBLE);
}
@Override
diff --git a/src/com/android/car/settings/security/ChooseLockPasswordActivity.java b/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
index 8a81b84..063ed25 100644
--- a/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
+++ b/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
@@ -16,13 +16,6 @@
package com.android.car.settings.security;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
-
-import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.content.Intent;
@@ -31,6 +24,7 @@
import android.os.Message;
import android.os.UserHandle;
import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.InputType;
@@ -48,20 +42,21 @@
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.car.settings.R;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TextViewInputDisabler;
-import com.android.car.settings.R;
-
import java.util.ArrayList;
import java.util.List;
/**
* Activity for choosing a lock password/pin.
*/
-public class ChooseLockPasswordActivity extends FragmentActivity
- implements OnEditorActionListener, TextWatcher, View.OnClickListener {
+public class ChooseLockPasswordActivity extends FragmentActivity implements
+ OnEditorActionListener,
+ TextWatcher,
+ View.OnClickListener,
+ SaveChosenLockWorkerBase.Listener {
/**
* Password must contain at least one number, one letter,
* can not have white space, should be between 4 to 8 characters.
@@ -88,11 +83,9 @@
@VisibleForTesting
static final int BLACKLISTED = 1 << 5;
- private static final String TAG = "ChooseLockPattern";
+ private static final String TAG = "ChooseLockPassword";
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
- private static final int RESULT_FINISHED = RESULT_FIRST_USER;
-
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private boolean mIsAlphaMode;
private String mEnteredPassword;
@@ -100,7 +93,7 @@
private EditText mPasswordEntry;
private TextViewInputDisabler mPasswordEntryInputDisabler;
- private SaveAndFinishWorker mSaveAndFinishWorker;
+ private SaveLockPasswordWorker mSaveLockPasswordWorker;
private String mFirstPassword;
@@ -111,25 +104,61 @@
private TextChangedHandler mTextChangedHandler;
- private final SaveAndFinishWorker.Listener mWorkerListener =
- new SaveAndFinishWorker.Listener() {
- @Override
- public void onChosenLockSaveFinished(Intent resultData) {
- // TODO b/73551228 - handle failure to save
- setResult(RESULT_OK, resultData);
- finish();
- }
- };
+ // Keep track internally of where the user is in choosing a password.
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ protected enum Stage {
+ Introduction(
+ R.string.choose_lock_password_hints,
+ R.string.choose_lock_pin_hints,
+ R.string.continue_button_text,
+ R.string.lockpassword_cancel_label),
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
+ PasswordInvalid(
+ R.string.lockpassword_invalid_password,
+ R.string.lockpin_invalid_pin,
+ R.string.continue_button_text,
+ R.string.lockpassword_clear_label),
- if (resultCode != Activity.RESULT_OK) {
- setResult(RESULT_FINISHED);
- finish();
- } else {
- mCurrentPassword = data.getStringExtra(SaveChosenLockWorkerBase.EXTRA_KEY_PASSWORD);
+ NeedToConfirm(
+ R.string.confirm_your_password_header,
+ R.string.confirm_your_pin_header,
+ R.string.lockpassword_confirm_label,
+ R.string.lockpassword_cancel_label),
+
+ ConfirmWrong(
+ R.string.confirm_passwords_dont_match,
+ R.string.confirm_pins_dont_match,
+ R.string.continue_button_text,
+ R.string.lockpassword_cancel_label),
+
+ SaveFailure(
+ R.string.error_saving_password,
+ R.string.error_saving_lockpin,
+ R.string.lockscreen_retry_button_text,
+ R.string.lockpassword_cancel_label);
+
+ public final int alphaHint;
+ public final int numericHint;
+ public final int primaryButtonText;
+ public final int secondaryButtonText;
+
+ Stage(int hintInAlpha,
+ int hintInNumeric,
+ int primaryButtonText,
+ int secondaryButtonText) {
+ this.alphaHint = hintInAlpha;
+ this.numericHint = hintInNumeric;
+ this.primaryButtonText = primaryButtonText;
+ this.secondaryButtonText = secondaryButtonText;
+ }
+
+ @StringRes
+ public int getHint(boolean isAlpha) {
+ if (isAlpha) {
+ return alphaHint;
+ } else {
+ return numericHint;
+ }
}
}
@@ -174,6 +203,19 @@
}
}
+ @Override
+ public void onChosenLockSaveFinished(Intent data) {
+ boolean isSaveSuccessful =
+ data.getBooleanExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+ if (isSaveSuccessful) {
+ setResult(RESULT_OK, data);
+ finish();
+ } else {
+ mSaveLockPasswordWorker = null; // Allow new worker to be created
+ updateStage(Stage.SaveFailure);
+ }
+ }
+
/**
* Validates PIN/Password and returns the validation result.
*
@@ -187,13 +229,13 @@
// Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
// user finds some way to bring up soft keyboard.
- if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
- || mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ if (mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ || mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX) {
if (metrics.letters > 0 || metrics.symbols > 0) {
errorCode |= CONTAIN_NON_DIGITS;
}
- if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ if (mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX) {
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
final int sequence = PasswordMetrics.maxLengthSequence(password);
if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
@@ -218,7 +260,7 @@
}
@VisibleForTesting
- protected void setPasswordQuality(int passwordQuality) {
+ void setPasswordQuality(int passwordQuality) {
mRequestedQuality = passwordQuality;
}
@@ -270,58 +312,10 @@
}
@Override
- protected void onStart() {
- super.onStart();
- }
-
- // Keep track internally of where the user is in choosing a password.
- private enum Stage {
- Introduction(
- R.string.choose_lock_password_hints,
- R.string.choose_lock_pin_hints,
- R.string.continue_button_text,
- R.string.lockpassword_cancel_label),
-
- PasswordInvalid(
- R.string.lockpassword_invalid_password,
- R.string.lockpin_invalid_pin,
- R.string.continue_button_text,
- R.string.lockpassword_clear_label),
-
- NeedToConfirm(
- R.string.confirm_your_password_header,
- R.string.confirm_your_pin_header,
- R.string.lockpassword_confirm_label,
- R.string.lockpassword_cancel_label),
-
- ConfirmWrong(
- R.string.confirm_passwords_dont_match,
- R.string.confirm_pins_dont_match,
- R.string.continue_button_text,
- R.string.lockpassword_cancel_label);
-
- Stage(int hintInAlpha,
- int hintInNumeric,
- int primaryButtonText,
- int secondaryButtonText) {
- this.alphaHint = hintInAlpha;
- this.numericHint = hintInNumeric;
- this.primaryButtonText = primaryButtonText;
- this.secondaryButtonText = secondaryButtonText;
- }
-
- public final int alphaHint;
- public final int numericHint;
- public final int primaryButtonText;
- public final int secondaryButtonText;
-
- @StringRes
- public int getHint(boolean isAlpha) {
- if (isAlpha) {
- return alphaHint;
- } else {
- return numericHint;
- }
+ public void onStop() {
+ super.onStop();
+ if (mSaveLockPasswordWorker != null) {
+ mSaveLockPasswordWorker.setListener(null);
}
}
@@ -333,10 +327,6 @@
mPrimaryButton.setText(textId);
}
- private void setSecondaryButtonVisible(boolean visible) {
- mSecondaryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
private void setSecondaryButtonEnabled(boolean enabled) {
mSecondaryButton.setEnabled(enabled);
}
@@ -348,7 +338,7 @@
// Updates display message and proceed to next step according to the different text on
// the secondary button.
private void handleSecondaryButtonClick() {
- if (mSaveAndFinishWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
+ if (mSaveLockPasswordWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
finish();
return;
}
@@ -366,7 +356,7 @@
// the primary button.
private void handlePrimaryButtonClick() {
mEnteredPassword = mPasswordEntry.getText().toString();
- if (mSaveAndFinishWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
+ if (mSaveLockPasswordWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
return;
}
@@ -381,6 +371,7 @@
}
break;
case NeedToConfirm:
+ case SaveFailure:
if (mFirstPassword.equals(mEnteredPassword)) {
startSaveAndFinish();
} else {
@@ -398,7 +389,7 @@
// Starts an async task to save the chosen password.
private void startSaveAndFinish() {
- if (mSaveAndFinishWorker != null) {
+ if (mSaveLockPasswordWorker != null) {
Log.v(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
return;
}
@@ -406,22 +397,20 @@
mPasswordEntryInputDisabler.setInputEnabled(false);
setPrimaryButtonEnabled(false);
- mSaveAndFinishWorker = new SaveAndFinishWorker();
- mSaveAndFinishWorker.setListener(mWorkerListener);
+ mSaveLockPasswordWorker = new SaveLockPasswordWorker();
+ mSaveLockPasswordWorker.setListener(this);
- getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
+ getFragmentManager().beginTransaction().add(mSaveLockPasswordWorker,
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
getFragmentManager().executePendingTransactions();
- mSaveAndFinishWorker = new SaveAndFinishWorker();
- mSaveAndFinishWorker.setListener(mWorkerListener);
- mSaveAndFinishWorker.start(new LockPatternUtils(this),
+ mSaveLockPasswordWorker.start(new LockPatternUtils(this),
mEnteredPassword, mCurrentPassword, mRequestedQuality, mUserId);
}
// Updates the hint based on current Stage and length of password entry
private void updateUi() {
- boolean canInput = mSaveAndFinishWorker == null;
+ boolean inputAllowed = mSaveLockPasswordWorker == null;
if (mUiStage == Stage.Introduction) {
final int errorCode = validatePassword(mEnteredPassword);
@@ -433,16 +422,17 @@
} else {
mHintMessage.setText(getString(mUiStage.getHint(mIsAlphaMode)));
boolean hasPassword = mEnteredPassword == "" ? false : mEnteredPassword.length() > 0;
- setPrimaryButtonEnabled(canInput && hasPassword);
- setSecondaryButtonEnabled(canInput && hasPassword);
+ setPrimaryButtonEnabled(inputAllowed && hasPassword);
+ setSecondaryButtonEnabled(inputAllowed && hasPassword);
}
setPrimaryButtonTextId(mUiStage.primaryButtonText);
setSecondaryButtonTextId(mUiStage.secondaryButtonText);
- mPasswordEntryInputDisabler.setInputEnabled(canInput);
+ mPasswordEntryInputDisabler.setInputEnabled(inputAllowed);
}
- private void updateStage(Stage stage) {
+ @VisibleForTesting
+ void updateStage(Stage stage) {
mUiStage = stage;
updateUi();
}
@@ -492,35 +482,4 @@
}
return messages.toArray(new String[0]);
}
-
- /**
- * Worker to store chosen password using LockPatternUtils.
- */
- public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
- private String mEnteredPassword;
- private String mCurrentPassword;
- private int mRequestedQuality;
-
- public void start(LockPatternUtils utils,
- String enteredPassword, String currentPassword, int requestedQuality, int userId) {
- prepare(utils, userId);
-
- mEnteredPassword = enteredPassword;
- mCurrentPassword = currentPassword;
- mRequestedQuality = requestedQuality;
- mUserId = userId;
-
- start();
- }
-
- @Override
- protected Intent saveAndVerifyInBackground() {
- Intent result = null;
- mUtils.saveLockPassword(mEnteredPassword, mCurrentPassword, mRequestedQuality,
- mUserId);
-
- return result;
- }
- }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/car/settings/security/ChooseLockPatternActivity.java b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
index 06dd065..6209bf1 100644
--- a/src/com/android/car/settings/security/ChooseLockPatternActivity.java
+++ b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
@@ -26,13 +26,12 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.car.settings.R;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockPatternView.DisplayMode;
-import com.android.car.settings.R;
-
import com.google.android.collect.Lists;
import java.util.ArrayList;
@@ -42,7 +41,9 @@
/**
* Activity for choosing security lock pattern.
*/
-public class ChooseLockPatternActivity extends FragmentActivity implements View.OnClickListener {
+public class ChooseLockPatternActivity extends FragmentActivity implements
+ View.OnClickListener, SaveChosenLockWorkerBase.Listener {
+
private static final String TAG = "ChooseLockPattern";
private static final int ID_EMPTY_MESSAGE = -1;
// How long we wait to clear a wrong pattern
@@ -55,7 +56,7 @@
private List<LockPatternView.Cell> mChosenPattern;
private String mCurrentPattern;
- private SaveAndFinishWorker mSaveAndFinishWorker;
+ private SaveLockPatternWorker mSaveLockPatternWorker;
private int mUserId;
@Override
@@ -105,17 +106,31 @@
super.onStart();
updateStage(mUiStage);
- if (mSaveAndFinishWorker != null) {
+ if (mSaveLockPatternWorker != null) {
setPrimaryButtonEnabled(true);
- mSaveAndFinishWorker.setListener(mWorkerListener);
+ mSaveLockPatternWorker.setListener(this);
}
}
@Override
public void onStop() {
super.onStop();
- if (mSaveAndFinishWorker != null) {
- mSaveAndFinishWorker.setListener(null);
+ if (mSaveLockPatternWorker != null) {
+ mSaveLockPatternWorker.setListener(null);
+ }
+ }
+
+ @Override
+ public void onChosenLockSaveFinished(Intent data) {
+ boolean isSaveSuccessful =
+ data.getBooleanExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+
+ if (isSaveSuccessful) {
+ setResult(RESULT_OK, data);
+ finish();
+ } else {
+ mSaveLockPatternWorker = null; // Allow new worker to be created
+ updateStage(Stage.SaveFailure);
}
}
@@ -179,8 +194,21 @@
ChoiceConfirmed(
R.string.lockpattern_pattern_confirmed,
SecondaryButtonState.Cancel, PrimaryButtonState.Confirm,
+ false /* patternEnabled */ ),
+
+ /**
+ * Error saving pattern.
+ * Pattern disabled, primary button shows Retry, secondary button allows for cancel
+ */
+ SaveFailure(
+ R.string.error_saving_lockpattern,
+ SecondaryButtonState.Cancel, PrimaryButtonState.Retry,
false /* patternEnabled */ );
+ final int messageId;
+ final SecondaryButtonState secondaryButtonState;
+ final PrimaryButtonState primaryButtonState;
+ final boolean patternEnabled;
/**
* @param message The message displayed as instruction.
@@ -197,11 +225,6 @@
this.primaryButtonState = primaryButtonState;
this.patternEnabled = patternEnabled;
}
-
- final int messageId;
- final SecondaryButtonState secondaryButtonState;
- final PrimaryButtonState primaryButtonState;
- final boolean patternEnabled;
}
/**
@@ -297,6 +320,7 @@
ContinueDisabled(R.string.continue_button_text, false),
Confirm(R.string.lockpattern_confirm_button_text, true),
ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
+ Retry(R.string.lockscreen_retry_button_text, true),
Ok(R.string.okay, true);
/**
@@ -452,6 +476,13 @@
}
startSaveAndFinish();
break;
+ case Retry:
+ if (mUiStage != Stage.SaveFailure) {
+ throw new IllegalStateException("expected ui stage " + Stage.SaveFailure
+ + " when button is " + PrimaryButtonState.Retry);
+ }
+ startSaveAndFinish();
+ break;
case Ok:
if (mUiStage != Stage.HelpScreen) {
throw new IllegalStateException("Help screen is only mode with ok button, "
@@ -468,57 +499,18 @@
// Save recorded pattern as an async task and proceed to next
private void startSaveAndFinish() {
- if (mSaveAndFinishWorker != null) {
- Log.v(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
+ if (mSaveLockPatternWorker != null) {
+ Log.v(TAG, "startSaveAndFinish with an existing SaveLockPatternWorker.");
return;
}
setPrimaryButtonEnabled(false);
- mSaveAndFinishWorker = new SaveAndFinishWorker();
- mSaveAndFinishWorker.setListener(mWorkerListener);
- mSaveAndFinishWorker.start(new LockPatternUtils(this),
+ mSaveLockPatternWorker = new SaveLockPatternWorker();
+ mSaveLockPatternWorker.setListener(this);
+ mSaveLockPatternWorker.start(new LockPatternUtils(this),
mChosenPattern, mCurrentPattern, mUserId);
}
- private SaveAndFinishWorker.Listener mWorkerListener = (resultData) -> {
- setResult(RESULT_OK);
- finish();
- };
- /**
- * Async task to save the chosen lock pattern.
- */
- public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
- private List<LockPatternView.Cell> mChosenPattern;
- private String mCurrentPattern;
-
- public void start(LockPatternUtils utils,
- List<LockPatternView.Cell> chosenPattern, String currentPattern, int userId) {
- prepare(utils, userId);
-
- mCurrentPattern = currentPattern;
- mChosenPattern = chosenPattern;
- mUserId = userId;
-
- start();
- }
-
- @Override
- protected Intent saveAndVerifyInBackground() {
- Intent result = null;
- final int userId = mUserId;
- mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
- return result;
- }
-
- @Override
- protected void finish(Intent resultData) {
- if (!mUtils.isPatternEverChosen(mUserId)) {
- mUtils.setVisiblePatternEnabled(true, mUserId);
- }
-
- super.finish(resultData);
- }
- }
}
diff --git a/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
index ff8bd85..1626ea5 100644
--- a/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
+++ b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
@@ -16,6 +16,7 @@
package com.android.car.settings.security;
+import android.annotation.CallSuper;
import android.annotation.WorkerThread;
import android.app.Fragment;
import android.content.Intent;
@@ -32,13 +33,21 @@
public static final String EXTRA_KEY_PATTERN = "pattern";
public static final String EXTRA_KEY_PASSWORD = "password";
+ public static final String EXTRA_KEY_SUCCESS = "success";
private Listener mListener;
private boolean mFinished;
private Intent mResultData;
+ private LockPatternUtils mUtils;
+ private int mUserId;
- protected LockPatternUtils mUtils;
- protected int mUserId;
+ final LockPatternUtils getUtils() {
+ return mUtils;
+ }
+
+ final int getUserId() {
+ return mUserId;
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -74,7 +83,7 @@
/**
* Start executing the async task.
*/
- protected void start() {
+ final void start() {
new Task().execute();
}
@@ -87,6 +96,7 @@
/**
* Send result data via the listener when task finishes.
*/
+ @CallSuper
protected void finish(Intent resultData) {
mFinished = true;
mResultData = resultData;
@@ -112,6 +122,6 @@
* Call back when finishing save the chosen lock.
*/
interface Listener {
- public void onChosenLockSaveFinished(Intent resultData);
+ void onChosenLockSaveFinished(Intent resultData);
}
}
\ No newline at end of file
diff --git a/src/com/android/car/settings/security/SaveLockPasswordWorker.java b/src/com/android/car/settings/security/SaveLockPasswordWorker.java
new file mode 100644
index 0000000..f46e3fd
--- /dev/null
+++ b/src/com/android/car/settings/security/SaveLockPasswordWorker.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.car.settings.security;
+
+
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Worker to store chosen password using LockPatternUtils.
+ */
+public class SaveLockPasswordWorker extends SaveChosenLockWorkerBase {
+
+ private final String TAG = "SavePasswordWorker";
+ private String mEnteredPassword;
+ private String mCurrentPassword;
+ private int mRequestedQuality;
+
+
+ public void start(LockPatternUtils utils,
+ String enteredPassword, String currentPassword, int requestedQuality, int userId) {
+ prepare(utils, userId);
+
+ mEnteredPassword = enteredPassword;
+ mCurrentPassword = currentPassword;
+ mRequestedQuality = requestedQuality;
+
+ start();
+ }
+
+ @VisibleForTesting
+ void saveLockPassword() {
+ getUtils().saveLockPassword(mEnteredPassword, mCurrentPassword, mRequestedQuality,
+ getUserId());
+ }
+
+ @Override
+ protected Intent saveAndVerifyInBackground() {
+ Intent result = new Intent();
+ boolean isSaveSuccessful = true;
+
+ try {
+ saveLockPassword();
+ } catch (Exception e) {
+ Log.e(TAG, "Save lock exception", e);
+ isSaveSuccessful = false;
+ }
+
+ result.putExtra(EXTRA_KEY_SUCCESS, isSaveSuccessful);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/car/settings/security/SaveLockPatternWorker.java b/src/com/android/car/settings/security/SaveLockPatternWorker.java
new file mode 100644
index 0000000..57ed292
--- /dev/null
+++ b/src/com/android/car/settings/security/SaveLockPatternWorker.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.car.settings.security;
+
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+
+import java.util.List;
+
+/**
+ * Async task to save the chosen lock pattern.
+ */
+public class SaveLockPatternWorker extends SaveChosenLockWorkerBase {
+
+ private final String TAG = "SaveLockPatternWorker";
+ private List<LockPatternView.Cell> mChosenPattern;
+ private String mCurrentPattern;
+
+ public void start(LockPatternUtils utils,
+ List<LockPatternView.Cell> chosenPattern, String currentPattern, int userId) {
+ prepare(utils, userId);
+
+ mCurrentPattern = currentPattern;
+ mChosenPattern = chosenPattern;
+
+ start();
+ }
+
+ @VisibleForTesting
+ void saveLockPattern() {
+ getUtils().saveLockPattern(mChosenPattern, mCurrentPattern, getUserId());
+ }
+
+ @Override
+ protected Intent saveAndVerifyInBackground() {
+ Intent result = new Intent();
+ boolean isSaveSuccessful = true;
+
+ try {
+ saveLockPattern();
+ } catch (Exception e) {
+ Log.e(TAG, "Save lock exception", e);
+ isSaveSuccessful = false;
+ }
+
+ result.putExtra(EXTRA_KEY_SUCCESS, isSaveSuccessful);
+ return result;
+ }
+
+ @Override
+ protected void finish(Intent resultData) {
+ if (!getUtils().isPatternEverChosen(getUserId())) {
+ getUtils().setVisiblePatternEnabled(true, getUserId());
+ }
+
+ super.finish(resultData);
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk
index 67589dc..c0ce577 100644
--- a/tests/robotests/Android.mk
+++ b/tests/robotests/Android.mk
@@ -4,20 +4,18 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_MODULE := CarSettingsRoboTests
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-# Include the testing libraries (JUnit4 + Robolectric libs).
-LOCAL_STATIC_JAVA_LIBRARIES := \
- truth-prebuilt \
- mockito-robolectric-prebuilt
-
+# Include the testing libraries
LOCAL_JAVA_LIBRARIES := \
- junit \
- platform-robolectric-3.6.1-prebuilt \
- sdk_vcurrent
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt
LOCAL_INSTRUMENTATION_FOR := CarSettings
-LOCAL_MODULE := CarSettingsRoboTests
LOCAL_MODULE_TAGS := optional
@@ -30,13 +28,15 @@
LOCAL_MODULE := RunCarSettingsRoboTests
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- CarSettingsRoboTests
+LOCAL_JAVA_LIBRARIES := \
+ CarSettingsRoboTests \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt
LOCAL_TEST_PACKAGE := CarSettings
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
-include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
+include external/robolectric-shadows/run_robotests.mk
diff --git a/tests/robotests/config/robolectric.properties b/tests/robotests/config/robolectric.properties
new file mode 100644
index 0000000..e55c12e
--- /dev/null
+++ b/tests/robotests/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2018 Google Inc.
+#
+# 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.
+#
+manifest=packages/apps/Car/Settings/tests/robotests/AndroidManifest.xml
+sdk=NEWEST_SDK
diff --git a/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
index 1ab07e8..93c3099 100644
--- a/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
+++ b/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
@@ -15,6 +15,8 @@
*/
package com.android.car.settings;
+import android.support.annotation.NonNull;
+
import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -22,6 +24,9 @@
import org.robolectric.res.Fs;
import org.robolectric.res.ResourcePath;
+import java.net.MalformedURLException;
+
+import java.net.URL;
import java.util.List;
/**
@@ -38,6 +43,14 @@
super(testClass);
}
+ private static ResourcePath createResourcePath(@NonNull String filePath) {
+ try {
+ return new ResourcePath(null, Fs.fromURL(new URL(filePath)), null);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("CarSettingsobolectricTestRunner failure", e);
+ }
+ }
+
/**
* We are going to create our own custom manifest so that we can add multiple resource
* paths to it. This lets us access resources in both Settings and SettingsLib in our tests.
@@ -53,38 +66,21 @@
// By adding any resources from libraries we need to the AndroidManifest, we can access
// them from within the parallel universe's resource loader.
return new AndroidManifest(Fs.fileFromPath(manifestPath), Fs.fileFromPath(resDir),
- Fs.fileFromPath(assetsDir), "com.android.car.settings") {
+ Fs.fileFromPath(assetsDir)) {
@Override
public List<ResourcePath> getIncludedResourcePaths() {
List<ResourcePath> paths = super.getIncludedResourcePaths();
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./packages/apps/Car/Settings/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/base/core/res/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/support/car/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./packages/apps/Car/libs/car-stream-ui-lib/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./packages/apps/Car/libs/car-list/res"),
- null));
+ paths.add(createResourcePath("file:packages/apps/Car/Settings/res"));
+ paths.add(createResourcePath("file:frameworks/support/v7/appcompat/res"));
+ paths.add(createResourcePath("file:frameworks/support/car/res"));
+ paths.add(createResourcePath("file:packages/apps/Car/libs/car-stream-ui-lib/res "));
+ paths.add(createResourcePath("file:packages/apps/Car/libs/car-list/res"));
+ paths.add(createResourcePath("file:frameworks/base/packages/SettingsLib/res"));
+ paths.add(createResourcePath(
+ "file:frameworks/opt/setupwizard/library/gingerbread/res"));
+ paths.add(createResourcePath(
+ "file:frameworks/opt/setupwizard/library/main/res"));
+
return paths;
}
};
diff --git a/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java b/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
index a4d622c..e8dea99 100644
--- a/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
@@ -16,31 +16,31 @@
package com.android.car.settings.security;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+
import com.android.car.settings.CarSettingsRobolectricTestRunner;
-import com.android.car.settings.TestConfig;
-import com.android.car.settings.testutils.ShadowActivityManager;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
/**
* Tests for ChooseLockPasswordActivity class.
*/
@RunWith(CarSettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
- shadows = {ShadowActivityManager.class})
-@Ignore
public class ChooseLockPasswordActivityTest {
private ChooseLockPasswordActivity mActivity;
@@ -146,4 +146,34 @@
assertThat(mActivity.validatePassword(password))
.isEqualTo(ChooseLockPasswordActivity.CONTAIN_NON_DIGITS);
}
+
+ /**
+ * A test to verify that the activity is finished when save worker succeeds
+ */
+ @Test
+ public void testActivityIsFinishedWhenSaveWorkerSucceeds() {
+ ChooseLockPasswordActivity spyActivity = spy(mActivity);
+
+ Intent intent = new Intent();
+ intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, true);
+ spyActivity.onChosenLockSaveFinished(intent);
+
+ verify(spyActivity).finish();
+ }
+
+ /**
+ * A test to verify that the UI stage is updated when save worker fails
+ */
+ @Test
+ public void testStageIsUpdatedWhenSaveWorkerFails() {
+ ChooseLockPasswordActivity spyActivity = spy(mActivity);
+ doNothing().when(spyActivity).updateStage(ChooseLockPasswordActivity.Stage.SaveFailure);
+
+ Intent intent = new Intent();
+ intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+ spyActivity.onChosenLockSaveFinished(intent);
+
+ verify(spyActivity, never()).finish();
+ verify(spyActivity).updateStage(ChooseLockPasswordActivity.Stage.SaveFailure);
+ }
}
diff --git a/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java b/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java
new file mode 100644
index 0000000..cce0e64
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+
+/**
+ * Tests for ChooseLockPatternActivity class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class ChooseLockPatternActivityTest {
+ private ChooseLockPatternActivity mActivity;
+
+ @Before
+ public void setUp() {
+ mActivity = Robolectric.buildActivity(ChooseLockPatternActivity.class)
+ .create()
+ .get();
+ }
+
+ /**
+ * A test to verify that the activity is finished when save worker succeeds
+ */
+ @Test
+ public void testActivityIsFinishedWhenSaveWorkerSucceeds() {
+ ChooseLockPatternActivity spyActivity = spy(mActivity);
+
+ Intent intent = new Intent();
+ intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, true);
+ spyActivity.onChosenLockSaveFinished(intent);
+
+ verify(spyActivity).finish();
+ }
+
+ /**
+ * A test to verify that the UI stage is updated when save worker fails
+ */
+ @Test
+ public void testStageIsUpdatedWhenSaveWorkerFails() {
+ ChooseLockPatternActivity spyActivity = spy(mActivity);
+ doNothing().when(spyActivity).updateStage(ChooseLockPatternActivity.Stage.SaveFailure);
+
+ Intent intent = new Intent();
+ intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+ spyActivity.onChosenLockSaveFinished(intent);
+
+ verify(spyActivity, never()).finish();
+ verify(spyActivity).updateStage(ChooseLockPatternActivity.Stage.SaveFailure);
+ }
+}
diff --git a/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java b/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java
new file mode 100644
index 0000000..94c5329
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java
@@ -0,0 +1,66 @@
+/*
+* Copyright (C) 2018 Google Inc.
+*
+* 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.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for SaveLockPasswordWorker class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class SaveLockPasswordWorkerTest {
+ /**
+ * A test to check return value when save worker succeeds
+ */
+ @Test
+ public void testSaveLockSuccessReturnsTrue() {
+ SaveLockPasswordWorker worker = spy(new SaveLockPasswordWorker());
+
+ doNothing().when(worker).saveLockPassword();
+
+ assertThat(worker
+ .saveAndVerifyInBackground()
+ .getBooleanExtra(SaveLockPasswordWorker.EXTRA_KEY_SUCCESS,
+ false))
+ .isTrue();
+ }
+
+ /**
+ * A test to check return value when save worker fails
+ */
+ @Test
+ public void testSaveLockFailureReturnsFalse() {
+ SaveLockPasswordWorker worker = spy(new SaveLockPasswordWorker());
+
+ doThrow(new RuntimeException()).when(worker).saveLockPassword();
+
+ assertThat(worker
+ .saveAndVerifyInBackground()
+ .getBooleanExtra(SaveLockPasswordWorker.EXTRA_KEY_SUCCESS,
+ true))
+ .isFalse();
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java b/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java
new file mode 100644
index 0000000..11de1dd
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for SaveLockPatternWorker class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class SaveLockPatternWorkerTest {
+ /**
+ * A test to check return value when save worker succeeds
+ */
+ @Test
+ public void testSaveLockSuccessReturnsTrue() {
+ SaveLockPatternWorker worker = spy(new SaveLockPatternWorker());
+
+ doNothing().when(worker).saveLockPattern();
+
+ assertThat(worker
+ .saveAndVerifyInBackground()
+ .getBooleanExtra(SaveLockPatternWorker.EXTRA_KEY_SUCCESS,
+ false))
+ .isTrue();
+ }
+
+ /**
+ * A test to check return value when save worker fails
+ */
+ @Test
+ public void testSaveLockFailureReturnsFalse() {
+ SaveLockPatternWorker worker = spy(new SaveLockPatternWorker());
+
+ doThrow(new RuntimeException()).when(worker).saveLockPattern();
+
+ assertThat(worker
+ .saveAndVerifyInBackground()
+ .getBooleanExtra(SaveLockPatternWorker.EXTRA_KEY_SUCCESS,
+ true))
+ .isFalse();
+ }
+}
\ No newline at end of file