ChooseLockPassword.java revision abc3dc64cf61c9f5c28b6f2640b221d63cb86a0a
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(this);
82        mRequestedMode = getIntent().getIntExtra("password_mode", mRequestedMode);
83        mPasswordMinLength = getIntent().getIntExtra("password_min_length", mPasswordMinLength);
84        mPasswordMaxLength = getIntent().getIntExtra("password_max_length", mPasswordMaxLength);
85        int minMode = mLockPatternUtils.getRequestedPasswordMode();
86        if (mRequestedMode < minMode) {
87            mRequestedMode = minMode;
88        }
89        int minLength = mLockPatternUtils.getRequestedMinimumPasswordLength();
90        if (mPasswordMinLength < minLength) {
91            mPasswordMinLength = minLength;
92        }
93        initViews();
94        mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
95        if (savedInstanceState == null) {
96            updateStage(Stage.Introduction);
97            mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST);
98        }
99    }
100
101    private void initViews() {
102        switch(mRequestedMode) {
103            case LockPatternUtils.MODE_PIN:
104            case LockPatternUtils.MODE_PASSWORD:
105            case LockPatternUtils.MODE_PATTERN:
106                setContentView(R.layout.choose_lock_pin);
107                // TODO: alphanumeric layout
108                // setContentView(R.layout.choose_lock_password);
109                for (int i = 0; i < digitIds.length; i++) {
110                    Button button = (Button) findViewById(digitIds[i]);
111                    button.setOnClickListener(this);
112                    button.setText(Integer.toString(i));
113                }
114                break;
115        }
116        findViewById(R.id.ok).setOnClickListener(this);
117        findViewById(R.id.cancel).setOnClickListener(this);
118        findViewById(R.id.backspace).setOnClickListener(this);
119        mPasswordTextView = (TextView) findViewById(R.id.pinDisplay);
120        mHeaderText = (TextView) findViewById(R.id.headerText);
121    }
122
123    @Override
124    protected void onActivityResult(int requestCode, int resultCode,
125            Intent data) {
126        super.onActivityResult(requestCode, resultCode, data);
127        switch (requestCode) {
128            case CONFIRM_EXISTING_REQUEST:
129                if (resultCode != Activity.RESULT_OK) {
130                    setResult(RESULT_FINISHED);
131                    finish();
132                }
133                break;
134        }
135    }
136
137    protected void updateStage(Stage stage) {
138        mHeaderText.setText(stage.headerMessage);
139        mPasswordTextView.setText("");
140        mUiStage = stage;
141    }
142
143    /**
144     * Validates PIN and returns a message to display if PIN fails test.
145     * @param pin
146     * @return message id to display to user
147     */
148    private String validatePassword(String pin) {
149        if (pin.length() < mPasswordMinLength) {
150            return getString(R.string.pin_password_too_short, mPasswordMinLength);
151        }
152        if (pin.length() > mPasswordMaxLength) {
153            return getString(R.string.pin_password_too_long, mPasswordMaxLength);
154        }
155        if (LockPatternUtils.MODE_PIN == mRequestedMode) {
156            Pattern p = Pattern.compile("[0-9]+");
157            Matcher m = p.matcher(pin);
158            if (!m.find()) {
159                return getString(R.string.pin_password_contains_non_digits);
160            }
161        } else if (LockPatternUtils.MODE_PASSWORD == mRequestedMode) {
162            // allow Latin-1 characters only
163            for (int i = 0; i < pin.length(); i++) {
164                char c = pin.charAt(i);
165                if (c <= 32 || c > 127) {
166                    return getString(R.string.pin_password_illegal_character);
167                }
168            }
169        }
170        return null;
171    }
172
173    public void onClick(View v) {
174        switch (v.getId()) {
175            case R.id.ok:
176                {
177                    final String pin = mPasswordTextView.getText().toString();
178                    if (TextUtils.isEmpty(pin)) {
179                        break;
180                    }
181                    String errorMsg = null;
182                    if (mUiStage == Stage.Introduction) {
183                        errorMsg = validatePassword(pin);
184                        if (errorMsg == null) {
185                            mFirstPin = pin;
186                            updateStage(Stage.NeedToConfirm);
187                        }
188                    } else if (mUiStage == Stage.NeedToConfirm) {
189                        if (mFirstPin.equals(pin)) {
190                            // TODO: move these to LockPatternUtils
191                            mLockPatternUtils.setLockPatternEnabled(false);
192                            mLockPatternUtils.saveLockPattern(null);
193                            mLockPatternUtils.saveLockPassword(pin, mRequestedMode);
194                            finish();
195                        } else {
196                            int msg = R.string.lockpassword_confirm_passwords_dont_match;
197                            errorMsg = getString(msg);
198                        }
199                    }
200                    if (errorMsg != null) {
201                        showError(errorMsg, Stage.Introduction);
202                    }
203                }
204                break;
205
206            case R.id.backspace:
207                {
208                    final Editable digits = mPasswordTextView.getEditableText();
209                    final int len = digits.length();
210                    if (len > 0) {
211                        digits.delete(len-1, len);
212                    }
213                }
214                break;
215
216            case R.id.cancel:
217                finish();
218                break;
219
220            default:
221                // Digits
222                for (int i = 0; i < digitIds.length; i++) {
223                    if (v.getId() == digitIds[i]) {
224                        mPasswordTextView.append(Integer.toString(i));
225                        return;
226                    }
227                }
228                break;
229        }
230    }
231
232    private void showError(String msg, final Stage next) {
233        mHeaderText.setText(msg);
234        mPasswordTextView.setText("");
235        mHandler.postDelayed(new Runnable() {
236            public void run() {
237                updateStage(next);
238            }
239        }, ERROR_MESSAGE_TIMEOUT);
240    }
241}
242