blob: 753ae3f1c67dca98195f74fe27d66d83f9ef279b [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
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
17package com.android.phone;
18
19import static android.view.Window.PROGRESS_VISIBILITY_OFF;
20import static android.view.Window.PROGRESS_VISIBILITY_ON;
21
22import android.app.Activity;
23import android.content.AsyncQueryHandler;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Intent;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.net.Uri;
30import android.os.Bundle;
31import android.os.Handler;
32import android.provider.Contacts.PeopleColumns;
33import android.provider.Contacts.PhonesColumns;
34import android.telephony.PhoneNumberUtils;
35import android.text.Selection;
36import android.text.Spannable;
37import android.text.TextUtils;
38import android.text.method.DialerKeyListener;
39import android.util.Log;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.View;
43import android.view.Window;
44import android.widget.Button;
45import android.widget.EditText;
46import android.widget.LinearLayout;
47import android.widget.TextView;
48import android.widget.Toast;
49
Wink Saville9de0f752013-10-22 19:04:03 -070050import com.android.internal.telephony.Phone;
51import com.android.internal.telephony.PhoneFactory;
52
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053/**
54 * Activity to let the user add or edit an FDN contact.
55 */
56public class EditFdnContactScreen extends Activity {
57 private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
58 private static final boolean DBG = false;
59
60 // Menu item codes
61 private static final int MENU_IMPORT = 1;
62 private static final int MENU_DELETE = 2;
63
64 private static final String INTENT_EXTRA_NAME = "name";
65 private static final String INTENT_EXTRA_NUMBER = "number";
66
67 private static final int PIN2_REQUEST_CODE = 100;
68
69 private String mName;
70 private String mNumber;
71 private String mPin2;
72 private boolean mAddContact;
73 private QueryHandler mQueryHandler;
74
75 private EditText mNameField;
76 private EditText mNumberField;
77 private LinearLayout mPinFieldContainer;
78 private Button mButton;
79
80 private Handler mHandler = new Handler();
81
82 /**
83 * Constants used in importing from contacts
84 */
85 /** request code when invoking subactivity */
86 private static final int CONTACTS_PICKER_CODE = 200;
87 /** projection for phone number query */
88 private static final String NUM_PROJECTION[] = {PeopleColumns.DISPLAY_NAME,
89 PhonesColumns.NUMBER};
90 /** static intent to invoke phone number picker */
91 private static final Intent CONTACT_IMPORT_INTENT;
92 static {
93 CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_GET_CONTENT);
94 CONTACT_IMPORT_INTENT.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
95 }
96 /** flag to track saving state */
97 private boolean mDataBusy;
98
99 @Override
100 protected void onCreate(Bundle icicle) {
101 super.onCreate(icicle);
102
103 resolveIntent();
104
105 getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
106 setContentView(R.layout.edit_fdn_contact_screen);
107 setupView();
108 setTitle(mAddContact ?
109 R.string.add_fdn_contact : R.string.edit_fdn_contact);
110
111 displayProgress(false);
112 }
113
114 /**
115 * We now want to bring up the pin request screen AFTER the
116 * contact information is displayed, to help with user
117 * experience.
118 *
119 * Also, process the results from the contact picker.
120 */
121 @Override
122 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
123 if (DBG) log("onActivityResult request:" + requestCode + " result:" + resultCode);
124
125 switch (requestCode) {
126 case PIN2_REQUEST_CODE:
127 Bundle extras = (intent != null) ? intent.getExtras() : null;
128 if (extras != null) {
129 mPin2 = extras.getString("pin2");
130 if (mAddContact) {
131 addContact();
132 } else {
133 updateContact();
134 }
135 } else if (resultCode != RESULT_OK) {
136 // if they cancelled, then we just cancel too.
137 if (DBG) log("onActivityResult: cancelled.");
138 finish();
139 }
140 break;
141
142 // look for the data associated with this number, and update
143 // the display with it.
144 case CONTACTS_PICKER_CODE:
145 if (resultCode != RESULT_OK) {
146 if (DBG) log("onActivityResult: cancelled.");
147 return;
148 }
149 Cursor cursor = null;
150 try {
151 cursor = getContentResolver().query(intent.getData(),
152 NUM_PROJECTION, null, null, null);
153 if ((cursor == null) || (!cursor.moveToFirst())) {
154 Log.w(LOG_TAG,"onActivityResult: bad contact data, no results found.");
155 return;
156 }
157 mNameField.setText(cursor.getString(0));
158 mNumberField.setText(cursor.getString(1));
159 } finally {
160 if (cursor != null) {
161 cursor.close();
162 }
163 }
164 break;
165 }
166 }
167
168 /**
169 * Overridden to display the import and delete commands.
170 */
171 @Override
172 public boolean onCreateOptionsMenu(Menu menu) {
173 super.onCreateOptionsMenu(menu);
174
175 Resources r = getResources();
176
177 // Added the icons to the context menu
178 menu.add(0, MENU_IMPORT, 0, r.getString(R.string.importToFDNfromContacts))
179 .setIcon(R.drawable.ic_menu_contact);
180 menu.add(0, MENU_DELETE, 0, r.getString(R.string.menu_delete))
181 .setIcon(android.R.drawable.ic_menu_delete);
182 return true;
183 }
184
185 /**
186 * Allow the menu to be opened ONLY if we're not busy.
187 */
188 @Override
189 public boolean onPrepareOptionsMenu(Menu menu) {
190 boolean result = super.onPrepareOptionsMenu(menu);
191 return mDataBusy ? false : result;
192 }
193
194 /**
195 * Overridden to allow for handling of delete and import.
196 */
197 @Override
198 public boolean onOptionsItemSelected(MenuItem item) {
199 switch (item.getItemId()) {
200 case MENU_IMPORT:
201 startActivityForResult(CONTACT_IMPORT_INTENT, CONTACTS_PICKER_CODE);
202 return true;
203
204 case MENU_DELETE:
205 deleteSelected();
206 return true;
207 }
208
209 return super.onOptionsItemSelected(item);
210 }
211
212 private void resolveIntent() {
213 Intent intent = getIntent();
214
215 mName = intent.getStringExtra(INTENT_EXTRA_NAME);
216 mNumber = intent.getStringExtra(INTENT_EXTRA_NUMBER);
217
218 mAddContact = TextUtils.isEmpty(mNumber);
219 }
220
221 /**
222 * We have multiple layouts, one to indicate that the user needs to
223 * open the keyboard to enter information (if the keybord is hidden).
224 * So, we need to make sure that the layout here matches that in the
225 * layout file.
226 */
227 private void setupView() {
228 mNameField = (EditText) findViewById(R.id.fdn_name);
229 if (mNameField != null) {
230 mNameField.setOnFocusChangeListener(mOnFocusChangeHandler);
231 mNameField.setOnClickListener(mClicked);
232 }
233
234 mNumberField = (EditText) findViewById(R.id.fdn_number);
235 if (mNumberField != null) {
236 mNumberField.setKeyListener(DialerKeyListener.getInstance());
237 mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler);
238 mNumberField.setOnClickListener(mClicked);
239 }
240
241 if (!mAddContact) {
242 if (mNameField != null) {
243 mNameField.setText(mName);
244 }
245 if (mNumberField != null) {
246 mNumberField.setText(mNumber);
247 }
248 }
249
250 mButton = (Button) findViewById(R.id.button);
251 if (mButton != null) {
252 mButton.setOnClickListener(mClicked);
253 }
254
255 mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
256
257 }
258
259 private String getNameFromTextField() {
260 return mNameField.getText().toString();
261 }
262
263 private String getNumberFromTextField() {
264 return mNumberField.getText().toString();
265 }
266
267 private Uri getContentURI() {
268 return Uri.parse("content://icc/fdn");
269 }
270
271 /**
272 * @param number is voice mail number
273 * @return true if number length is less than 20-digit limit
274 *
275 * TODO: Fix this logic.
276 */
277 private boolean isValidNumber(String number) {
278 return (number.length() <= 20);
279 }
280
281
282 private void addContact() {
283 if (DBG) log("addContact");
284
285 final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
286
287 if (!isValidNumber(number)) {
288 handleResult(false, true);
289 return;
290 }
291
292 Uri uri = getContentURI();
293
294 ContentValues bundle = new ContentValues(3);
295 bundle.put("tag", getNameFromTextField());
296 bundle.put("number", number);
297 bundle.put("pin2", mPin2);
298
299 mQueryHandler = new QueryHandler(getContentResolver());
300 mQueryHandler.startInsert(0, null, uri, bundle);
301 displayProgress(true);
302 showStatus(getResources().getText(R.string.adding_fdn_contact));
303 }
304
305 private void updateContact() {
306 if (DBG) log("updateContact");
307
308 final String name = getNameFromTextField();
309 final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
310
311 if (!isValidNumber(number)) {
312 handleResult(false, true);
313 return;
314 }
315 Uri uri = getContentURI();
316
317 ContentValues bundle = new ContentValues();
318 bundle.put("tag", mName);
319 bundle.put("number", mNumber);
320 bundle.put("newTag", name);
321 bundle.put("newNumber", number);
322 bundle.put("pin2", mPin2);
323
324 mQueryHandler = new QueryHandler(getContentResolver());
325 mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
326 displayProgress(true);
327 showStatus(getResources().getText(R.string.updating_fdn_contact));
328 }
329
330 /**
331 * Handle the delete command, based upon the state of the Activity.
332 */
333 private void deleteSelected() {
334 // delete ONLY if this is NOT a new contact.
335 if (!mAddContact) {
336 Intent intent = new Intent();
337 intent.setClass(this, DeleteFdnContactScreen.class);
338 intent.putExtra(INTENT_EXTRA_NAME, mName);
339 intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
340 startActivity(intent);
341 }
342 finish();
343 }
344
345 private void authenticatePin2() {
346 Intent intent = new Intent();
347 intent.setClass(this, GetPin2Screen.class);
348 startActivityForResult(intent, PIN2_REQUEST_CODE);
349 }
350
351 private void displayProgress(boolean flag) {
352 // indicate we are busy.
353 mDataBusy = flag;
354 getWindow().setFeatureInt(
355 Window.FEATURE_INDETERMINATE_PROGRESS,
356 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
357 // make sure we don't allow calls to save when we're
358 // not ready for them.
359 mButton.setClickable(!mDataBusy);
360 }
361
362 /**
363 * Removed the status field, with preference to displaying a toast
364 * to match the rest of settings UI.
365 */
366 private void showStatus(CharSequence statusMsg) {
367 if (statusMsg != null) {
368 Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
369 .show();
370 }
371 }
372
373 private void handleResult(boolean success, boolean invalidNumber) {
374 if (success) {
375 if (DBG) log("handleResult: success!");
376 showStatus(getResources().getText(mAddContact ?
377 R.string.fdn_contact_added : R.string.fdn_contact_updated));
378 } else {
379 if (DBG) log("handleResult: failed!");
380 if (invalidNumber) {
381 showStatus(getResources().getText(R.string.fdn_invalid_number));
382 } else {
Wink Saville9de0f752013-10-22 19:04:03 -0700383 if (PhoneFactory.getDefaultPhone().getIccCard().getIccPin2Blocked()) {
384 showStatus(getResources().getText(R.string.fdn_enable_puk2_requested));
385 } else if (PhoneFactory.getDefaultPhone().getIccCard().getIccPuk2Blocked()) {
386 showStatus(getResources().getText(R.string.puk2_blocked));
387 } else {
388 // There's no way to know whether the failure is due to incorrect PIN2 or
389 // an inappropriate phone number.
390 showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
391 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700392 }
393 }
394
395 mHandler.postDelayed(new Runnable() {
396 @Override
397 public void run() {
398 finish();
399 }
400 }, 2000);
401
402 }
403
404 private final View.OnClickListener mClicked = new View.OnClickListener() {
405 @Override
406 public void onClick(View v) {
407 if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
408 return;
409 }
410
411 if (v == mNameField) {
412 mNumberField.requestFocus();
413 } else if (v == mNumberField) {
414 mButton.requestFocus();
415 } else if (v == mButton) {
416 // Authenticate the pin AFTER the contact information
417 // is entered, and if we're not busy.
418 if (!mDataBusy) {
419 authenticatePin2();
420 }
421 }
422 }
423 };
424
425 private final View.OnFocusChangeListener mOnFocusChangeHandler =
426 new View.OnFocusChangeListener() {
427 @Override
428 public void onFocusChange(View v, boolean hasFocus) {
429 if (hasFocus) {
430 TextView textView = (TextView) v;
431 Selection.selectAll((Spannable) textView.getText());
432 }
433 }
434 };
435
436 private class QueryHandler extends AsyncQueryHandler {
437 public QueryHandler(ContentResolver cr) {
438 super(cr);
439 }
440
441 @Override
442 protected void onQueryComplete(int token, Object cookie, Cursor c) {
443 }
444
445 @Override
446 protected void onInsertComplete(int token, Object cookie, Uri uri) {
447 if (DBG) log("onInsertComplete");
448 displayProgress(false);
449 handleResult(uri != null, false);
450 }
451
452 @Override
453 protected void onUpdateComplete(int token, Object cookie, int result) {
454 if (DBG) log("onUpdateComplete");
455 displayProgress(false);
456 handleResult(result > 0, false);
457 }
458
459 @Override
460 protected void onDeleteComplete(int token, Object cookie, int result) {
461 }
462 }
463
464 private void log(String msg) {
465 Log.d(LOG_TAG, "[EditFdnContact] " + msg);
466 }
467}