AI 144185: Integrate cdma into the main code base.
[android/platform/packages/apps/Phone.git] / src / com / android / phone / IccPinUnlockPanel.java
1 /*
2  * Copyright (C) 2006 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
17 package com.android.phone;
18
19 import android.content.Context;
20 import android.os.AsyncResult;
21 import android.os.Handler;
22 import android.os.Message;
23 import android.os.Bundle;
24 import android.text.Editable;
25 import android.text.Spannable;
26 import android.text.TextUtils;
27 import android.text.TextWatcher;
28 import android.text.method.DialerKeyListener;
29 import android.util.Log;
30 import android.view.KeyEvent;
31 import android.view.View;
32 import android.widget.Button;
33 import android.widget.EditText;
34 import android.widget.LinearLayout;
35 import android.widget.TextView;
36
37 import com.android.internal.telephony.CommandException;
38 import com.android.internal.telephony.IccCard;
39
40 /**
41  * Panel where you enter your PIN to unlock the SIM card.
42  */
43 public class IccPinUnlockPanel extends IccPanel {
44     private static final boolean DBG = false;
45
46     private static final int EVENT_ICC_UNLOCKED_RESULT = 100;
47
48     private enum IccLockState {
49         UNLOCKED,
50         REQUIRE_PIN,
51         REQUIRE_PUK,
52         REQUIRE_NEW_PIN,
53         VERIFY_NEW_PIN,
54         VERIFY_NEW_PIN_FAILED
55     };
56
57     private IccLockState mState;
58     private String mPUKCode;
59     private String mNewPinCode;
60
61     private EditText mEntry;
62     private TextView mFailure;
63     private TextView mLabel;
64     private TextView mStatus;
65     private Button mUnlockButton;
66     private Button mDismissButton;
67     private LinearLayout mUnlockPane;
68     private LinearLayout mUnlockInProgressPane;
69
70     private Handler mHandler = new Handler() {
71         public void handleMessage(Message msg) {
72             switch (msg.what) {
73                 case EVENT_ICC_UNLOCKED_RESULT:
74                     AsyncResult ar = (AsyncResult) msg.obj;
75                     handleUnlockResult(ar);
76                     break;
77             }
78         }
79     };
80
81     private class MyTextWatcher implements TextWatcher {
82         Context mContext;
83
84         public MyTextWatcher(Context context) {
85             mContext = context;
86         }
87
88         public void beforeTextChanged(CharSequence buffer,
89                                       int start, int olen, int nlen) {
90         }
91
92         public void onTextChanged(CharSequence buffer,
93                                   int start, int olen, int nlen) {
94         }
95
96         public void afterTextChanged(Editable buffer) {
97             if (SpecialCharSequenceMgr.handleChars(
98                     mContext, buffer.toString(), true)) {
99                 mEntry.getText().clear();
100             }
101         }
102     }
103
104     public IccPinUnlockPanel(Context context) {
105         super(context);
106     }
107
108     @Override
109     protected void onCreate(Bundle savedInstanceState) {
110         super.onCreate(savedInstanceState);
111         setContentView(R.layout.sim_unlock);
112         updateState();
113         initView();
114         updateView();
115     }
116
117     @Override
118     protected void onStart() {
119         super.onStart();
120         if (updateState()) {
121             updateView(); // need to update the view only if state changed
122         }
123     }
124
125     /**
126      * @return Whether the state changed.
127      */
128     boolean updateState() {
129         PhoneApp app = PhoneApp.getInstance();
130         IccCard iccCardInterface = app.phone.getIccCard();
131
132         try {
133             if (iccCardInterface.getState() == IccCard.State.PUK_REQUIRED) {
134                 if (getState() != IccLockState.REQUIRE_PUK) {
135                     setState(IccLockState.REQUIRE_PUK);
136                     return true;
137                 }
138             } else if (getState() != IccLockState.REQUIRE_PIN){
139                 setState(IccLockState.REQUIRE_PIN);
140                 return true;
141             }
142         } catch (Exception ex) {
143         }
144         return false;
145     }
146
147     void setState(IccLockState state) {
148         mState = state;
149     }
150
151     IccLockState getState() {
152         return mState;
153     }
154
155     void initView() {
156         mUnlockPane = (LinearLayout) findViewById(R.id.simPINPane);
157         mUnlockInProgressPane = (LinearLayout) findViewById(R.id.progress);
158
159         mEntry = (EditText) findViewById(R.id.entry);
160         mEntry.setKeyListener(DialerKeyListener.getInstance());
161         mEntry.setMovementMethod(null);
162         mEntry.setOnClickListener(mUnlockListener);
163
164         // set up the text watcher
165         CharSequence text = mEntry.getText();
166         Spannable span = (Spannable) text;
167         span.setSpan(new MyTextWatcher(this.getContext()),
168                 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
169
170         mFailure = (TextView) findViewById(R.id.failure);
171         mLabel = (TextView) findViewById(R.id.label);
172         mStatus = (TextView) findViewById(R.id.status);
173
174         mUnlockButton = (Button) findViewById(R.id.unlock);
175         mUnlockButton.setOnClickListener(mUnlockListener);
176
177         mDismissButton = (Button) findViewById(R.id.dismiss);
178
179         // if we are using the ICC pin for keyguard password, force the
180         // user to enter the correct PIN to proceed. Otherwise, we won't
181         // know what the correct keyguard password is.
182         mDismissButton.setOnClickListener(mDismissListener);
183     }
184
185     void updateView() {
186         Context context = getContext();
187
188         switch (mState) {
189             case REQUIRE_PIN:
190                 mLabel.setText(context.getText(R.string.enterPin));
191                 break;
192
193             case REQUIRE_PUK:
194                 mLabel.setText(context.getText(R.string.enterPuk));
195                 mUnlockButton.setText(
196                         context.getText(R.string.buttonTxtContinue));
197                 break;
198
199             case REQUIRE_NEW_PIN:
200                 hideIncorrectPinMessage();
201                 mLabel.setText(context.getText(R.string.enterNewPin));
202                 break;
203
204             case VERIFY_NEW_PIN:
205                 mLabel.setText(context.getText(R.string.verifyNewPin));
206                 break;
207
208             case VERIFY_NEW_PIN_FAILED:
209                 mLabel.setText(context.getText(R.string.verifyFailed));
210                 setState(IccLockState.REQUIRE_NEW_PIN);
211                 break;
212         }
213
214         mEntry.getText().clear();
215         mEntry.requestFocus(View.FOCUS_FORWARD);
216     }
217
218     public boolean onKeyDown(int keyCode, KeyEvent event) {
219         if (keyCode == KeyEvent.KEYCODE_BACK) {
220             return true;
221         }
222
223         return super.onKeyDown(keyCode, event);
224     }
225
226     void handleUnlockResult(AsyncResult ar) {
227         if (ar.exception == null) {
228             handleSuccess();
229             return;
230         }
231
232         // pin/puk unlock failed!
233         if (ar.exception instanceof com.android.internal.telephony.CommandException &&
234                 ((CommandException) ar.exception).getCommandError() ==
235                     CommandException.Error.PASSWORD_INCORRECT) {
236             hidePinUnlockInProgress();
237             handleFailure();
238         }
239
240         // PENDING: any other failure types?
241     }
242
243     void handleSuccess() {
244         if (DBG) log("unlock successful!");
245         showUnlockSuccess();
246
247         // store the ICC pin in memory, to be used later for keyguard lock
248         // and radio reboots.
249         PhoneApp.getInstance().setCachedSimPin(mEntry.getText().toString()); 
250     }
251
252     void handleFailure() {
253         if (DBG) log("unlock failed");
254         showIncorrectPinMessage();
255         mEntry.getText().clear();
256         updateState();
257         updateView();
258     }
259
260     void showIncorrectPinMessage() {
261         CharSequence msg;
262         Context context = getContext();
263
264         if (getState() == IccLockState.REQUIRE_PIN) {
265             msg = context.getText(R.string.badPin);
266         } else {
267             msg = context.getText(R.string.badPuk);
268         }
269
270         mFailure.setText(msg);
271         mFailure.setVisibility(View.VISIBLE);
272     }
273
274     void hideIncorrectPinMessage() {
275         mFailure.setVisibility(View.GONE);
276     }
277
278     void showUnlockInProgress() {
279         mUnlockInProgressPane.setVisibility(View.VISIBLE);
280         mUnlockPane.setVisibility(View.GONE);
281     }
282
283     void hidePinUnlockInProgress() {
284         mUnlockInProgressPane.setVisibility(View.GONE);
285         mUnlockPane.setVisibility(View.VISIBLE);
286     }
287
288     void showUnlockSuccess() {
289         mStatus.setText(getContext().getText(R.string.pinUnlocked));
290         mHandler.postDelayed(
291                 new Runnable() {
292                     public void run() {
293                         dismiss();
294                     }
295                 }, 1000);
296     }
297
298     private boolean verifyNewPin(String pin2) {
299         if (mNewPinCode.equals(pin2)) {
300             return true;
301         }
302
303         return false;
304     }
305
306     //***** Listeners
307
308     View.OnClickListener mUnlockListener = new View.OnClickListener() {
309         public void onClick(View v) {
310             String code = mEntry.getText().toString();
311
312             if (TextUtils.isEmpty(code)) {
313                 return;
314             }
315
316             PhoneApp app = PhoneApp.getInstance();
317             IccCard iccCardInterface = app.phone.getIccCard();
318             if (iccCardInterface != null) {
319                 Message callBack = Message.obtain(mHandler,
320                         EVENT_ICC_UNLOCKED_RESULT);
321
322                 switch (mState) {
323                     case REQUIRE_PIN:
324                         if (DBG) log("unlock attempt: PIN code entered = " +
325                                 code);
326                         showUnlockInProgress();
327                         iccCardInterface.supplyPin(code, callBack);
328                         break;
329
330                     case REQUIRE_PUK:
331                         if (DBG) log("puk code entered, request for new pin");
332                         mPUKCode = code;
333                         setState(IccLockState.REQUIRE_NEW_PIN);
334                         updateView();
335                         break;
336
337                     case REQUIRE_NEW_PIN:
338                         if (DBG) log("new pin code entered, verify pin");
339                         mNewPinCode = code;
340                         setState(IccLockState.VERIFY_NEW_PIN);
341                         updateView();
342                         break;
343
344                     case VERIFY_NEW_PIN:
345                         if (DBG) log("new pin code re-entered");
346
347                         if (verifyNewPin(code)) {
348                             // proceed
349                             showUnlockInProgress();
350                             iccCardInterface.supplyPuk(mPUKCode, mNewPinCode,
351                                     callBack);
352                         } else {
353                             setState(IccLockState.VERIFY_NEW_PIN_FAILED);
354                             updateView();
355                         }
356
357                         break;
358                 }
359             }
360         }
361     };
362
363     View.OnClickListener mDismissListener = new View.OnClickListener() {
364         public void onClick(View v) {
365             dismiss();
366         }
367     };
368
369     private void log(String msg) {
370         Log.v(TAG, "[SimPinUnlock] " + msg);
371     }
372 }