1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.keyguard;
18
19import android.content.Context;
20import android.graphics.drawable.Drawable;
21import android.os.CountDownTimer;
22import android.os.SystemClock;
23import android.util.AttributeSet;
24import android.view.HapticFeedbackConstants;
25import android.view.KeyEvent;
26import android.view.View;
27import android.widget.LinearLayout;
28
29import com.android.internal.widget.LockPatternUtils;
30
31/**
32 * Base class for PIN and password unlock screens.
33 */
34public abstract class KeyguardAbsKeyInputView extends LinearLayout
35        implements KeyguardSecurityView {
36    protected KeyguardSecurityCallback mCallback;
37    protected LockPatternUtils mLockPatternUtils;
38    protected SecurityMessageDisplay mSecurityMessageDisplay;
39    protected View mEcaView;
40    private Drawable mBouncerFrame;
41    protected boolean mEnableHaptics;
42
43    // To avoid accidental lockout due to events while the device in in the pocket, ignore
44    // any passwords with length less than or equal to this length.
45    protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
46
47    public KeyguardAbsKeyInputView(Context context) {
48        this(context, null);
49    }
50
51    public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
52        super(context, attrs);
53    }
54
55    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
56        mCallback = callback;
57    }
58
59    public void setLockPatternUtils(LockPatternUtils utils) {
60        mLockPatternUtils = utils;
61        mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
62    }
63
64    public void reset() {
65        // start fresh
66        resetPasswordText(false /* animate */);
67        // if the user is currently locked out, enforce it.
68        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
69        if (shouldLockout(deadline)) {
70            handleAttemptLockout(deadline);
71        } else {
72            resetState();
73        }
74    }
75
76    // Allow subclasses to override this behavior
77    protected boolean shouldLockout(long deadline) {
78        return deadline != 0;
79    }
80
81    protected abstract int getPasswordTextViewId();
82    protected abstract void resetState();
83
84    @Override
85    protected void onFinishInflate() {
86        mLockPatternUtils = new LockPatternUtils(mContext);
87        mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
88        mEcaView = findViewById(R.id.keyguard_selector_fade_container);
89        View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame);
90        if (bouncerFrameView != null) {
91            mBouncerFrame = bouncerFrameView.getBackground();
92        }
93    }
94
95    /*
96     * Override this if you have a different string for "wrong password"
97     *
98     * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this
99     */
100    protected int getWrongPasswordStringId() {
101        return R.string.kg_wrong_password;
102    }
103
104    protected void verifyPasswordAndUnlock() {
105        String entry = getPasswordText();
106        if (mLockPatternUtils.checkPassword(entry)) {
107            mCallback.reportUnlockAttempt(true);
108            mCallback.dismiss(true);
109        } else {
110            if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
111                // to avoid accidental lockout, only count attempts that are long enough to be a
112                // real password. This may require some tweaking.
113                mCallback.reportUnlockAttempt(false);
114                int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
115                if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
116                    long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
117                    handleAttemptLockout(deadline);
118                }
119            }
120            mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
121        }
122        resetPasswordText(true /* animate */);
123    }
124
125    protected abstract void resetPasswordText(boolean animate);
126    protected abstract String getPasswordText();
127    protected abstract void setPasswordEntryEnabled(boolean enabled);
128
129    // Prevent user from using the PIN/Password entry until scheduled deadline.
130    protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
131        setPasswordEntryEnabled(false);
132        long elapsedRealtime = SystemClock.elapsedRealtime();
133        new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
134
135            @Override
136            public void onTick(long millisUntilFinished) {
137                int secondsRemaining = (int) (millisUntilFinished / 1000);
138                mSecurityMessageDisplay.setMessage(
139                        R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
140            }
141
142            @Override
143            public void onFinish() {
144                mSecurityMessageDisplay.setMessage("", false);
145                resetState();
146            }
147        }.start();
148    }
149
150    @Override
151    public boolean onKeyDown(int keyCode, KeyEvent event) {
152        mCallback.userActivity();
153        return false;
154    }
155
156    @Override
157    public boolean needsInput() {
158        return false;
159    }
160
161    @Override
162    public void onPause() {
163
164    }
165
166    @Override
167    public void onResume(int reason) {
168        reset();
169    }
170
171    @Override
172    public KeyguardSecurityCallback getCallback() {
173        return mCallback;
174    }
175
176    // Cause a VIRTUAL_KEY vibration
177    public void doHapticKeyClick() {
178        if (mEnableHaptics) {
179            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
180                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
181                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
182        }
183    }
184
185    @Override
186    public void showBouncer(int duration) {
187        KeyguardSecurityViewHelper.
188                showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
189    }
190
191    @Override
192    public void hideBouncer(int duration) {
193        KeyguardSecurityViewHelper.
194                hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
195    }
196
197    @Override
198    public boolean startDisappearAnimation(Runnable finishRunnable) {
199        return false;
200    }
201}
202
203