ChooseLockPassword.java revision 00d2476d8fc122b4eef59d6be6f7aa41e9c56fde
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21
22import com.android.internal.widget.LockPatternUtils;
23import com.android.settings.ChooseLockPattern.LeftButtonMode;
24import com.android.settings.ChooseLockPattern.RightButtonMode;
25import com.android.settings.ChooseLockPattern.Stage;
26
27import android.app.Activity;
28import android.content.Intent;
29import android.os.Bundle;
30import android.os.Handler;
31import android.text.Editable;
32import android.text.TextUtils;
33import android.view.View;
34import android.view.View.OnClickListener;
35import android.widget.Button;
36import android.widget.TextView;
37
38
39public class ChooseLockPassword extends Activity implements OnClickListener {
40    private final int digitIds[] = new int[] { R.id.zero, R.id.one, R.id.two, R.id.three,
41            R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine };
42    private TextView mPasswordTextView;
43    private int mPasswordMinLength = 4;
44    private int mPasswordMaxLength = 8;
45    private LockPatternUtils mLockPatternUtils;
46    private int mRequestedMode = LockPatternUtils.MODE_PIN;
47    private ChooseLockSettingsHelper mChooseLockSettingsHelper;
48    private com.android.settings.ChooseLockPassword.Stage mUiStage = Stage.Introduction;
49    private TextView mHeaderText;
50    private String mFirstPin;
51    public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
52    public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
53    private static Handler mHandler = new Handler();
54    private static final int CONFIRM_EXISTING_REQUEST = 58;
55    static final int RESULT_FINISHED = RESULT_FIRST_USER;
56    private static final long ERROR_MESSAGE_TIMEOUT = 3000;
57
58    /**
59     * Keep track internally of where the user is in choosing a pattern.
60     */
61    protected enum Stage {
62
63        Introduction(R.string.lockpassword_choose_your_password_header),
64        NeedToConfirm(R.string.lockpassword_confirm_your_password_header),
65        ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match),
66        ChoiceConfirmed(R.string.lockpassword_password_confirmed_header);
67
68        /**
69         * @param headerMessage The message displayed at the top.
70         */
71        Stage(int headerMessage) {
72            this.headerMessage = headerMessage;
73        }
74
75        final int headerMessage;
76    }
77
78    @Override
79    protected void onCreate(Bundle savedInstanceState) {
80        super.onCreate(savedInstanceState);
81        mLockPatternUtils = new LockPatternUtils(getContentResolver());
82        mRequestedMode = getIntent().getIntExtra("password_mode", mRequestedMode);
83        mPasswordMinLength = getIntent().getIntExtra("password_min_length", mPasswordMinLength);
84        mPasswordMaxLength = getIntent().getIntExtra("password_max_length", mPasswordMaxLength);
85        initViews();
86        mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
87        if (savedInstanceState == null) {
88            updateStage(Stage.Introduction);
89            mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST);
90        }
91    }
92
93    private void initViews() {
94        if (LockPatternUtils.MODE_PIN == mRequestedMode
95                || LockPatternUtils.MODE_PASSWORD == mRequestedMode) {
96            setContentView(R.layout.choose_lock_pin);
97            // TODO: alphanumeric layout
98            // setContentView(R.layout.choose_lock_password);
99            for (int i = 0; i < digitIds.length; i++) {
100                Button button = (Button) findViewById(digitIds[i]);
101                button.setOnClickListener(this);
102                button.setText(Integer.toString(i));
103            }
104            findViewById(R.id.ok).setOnClickListener(this);
105            findViewById(R.id.cancel).setOnClickListener(this);
106        }
107        findViewById(R.id.backspace).setOnClickListener(this);
108        mPasswordTextView = (TextView) findViewById(R.id.pinDisplay);
109        mHeaderText = (TextView) findViewById(R.id.headerText);
110    }
111
112    @Override
113    protected void onActivityResult(int requestCode, int resultCode,
114            Intent data) {
115        super.onActivityResult(requestCode, resultCode, data);
116        switch (requestCode) {
117            case CONFIRM_EXISTING_REQUEST:
118                if (resultCode != Activity.RESULT_OK) {
119                    setResult(RESULT_FINISHED);
120                    finish();
121                }
122                break;
123        }
124    }
125
126    protected void updateStage(Stage stage) {
127        mHeaderText.setText(stage.headerMessage);
128        mPasswordTextView.setText("");
129        mUiStage = stage;
130    }
131
132    /**
133     * Validates PIN and returns a message to display if PIN fails test.
134     * @param pin
135     * @return message id to display to user
136     */
137    private String validatePassword(String pin) {
138        if (pin.length() < mPasswordMinLength) {
139            return getString(R.string.pin_password_too_short, mPasswordMinLength);
140        }
141        if (pin.length() > mPasswordMaxLength) {
142            return getString(R.string.pin_password_too_long, mPasswordMaxLength);
143        }
144        if (LockPatternUtils.MODE_PIN == mRequestedMode) {
145            Pattern p = Pattern.compile("[0-9]+");
146            Matcher m = p.matcher(pin);
147            if (!m.find()) {
148                return getString(R.string.pin_password_contains_non_digits);
149            }
150        } else if (LockPatternUtils.MODE_PASSWORD == mRequestedMode) {
151            // allow Latin-1 characters only
152            for (int i = 0; i < pin.length(); i++) {
153                char c = pin.charAt(i);
154                if (c <= 32 || c > 127) {
155                    return getString(R.string.pin_password_illegal_character);
156                }
157            }
158        }
159        return null;
160    }
161
162    public void onClick(View v) {
163        switch (v.getId()) {
164            case R.id.ok:
165                {
166                    final String pin = mPasswordTextView.getText().toString();
167                    if (TextUtils.isEmpty(pin)) {
168                        break;
169                    }
170                    String errorMsg = null;
171                    if (mUiStage == Stage.Introduction) {
172                        errorMsg = validatePassword(pin);
173                        if (errorMsg == null) {
174                            mFirstPin = pin;
175                            updateStage(Stage.NeedToConfirm);
176                        }
177                    } else if (mUiStage == Stage.NeedToConfirm) {
178                        if (mFirstPin.equals(pin)) {
179                            // TODO: move these to LockPatternUtils
180                            mLockPatternUtils.setLockPatternEnabled(false);
181                            mLockPatternUtils.saveLockPattern(null);
182
183
184                            mLockPatternUtils.saveLockPassword(pin);
185                            finish();
186                        } else {
187                            int msg = R.string.lockpassword_confirm_passwords_dont_match;
188                            errorMsg = getString(msg);
189                        }
190                    }
191                    if (errorMsg != null) {
192                        showError(errorMsg, Stage.Introduction);
193                    }
194                }
195                break;
196
197            case R.id.backspace:
198                {
199                    final Editable digits = mPasswordTextView.getEditableText();
200                    final int len = digits.length();
201                    if (len > 0) {
202                        digits.delete(len-1, len);
203                    }
204                }
205                break;
206
207            case R.id.cancel:
208                finish();
209                break;
210
211            default:
212                // Digits
213                for (int i = 0; i < digitIds.length; i++) {
214                    if (v.getId() == digitIds[i]) {
215                        mPasswordTextView.append(Integer.toString(i));
216                        return;
217                    }
218                }
219                break;
220        }
221    }
222
223    private void showError(String msg, final Stage next) {
224        mHeaderText.setText(msg);
225        mPasswordTextView.setText("");
226        mHandler.postDelayed(new Runnable() {
227            public void run() {
228                updateStage(next);
229            }
230        }, ERROR_MESSAGE_TIMEOUT);
231    }
232}
233