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