/* * Copyright (C) 2012 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.keyguard; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.CountDownTimer; import android.os.SystemClock; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.View; import android.widget.LinearLayout; import com.android.internal.widget.LockPatternUtils; /** * Base class for PIN and password unlock screens. */ public abstract class KeyguardAbsKeyInputView extends LinearLayout implements KeyguardSecurityView { protected KeyguardSecurityCallback mCallback; protected LockPatternUtils mLockPatternUtils; protected SecurityMessageDisplay mSecurityMessageDisplay; protected View mEcaView; private Drawable mBouncerFrame; protected boolean mEnableHaptics; // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; public KeyguardAbsKeyInputView(Context context) { this(context, null); } public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { super(context, attrs); } public void setKeyguardCallback(KeyguardSecurityCallback callback) { mCallback = callback; } public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); } public void reset() { // start fresh resetPasswordText(false /* animate */); // if the user is currently locked out, enforce it. long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); if (shouldLockout(deadline)) { handleAttemptLockout(deadline); } else { resetState(); } } // Allow subclasses to override this behavior protected boolean shouldLockout(long deadline) { return deadline != 0; } protected abstract int getPasswordTextViewId(); protected abstract void resetState(); @Override protected void onFinishInflate() { mLockPatternUtils = new LockPatternUtils(mContext); mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); mEcaView = findViewById(R.id.keyguard_selector_fade_container); View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); if (bouncerFrameView != null) { mBouncerFrame = bouncerFrameView.getBackground(); } } /* * Override this if you have a different string for "wrong password" * * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this */ protected int getWrongPasswordStringId() { return R.string.kg_wrong_password; } protected void verifyPasswordAndUnlock() { String entry = getPasswordText(); if (mLockPatternUtils.checkPassword(entry)) { mCallback.reportUnlockAttempt(true); mCallback.dismiss(true); } else { if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. mCallback.reportUnlockAttempt(false); int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); handleAttemptLockout(deadline); } } mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } resetPasswordText(true /* animate */); } protected abstract void resetPasswordText(boolean animate); protected abstract String getPasswordText(); protected abstract void setPasswordEntryEnabled(boolean enabled); // Prevent user from using the PIN/Password entry until scheduled deadline. protected void handleAttemptLockout(long elapsedRealtimeDeadline) { setPasswordEntryEnabled(false); long elapsedRealtime = SystemClock.elapsedRealtime(); new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { @Override public void onTick(long millisUntilFinished) { int secondsRemaining = (int) (millisUntilFinished / 1000); mSecurityMessageDisplay.setMessage( R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining); } @Override public void onFinish() { mSecurityMessageDisplay.setMessage("", false); resetState(); } }.start(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { mCallback.userActivity(); return false; } @Override public boolean needsInput() { return false; } @Override public void onPause() { } @Override public void onResume(int reason) { reset(); } @Override public KeyguardSecurityCallback getCallback() { return mCallback; } // Cause a VIRTUAL_KEY vibration public void doHapticKeyClick() { if (mEnableHaptics) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } } @Override public void showBouncer(int duration) { KeyguardSecurityViewHelper. showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); } @Override public void hideBouncer(int duration) { KeyguardSecurityViewHelper. hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); } @Override public boolean startDisappearAnimation(Runnable finishRunnable) { return false; } }