| /* |
| * 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.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.os.CountDownTimer; |
| import android.os.SystemClock; |
| import android.text.Editable; |
| import android.text.TextWatcher; |
| import android.util.AttributeSet; |
| import android.view.HapticFeedbackConstants; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.inputmethod.EditorInfo; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| import android.widget.TextView.OnEditorActionListener; |
| |
| import com.android.internal.widget.LockPatternUtils; |
| |
| /** |
| * Base class for PIN and password unlock screens. |
| */ |
| public abstract class KeyguardAbsKeyInputView extends LinearLayout |
| implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { |
| protected KeyguardSecurityCallback mCallback; |
| protected TextView mPasswordEntry; |
| 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 |
| mPasswordEntry.setText(""); |
| mPasswordEntry.requestFocus(); |
| |
| // 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); |
| |
| mPasswordEntry = (TextView) findViewById(getPasswordTextViewId()); |
| mPasswordEntry.setOnEditorActionListener(this); |
| mPasswordEntry.addTextChangedListener(this); |
| |
| // Set selected property on so the view can send accessibility events. |
| mPasswordEntry.setSelected(true); |
| |
| // Poke the wakelock any time the text is selected or modified |
| mPasswordEntry.setOnClickListener(new OnClickListener() { |
| public void onClick(View v) { |
| mCallback.userActivity(0); // TODO: customize timeout for text? |
| } |
| }); |
| |
| mPasswordEntry.addTextChangedListener(new TextWatcher() { |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| } |
| |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
| } |
| |
| public void afterTextChanged(Editable s) { |
| if (mCallback != null) { |
| mCallback.userActivity(0); |
| } |
| } |
| }); |
| 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 |
| protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { |
| // send focus to the password field |
| return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); |
| } |
| |
| /* |
| * 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 = mPasswordEntry.getText().toString(); |
| 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); |
| } |
| mPasswordEntry.setText(""); |
| } |
| |
| // Prevent user from using the PIN/Password entry until scheduled deadline. |
| protected void handleAttemptLockout(long elapsedRealtimeDeadline) { |
| mPasswordEntry.setEnabled(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(0); |
| return false; |
| } |
| |
| @Override |
| public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { |
| // Check if this was the result of hitting the enter key |
| if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE |
| || actionId == EditorInfo.IME_ACTION_NEXT) { |
| verifyPasswordAndUnlock(); |
| return true; |
| } |
| 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; |
| } |
| |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
| if (mCallback != null) { |
| mCallback.userActivity(KeyguardConstants.DIGIT_PRESS_WAKE_MILLIS); |
| } |
| } |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| } |
| |
| @Override |
| public void afterTextChanged(Editable s) { |
| } |
| |
| // 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); |
| } |
| } |
| |