diff options
10 files changed, 711 insertions, 165 deletions
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml new file mode 100644 index 0000000..6a4ba71 --- /dev/null +++ b/core/res/res/layout/keyguard_pin_view.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> + +<com.android.internal.policy.impl.keyguard.KeyguardPINView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/keyguard_pin_view" + android:layout_width="350dp" + android:layout_height="350dp" + android:orientation="vertical" + > + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:orientation="horizontal" + android:layout_weight="1" + > + <EditText android:id="@+id/passwordEntry" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center_horizontal" + android:layout_gravity="center_vertical" + android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" + android:singleLine="true" + android:textStyle="normal" + android:inputType="textPassword" + android:cursorVisible="false" + android:textSize="36sp" + android:background="@null" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="#ffffffff" + android:imeOptions="flagForceAscii|actionDone" + /> + + <ImageButton android:id="@+id/delete_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:src="@*android:drawable/ic_input_delete" + android:clickable="true" + android:padding="8dip" + android:background="?android:attr/selectableItemBackground" + /> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="horizontal" + > + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key1" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="1" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key2" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="2" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key3" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="3" + /> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="horizontal" + > + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key4" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="4" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key5" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="5" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key6" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="6" + /> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:orientation="horizontal" + android:layout_weight="1" + > + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key7" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="7" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key8" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="8" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key9" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="9" + /> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="horizontal" + > + <Space + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + /> + <view class="com.android.internal.policy.impl.keyguard.NumPadKey" + android:id="@+id/key0" + style="@android:style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:textView="@+id/passwordEntry" + android:digit="0" + /> + <Button + android:id="@+id/key_enter" + style="@android:style/Widget.Button.NumPadKey" + android:gravity="center" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:text="@android:string/ok" + /> + </LinearLayout> +</com.android.internal.policy.impl.keyguard.KeyguardPINView> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d5589e8..6656de0 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5788,4 +5788,9 @@ <attr name="gravity" /> </declare-styleable> + <!-- Keyguard PIN entry --> + <declare-styleable name="NumPadKey"> + <attr name="digit" format="integer" /> + <attr name="textView" format="reference" /> + </declare-styleable> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 4371aec..1dfb6e0 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -2478,4 +2478,27 @@ please see styles_device_defaults.xml. <item name="android:contentDescription">@android:string/media_route_button_content_description</item> </style> + <!-- Keyguard PIN pad styles --> + <style name="Widget.Button.NumPadKey"> + <item name="android:singleLine">true</item> + <item name="android:padding">6dip</item> + <item name="android:gravity">left|center_vertical</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:textSize">34dp</item> + <item name="android:fontFamily">sans-serif</item> + <item name="android:textStyle">normal</item> + <item name="android:textColor">#ffffff</item> + </style> + <style name="TextAppearance.NumPadKey"> + <item name="android:textSize">34dp</item> + <item name="android:fontFamily">sans-serif</item> + <item name="android:textStyle">normal</item> + <item name="android:textColor">#ffffff</item> + </style> + <style name="TextAppearance.NumPadKey.Klondike"> + <item name="android:textSize">20dp</item> + <item name="android:fontFamily">sans-serif-condensed</item> + <item name="android:textStyle">normal</item> + <item name="android:textColor">#80ffffff</item> + </style> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 51b8467..94ca080 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1289,6 +1289,7 @@ <java-symbol type="id" name="keyguard_selector_view" /> <java-symbol type="id" name="keyguard_pattern_view" /> <java-symbol type="id" name="keyguard_password_view" /> + <java-symbol type="id" name="keyguard_pin_view" /> <java-symbol type="id" name="keyguard_face_unlock_view" /> <java-symbol type="id" name="keyguard_sim_pin_view" /> <java-symbol type="id" name="keyguard_sim_puk_view" /> @@ -1319,6 +1320,7 @@ <java-symbol type="id" name="keyguard_add_widget" /> <java-symbol type="id" name="keyguard_add_widget_view" /> <java-symbol type="id" name="sliding_layout" /> + <java-symbol type="id" name="key_enter" /> <java-symbol type="integer" name="config_carDockRotation" /> <java-symbol type="integer" name="config_defaultUiModeType" /> <java-symbol type="integer" name="config_deskDockRotation" /> @@ -1343,6 +1345,7 @@ <java-symbol type="layout" name="keyguard_selector_view" /> <java-symbol type="layout" name="keyguard_pattern_view" /> <java-symbol type="layout" name="keyguard_password_view" /> + <java-symbol type="layout" name="keyguard_pin_view" /> <java-symbol type="layout" name="keyguard_face_unlock_view" /> <java-symbol type="layout" name="keyguard_sim_pin_view" /> <java-symbol type="layout" name="keyguard_sim_puk_view" /> @@ -1412,6 +1415,8 @@ <java-symbol type="style" name="Animation.LockScreen" /> <java-symbol type="style" name="Theme.Dialog.RecentApplications" /> <java-symbol type="style" name="Theme.ExpandedMenu" /> + <java-symbol type="style" name="Widget.Button.NumPadKey" /> + <java-symbol type="style" name="TextAppearance.NumPadKey.Klondike" /> <java-symbol type="string" name="kg_emergency_call_label" /> <java-symbol type="string" name="kg_forgot_pattern_button_text" /> <java-symbol type="string" name="kg_wrong_pattern" /> diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java new file mode 100644 index 0000000..6b9abeb --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java @@ -0,0 +1,247 @@ +/* + * 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.internal.policy.impl.keyguard; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewParent; + +import com.android.internal.widget.LockPatternUtils; +import java.util.List; + +import android.app.admin.DevicePolicyManager; +import android.content.res.Configuration; +import android.graphics.Rect; + +import com.android.internal.widget.PasswordEntryKeyboardView; + +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.text.Editable; +import android.text.InputType; +import android.text.SpannableStringBuilder; +import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; +import android.text.method.TextKeyListener; +import android.text.style.TextAppearanceSpan; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import com.android.internal.R; + +/** + * Base class for PIN and password unlock screens. + */ +public abstract class KeyguardAbsKeyInputView extends LinearLayout + implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { + protected KeyguardSecurityCallback mCallback; + protected EditText mPasswordEntry; + protected LockPatternUtils mLockPatternUtils; + protected SecurityMessageDisplay mSecurityMessageDisplay; + + // 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; + + // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing + protected static final boolean ENABLE_HIDE_KEYBOARD = false; + + 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; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + if (hasWindowFocus) { + reset(); + } + } + + public void reset() { + // start fresh + mPasswordEntry.setText(""); + mPasswordEntry.requestFocus(); + + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); + if (deadline != 0) { + handleAttemptLockout(deadline); + } else { + resetState(); + } + } + + protected abstract void resetState(); + + @Override + protected void onFinishInflate() { + // We always set a dummy NavigationManager to avoid null checks + mSecurityMessageDisplay = new KeyguardNavigationManager(null); + + mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one + + mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); + mPasswordEntry.setOnEditorActionListener(this); + mPasswordEntry.addTextChangedListener(this); + + // 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); + } + } + }); + } + + @Override + protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { + // send focus to the password field + return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); + } + + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText().toString(); + if (mLockPatternUtils.checkPassword(entry)) { + mCallback.reportSuccessfulUnlockAttempt(); + 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.reportFailedUnlockAttempt(); + if (0 == (mCallback.getFailedAttempts() + % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + handleAttemptLockout(deadline); + } + mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pin, 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() { + 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() { + reset(); + } + + @Override + public KeyguardSecurityCallback getCallback() { + return mCallback; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (mCallback != null) { + mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); + } + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + } + + @Override + public void setSecurityMessageDisplay(SecurityMessageDisplay display) { + mSecurityMessageDisplay = display; + reset(); + } +} + diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index 34b14a4..c6fb099 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -333,13 +333,11 @@ public class KeyguardHostView extends KeyguardViewBase { case Pattern: messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; break; - - case Password: { - final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() == - DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message - : R.string.kg_too_many_failed_password_attempts_dialog_message; - } + case PIN: + messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message; + break; + case Password: + messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; break; } @@ -481,6 +479,7 @@ public class KeyguardHostView extends KeyguardViewBase { switch (mCurrentSecuritySelection) { case Pattern: case Password: + case PIN: case Account: case Biometric: finish = true; @@ -746,6 +745,7 @@ public class KeyguardHostView extends KeyguardViewBase { case Pattern: return mLockPatternUtils.isLockPatternEnabled(); case Password: + case PIN: return mLockPatternUtils.isLockPasswordEnabled(); case SimPin: case SimPuk: @@ -780,6 +780,7 @@ public class KeyguardHostView extends KeyguardViewBase { mViewMediatorCallback.keyguardDone(true); } } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern + && securityMode != KeyguardSecurityModel.SecurityMode.PIN && securityMode != KeyguardSecurityModel.SecurityMode.Password) { // can only verify unlock when in pattern/password mode if (mViewMediatorCallback != null) { @@ -796,6 +797,7 @@ public class KeyguardHostView extends KeyguardViewBase { switch (securityMode) { case None: return R.id.keyguard_selector_view; case Pattern: return R.id.keyguard_pattern_view; + case PIN: return R.id.keyguard_pin_view; case Password: return R.id.keyguard_password_view; case Biometric: return R.id.keyguard_face_unlock_view; case Account: return R.id.keyguard_account_view; @@ -809,6 +811,7 @@ public class KeyguardHostView extends KeyguardViewBase { switch (securityMode) { case None: return R.layout.keyguard_selector_view; case Pattern: return R.layout.keyguard_pattern_view; + case PIN: return R.layout.keyguard_pin_view; case Password: return R.layout.keyguard_password_view; case Biometric: return R.layout.keyguard_face_unlock_view; case Account: return R.layout.keyguard_account_view; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java new file mode 100644 index 0000000..809186d --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java @@ -0,0 +1,117 @@ +/* + * 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.internal.policy.impl.keyguard; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewParent; + +import com.android.internal.widget.LockPatternUtils; +import java.util.List; + +import android.app.admin.DevicePolicyManager; +import android.content.res.Configuration; +import android.graphics.Rect; + +import com.android.internal.widget.PasswordEntryKeyboardView; + +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.text.Editable; +import android.text.InputType; +import android.text.SpannableStringBuilder; +import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; +import android.text.method.TextKeyListener; +import android.text.style.TextAppearanceSpan; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import com.android.internal.R; + +/** + * Displays a PIN pad for unlocking. + */ +public class KeyguardPINView extends KeyguardAbsKeyInputView + implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { + + // 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. + private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; + + // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing + private static final boolean ENABLE_HIDE_KEYBOARD = false; + + public KeyguardPINView(Context context) { + this(context, null); + } + + public KeyguardPINView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + protected void resetState() { + mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false); + mPasswordEntry.setEnabled(true); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final View ok = findViewById(R.id.key_enter); + if (ok != null) { + ok.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + verifyPasswordAndUnlock(); + } + }); + } + + // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, + // not a separate view + View pinDelete = findViewById(R.id.delete_button); + if (pinDelete != null) { + pinDelete.setVisibility(View.VISIBLE); + pinDelete.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + Editable str = mPasswordEntry.getText(); + if (str.length() > 0) { + mPasswordEntry.setText(str.subSequence(0, str.length()-1)); + } + } + }); + } + + mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER + | InputType.TYPE_NUMBER_VARIATION_PASSWORD); + + mPasswordEntry.requestFocus(); + } +} + diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java index 1868507..635cadc 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java @@ -53,22 +53,12 @@ import com.android.internal.widget.PasswordEntryKeyboardHelper; * an unlock password */ -public class KeyguardPasswordView extends LinearLayout +public class KeyguardPasswordView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { - private KeyguardSecurityCallback mCallback; - private EditText mPasswordEntry; - private LockPatternUtils mLockPatternUtils; + private PasswordEntryKeyboardView mKeyboardView; private PasswordEntryKeyboardHelper mKeyboardHelper; private boolean mIsAlpha; - private SecurityMessageDisplay mSecurityMessageDisplay; - - // 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. - private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; - - // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing - private static final boolean ENABLE_HIDE_KEYBOARD = false; public KeyguardPasswordView(Context context) { super(context); @@ -78,36 +68,7 @@ public class KeyguardPasswordView extends LinearLayout super(context, attrs); } - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mCallback = callback; - } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - } - - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - if (hasWindowFocus) { - reset(); - } - } - - public void reset() { - // start fresh - mPasswordEntry.setText(""); - mPasswordEntry.requestFocus(); - - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); - if (deadline != 0) { - handleAttemptLockout(deadline); - } else { - resetState(); - } - } - - private void resetState() { + protected void resetState() { mSecurityMessageDisplay.setMessage( mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions, false); mPasswordEntry.setEnabled(true); @@ -116,10 +77,7 @@ public class KeyguardPasswordView extends LinearLayout @Override protected void onFinishInflate() { - // We always set a dummy NavigationManager to avoid null checks - mSecurityMessageDisplay = new KeyguardNavigationManager(null); - - mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one + super.onFinishInflate(); final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(); mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality @@ -127,9 +85,6 @@ public class KeyguardPasswordView extends LinearLayout || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality; mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); - mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); - mPasswordEntry.setOnEditorActionListener(this); - mPasswordEntry.addTextChangedListener(this); mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false, new int[] { @@ -170,8 +125,6 @@ public class KeyguardPasswordView extends LinearLayout } } - mPasswordEntry.requestFocus(); - // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys. if (mIsAlpha) { mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); @@ -204,6 +157,8 @@ public class KeyguardPasswordView extends LinearLayout } }); + mPasswordEntry.requestFocus(); + // If there's more than one IME, enable the IME switcher button View switchImeButton = findViewById(R.id.switch_ime_button); final InputMethodManager imm = (InputMethodManager) getContext().getSystemService( @@ -278,110 +233,5 @@ public class KeyguardPasswordView extends LinearLayout // input method subtype (The current IME should be LatinIME.) || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - // send focus to the password field - return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); - } - - private void verifyPasswordAndUnlock() { - String entry = mPasswordEntry.getText().toString(); - if (mLockPatternUtils.checkPassword(entry)) { - mCallback.reportSuccessfulUnlockAttempt(); - 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.reportFailedUnlockAttempt(); - if (0 == (mCallback.getFailedAttempts() - % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); - handleAttemptLockout(deadline); - } - mSecurityMessageDisplay.setMessage( - mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin, true); - } - mPasswordEntry.setText(""); - } - - // Prevent user from using the PIN/Password entry until scheduled deadline. - private void handleAttemptLockout(long elapsedRealtimeDeadline) { - mPasswordEntry.setEnabled(false); - mKeyboardView.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() { - 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 mIsAlpha; - } - - @Override - public void onPause() { - - } - - @Override - public void onResume() { - reset(); - } - - @Override - public KeyguardSecurityCallback getCallback() { - return mCallback; - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (mCallback != null) { - mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); - } - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - } - - @Override - public void setSecurityMessageDisplay(SecurityMessageDisplay display) { - mSecurityMessageDisplay = display; - reset(); - } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java index 59e2ca9..7a69586 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java @@ -31,7 +31,8 @@ public class KeyguardSecurityModel { Invalid, // NULL state None, // No security enabled Pattern, // Unlock by drawing a pattern. - Password, // Unlock by entering a password or PIN + Password, // Unlock by entering an alphanumeric password + PIN, // Strictly numeric password Biometric, // Unlock with a biometric key (e.g. finger print or face unlock) Account, // Unlock by entering an account's login and password. SimPin, // Unlock by entering a sim pin. @@ -85,6 +86,9 @@ public class KeyguardSecurityModel { final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality(); switch (security) { case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + mode = mLockPatternUtils.isLockPasswordEnabled() ? + SecurityMode.PIN : SecurityMode.None; + break; case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: @@ -117,7 +121,9 @@ public class KeyguardSecurityModel { */ SecurityMode getAlternateFor(SecurityMode mode) { if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed() - && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) { + && (mode == SecurityMode.Password + || mode == SecurityMode.PIN + || mode == SecurityMode.Pattern)) { return SecurityMode.Biometric; } return mode; // no alternate, return what was given diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java new file mode 100644 index 0000000..90f49c4 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java @@ -0,0 +1,97 @@ +/* + * 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.internal.policy.impl.keyguard; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.SpannableStringBuilder; +import android.text.style.TextAppearanceSpan; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.R; + +public class NumPadKey extends Button { + // XXX localize + static final String KLONDIKE[] = { + "", "", " ABC", " DEF", " GHI", " JKL", " MNO", " PQRS", " TUV", " WXYZ" }; + + int mDigit = -1; + int mTextViewResId; + TextView mTextView = null; + + private View.OnClickListener mListener = new View.OnClickListener() { + @Override + public void onClick(View thisView) { + if (mTextView == null) { + if (mTextViewResId > 0) { + final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId); + if (v != null && v instanceof TextView) { + mTextView = (TextView) v; + } + } + } + if (mTextView != null) { + mTextView.append(String.valueOf(mDigit)); + } + } + }; + + public NumPadKey(Context context) { + this(context, null); + } + + public NumPadKey(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NumPadKey(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey); + mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit); + setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0)); + + setOnClickListener(mListener); + + SpannableStringBuilder builder = new SpannableStringBuilder(); + builder.append(String.valueOf(mDigit)); + if (mDigit >= 0) { + final String extra = KLONDIKE[mDigit]; + final int extraLen = extra.length(); + if (extraLen > 0) { + builder.append(extra); + builder.setSpan( + new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike), + builder.length()-extraLen, builder.length(), 0); + } + } + setText(builder); + } + + public void setTextView(TextView tv) { + mTextView = tv; + } + + public void setTextViewResId(int resId) { + mTextView = null; + mTextViewResId = resId; + } +} |
