summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/res/res/layout/keyguard_pin_view.xml193
-rwxr-xr-xcore/res/res/values/attrs.xml5
-rw-r--r--core/res/res/values/styles.xml23
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java247
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java17
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java117
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java162
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java10
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java97
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;
+ }
+}