package me.shouheng.omnilist.activity; import android.app.Activity; import android.app.Fragment; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.text.InputType; import android.text.TextUtils; import android.text.format.DateUtils; import com.afollestad.materialdialogs.MaterialDialog; import com.andrognito.pinlockview.IndicatorDots; import com.andrognito.pinlockview.PinLockListener; import me.shouheng.omnilist.PalmApp; import me.shouheng.omnilist.R; import me.shouheng.omnilist.activity.base.CommonActivity; import me.shouheng.omnilist.databinding.ActivityLockBinding; import me.shouheng.omnilist.utils.ActivityUtils; import me.shouheng.omnilist.utils.RSAUtil; import me.shouheng.omnilist.utils.SystemUiVisibilityUtil; import me.shouheng.omnilist.utils.ToastUtils; import me.shouheng.omnilist.utils.preferences.LockPreferences; public class LockActivity extends CommonActivity<ActivityLockBinding> { private final static String ACTION_SET_PASSWORD = "action_set_password"; private final static String ACTION_REQUIRE_PERMISSION = "action_require_password"; private final static String ACTION_REQUIRE_LAUNCH_APP = "action_require_launch_app"; private String lastInputPassword, savedPassword; private int errorTimes = 0; private boolean isPasswordFrozen = false; private long psdFreezeLength; private LockPreferences lockPreferences; public static void setPassword(Fragment fragment, int requestCode) { Intent intent = new Intent(fragment.getActivity(), LockActivity.class); intent.setAction(ACTION_SET_PASSWORD); fragment.startActivityForResult(intent, requestCode); } public static void requirePassword(Activity activity, int requestCode) { Intent intent = new Intent(activity, LockActivity.class); intent.setAction(ACTION_REQUIRE_PERMISSION); activity.startActivityForResult(intent, requestCode); } public static void requireLaunch(Activity activity, int requestCode) { Intent intent = new Intent(activity, LockActivity.class); intent.setAction(ACTION_REQUIRE_LAUNCH_APP); activity.startActivityForResult(intent, requestCode); } @Override protected int getLayoutResId() { return R.layout.activity_lock; } @Override protected void doCreateView(Bundle savedInstanceState) { lockPreferences = LockPreferences.getInstance(); psdFreezeLength = lockPreferences.getPasswordFreezeTime() * DateUtils.MINUTE_IN_MILLIS; savedPassword = lockPreferences.getPassword(); configSystemUI(); configViews(); checkPassword(); } private void configSystemUI() { runOnUiThread(() -> getWindow().getDecorView().setSystemUiVisibility( SystemUiVisibilityUtil.getSystemVisibility())); } private void configViews() { getBinding().pinLockView.attachIndicatorDots(getBinding().indicatorDots); getBinding().pinLockView.setPinLockListener(mPinLockListener); getBinding().pinLockView.setPinLength(4); getBinding().pinLockView.setTextColor(ContextCompat.getColor(this, R.color.white)); getBinding().indicatorDots.setIndicatorType(IndicatorDots.IndicatorType.FIXED); if (ACTION_SET_PASSWORD.equals(getIntent().getAction())) { getBinding().profileName.setText(R.string.setting_input_password_newly); } } /* * If there is no password saved in preferences, pass the password check directly. */ private void checkPassword() { if (ACTION_REQUIRE_PERMISSION.equals(getIntent().getAction()) || ACTION_REQUIRE_LAUNCH_APP.equals(getIntent().getAction())) { if (TextUtils.isEmpty(savedPassword)) { passCheck(); } } } private PinLockListener mPinLockListener = new PinLockListener() { @Override public void onComplete(String pin) { if (ACTION_REQUIRE_PERMISSION.equals(getIntent().getAction()) || ACTION_REQUIRE_LAUNCH_APP.equals(getIntent().getAction())) { onCompleteForRequirement(pin); } else if (ACTION_SET_PASSWORD.equals(getIntent().getAction())) { onCompleteForSetting(pin); } } @Override public void onEmpty() {} @Override public void onPinChange(int pinLength, String intermediatePin) {} }; private void onCompleteForRequirement(String var) { String encryptedPin = RSAUtil.getEncryptString(var); /* * If the saved password is empty, pass the password check logic. */ if (TextUtils.isEmpty(savedPassword)) { passCheck(); return; } /* * Check the freeze time first. */ if (lockPreferences.getLastInputErrorTime() + psdFreezeLength > System.currentTimeMillis()) { if (!TextUtils.isEmpty(lockPreferences.getPasswordQuestion()) && !TextUtils.isEmpty(lockPreferences.getPasswordAnswer())) { showFreezeDialog(); } else { showFreezeDialog(); } // rest the pin getBinding().pinLockView.resetPinLockView(); return; } else if (isPasswordFrozen) { // clear the freeze info isPasswordFrozen = false; errorTimes = 0; } if (savedPassword.equals(encryptedPin)) { /* * If the input password is the same as saved one -> back and record.*/ passCheck(); } else { /* * Input wrong password. */ getBinding().pinLockView.resetPinLockView(); if (++errorTimes == 5) { /* * Input wrong password for too many times, record last error time and save the frozen state. */ lockPreferences.setLastInputErrorTime(System.currentTimeMillis()); isPasswordFrozen = true; showFreezeToast(); } else { ToastUtils.makeToast(String.format(getString(R.string.setting_input_wrong_password), 5 - errorTimes)); } } } /** * On password input completed for setting password. * Should input one same password twice before save it to settings. * * @param var the password numeric string */ private void onCompleteForSetting(String var) { String encryptedPin = RSAUtil.getEncryptString(var); if (TextUtils.isEmpty(lastInputPassword)) { /* * record last input password witch will be used to check twice-input-logic */ lastInputPassword = encryptedPin; getBinding().profileName.setText(R.string.setting_input_password_again); getBinding().pinLockView.resetPinLockView(); } else { if (lastInputPassword.equals(encryptedPin)) { passSetting(encryptedPin); } else { /* * Clear last input password, need to input same password twice. */ lastInputPassword = null; getBinding().profileName.setText(R.string.setting_input_password_newly); getBinding().pinLockView.resetPinLockView(); } } } private void showFreezeToast() { String msg = String.format(getString(R.string.setting_password_frozen_minutes), lockPreferences.getPasswordFreezeTime()); ToastUtils.makeToast(msg); } private void showFreezeDialog() { new MaterialDialog.Builder(this) .title(R.string.text_tips) .content(R.string.setting_password_frozen) .positiveText(R.string.text_ok) .onPositive((dialog, which) -> showQuestionDialog()) .negativeText(R.string.text_cancel) .build().show(); } private void showQuestionDialog() { String question = lockPreferences.getPasswordQuestion(); String answer = lockPreferences.getPasswordAnswer(); new MaterialDialog.Builder(this) .title(R.string.setting_answer_question) .content(question) .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD) .input(null, null, (dialog, input) -> { String encryptAnswer = RSAUtil.getEncryptString(input.toString()); if (answer.equals(encryptAnswer)) { lockPreferences.setPasswordRequired(false); // remove the last input error time lockPreferences.setLastInputErrorTime(0); showDisableDialog(); } else { ToastUtils.makeToast(R.string.setting_wrong_answer); } }) .negativeText(R.string.text_cancel) .positiveText(R.string.text_confirm) .build().show(); } private void showDisableDialog() { MaterialDialog dlg = new MaterialDialog.Builder(this) .title(R.string.text_tips) .content(R.string.setting_disable_password) .positiveText(R.string.text_ok) .onPositive((dialog, which) -> passCheck()) .build(); dlg.show(); dlg.setOnDismissListener(dialog -> passCheck()); } private void passCheck() { Intent intent = new Intent(); setResult(Activity.RESULT_OK, intent); PalmApp.setPasswordChecked(); finish(); } private void passSetting(String encryptedPin) { /* * The password input twice is the same, save it to settings and finish activity. */ lockPreferences.setPassword(encryptedPin); Intent intent = new Intent(); setResult(Activity.RESULT_OK, intent); finish(); } @Override public void onBackPressed() { if (ACTION_REQUIRE_LAUNCH_APP.equals(getIntent().getAction())) { ActivityUtils.finishAll(); } else if (ACTION_SET_PASSWORD.equals(getIntent().getAction())) { Intent intent = new Intent(); setResult(Activity.RESULT_CANCELED, intent); finish(); } else { super.onBackPressed(); } } }