Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2010 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 
     17 package com.android.internal.policy.impl;
     18 
     19 import android.app.admin.DevicePolicyManager;
     20 import android.content.Context;
     21 import android.content.res.Configuration;
     22 import android.graphics.Rect;
     23 
     24 import com.android.internal.policy.impl.PatternUnlockScreen.FooterMode;
     25 import com.android.internal.widget.LockPatternUtils;
     26 import com.android.internal.widget.PasswordEntryKeyboardView;
     27 
     28 import android.os.CountDownTimer;
     29 import android.os.SystemClock;
     30 import android.telephony.TelephonyManager;
     31 import android.text.method.DigitsKeyListener;
     32 import android.text.method.TextKeyListener;
     33 import android.view.KeyEvent;
     34 import android.view.LayoutInflater;
     35 import android.view.View;
     36 import android.view.inputmethod.EditorInfo;
     37 import android.widget.Button;
     38 import android.widget.EditText;
     39 import android.widget.LinearLayout;
     40 import android.widget.TextView;
     41 import android.widget.TextView.OnEditorActionListener;
     42 
     43 import com.android.internal.R;
     44 import com.android.internal.widget.PasswordEntryKeyboardHelper;
     45 
     46 /**
     47  * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
     48  * an unlock password
     49  */
     50 public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
     51         View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener {
     52 
     53     private final KeyguardUpdateMonitor mUpdateMonitor;
     54     private final KeyguardScreenCallback mCallback;
     55 
     56     private EditText mPasswordEntry;
     57     private Button mEmergencyCallButton;
     58     private LockPatternUtils mLockPatternUtils;
     59     private PasswordEntryKeyboardView mKeyboardView;
     60     private PasswordEntryKeyboardHelper mKeyboardHelper;
     61 
     62     private int mCreationOrientation;
     63     private int mCreationHardKeyboardHidden;
     64     private CountDownTimer mCountdownTimer;
     65     private TextView mTitle;
     66 
     67     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     68     // any passwords with length less than or equal to this length.
     69     private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
     70 
     71     public PasswordUnlockScreen(Context context, Configuration configuration,
     72             LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor,
     73             KeyguardScreenCallback callback) {
     74         super(context);
     75 
     76         mCreationHardKeyboardHidden = configuration.hardKeyboardHidden;
     77         mCreationOrientation = configuration.orientation;
     78         mUpdateMonitor = updateMonitor;
     79         mCallback = callback;
     80         mLockPatternUtils = lockPatternUtils;
     81 
     82         LayoutInflater layoutInflater = LayoutInflater.from(context);
     83         if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
     84             layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true);
     85         } else {
     86             layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
     87         }
     88 
     89         final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
     90         final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
     91                 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality;
     92 
     93         mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
     94         mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
     95         mPasswordEntry.setOnEditorActionListener(this);
     96         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
     97         mEmergencyCallButton.setOnClickListener(this);
     98         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     99         mTitle = (TextView) findViewById(R.id.enter_password_label);
    100 
    101         mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this);
    102         mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
    103                 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
    104 
    105         mKeyboardView.setVisibility(mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
    106                 ? View.INVISIBLE : View.VISIBLE);
    107         mPasswordEntry.requestFocus();
    108 
    109         // This allows keyboards with overlapping qwerty/numeric keys to choose just the
    110         // numeric keys.
    111         if (isAlpha) {
    112             mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
    113         } else {
    114             mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
    115         }
    116 
    117         mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
    118                 com.android.internal.R.array.config_virtualKeyVibePattern : 0);
    119     }
    120 
    121     @Override
    122     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
    123         // send focus to the password field
    124         return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
    125     }
    126 
    127     /** {@inheritDoc} */
    128     public boolean needsInput() {
    129         return false;
    130     }
    131 
    132     /** {@inheritDoc} */
    133     public void onPause() {
    134 
    135     }
    136 
    137     /** {@inheritDoc} */
    138     public void onResume() {
    139         // start fresh
    140         mPasswordEntry.setText("");
    141         mPasswordEntry.requestFocus();
    142         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
    143 
    144         // if the user is currently locked out, enforce it.
    145         long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
    146         if (deadline != 0) {
    147             handleAttemptLockout(deadline);
    148         }
    149     }
    150 
    151     /** {@inheritDoc} */
    152     public void cleanUp() {
    153         mUpdateMonitor.removeCallback(this);
    154     }
    155 
    156     public void onClick(View v) {
    157         if (v == mEmergencyCallButton) {
    158             mCallback.takeEmergencyCallAction();
    159         }
    160         mCallback.pokeWakelock();
    161     }
    162 
    163     private void verifyPasswordAndUnlock() {
    164         String entry = mPasswordEntry.getText().toString();
    165         if (mLockPatternUtils.checkPassword(entry)) {
    166             mCallback.keyguardDone(true);
    167             mCallback.reportSuccessfulUnlockAttempt();
    168         } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
    169             // to avoid accidental lockout, only count attempts that are long enough to be a
    170             // real password. This may require some tweaking.
    171             mCallback.reportFailedUnlockAttempt();
    172             if (0 == (mUpdateMonitor.getFailedAttempts()
    173                     % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
    174                 long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
    175                 handleAttemptLockout(deadline);
    176             }
    177         }
    178         mPasswordEntry.setText("");
    179     }
    180 
    181     // Prevent user from using the PIN/Password entry until scheduled deadline.
    182     private void handleAttemptLockout(long elapsedRealtimeDeadline) {
    183         mPasswordEntry.setEnabled(false);
    184         mKeyboardView.setEnabled(false);
    185         long elapsedRealtime = SystemClock.elapsedRealtime();
    186         mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
    187 
    188             @Override
    189             public void onTick(long millisUntilFinished) {
    190                 int secondsRemaining = (int) (millisUntilFinished / 1000);
    191                 String instructions = getContext().getString(
    192                         R.string.lockscreen_too_many_failed_attempts_countdown,
    193                         secondsRemaining);
    194                 mTitle.setText(instructions);
    195             }
    196 
    197             @Override
    198             public void onFinish() {
    199                 mPasswordEntry.setEnabled(true);
    200                 mTitle.setText(R.string.keyguard_password_enter_password_code);
    201                 mKeyboardView.setEnabled(true);
    202             }
    203         }.start();
    204     }
    205 
    206 
    207     @Override
    208     public boolean onKeyDown(int keyCode, KeyEvent event) {
    209         mCallback.pokeWakelock();
    210         return false;
    211     }
    212 
    213     @Override
    214     protected void onAttachedToWindow() {
    215         super.onAttachedToWindow();
    216         Configuration config = getResources().getConfiguration();
    217         if (config.orientation != mCreationOrientation
    218                 || config.hardKeyboardHidden != mCreationHardKeyboardHidden) {
    219             mCallback.recreateMe(config);
    220         }
    221     }
    222 
    223     /** {@inheritDoc} */
    224     @Override
    225     protected void onConfigurationChanged(Configuration newConfig) {
    226         super.onConfigurationChanged(newConfig);
    227         if (newConfig.orientation != mCreationOrientation
    228                 || newConfig.hardKeyboardHidden != mCreationHardKeyboardHidden) {
    229             mCallback.recreateMe(newConfig);
    230         }
    231     }
    232 
    233     public void onKeyboardChange(boolean isKeyboardOpen) {
    234         // Don't show the soft keyboard when the real keyboard is open
    235         mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE);
    236     }
    237 
    238     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    239         // Check if this was the result of hitting the enter key
    240         if (actionId == EditorInfo.IME_NULL) {
    241             verifyPasswordAndUnlock();
    242             return true;
    243         }
    244         return false;
    245     }
    246 
    247     public void onPhoneStateChanged(String newState) {
    248         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
    249     }
    250 
    251     public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
    252 
    253     }
    254 
    255     public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
    256 
    257     }
    258 
    259     public void onRingerModeChanged(int state) {
    260 
    261     }
    262 
    263     public void onTimeChanged() {
    264 
    265     }
    266 
    267 }
    268