Fix 3148496: Initial pass at fragmentizing lockscreen settings.

This converts most of the existing activities to fragments and wraps
them in PreferenceActivities so they can be launched as before
(e.g. by a DevicePolicyManager)

Upload after sync/rebase.

Change-Id: I4f351b75d9fca0498bcb04b4e11ff3b70765a4ba
This commit is contained in:
Jim Miller
2010-12-07 20:41:41 -08:00
parent 1c7e49ba67
commit 17e9e19330
17 changed files with 2120 additions and 1267 deletions

View File

@@ -651,9 +651,8 @@
<!-- Second and third-level settings --> <!-- Second and third-level settings -->
<activity android:name="ConfirmLockPattern"/> <activity android:name="ConfirmLockPattern"/>
<activity android:name="ConfirmLockPassword"
android:theme="@android:style/Theme.NoTitleBar"> <activity android:name="ConfirmLockPassword"/>>
</activity>
<activity android:name="ChooseLockGeneric"> <activity android:name="ChooseLockGeneric">
<intent-filter> <intent-filter>
@@ -669,7 +668,6 @@
<activity android:name="ChooseLockPassword" <activity android:name="ChooseLockPassword"
android:label="@string/lockpattern_change_lock_pin_label" android:label="@string/lockpattern_change_lock_pin_label"
android:theme="@android:style/Theme.NoTitleBar"
android:exported="false"> android:exported="false">
</activity> </activity>

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2008, 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- "Enter PIN(Password) to unlock" -->
<TextView android:id="@+id/headerText"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:layout_marginRight="6dip"
android:layout_marginLeft="6dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:gravity="left"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceLarge"
/>
<!-- Password entry field -->
<EditText android:id="@+id/password_entry"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:textStyle="bold"
android:inputType="textPassword"
android:gravity="center"
android:layout_gravity="center"
android:textSize="24sp"
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
android:textAppearance="?android:attr/textAppearanceLarge"
android:background="@drawable/password_field_default"
android:textColor="#ffffffff"
/>
</LinearLayout>
<!-- Spacer between password entry and keyboard -->
<View
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
<!-- Alphanumeric keyboard -->
<com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
android:visibility="gone"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<Button android:id="@+id/cancel_button"
android:layout_height="wrap_content"
android:layout_width="140dip"
android:ellipsize="marquee"
android:singleLine="true"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/next_button"
android:text="@string/lockpassword_cancel_label"
/>
<Button android:id="@+id/next_button"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:text="@string/lockpassword_continue_label"
/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.widget.LinearLayoutWithDefaultTouchRecepient
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- left side: lock pattern -->
<com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- right side: pattern and messages -->
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1.0">
<!-- header message -->
<TextView android:id="@+id/headerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="22sp"
android:gravity="left"/>
<!-- footer message -->
<TextView android:id="@+id/footerText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:layout_marginTop="40dip"
android:visibility="gone"/>
<!-- confirm / restart buttons -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<!-- right / bottom button: confirm or ok -->
<Button android:id="@+id/footerRightButton"
android:layout_height="wrap_content"
android:layout_width="140dip"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:ellipsize="marquee"
android:singleLine="true"
android:text="@string/lockpattern_confirm_button_text"/>
<!-- left / top button: skip, or re-try -->
<Button android:id="@+id/footerLeftButton"
android:layout_height="wrap_content"
android:layout_width="140dip"
android:layout_toLeftOf="@id/footerRightButton"
android:layout_alignParentBottom="true"
android:ellipsize="marquee"
android:singleLine="true"
android:text="@string/lockpattern_restart_button_text"/>
</RelativeLayout>
</LinearLayout>
</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2008, 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.
*/
-->
<!-- This is the same layout as choose_lock_password. TODO: find out why merge fails -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- "Enter PIN(Password) to unlock" -->
<TextView android:id="@+id/headerText"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:layout_marginRight="6dip"
android:layout_marginLeft="6dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:gravity="left"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceLarge"
/>
<!-- Password entry field -->
<EditText android:id="@+id/password_entry"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:textStyle="bold"
android:inputType="textPassword"
android:gravity="center"
android:layout_gravity="center"
android:textSize="24sp"
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
android:textAppearance="?android:attr/textAppearanceLarge"
android:background="@drawable/password_field_default"
android:textColor="#ffffffff"
/>
</LinearLayout>
<!-- Spacer between password entry and keyboard -->
<View
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
<!-- Alphanumeric keyboard -->
<com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
android:visibility="gone"
/>
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:visibility="visible">
<Button android:id="@+id/cancel_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_toLeftOf="@id/next_button"
android:text="@string/lockpassword_cancel_label"
/>
<Button android:id="@+id/next_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentRight="true"
android:drawableRight="@drawable/ic_btn_next"
android:drawablePadding="10dip"
android:text="@string/lockpassword_continue_label"
/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.widget.LinearLayoutWithDefaultTouchRecepient
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- left side: lock pattern -->
<com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- right side: instructions and buttons -->
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1.0">
<!-- header message -->
<TextView android:id="@+id/headerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:textSize="22sp"/>
<!-- footer message -->
<TextView android:id="@+id/footerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dip"
android:gravity="left"
android:textSize="22sp"/>
</LinearLayout>
</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2008, 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@android:color/background_dark"
android:gravity="center_horizontal">
<!-- header text ('Enter Pin') -->
<TextView android:id="@+id/headerText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="2"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<!-- spacer above text entry field -->
<View
android:id="@+id/spacerBottom"
android:layout_width="fill_parent"
android:layout_height="1dip"
android:layout_marginTop="6dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<!-- Password entry field -->
<EditText android:id="@+id/password_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textStyle="bold"
android:inputType="textPassword"
android:gravity="center"
android:layout_gravity="center"
android:textSize="32sp"
android:layout_marginTop="15dip"
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:textAppearance="?android:attr/textAppearanceLarge"
android:background="@drawable/password_field_default"
android:textColor="#ffffffff"
/>
<!-- Spacer between password entry and keyboard -->
<View
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
<!-- Alphanumeric keyboard -->
<com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:layout_marginBottom="30dip"
android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
/>
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button android:id="@+id/cancel_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
android:text="@string/lockpassword_cancel_label"
/>
<Button android:id="@+id/next_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentRight="true"
android:drawableRight="@drawable/ic_btn_next"
android:drawablePadding="10dip"
android:text="@string/lockpassword_continue_label"
/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.widget.LinearLayoutWithDefaultTouchRecepient
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topLayout"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/black">
<!-- takes up all space above button bar at bottom -->
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<TextView android:id="@+id/headerText"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center"
android:textSize="18sp"/>
<View
android:background="@*android:drawable/code_lock_top"
android:layout_width="match_parent"
android:layout_height="2dip" />
<com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- bottom line looks bad when button bar is their too, omit in this case -->
<!--View
android:background="@*android:drawable/code_lock_bottom"
android:layout_width="match_parent"
android:layout_height="8dip" /-->
<TextView android:id="@+id/footerText"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center"
android:textSize="14sp"/>
</LinearLayout>
<LinearLayout style="@android:style/ButtonBar"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/footerLeftButton"
android:layout_height="wrap_content"
android:layout_width="140dip"
android:ellipsize="marquee"
android:singleLine="true"
android:text="@string/lockpattern_restart_button_text"/>
<View
android:visibility="invisible"
android:layout_height="0dip"
android:layout_width="1dip"
android:layout_weight="1" />
<Button android:id="@+id/footerRightButton"
android:layout_height="wrap_content"
android:layout_width="140dip"
android:ellipsize="marquee"
android:singleLine="true"
android:drawableRight="@drawable/ic_btn_next"
android:drawablePadding="3dip"
android:text="@string/lockpattern_confirm_button_text"/>
</LinearLayout>
</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2008, 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.
*/
-->
<!-- This is the same layout as choose_lock_password -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@android:color/background_dark"
android:gravity="center_horizontal">
<!-- header text ('Enter Pin') -->
<TextView android:id="@+id/headerText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="2"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<!-- spacer above text entry field -->
<View
android:id="@+id/spacerBottom"
android:layout_width="fill_parent"
android:layout_height="1dip"
android:layout_marginTop="6dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<!-- Password entry field -->
<EditText android:id="@+id/password_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textStyle="bold"
android:inputType="textPassword"
android:gravity="center"
android:layout_gravity="center"
android:textSize="32sp"
android:layout_marginTop="15dip"
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:textAppearance="?android:attr/textAppearanceLarge"
android:background="@drawable/password_field_default"
android:textColor="#ffffffff"
/>
<!-- Spacer between password entry and keyboard -->
<View
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
<!-- Alphanumeric keyboard -->
<com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:layout_marginBottom="30dip"
android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
/>
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button android:id="@+id/cancel_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
android:text="@string/lockpassword_cancel_label"
/>
<Button android:id="@+id/next_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentRight="true"
android:drawableRight="@drawable/ic_btn_next"
android:drawablePadding="10dip"
android:text="@string/lockpassword_continue_label"
/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.widget.LinearLayoutWithDefaultTouchRecepient
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topLayout"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/black">
<TextView android:id="@+id/headerText"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gravity="center"
android:textSize="18sp"/>
<View
android:background="@*android:drawable/code_lock_top"
android:layout_width="match_parent"
android:layout_height="2dip" />
<com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<View
android:background="@*android:drawable/code_lock_bottom"
android:layout_width="match_parent"
android:layout_height="8dip" />
<TextView android:id="@+id/footerText"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gravity="center"
android:textSize="14sp"/>
</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>

View File

@@ -18,6 +18,7 @@ package com.android.settings;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -28,179 +29,190 @@ import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
public class ChooseLockGeneric extends PreferenceActivity { public class ChooseLockGeneric extends PreferenceActivity {
private static final int MIN_PASSWORD_LENGTH = 4;
private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off";
private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none";
private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin";
private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password";
private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern";
private static final int CONFIRM_EXISTING_REQUEST = 100;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private DevicePolicyManager mDPM;
private boolean mPasswordConfirmed = false;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public Intent getIntent() {
super.onCreate(savedInstanceState); Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockGenericFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this); private static final int MIN_PASSWORD_LENGTH = 4;
private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off";
private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none";
private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin";
private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password";
private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern";
private static final int CONFIRM_EXISTING_REQUEST = 100;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
if (savedInstanceState != null) { private ChooseLockSettingsHelper mChooseLockSettingsHelper;
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); private DevicePolicyManager mDPM;
} private boolean mPasswordConfirmed = false;
if (!mPasswordConfirmed) { @Override
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this); public void onCreate(Bundle savedInstanceState) {
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) { super.onCreate(savedInstanceState);
mPasswordConfirmed = true; // no password set, so no need to confirm
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
if (savedInstanceState != null) {
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
}
if (!mPasswordConfirmed) {
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this);
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
mPasswordConfirmed = true; // no password set, so no need to confirm
updatePreferencesOrFinish();
}
} else {
updatePreferencesOrFinish(); updatePreferencesOrFinish();
} }
} else {
updatePreferencesOrFinish();
} }
}
@Override @Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) { Preference preference) {
final String key = preference.getKey(); final String key = preference.getKey();
boolean handled = true; boolean handled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) { if (KEY_UNLOCK_SET_OFF.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true); updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true);
} else if (KEY_UNLOCK_SET_NONE.equals(key)) { } else if (KEY_UNLOCK_SET_NONE.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false); updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false);
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) { } else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false); updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
} else if (KEY_UNLOCK_SET_PIN.equals(key)) { } else if (KEY_UNLOCK_SET_PIN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false); updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) { } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false); updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
} else { } else {
handled = false; handled = false;
}
return handled;
} }
return handled;
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == RESULT_OK) { if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
mPasswordConfirmed = true; mPasswordConfirmed = true;
updatePreferencesOrFinish(); updatePreferencesOrFinish();
} else { } else {
setResult(RESULT_CANCELED); getActivity().setResult(Activity.RESULT_CANCELED);
finish();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Saved so we don't force user to re-enter their password if configuration changes
outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
}
private void updatePreferencesOrFinish() {
int quality = getActivity().getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality == -1) {
// If caller didn't specify password quality, show the UI and allow the user to choose.
quality = mChooseLockSettingsHelper.utils().getKeyguardStoredPasswordQuality();
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
}
addPreferencesFromResource(R.xml.security_settings_picker);
disableUnusablePreferences(mDPM.getPasswordQuality(null));
} else {
updateUnlockMethodAndFinish(quality, false);
}
}
/***
* Disables preferences that are less secure than required quality.
*
* @param quality the requested quality.
*/
private void disableUnusablePreferences(final int quality) {
final Preference picker = getPreferenceScreen().findPreference("security_picker_category");
final PreferenceCategory cat = (PreferenceCategory) picker;
final int preferenceCount = cat.getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
Preference pref = cat.getPreference(i);
if (pref instanceof PreferenceScreen) {
final String key = ((PreferenceScreen) pref).getKey();
boolean enabled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
}
if (!enabled) {
pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
pref.setEnabled(false);
}
}
}
}
/**
* Invokes an activity to change the user's pattern, password or PIN based on given quality
* and minimum quality specified by DevicePolicyManager. If quality is
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
*
* @param quality the desired quality. Ignored if DevicePolicyManager requires more security.
* @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
*/
void updateUnlockMethodAndFinish(int quality, boolean disabled) {
// Sanity check. We should never get here without confirming user's existing password first.
if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming first");
}
// Compare minimum allowed password quality and launch appropriate security setting method
int minQuality = mDPM.getPasswordQuality(null);
if (quality < minQuality) {
quality = minQuality;
}
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
minLength = MIN_PASSWORD_LENGTH;
}
final int maxLength = mDPM.getPasswordMaximumLength(quality);
Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class);
intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
boolean showTutorial = !mChooseLockSettingsHelper.utils().isPatternEverChosen();
Intent intent = new Intent();
intent.setClass(getActivity(), showTutorial
? ChooseLockPatternTutorial.class
: ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("key_lock_method", "pattern");
intent.putExtra(CONFIRM_CREDENTIALS, false);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
mChooseLockSettingsHelper.utils().clearLock();
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
getActivity().setResult(Activity.RESULT_OK);
}
finish(); finish();
} }
} }
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Saved so we don't force user to re-enter their password if configuration changes
outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
}
private void updatePreferencesOrFinish() {
int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality == -1) {
// If caller didn't specify password quality, show the UI and allow the user to choose.
quality = mChooseLockSettingsHelper.utils().getKeyguardStoredPasswordQuality();
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
}
addPreferencesFromResource(R.xml.security_settings_picker);
disableUnusablePreferences(mDPM.getPasswordQuality(null));
} else {
updateUnlockMethodAndFinish(quality, false);
}
}
/***
* Disables preferences that are less secure than required quality.
*
* @param quality the requested quality.
*/
private void disableUnusablePreferences(final int quality) {
final Preference picker = getPreferenceScreen().findPreference("security_picker_category");
final PreferenceCategory cat = (PreferenceCategory) picker;
final int preferenceCount = cat.getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
Preference pref = cat.getPreference(i);
if (pref instanceof PreferenceScreen) {
final String key = ((PreferenceScreen) pref).getKey();
boolean enabled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
}
if (!enabled) {
pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
pref.setEnabled(false);
}
}
}
}
/**
* Invokes an activity to change the user's pattern, password or PIN based on given quality
* and minimum quality specified by DevicePolicyManager. If quality is
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
*
* @param quality the desired quality. Ignored if DevicePolicyManager requires more security.
* @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
*/
void updateUnlockMethodAndFinish(int quality, boolean disabled) {
// Sanity check. We should never get here without confirming user's existing password first.
if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming first");
}
// Compare minimum allowed password quality and launch appropriate security setting method
int minQuality = mDPM.getPasswordQuality(null);
if (quality < minQuality) {
quality = minQuality;
}
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
minLength = MIN_PASSWORD_LENGTH;
}
final int maxLength = mDPM.getPasswordMaximumLength(quality);
Intent intent = new Intent().setClass(this, ChooseLockPassword.class);
intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
boolean showTutorial = !mChooseLockSettingsHelper.utils().isPatternEverChosen();
Intent intent = new Intent();
intent.setClass(this, showTutorial
? ChooseLockPatternTutorial.class
: ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("key_lock_method", "pattern");
intent.putExtra(CONFIRM_CREDENTIALS, false);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
mChooseLockSettingsHelper.utils().clearLock();
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
setResult(RESULT_OK);
}
finish();
}
} }

View File

@@ -21,19 +21,22 @@ import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView; import com.android.internal.widget.PasswordEntryKeyboardView;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Intent; import android.content.Intent;
import android.inputmethodservice.KeyboardView; import android.inputmethodservice.KeyboardView;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceActivity;
import android.text.Editable; import android.text.Editable;
import android.text.Selection; import android.text.Selection;
import android.text.Spannable; import android.text.Spannable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.ViewGroup;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.Button; import android.widget.Button;
@@ -41,30 +44,7 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
public class ChooseLockPassword extends Activity implements OnClickListener, OnEditorActionListener, public class ChooseLockPassword extends PreferenceActivity {
TextWatcher {
private static final String KEY_FIRST_PIN = "first_pin";
private static final String KEY_UI_STAGE = "ui_stage";
private TextView mPasswordEntry;
private int mPasswordMinLength = 4;
private int mPasswordMaxLength = 16;
private int mPasswordMinLetters = 0;
private int mPasswordMinUpperCase = 0;
private int mPasswordMinLowerCase = 0;
private int mPasswordMinSymbols = 0;
private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0;
private LockPatternUtils mLockPatternUtils;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private com.android.settings.ChooseLockPassword.Stage mUiStage = Stage.Introduction;
private TextView mHeaderText;
private String mFirstPin;
private KeyboardView mKeyboardView;
private PasswordEntryKeyboardHelper mKeyboardHelper;
private boolean mIsAlphaMode;
private Button mCancelButton;
private Button mNextButton;
public static final String PASSWORD_MIN_KEY = "lockscreen.password_min"; public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
public static final String PASSWORD_MAX_KEY = "lockscreen.password_max"; public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters"; public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
@@ -73,345 +53,393 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE
public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric"; public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols"; public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter"; public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
private static Handler mHandler = new Handler();
private static final int CONFIRM_EXISTING_REQUEST = 58;
static final int RESULT_FINISHED = RESULT_FIRST_USER;
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
/** @Override
* Keep track internally of where the user is in choosing a pattern. public Intent getIntent() {
*/ Intent modIntent = new Intent(super.getIntent());
protected enum Stage { modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPasswordFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
Introduction(R.string.lockpassword_choose_your_password_header, @Override
R.string.lockpassword_choose_your_pin_header, public void onCreate(Bundle savedInstanceState) {
R.string.lockpassword_continue_label), // TODO: Fix on phones
// Disable IME on our window since we provide our own keyboard
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
//WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
super.onCreate(savedInstanceState);
}
NeedToConfirm(R.string.lockpassword_confirm_your_password_header, public static class ChooseLockPasswordFragment extends Fragment
R.string.lockpassword_confirm_your_pin_header, implements OnClickListener, OnEditorActionListener, TextWatcher {
R.string.lockpassword_ok_label), private static final String KEY_FIRST_PIN = "first_pin";
private static final String KEY_UI_STAGE = "ui_stage";
ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match, private TextView mPasswordEntry;
R.string.lockpassword_confirm_pins_dont_match, private int mPasswordMinLength = 4;
R.string.lockpassword_continue_label); private int mPasswordMaxLength = 16;
private int mPasswordMinLetters = 0;
private int mPasswordMinUpperCase = 0;
private int mPasswordMinLowerCase = 0;
private int mPasswordMinSymbols = 0;
private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0;
private LockPatternUtils mLockPatternUtils;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private Stage mUiStage = Stage.Introduction;
private TextView mHeaderText;
private String mFirstPin;
private KeyboardView mKeyboardView;
private PasswordEntryKeyboardHelper mKeyboardHelper;
private boolean mIsAlphaMode;
private Button mCancelButton;
private Button mNextButton;
private static Handler mHandler = new Handler();
private static final int CONFIRM_EXISTING_REQUEST = 58;
static final int RESULT_FINISHED = RESULT_FIRST_USER;
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
/** /**
* @param headerMessage The message displayed at the top. * Keep track internally of where the user is in choosing a pattern.
*/ */
Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) { protected enum Stage {
this.alphaHint = hintInAlpha;
this.numericHint = hintInNumeric;
this.buttonText = nextButtonText;
}
public final int alphaHint; Introduction(R.string.lockpassword_choose_your_password_header,
public final int numericHint; R.string.lockpassword_choose_your_pin_header,
public final int buttonText; R.string.lockpassword_continue_label),
}
@Override NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
protected void onCreate(Bundle savedInstanceState) { R.string.lockpassword_confirm_your_pin_header,
super.onCreate(savedInstanceState); R.string.lockpassword_ok_label),
mLockPatternUtils = new LockPatternUtils(this);
mRequestedQuality = Math.max(getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
mPasswordMinLength = Math.max(
getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
.getRequestedMinimumPasswordLength());
mPasswordMaxLength = getIntent().getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
mPasswordMinLetters = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LETTERS_KEY,
mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
mPasswordMinUpperCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
mPasswordMinLowerCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
mPasswordMinNumeric = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
mPasswordMinSymbols = Math.max(getIntent().getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
mPasswordMinNonLetter = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true);
ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
R.string.lockpassword_confirm_pins_dont_match,
R.string.lockpassword_continue_label);
initViews(); /**
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this); * @param headerMessage The message displayed at the top.
if (savedInstanceState == null) { */
updateStage(Stage.Introduction); Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
if (confirmCredentials) { this.alphaHint = hintInAlpha;
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, this.numericHint = hintInNumeric;
null, null); this.buttonText = nextButtonText;
} }
public final int alphaHint;
public final int numericHint;
public final int buttonText;
} }
}
private void initViews() { // required constructor for fragments
setContentView(R.layout.choose_lock_password); public ChooseLockPasswordFragment() {
// Disable IME on our window since we provide our own keyboard
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
mCancelButton = (Button) findViewById(R.id.cancel_button); }
mCancelButton.setOnClickListener(this);
mNextButton = (Button) findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); @Override
mPasswordEntry = (TextView) findViewById(R.id.password_entry); public void onCreate(Bundle savedInstanceState) {
mPasswordEntry.setOnEditorActionListener(this); super.onCreate(savedInstanceState);
mPasswordEntry.addTextChangedListener(this); mLockPatternUtils = new LockPatternUtils(getActivity());
Intent intent = getActivity().getIntent();
mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
mPasswordMinLength = Math.max(
intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
.getRequestedMinimumPasswordLength());
mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality }
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mHeaderText = (TextView) findViewById(R.id.headerText); @Override
mKeyboardView.requestFocus(); public View onCreateView(LayoutInflater inflater, ViewGroup container,
} Bundle savedInstanceState) {
@Override View view = inflater.inflate(R.layout.choose_lock_password, null);
protected void onResume() {
super.onResume();
updateStage(mUiStage);
mKeyboardView.requestFocus();
}
@Override mCancelButton = (Button) view.findViewById(R.id.cancel_button);
protected void onSaveInstanceState(Bundle outState) { mCancelButton.setOnClickListener(this);
super.onSaveInstanceState(outState); mNextButton = (Button) view.findViewById(R.id.next_button);
outState.putString(KEY_UI_STAGE, mUiStage.name()); mNextButton.setOnClickListener(this);
outState.putString(KEY_FIRST_PIN, mFirstPin);
}
@Override mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
protected void onRestoreInstanceState(Bundle savedInstanceState) { || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
super.onRestoreInstanceState(savedInstanceState); || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
String state = savedInstanceState.getString(KEY_UI_STAGE); mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN); mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
if (state != null) { mPasswordEntry.setOnEditorActionListener(this);
mUiStage = Stage.valueOf(state); mPasswordEntry.addTextChangedListener(this);
mKeyboardHelper = new PasswordEntryKeyboardHelper(getActivity(),
mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
mKeyboardView.requestFocus();
Intent intent = getActivity().getIntent();
final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
}
} else {
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
final String state = savedInstanceState.getString(KEY_UI_STAGE);
if (state != null) {
mUiStage = Stage.valueOf(state);
updateStage(mUiStage);
}
}
return view;
}
@Override
public void onResume() {
super.onResume();
updateStage(mUiStage); updateStage(mUiStage);
mKeyboardView.requestFocus();
} }
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, public void onSaveInstanceState(Bundle outState) {
Intent data) { super.onSaveInstanceState(outState);
super.onActivityResult(requestCode, resultCode, data); outState.putString(KEY_UI_STAGE, mUiStage.name());
switch (requestCode) { outState.putString(KEY_FIRST_PIN, mFirstPin);
case CONFIRM_EXISTING_REQUEST:
if (resultCode != Activity.RESULT_OK) {
setResult(RESULT_FINISHED);
finish();
}
break;
} }
}
protected void updateStage(Stage stage) { @Override
mUiStage = stage; public void onActivityResult(int requestCode, int resultCode,
updateUi(); Intent data) {
} super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
/** case CONFIRM_EXISTING_REQUEST:
* Validates PIN and returns a message to display if PIN fails test. if (resultCode != Activity.RESULT_OK) {
* @param password the raw password the user typed in getActivity().setResult(RESULT_FINISHED);
* @return error message to show to user or null if password is OK getActivity().finish();
*/ }
private String validatePassword(String password) { break;
if (password.length() < mPasswordMinLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength);
}
if (password.length() > mPasswordMaxLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_long
: R.string.lockpassword_pin_too_long, mPasswordMaxLength);
}
int letters = 0;
int numbers = 0;
int lowercase = 0;
int symbols = 0;
int uppercase = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
// allow non white space Latin-1 characters only
if (c <= 32 || c > 127) {
return getString(R.string.lockpassword_illegal_character);
}
if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else {
symbols++;
nonletter++;
} }
} }
if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
&& (letters > 0 || symbols > 0)) {
// This shouldn't be possible unless user finds some way to bring up
// soft keyboard
return getString(R.string.lockpassword_pin_contains_non_digits);
} else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
if (letters < mPasswordMinLetters) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
mPasswordMinLetters);
} else if (numbers < mPasswordMinNumeric) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
mPasswordMinNumeric);
} else if (lowercase < mPasswordMinLowerCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
mPasswordMinLowerCase);
} else if (uppercase < mPasswordMinUpperCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
mPasswordMinUpperCase);
} else if (symbols < mPasswordMinSymbols) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
mPasswordMinSymbols);
} else if (nonletter < mPasswordMinNonLetter) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
mPasswordMinNonLetter);
}
} else {
final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
== mRequestedQuality;
final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
== mRequestedQuality;
if ((alphabetic || alphanumeric) && letters == 0) {
return getString(R.string.lockpassword_password_requires_alpha);
}
if (alphanumeric && numbers == 0) {
return getString(R.string.lockpassword_password_requires_digit);
}
}
if(mLockPatternUtils.checkPasswordHistory(password)) {
return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
: R.string.lockpassword_pin_recently_used);
}
return null;
}
private void handleNext() { protected void updateStage(Stage stage) {
final String pin = mPasswordEntry.getText().toString(); mUiStage = stage;
if (TextUtils.isEmpty(pin)) { updateUi();
return;
} }
String errorMsg = null;
if (mUiStage == Stage.Introduction) {
errorMsg = validatePassword(pin);
if (errorMsg == null) {
mFirstPin = pin;
updateStage(Stage.NeedToConfirm);
mPasswordEntry.setText("");
}
} else if (mUiStage == Stage.NeedToConfirm) {
if (mFirstPin.equals(pin)) {
mLockPatternUtils.clearLock();
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality);
finish();
} else {
updateStage(Stage.ConfirmWrong);
CharSequence tmp = mPasswordEntry.getText();
if (tmp != null) {
Selection.setSelection((Spannable) tmp, 0, tmp.length());
}
}
}
if (errorMsg != null) {
showError(errorMsg, mUiStage);
}
}
public void onClick(View v) { /**
switch (v.getId()) { * Validates PIN and returns a message to display if PIN fails test.
case R.id.next_button: * @param password the raw password the user typed in
handleNext(); * @return error message to show to user or null if password is OK
break; */
private String validatePassword(String password) {
case R.id.cancel_button: if (password.length() < mPasswordMinLength) {
finish(); return getString(mIsAlphaMode ?
break; R.string.lockpassword_password_too_short
}
}
private void showError(String msg, final Stage next) {
mHeaderText.setText(msg);
mHandler.postDelayed(new Runnable() {
public void run() {
updateStage(next);
}
}, ERROR_MESSAGE_TIMEOUT);
}
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 && event.getAction() == KeyEvent.ACTION_DOWN) {
handleNext();
return true;
}
return false;
}
/**
* Update the hint based on current Stage and length of password entry
*/
private void updateUi() {
String password = mPasswordEntry.getText().toString();
final int length = password.length();
if (mUiStage == Stage.Introduction && length > 0) {
if (length < mPasswordMinLength) {
String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength); : R.string.lockpassword_pin_too_short, mPasswordMinLength);
mHeaderText.setText(msg); }
mNextButton.setEnabled(false); if (password.length() > mPasswordMaxLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_long
: R.string.lockpassword_pin_too_long, mPasswordMaxLength);
}
int letters = 0;
int numbers = 0;
int lowercase = 0;
int symbols = 0;
int uppercase = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
// allow non white space Latin-1 characters only
if (c <= 32 || c > 127) {
return getString(R.string.lockpassword_illegal_character);
}
if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else {
symbols++;
nonletter++;
}
}
if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
&& (letters > 0 || symbols > 0)) {
// This shouldn't be possible unless user finds some way to bring up
// soft keyboard
return getString(R.string.lockpassword_pin_contains_non_digits);
} else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
if (letters < mPasswordMinLetters) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
mPasswordMinLetters);
} else if (numbers < mPasswordMinNumeric) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
mPasswordMinNumeric);
} else if (lowercase < mPasswordMinLowerCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
mPasswordMinLowerCase);
} else if (uppercase < mPasswordMinUpperCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
mPasswordMinUpperCase);
} else if (symbols < mPasswordMinSymbols) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
mPasswordMinSymbols);
} else if (nonletter < mPasswordMinNonLetter) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
mPasswordMinNonLetter);
}
} else { } else {
String error = validatePassword(password); final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
if (error != null) { == mRequestedQuality;
mHeaderText.setText(error); final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
== mRequestedQuality;
if ((alphabetic || alphanumeric) && letters == 0) {
return getString(R.string.lockpassword_password_requires_alpha);
}
if (alphanumeric && numbers == 0) {
return getString(R.string.lockpassword_password_requires_digit);
}
}
if(mLockPatternUtils.checkPasswordHistory(password)) {
return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
: R.string.lockpassword_pin_recently_used);
}
return null;
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (TextUtils.isEmpty(pin)) {
return;
}
String errorMsg = null;
if (mUiStage == Stage.Introduction) {
errorMsg = validatePassword(pin);
if (errorMsg == null) {
mFirstPin = pin;
updateStage(Stage.NeedToConfirm);
mPasswordEntry.setText("");
}
} else if (mUiStage == Stage.NeedToConfirm) {
if (mFirstPin.equals(pin)) {
mLockPatternUtils.clearLock();
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality);
getActivity().finish();
} else {
updateStage(Stage.ConfirmWrong);
CharSequence tmp = mPasswordEntry.getText();
if (tmp != null) {
Selection.setSelection((Spannable) tmp, 0, tmp.length());
}
}
}
if (errorMsg != null) {
showError(errorMsg, mUiStage);
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
getActivity().finish();
break;
}
}
private void showError(String msg, final Stage next) {
mHeaderText.setText(msg);
mHandler.postDelayed(new Runnable() {
public void run() {
updateStage(next);
}
}, ERROR_MESSAGE_TIMEOUT);
}
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 && event.getAction() == KeyEvent.ACTION_DOWN) {
handleNext();
return true;
}
return false;
}
/**
* Update the hint based on current Stage and length of password entry
*/
private void updateUi() {
String password = mPasswordEntry.getText().toString();
final int length = password.length();
if (mUiStage == Stage.Introduction && length > 0) {
if (length < mPasswordMinLength) {
String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength);
mHeaderText.setText(msg);
mNextButton.setEnabled(false); mNextButton.setEnabled(false);
} else { } else {
mHeaderText.setText(R.string.lockpassword_press_continue); String error = validatePassword(password);
mNextButton.setEnabled(true); if (error != null) {
mHeaderText.setText(error);
mNextButton.setEnabled(false);
} else {
mHeaderText.setText(R.string.lockpassword_press_continue);
mNextButton.setEnabled(true);
}
} }
} else {
mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
mNextButton.setEnabled(length > 0);
} }
} else { mNextButton.setText(mUiStage.buttonText);
mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
mNextButton.setEnabled(length > 0);
} }
mNextButton.setText(mUiStage.buttonText);
}
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
// Changing the text while error displayed resets to NeedToConfirm state // Changing the text while error displayed resets to NeedToConfirm state
if (mUiStage == Stage.ConfirmWrong) { if (mUiStage == Stage.ConfirmWrong) {
mUiStage = Stage.NeedToConfirm; mUiStage = Stage.NeedToConfirm;
}
updateUi();
} }
updateUi();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
}
} }
} }

View File

@@ -22,14 +22,18 @@ import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockPatternView.Cell;
import static com.android.internal.widget.LockPatternView.DisplayMode; import static com.android.internal.widget.LockPatternView.DisplayMode;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.Window; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList; import java.util.ArrayList;
@@ -44,7 +48,7 @@ import java.util.List;
* - asks for confirmation / restart * - asks for confirmation / restart
* - saves chosen password when confirmed * - saves chosen password when confirmed
*/ */
public class ChooseLockPattern extends Activity implements View.OnClickListener{ public class ChooseLockPattern extends PreferenceActivity {
/** /**
* Used by the choose lock pattern wizard to indicate the wizard is * Used by the choose lock pattern wizard to indicate the wizard is
* finished, and each activity in the wizard should finish. * finished, and each activity in the wizard should finish.
@@ -56,440 +60,461 @@ public class ChooseLockPattern extends Activity implements View.OnClickListener{
*/ */
static final int RESULT_FINISHED = RESULT_FIRST_USER; static final int RESULT_FINISHED = RESULT_FIRST_USER;
public static final int CONFIRM_EXISTING_REQUEST = 55; @Override
public Intent getIntent() {
// how long after a confirmation message is shown before moving on Intent modIntent = new Intent(super.getIntent());
static final int INFORMATION_MSG_TIMEOUT_MS = 3000; modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
// how long we wait to clear a wrong pattern return modIntent;
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000; }
private static final int ID_EMPTY_MESSAGE = -1;
protected TextView mHeaderText;
protected LockPatternView mLockPatternView;
protected TextView mFooterText;
private TextView mFooterLeftButton;
private TextView mFooterRightButton;
protected List<LockPatternView.Cell> mChosenPattern = null;
/**
* The patten used during the help screen to show how to draw a pattern.
*/
private final List<LockPatternView.Cell> mAnimatePattern =
Collections.unmodifiableList(Lists.newArrayList(
LockPatternView.Cell.of(0, 0),
LockPatternView.Cell.of(0, 1),
LockPatternView.Cell.of(1, 1),
LockPatternView.Cell.of(2, 1)
));
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, public void onCreate(Bundle savedInstanceState) {
Intent data) { // requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CONFIRM_EXISTING_REQUEST:
if (resultCode != Activity.RESULT_OK) {
setResult(RESULT_FINISHED);
finish();
}
updateStage(Stage.Introduction);
break;
}
}
/**
* The pattern listener that responds according to a user choosing a new
* lock pattern.
*/
protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
patternInProgress();
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
if (mChosenPattern == null) throw new IllegalStateException("null chosen pattern in stage 'need to confirm");
if (mChosenPattern.equals(pattern)) {
updateStage(Stage.ChoiceConfirmed);
} else {
updateStage(Stage.ConfirmWrong);
}
} else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
updateStage(Stage.ChoiceTooShort);
} else {
mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
updateStage(Stage.FirstChoiceValid);
}
} else {
throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
+ "entering the pattern.");
}
}
public void onPatternCellAdded(List<Cell> pattern) {
}
private void patternInProgress() {
mHeaderText.setText(R.string.lockpattern_recording_inprogress);
mFooterText.setText("");
mFooterLeftButton.setEnabled(false);
mFooterRightButton.setEnabled(false);
}
};
/**
* The states of the left footer button.
*/
enum LeftButtonMode {
Cancel(R.string.cancel, true),
CancelDisabled(R.string.cancel, false),
Retry(R.string.lockpattern_retry_button_text, true),
RetryDisabled(R.string.lockpattern_retry_button_text, false),
Gone(ID_EMPTY_MESSAGE, false);
/**
* @param text The displayed text for this mode.
* @param enabled Whether the button should be enabled.
*/
LeftButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
}
/**
* The states of the right button.
*/
enum RightButtonMode {
Continue(R.string.lockpattern_continue_button_text, true),
ContinueDisabled(R.string.lockpattern_continue_button_text, false),
Confirm(R.string.lockpattern_confirm_button_text, true),
ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
Ok(android.R.string.ok, true);
/**
* @param text The displayed text for this mode.
* @param enabled Whether the button should be enabled.
*/
RightButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
}
/**
* Keep track internally of where the user is in choosing a pattern.
*/
protected enum Stage {
Introduction(
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
R.string.lockpattern_recording_intro_footer, true),
HelpScreen(
R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
R.string.lockpattern_need_to_confirm,
LeftButtonMode.CancelDisabled, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
R.string.lockpattern_need_to_unlock_wrong,
LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ChoiceConfirmed(
R.string.lockpattern_pattern_confirmed_header,
LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
/**
* @param headerMessage The message displayed at the top.
* @param leftMode The mode of the left button.
* @param rightMode The mode of the right button.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
Stage(int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.leftMode = leftMode;
this.rightMode = rightMode;
this.footerMessage = footerMessage;
this.patternEnabled = patternEnabled;
}
final int headerMessage;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
final int footerMessage;
final boolean patternEnabled;
}
private Stage mUiStage = Stage.Introduction;
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setupViews();
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
if (confirmCredentials) {
// first launch. As a security measure, we're in NeedToConfirm mode until we know
// there isn't an existing password or the user confirms their password.
updateStage(Stage.NeedToConfirm);
boolean launchedConfirmationActivity =
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
if (!launchedConfirmationActivity) {
updateStage(Stage.Introduction);
}
} else {
updateStage(Stage.Introduction);
}
} else {
// restore from previous state
final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE);
if (patternString != null) {
mChosenPattern = LockPatternUtils.stringToPattern(patternString);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
}
}
/**
* Keep all "find view" related stuff confined to this function since in
* case someone needs to subclass and customize.
*/
protected void setupViews() {
setContentView(R.layout.choose_lock_pattern);
mHeaderText = (TextView) findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
mLockPatternView.setTactileFeedbackEnabled(
mChooseLockSettingsHelper.utils().isTactileFeedbackEnabled());
mFooterText = (TextView) findViewById(R.id.footerText);
mFooterLeftButton = (TextView) findViewById(R.id.footerLeftButton);
mFooterRightButton = (TextView) findViewById(R.id.footerRightButton);
mFooterLeftButton.setOnClickListener(this);
mFooterRightButton.setOnClickListener(this);
}
public void onClick(View v) {
if (v == mFooterLeftButton) {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
mChosenPattern = null;
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
// They are canceling the entire wizard
setResult(RESULT_FINISHED);
finish();
} else {
throw new IllegalStateException("left footer button pressed, but stage of " +
mUiStage + " doesn't make sense");
}
} else if (v == mFooterRightButton) {
if (mUiStage.rightMode == RightButtonMode.Continue) {
if (mUiStage != Stage.FirstChoiceValid) {
throw new IllegalStateException("expected ui stage " + Stage.FirstChoiceValid
+ " when button is " + RightButtonMode.Continue);
}
updateStage(Stage.NeedToConfirm);
} else if (mUiStage.rightMode == RightButtonMode.Confirm) {
if (mUiStage != Stage.ChoiceConfirmed) {
throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+ " when button is " + RightButtonMode.Confirm);
}
saveChosenPatternAndFinish();
} else if (mUiStage.rightMode == RightButtonMode.Ok) {
if (mUiStage != Stage.HelpScreen) {
throw new IllegalStateException("Help screen is only mode with ok button, but " +
"stage is " + mUiStage);
}
mLockPatternView.clearPattern();
mLockPatternView.setDisplayMode(DisplayMode.Correct);
updateStage(Stage.Introduction);
}
}
} }
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { // *** TODO ***
if (mUiStage == Stage.HelpScreen) { // chooseLockPatternFragment.onKeyDown(keyCode, event);
updateStage(Stage.Introduction);
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
updateStage(Stage.HelpScreen);
return true;
}
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
} }
@Override public static class ChooseLockPatternFragment extends Fragment
protected void onSaveInstanceState(Bundle outState) { implements View.OnClickListener {
super.onSaveInstanceState(outState);
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal()); public static final int CONFIRM_EXISTING_REQUEST = 55;
if (mChosenPattern != null) {
outState.putString(KEY_PATTERN_CHOICE, LockPatternUtils.patternToString(mChosenPattern));
}
}
// how long after a confirmation message is shown before moving on
static final int INFORMATION_MSG_TIMEOUT_MS = 3000;
/** // how long we wait to clear a wrong pattern
* Updates the messages and buttons appropriate to what stage the user private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
* is at in choosing a view. This doesn't handle clearing out the pattern;
* the pattern is expected to be in the right state.
* @param stage
*/
protected void updateStage(Stage stage) {
mUiStage = stage; private static final int ID_EMPTY_MESSAGE = -1;
// header text, footer text, visibility and protected TextView mHeaderText;
// enabled state all known from the stage protected LockPatternView mLockPatternView;
if (stage == Stage.ChoiceTooShort) { protected TextView mFooterText;
mHeaderText.setText( private TextView mFooterLeftButton;
getResources().getString( private TextView mFooterRightButton;
stage.headerMessage, protected List<LockPatternView.Cell> mChosenPattern = null;
LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
} else { /**
mHeaderText.setText(stage.headerMessage); * The patten used during the help screen to show how to draw a pattern.
} */
if (stage.footerMessage == ID_EMPTY_MESSAGE) { private final List<LockPatternView.Cell> mAnimatePattern =
mFooterText.setText(""); Collections.unmodifiableList(Lists.newArrayList(
} else { LockPatternView.Cell.of(0, 0),
mFooterText.setText(stage.footerMessage); LockPatternView.Cell.of(0, 1),
LockPatternView.Cell.of(1, 1),
LockPatternView.Cell.of(2, 1)
));
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CONFIRM_EXISTING_REQUEST:
if (resultCode != Activity.RESULT_OK) {
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
}
updateStage(Stage.Introduction);
break;
}
} }
if (stage.leftMode == LeftButtonMode.Gone) { /**
mFooterLeftButton.setVisibility(View.GONE); * The pattern listener that responds according to a user choosing a new
} else { * lock pattern.
mFooterLeftButton.setVisibility(View.VISIBLE); */
mFooterLeftButton.setText(stage.leftMode.text); protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
mFooterLeftButton.setEnabled(stage.leftMode.enabled); new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
patternInProgress();
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
if (mChosenPattern == null) throw new IllegalStateException(
"null chosen pattern in stage 'need to confirm");
if (mChosenPattern.equals(pattern)) {
updateStage(Stage.ChoiceConfirmed);
} else {
updateStage(Stage.ConfirmWrong);
}
} else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
updateStage(Stage.ChoiceTooShort);
} else {
mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
updateStage(Stage.FirstChoiceValid);
}
} else {
throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
+ "entering the pattern.");
}
}
public void onPatternCellAdded(List<Cell> pattern) {
}
private void patternInProgress() {
mHeaderText.setText(R.string.lockpattern_recording_inprogress);
mFooterText.setText("");
mFooterLeftButton.setEnabled(false);
mFooterRightButton.setEnabled(false);
}
};
/**
* The states of the left footer button.
*/
enum LeftButtonMode {
Cancel(R.string.cancel, true),
CancelDisabled(R.string.cancel, false),
Retry(R.string.lockpattern_retry_button_text, true),
RetryDisabled(R.string.lockpattern_retry_button_text, false),
Gone(ID_EMPTY_MESSAGE, false);
/**
* @param text The displayed text for this mode.
* @param enabled Whether the button should be enabled.
*/
LeftButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
} }
mFooterRightButton.setText(stage.rightMode.text); /**
mFooterRightButton.setEnabled(stage.rightMode.enabled); * The states of the right button.
*/
enum RightButtonMode {
Continue(R.string.lockpattern_continue_button_text, true),
ContinueDisabled(R.string.lockpattern_continue_button_text, false),
Confirm(R.string.lockpattern_confirm_button_text, true),
ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
Ok(android.R.string.ok, true);
// same for whether the patten is enabled /**
if (stage.patternEnabled) { * @param text The displayed text for this mode.
mLockPatternView.enableInput(); * @param enabled Whether the button should be enabled.
} else { */
mLockPatternView.disableInput(); RightButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
} }
// the rest of the stuff varies enough that it is easier just to handle /**
// on a case by case basis. * Keep track internally of where the user is in choosing a pattern.
mLockPatternView.setDisplayMode(DisplayMode.Correct); */
protected enum Stage {
switch (mUiStage) { Introduction(
case Introduction: R.string.lockpattern_recording_intro_header,
LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
R.string.lockpattern_recording_intro_footer, true),
HelpScreen(
R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
R.string.lockpattern_need_to_confirm,
LeftButtonMode.CancelDisabled, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
R.string.lockpattern_need_to_unlock_wrong,
LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ChoiceConfirmed(
R.string.lockpattern_pattern_confirmed_header,
LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
/**
* @param headerMessage The message displayed at the top.
* @param leftMode The mode of the left button.
* @param rightMode The mode of the right button.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
Stage(int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.leftMode = leftMode;
this.rightMode = rightMode;
this.footerMessage = footerMessage;
this.patternEnabled = patternEnabled;
}
final int headerMessage;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
final int footerMessage;
final boolean patternEnabled;
}
private Stage mUiStage = Stage.Introduction;
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern(); mLockPatternView.clearPattern();
break; }
case HelpScreen: };
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
break;
case ChoiceTooShort:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case FirstChoiceValid:
break;
case NeedToConfirm:
mLockPatternView.clearPattern();
break;
case ConfirmWrong:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case ChoiceConfirmed:
break;
}
}
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
// clear the wrong pattern unless they have started a new one private static final String KEY_UI_STAGE = "uiStage";
// already private static final String KEY_PATTERN_CHOICE = "chosenPattern";
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
private void saveChosenPatternAndFinish() { @Override
LockPatternUtils utils = mChooseLockSettingsHelper.utils(); public void onCreate(Bundle savedInstanceState) {
final boolean lockVirgin = !utils.isPatternEverChosen(); super.onCreate(savedInstanceState);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
utils.saveLockPattern(mChosenPattern);
utils.setLockPatternEnabled(true);
if (lockVirgin) {
utils.setVisiblePatternEnabled(true);
utils.setTactileFeedbackEnabled(false);
} }
setResult(RESULT_FINISHED); @Override
finish(); public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// setupViews()
View view = inflater.inflate(R.layout.choose_lock_pattern, null);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
mLockPatternView.setTactileFeedbackEnabled(
mChooseLockSettingsHelper.utils().isTactileFeedbackEnabled());
mFooterText = (TextView) view.findViewById(R.id.footerText);
mFooterLeftButton = (TextView) view.findViewById(R.id.footerLeftButton);
mFooterRightButton = (TextView) view.findViewById(R.id.footerRightButton);
mFooterLeftButton.setOnClickListener(this);
mFooterRightButton.setOnClickListener(this);
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) view.findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
final boolean confirmCredentials = getActivity().getIntent()
.getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
if (confirmCredentials) {
// first launch. As a security measure, we're in NeedToConfirm mode until we
// know there isn't an existing password or the user confirms their password.
updateStage(Stage.NeedToConfirm);
boolean launchedConfirmationActivity =
mChooseLockSettingsHelper.launchConfirmationActivity(
CONFIRM_EXISTING_REQUEST, null, null);
if (!launchedConfirmationActivity) {
updateStage(Stage.Introduction);
}
} else {
updateStage(Stage.Introduction);
}
} else {
// restore from previous state
final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE);
if (patternString != null) {
mChosenPattern = LockPatternUtils.stringToPattern(patternString);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
}
return view;
}
public void onClick(View v) {
if (v == mFooterLeftButton) {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
mChosenPattern = null;
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
// They are canceling the entire wizard
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
} else {
throw new IllegalStateException("left footer button pressed, but stage of " +
mUiStage + " doesn't make sense");
}
} else if (v == mFooterRightButton) {
if (mUiStage.rightMode == RightButtonMode.Continue) {
if (mUiStage != Stage.FirstChoiceValid) {
throw new IllegalStateException("expected ui stage " + Stage.FirstChoiceValid
+ " when button is " + RightButtonMode.Continue);
}
updateStage(Stage.NeedToConfirm);
} else if (mUiStage.rightMode == RightButtonMode.Confirm) {
if (mUiStage != Stage.ChoiceConfirmed) {
throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+ " when button is " + RightButtonMode.Confirm);
}
saveChosenPatternAndFinish();
} else if (mUiStage.rightMode == RightButtonMode.Ok) {
if (mUiStage != Stage.HelpScreen) {
throw new IllegalStateException("Help screen is only mode with ok button, but " +
"stage is " + mUiStage);
}
mLockPatternView.clearPattern();
mLockPatternView.setDisplayMode(DisplayMode.Correct);
updateStage(Stage.Introduction);
}
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
if (mUiStage == Stage.HelpScreen) {
updateStage(Stage.Introduction);
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
updateStage(Stage.HelpScreen);
return true;
}
return false;
}
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
if (mChosenPattern != null) {
outState.putString(KEY_PATTERN_CHOICE,
LockPatternUtils.patternToString(mChosenPattern));
}
}
/**
* Updates the messages and buttons appropriate to what stage the user
* is at in choosing a view. This doesn't handle clearing out the pattern;
* the pattern is expected to be in the right state.
* @param stage
*/
protected void updateStage(Stage stage) {
mUiStage = stage;
// header text, footer text, visibility and
// enabled state all known from the stage
if (stage == Stage.ChoiceTooShort) {
mHeaderText.setText(
getResources().getString(
stage.headerMessage,
LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
} else {
mHeaderText.setText(stage.headerMessage);
}
if (stage.footerMessage == ID_EMPTY_MESSAGE) {
mFooterText.setText("");
} else {
mFooterText.setText(stage.footerMessage);
}
if (stage.leftMode == LeftButtonMode.Gone) {
mFooterLeftButton.setVisibility(View.GONE);
} else {
mFooterLeftButton.setVisibility(View.VISIBLE);
mFooterLeftButton.setText(stage.leftMode.text);
mFooterLeftButton.setEnabled(stage.leftMode.enabled);
}
mFooterRightButton.setText(stage.rightMode.text);
mFooterRightButton.setEnabled(stage.rightMode.enabled);
// same for whether the patten is enabled
if (stage.patternEnabled) {
mLockPatternView.enableInput();
} else {
mLockPatternView.disableInput();
}
// the rest of the stuff varies enough that it is easier just to handle
// on a case by case basis.
mLockPatternView.setDisplayMode(DisplayMode.Correct);
switch (mUiStage) {
case Introduction:
mLockPatternView.clearPattern();
break;
case HelpScreen:
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
break;
case ChoiceTooShort:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case FirstChoiceValid:
break;
case NeedToConfirm:
mLockPatternView.clearPattern();
break;
case ConfirmWrong:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case ChoiceConfirmed:
break;
}
}
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
private void saveChosenPatternAndFinish() {
LockPatternUtils utils = mChooseLockSettingsHelper.utils();
final boolean lockVirgin = !utils.isPatternEverChosen();
utils.saveLockPattern(mChosenPattern);
utils.setLockPatternEnabled(true);
if (lockVirgin) {
utils.setVisiblePatternEnabled(true);
utils.setTactileFeedbackEnabled(false);
}
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
}
} }
} }

View File

@@ -16,83 +16,100 @@
package com.android.settings; package com.android.settings;
import android.app.Activity; import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceActivity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
public class ChooseLockPatternExample extends Activity implements View.OnClickListener { public class ChooseLockPatternExample extends PreferenceActivity {
private static final long START_DELAY = 1000;
protected static final String TAG = "Settings"; // required constructor for fragments
private View mNextButton; public ChooseLockPatternExample() {
private View mSkipButton;
private View mImageView; }
private AnimationDrawable mAnimation;
private Handler mHandler = new Handler(); @Override
private Runnable mRunnable = new Runnable() { public Intent getIntent() {
public void run() { Intent modIntent = new Intent(super.getIntent());
startAnimation(mAnimation); modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternExampleFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
public static class ChooseLockPatternExampleFragment extends Fragment
implements View.OnClickListener {
private static final long START_DELAY = 1000;
protected static final String TAG = "Settings";
private View mNextButton;
private View mSkipButton;
private View mImageView;
private AnimationDrawable mAnimation;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
startAnimation(mAnimation);
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.choose_lock_pattern_example, null);
mNextButton = view.findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = view.findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
mImageView = (ImageView) view.findViewById(R.id.lock_anim);
mImageView.setBackgroundResource(R.drawable.lock_anim);
mImageView.setOnClickListener(this);
mAnimation = (AnimationDrawable) mImageView.getBackground();
return view;
} }
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onResume() {
super.onCreate(savedInstanceState); super.onResume();
setContentView(R.layout.choose_lock_pattern_example); mHandler.postDelayed(mRunnable, START_DELAY);
initViews(); }
}
@Override @Override
protected void onResume() { public void onPause() {
super.onResume(); super.onPause();
mHandler.postDelayed(mRunnable, START_DELAY);
}
@Override
protected void onPause() {
super.onPause();
stopAnimation(mAnimation);
}
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED);
finish();
} else if (v == mNextButton) {
stopAnimation(mAnimation); stopAnimation(mAnimation);
Intent intent = new Intent(this, ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
finish();
} }
}
private void initViews() { public void onClick(View v) {
mNextButton = findViewById(R.id.next_button); if (v == mSkipButton) {
mNextButton.setOnClickListener(this); // Canceling, so finish all
getActivity().setResult(ChooseLockPattern.RESULT_FINISHED);
mSkipButton = findViewById(R.id.skip_button); getActivity().finish();
mSkipButton.setOnClickListener(this); } else if (v == mNextButton) {
stopAnimation(mAnimation);
mImageView = (ImageView) findViewById(R.id.lock_anim); Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
mImageView.setBackgroundResource(R.drawable.lock_anim); intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
mImageView.setOnClickListener(this); intent.putExtra("confirm_credentials", false);
mAnimation = (AnimationDrawable) mImageView.getBackground(); startActivity(intent);
} getActivity().finish();
}
protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && !animation.isRunning()) {
animation.run();
} }
}
protected void stopAnimation(final AnimationDrawable animation) { protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && animation.isRunning()) animation.stop(); if (animation != null && !animation.isRunning()) {
animation.run();
}
}
protected void stopAnimation(final AnimationDrawable animation) {
if (animation != null && animation.isRunning()) animation.stop();
}
} }
} }

View File

@@ -18,49 +18,70 @@ package com.android.settings;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import android.app.Activity; import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
public class ChooseLockPatternTutorial extends Activity implements View.OnClickListener { public class ChooseLockPatternTutorial extends PreferenceActivity {
private View mNextButton;
private View mSkipButton; // required constructor for fragments
public ChooseLockPatternTutorial() {
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public Intent getIntent() {
super.onCreate(savedInstanceState); Intent modIntent = new Intent(super.getIntent());
// Don't show the tutorial if the user has seen it before. modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternTutorialFragment.class.getName());
LockPatternUtils lockPatternUtils = new LockPatternUtils(this); modIntent.putExtra(EXTRA_NO_HEADERS, true);
if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) { return modIntent;
Intent intent = new Intent(this, ChooseLockPattern.class); }
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false); public static class ChooseLockPatternTutorialFragment extends Fragment
startActivity(intent); implements View.OnClickListener {
finish(); private View mNextButton;
} else { private View mSkipButton;
initViews();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't show the tutorial if the user has seen it before.
LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) {
Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
getActivity().finish();
}
} }
}
private void initViews() { @Override
setContentView(R.layout.choose_lock_pattern_tutorial); public View onCreateView(LayoutInflater inflater, ViewGroup container,
mNextButton = findViewById(R.id.next_button); Bundle savedInstanceState) {
mNextButton.setOnClickListener(this); View view = inflater.inflate(R.layout.choose_lock_pattern_tutorial, null);
mSkipButton = findViewById(R.id.skip_button); mNextButton = view.findViewById(R.id.next_button);
mSkipButton.setOnClickListener(this); mNextButton.setOnClickListener(this);
} mSkipButton = view.findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
return view;
}
public void onClick(View v) { public void onClick(View v) {
if (v == mSkipButton) { if (v == mSkipButton) {
// Canceling, so finish all // Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED); getActivity().setResult(ChooseLockPattern.RESULT_FINISHED);
finish(); getActivity().finish();
} else if (v == mNextButton) { } else if (v == mNextButton) {
Intent intent = new Intent(this, ChooseLockPatternExample.class); Intent intent = new Intent(getActivity(), ChooseLockPatternExample.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent); startActivity(intent);
finish(); getActivity().finish();
}
} }
} }
} }

View File

@@ -20,113 +20,140 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardHelper; import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView; import com.android.internal.widget.PasswordEntryKeyboardView;
import android.app.Activity; import android.app.Fragment;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Editable; import android.preference.PreferenceActivity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.ViewGroup;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
public class ConfirmLockPassword extends Activity implements OnClickListener, public class ConfirmLockPassword extends PreferenceActivity {
OnEditorActionListener {
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private TextView mPasswordEntry;
private LockPatternUtils mLockPatternUtils;
private TextView mHeaderText;
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public Intent getIntent() {
super.onCreate(savedInstanceState); Intent modIntent = new Intent(super.getIntent());
mLockPatternUtils = new LockPatternUtils(this); modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
initViews(); modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
} }
private void initViews() { @Override
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(); public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.confirm_lock_password);
// Disable IME on our window since we provide our own keyboard // Disable IME on our window since we provide our own keyboard
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
super.onCreate(savedInstanceState);
findViewById(R.id.cancel_button).setOnClickListener(this);
findViewById(R.id.next_button).setOnClickListener(this);
mPasswordEntry = (TextView) findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
mHeaderText = (TextView) findViewById(R.id.headerText);
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header
: R.string.lockpassword_confirm_your_pin_header);
mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.requestFocus();
} }
@Override public static class ConfirmLockPasswordFragment extends Fragment implements OnClickListener,
protected void onPause() { OnEditorActionListener {
super.onPause(); private static final long ERROR_MESSAGE_TIMEOUT = 3000;
mKeyboardView.requestFocus(); private TextView mPasswordEntry;
} private LockPatternUtils mLockPatternUtils;
private TextView mHeaderText;
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mKeyboardView.requestFocus();
}
private void handleNext() { // required constructor for fragments
final String pin = mPasswordEntry.getText().toString(); public ConfirmLockPasswordFragment() {
if (mLockPatternUtils.checkPassword(pin)) {
setResult(RESULT_OK);
finish();
} else {
showError(R.string.lockpattern_need_to_unlock_wrong);
} }
}
public void onClick(View v) { @Override
switch (v.getId()) { public void onCreate(Bundle savedInstanceState) {
case R.id.next_button: super.onCreate(savedInstanceState);
handleNext(); mLockPatternUtils = new LockPatternUtils(getActivity());
break;
case R.id.cancel_button:
setResult(RESULT_CANCELED);
finish();
break;
} }
}
private void showError(int msg) { @Override
mHeaderText.setText(msg); public View onCreateView(LayoutInflater inflater, ViewGroup container,
mPasswordEntry.setText(null); Bundle savedInstanceState) {
mHandler.postDelayed(new Runnable() { final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
public void run() { View view = inflater.inflate(R.layout.confirm_lock_password, null);
mHeaderText.setText(R.string.lockpassword_confirm_your_password_header); // Disable IME on our window since we provide our own keyboard
view.findViewById(R.id.cancel_button).setOnClickListener(this);
view.findViewById(R.id.next_button).setOnClickListener(this);
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header
: R.string.lockpassword_confirm_your_pin_header);
mKeyboardHelper = new PasswordEntryKeyboardHelper(getActivity(),
mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.requestFocus();
return view;
}
@Override
public void onPause() {
super.onPause();
mKeyboardView.requestFocus();
}
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
mKeyboardView.requestFocus();
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(pin)) {
getActivity().setResult(RESULT_OK);
getActivity().finish();
} else {
showError(R.string.lockpattern_need_to_unlock_wrong);
} }
}, ERROR_MESSAGE_TIMEOUT);
}
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) {
handleNext();
return true;
} }
return false;
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
break;
}
}
private void showError(int msg) {
mHeaderText.setText(msg);
mPasswordEntry.setText(null);
mHandler.postDelayed(new Runnable() {
public void run() {
mHeaderText.setText(R.string.lockpassword_confirm_your_password_header);
}
}, ERROR_MESSAGE_TIMEOUT);
}
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) {
handleNext();
return true;
}
return false;
}
} }
} }

View File

@@ -22,12 +22,16 @@ import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockPatternView.Cell;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.widget.TextView; import android.widget.TextView;
import android.view.Window; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List; import java.util.List;
@@ -37,7 +41,7 @@ import java.util.List;
* Sets an activity result of {@link Activity#RESULT_OK} when the user * Sets an activity result of {@link Activity#RESULT_OK} when the user
* successfully confirmed their pattern. * successfully confirmed their pattern.
*/ */
public class ConfirmLockPattern extends Activity { public class ConfirmLockPattern extends PreferenceActivity {
/** /**
* Names of {@link CharSequence} fields within the originating {@link Intent} * Names of {@link CharSequence} fields within the originating {@link Intent}
@@ -45,30 +49,11 @@ public class ConfirmLockPattern extends Activity {
* The view will use the system-defined resource strings for any labels that * The view will use the system-defined resource strings for any labels that
* the caller does not supply. * the caller does not supply.
*/ */
public static final String HEADER_TEXT = "com.android.settings.ConfirmLockPattern.header"; public static final String PACKAGE = "com.android.settings";
public static final String FOOTER_TEXT = "com.android.settings.ConfirmLockPattern.footer"; public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
public static final String HEADER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.header_wrong"; public static final String FOOTER_TEXT = PACKAGE + ".ConfirmLockPattern.footer";
public static final String FOOTER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.footer_wrong"; public static final String HEADER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.header_wrong";
public static final String FOOTER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.footer_wrong";
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
private LockPatternView mLockPatternView;
private LockPatternUtils mLockPatternUtils;
private int mNumWrongConfirmAttempts;
private CountDownTimer mCountdownTimer;
private TextView mHeaderTextView;
private TextView mFooterTextView;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
private enum Stage { private enum Stage {
NeedToUnlock, NeedToUnlock,
@@ -77,194 +62,232 @@ public class ConfirmLockPattern extends Activity {
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public Intent getIntent() {
super.onCreate(savedInstanceState); Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
mLockPatternUtils = new LockPatternUtils(this); public static class ConfirmLockPatternFragment extends Fragment {
requestWindowFeature(Window.FEATURE_NO_TITLE); // how long we wait to clear a wrong pattern
setContentView(R.layout.confirm_lock_pattern); private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
mHeaderTextView = (TextView) findViewById(R.id.headerText); private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mFooterTextView = (TextView) findViewById(R.id.footerText);
// make it so unhandled touch events within the unlock screen go to the private LockPatternView mLockPatternView;
// lock pattern view. private LockPatternUtils mLockPatternUtils;
final LinearLayoutWithDefaultTouchRecepient topLayout private int mNumWrongConfirmAttempts;
= (LinearLayoutWithDefaultTouchRecepient) findViewById( private CountDownTimer mCountdownTimer;
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView); private TextView mHeaderTextView;
private TextView mFooterTextView;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
// required constructor for fragments
public ConfirmLockPatternFragment() {
Intent intent = getIntent();
if (intent != null) {
mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
} }
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); @Override
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener); public void onCreate(Bundle savedInstanceState) {
updateStage(Stage.NeedToUnlock); super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getActivity());
}
if (savedInstanceState != null) { @Override
mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS); public View onCreateView(LayoutInflater inflater, ViewGroup container,
} else { Bundle savedInstanceState) {
// on first launch, if no lock pattern is set, then finish with View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
// success (don't want user to get stuck confirming something that mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
// doesn't exist). mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
if (!mLockPatternUtils.savedPatternExists()) { mFooterTextView = (TextView) view.findViewById(R.id.footerText);
setResult(RESULT_OK);
finish(); // make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) view.findViewById(R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
Intent intent = getActivity().getIntent();
if (intent != null) {
mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
} }
}
}
@Override mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
protected void onSaveInstanceState(Bundle outState) { mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
// deliberately not calling super since we are managing this in full
outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
}
@Override
protected void onPause() {
super.onPause();
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
}
}
@Override
protected void onResume() {
super.onResume();
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
} else if (!mLockPatternView.isEnabled()) {
// The deadline has passed, but the timer was cancelled...
// Need to clean up.
mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock); updateStage(Stage.NeedToUnlock);
}
}
private void updateStage(Stage stage) { if (savedInstanceState != null) {
mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
}
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case LockedOut:
mLockPatternView.clearPattern();
// enabled = false means: disable input, and have the
// appearance of being disabled.
mLockPatternView.setEnabled(false); // appearance of being disabled
break;
}
}
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
/**
* The pattern listener that responds according to a user confirming
* an existing lock pattern.
*/
private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener = new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternCellAdded(List<Cell> pattern) {
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
setResult(RESULT_OK);
finish();
} else { } else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL && // on first launch, if no lock pattern is set, then finish with
++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { // success (don't want user to get stuck confirming something that
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); // doesn't exist).
handleAttemptLockout(deadline); if (!mLockPatternUtils.savedPatternExists()) {
} else { getActivity().setResult(Activity.RESULT_OK);
updateStage(Stage.NeedToUnlockWrong); getActivity().finish();
postClearPatternRunnable();
} }
} }
return view;
} }
};
@Override
public void onSaveInstanceState(Bundle outState) {
// deliberately not calling super since we are managing this in full
outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
}
private void handleAttemptLockout(long elapsedRealtimeDeadline) { @Override
updateStage(Stage.LockedOut); public void onPause() {
long elapsedRealtime = SystemClock.elapsedRealtime(); super.onPause();
mCountdownTimer = new CountDownTimer(
elapsedRealtimeDeadline - elapsedRealtime,
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
@Override if (mCountdownTimer != null) {
public void onTick(long millisUntilFinished) { mCountdownTimer.cancel();
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
} }
}
@Override @Override
public void onFinish() { public void onResume() {
super.onResume();
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
} else if (!mLockPatternView.isEnabled()) {
// The deadline has passed, but the timer was cancelled...
// Need to clean up.
mNumWrongConfirmAttempts = 0; mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock); updateStage(Stage.NeedToUnlock);
} }
}.start(); }
private void updateStage(Stage stage) {
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
}
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case LockedOut:
mLockPatternView.clearPattern();
// enabled = false means: disable input, and have the
// appearance of being disabled.
mLockPatternView.setEnabled(false); // appearance of being disabled
break;
}
}
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
/**
* The pattern listener that responds according to a user confirming
* an existing lock pattern.
*/
private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
= new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternCellAdded(List<Cell> pattern) {
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
++mNumWrongConfirmAttempts
>= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
updateStage(Stage.NeedToUnlockWrong);
postClearPatternRunnable();
}
}
}
};
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
updateStage(Stage.LockedOut);
long elapsedRealtime = SystemClock.elapsedRealtime();
mCountdownTimer = new CountDownTimer(
elapsedRealtimeDeadline - elapsedRealtime,
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
}
@Override
public void onFinish() {
mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock);
}
}.start();
}
} }
} }

View File

@@ -90,6 +90,8 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123; private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
private static final int FALLBACK_LOCK_AFTER_TIMEOUT_VALUE = 5000; // compatible with pre-Froyo private static final int FALLBACK_LOCK_AFTER_TIMEOUT_VALUE = 5000; // compatible with pre-Froyo
private static final String TAG = "SecuritySettings";
// Credential storage // Credential storage
private final CredentialStorage mCredentialStorage = new CredentialStorage(); private final CredentialStorage mCredentialStorage = new CredentialStorage();
@@ -359,8 +361,8 @@ public class SecuritySettings extends SettingsPreferenceFragment
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
Intent intent = new Intent(getActivity(), ChooseLockGeneric.class); startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
startActivityForResult(intent, SET_OR_CHANGE_LOCK_METHOD_REQUEST); SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
} else if (KEY_LOCK_ENABLED.equals(key)) { } else if (KEY_LOCK_ENABLED.equals(key)) {
lockPatternUtils.setLockPatternEnabled(isToggled(preference)); lockPatternUtils.setLockPatternEnabled(isToggled(preference));
} else if (KEY_VISIBLE_PATTERN.equals(key)) { } else if (KEY_VISIBLE_PATTERN.equals(key)) {