• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.view.Window.PROGRESS_VISIBILITY_OFF;
20 import static android.view.Window.PROGRESS_VISIBILITY_ON;
21 
22 import android.app.Activity;
23 import android.content.AsyncQueryHandler;
24 import android.content.ContentResolver;
25 import android.content.ContentValues;
26 import android.content.Intent;
27 import android.content.res.Resources;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.provider.Contacts.PeopleColumns;
33 import android.provider.Contacts.PhonesColumns;
34 import android.telephony.PhoneNumberUtils;
35 import android.text.Selection;
36 import android.text.Spannable;
37 import android.text.TextUtils;
38 import android.text.method.DialerKeyListener;
39 import android.util.Log;
40 import android.view.Menu;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.view.Window;
44 import android.widget.Button;
45 import android.widget.EditText;
46 import android.widget.LinearLayout;
47 import android.widget.TextView;
48 import android.widget.Toast;
49 
50 /**
51  * Activity to let the user add or edit an FDN contact.
52  */
53 public 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
onCreate(Bundle icicle)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
onActivityResult(int requestCode, int resultCode, Intent intent)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 = getContentResolver().query(intent.getData(),
147                         NUM_PROJECTION, null, null, null);
148                 if ((cursor == null) || (!cursor.moveToFirst())) {
149                     Log.w(LOG_TAG,"onActivityResult: bad contact data, no results found.");
150                     return;
151                 }
152                 mNameField.setText(cursor.getString(0));
153                 mNumberField.setText(cursor.getString(1));
154                 break;
155         }
156     }
157 
158     /**
159      * Overridden to display the import and delete commands.
160      */
161     @Override
onCreateOptionsMenu(Menu menu)162     public boolean onCreateOptionsMenu(Menu menu) {
163         super.onCreateOptionsMenu(menu);
164 
165         Resources r = getResources();
166 
167         // Added the icons to the context menu
168         menu.add(0, MENU_IMPORT, 0, r.getString(R.string.importToFDNfromContacts))
169                 .setIcon(R.drawable.ic_menu_contact);
170         menu.add(0, MENU_DELETE, 0, r.getString(R.string.menu_delete))
171                 .setIcon(android.R.drawable.ic_menu_delete);
172         return true;
173     }
174 
175     /**
176      * Allow the menu to be opened ONLY if we're not busy.
177      */
178     @Override
onPrepareOptionsMenu(Menu menu)179     public boolean onPrepareOptionsMenu(Menu menu) {
180         boolean result = super.onPrepareOptionsMenu(menu);
181         return mDataBusy ? false : result;
182     }
183 
184     /**
185      * Overridden to allow for handling of delete and import.
186      */
187     @Override
onOptionsItemSelected(MenuItem item)188     public boolean onOptionsItemSelected(MenuItem item) {
189         switch (item.getItemId()) {
190             case MENU_IMPORT:
191                 startActivityForResult(CONTACT_IMPORT_INTENT, CONTACTS_PICKER_CODE);
192                 return true;
193 
194             case MENU_DELETE:
195                 deleteSelected();
196                 return true;
197         }
198 
199         return super.onOptionsItemSelected(item);
200     }
201 
resolveIntent()202     private void resolveIntent() {
203         Intent intent = getIntent();
204 
205         mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
206         mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
207 
208         mAddContact = TextUtils.isEmpty(mNumber);
209     }
210 
211     /**
212      * We have multiple layouts, one to indicate that the user needs to
213      * open the keyboard to enter information (if the keybord is hidden).
214      * So, we need to make sure that the layout here matches that in the
215      * layout file.
216      */
setupView()217     private void setupView() {
218         mNameField = (EditText) findViewById(R.id.fdn_name);
219         if (mNameField != null) {
220             mNameField.setOnFocusChangeListener(mOnFocusChangeHandler);
221             mNameField.setOnClickListener(mClicked);
222         }
223 
224         mNumberField = (EditText) findViewById(R.id.fdn_number);
225         if (mNumberField != null) {
226             mNumberField.setKeyListener(DialerKeyListener.getInstance());
227             mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler);
228             mNumberField.setOnClickListener(mClicked);
229         }
230 
231         if (!mAddContact) {
232             if (mNameField != null) {
233                 mNameField.setText(mName);
234             }
235             if (mNumberField != null) {
236                 mNumberField.setText(mNumber);
237             }
238         }
239 
240         mButton = (Button) findViewById(R.id.button);
241         if (mButton != null) {
242             mButton.setOnClickListener(mClicked);
243         }
244 
245         mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
246 
247     }
248 
getNameFromTextField()249     private String getNameFromTextField() {
250         return mNameField.getText().toString();
251     }
252 
getNumberFromTextField()253     private String getNumberFromTextField() {
254         return mNumberField.getText().toString();
255     }
256 
getContentURI()257     private Uri getContentURI() {
258         return Uri.parse("content://icc/fdn");
259     }
260 
261     /**
262       * @param number is voice mail number
263       * @return true if number length is less than 20-digit limit
264       *
265       * TODO: Fix this logic.
266       */
isValidNumber(String number)267      private boolean isValidNumber(String number) {
268          return (number.length() <= 20);
269      }
270 
271 
addContact()272     private void addContact() {
273         if (DBG) log("addContact");
274 
275         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
276 
277         if (!isValidNumber(number)) {
278             handleResult(false, true);
279             return;
280         }
281 
282         Uri uri = getContentURI();
283 
284         ContentValues bundle = new ContentValues(3);
285         bundle.put("tag", getNameFromTextField());
286         bundle.put("number", number);
287         bundle.put("pin2", mPin2);
288 
289         mQueryHandler = new QueryHandler(getContentResolver());
290         mQueryHandler.startInsert(0, null, uri, bundle);
291         displayProgress(true);
292         showStatus(getResources().getText(R.string.adding_fdn_contact));
293     }
294 
updateContact()295     private void updateContact() {
296         if (DBG) log("updateContact");
297 
298         final String name = getNameFromTextField();
299         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
300 
301         if (!isValidNumber(number)) {
302             handleResult(false, true);
303             return;
304         }
305         Uri uri = getContentURI();
306 
307         ContentValues bundle = new ContentValues();
308         bundle.put("tag", mName);
309         bundle.put("number", mNumber);
310         bundle.put("newTag", name);
311         bundle.put("newNumber", number);
312         bundle.put("pin2", mPin2);
313 
314         mQueryHandler = new QueryHandler(getContentResolver());
315         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
316         displayProgress(true);
317         showStatus(getResources().getText(R.string.updating_fdn_contact));
318     }
319 
320     /**
321      * Handle the delete command, based upon the state of the Activity.
322      */
deleteSelected()323     private void deleteSelected() {
324         // delete ONLY if this is NOT a new contact.
325         if (!mAddContact) {
326             Intent intent = new Intent();
327             intent.setClass(this, DeleteFdnContactScreen.class);
328             intent.putExtra(INTENT_EXTRA_NAME, mName);
329             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
330             startActivity(intent);
331         }
332         finish();
333     }
334 
authenticatePin2()335     private void authenticatePin2() {
336         Intent intent = new Intent();
337         intent.setClass(this, GetPin2Screen.class);
338         startActivityForResult(intent, PIN2_REQUEST_CODE);
339     }
340 
displayProgress(boolean flag)341     private void displayProgress(boolean flag) {
342         // indicate we are busy.
343         mDataBusy = flag;
344         getWindow().setFeatureInt(
345                 Window.FEATURE_INDETERMINATE_PROGRESS,
346                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
347         // make sure we don't allow calls to save when we're
348         // not ready for them.
349         mButton.setClickable(!mDataBusy);
350     }
351 
352     /**
353      * Removed the status field, with preference to displaying a toast
354      * to match the rest of settings UI.
355      */
showStatus(CharSequence statusMsg)356     private void showStatus(CharSequence statusMsg) {
357         if (statusMsg != null) {
358             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
359                     .show();
360         }
361     }
362 
handleResult(boolean success, boolean invalidNumber)363     private void handleResult(boolean success, boolean invalidNumber) {
364         if (success) {
365             if (DBG) log("handleResult: success!");
366             showStatus(getResources().getText(mAddContact ?
367                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
368         } else {
369             if (DBG) log("handleResult: failed!");
370             if (invalidNumber) {
371                 showStatus(getResources().getText(R.string.fdn_invalid_number));
372             } else {
373                 // There's no way to know whether the failure is due to incorrect PIN2 or
374                 // an inappropriate phone number.
375                 showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
376             }
377         }
378 
379         mHandler.postDelayed(new Runnable() {
380             @Override
381             public void run() {
382                 finish();
383             }
384         }, 2000);
385 
386     }
387 
388     private final View.OnClickListener mClicked = new View.OnClickListener() {
389         @Override
390         public void onClick(View v) {
391             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
392                 return;
393             }
394 
395             if (v == mNameField) {
396                 mNumberField.requestFocus();
397             } else if (v == mNumberField) {
398                 mButton.requestFocus();
399             } else if (v == mButton) {
400                 // Authenticate the pin AFTER the contact information
401                 // is entered, and if we're not busy.
402                 if (!mDataBusy) {
403                     authenticatePin2();
404                 }
405             }
406         }
407     };
408 
409     private final View.OnFocusChangeListener mOnFocusChangeHandler =
410             new View.OnFocusChangeListener() {
411         @Override
412         public void onFocusChange(View v, boolean hasFocus) {
413             if (hasFocus) {
414                 TextView textView = (TextView) v;
415                 Selection.selectAll((Spannable) textView.getText());
416             }
417         }
418     };
419 
420     private class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver cr)421         public QueryHandler(ContentResolver cr) {
422             super(cr);
423         }
424 
425         @Override
onQueryComplete(int token, Object cookie, Cursor c)426         protected void onQueryComplete(int token, Object cookie, Cursor c) {
427         }
428 
429         @Override
onInsertComplete(int token, Object cookie, Uri uri)430         protected void onInsertComplete(int token, Object cookie, Uri uri) {
431             if (DBG) log("onInsertComplete");
432             displayProgress(false);
433             handleResult(uri != null, false);
434         }
435 
436         @Override
onUpdateComplete(int token, Object cookie, int result)437         protected void onUpdateComplete(int token, Object cookie, int result) {
438             if (DBG) log("onUpdateComplete");
439             displayProgress(false);
440             handleResult(result > 0, false);
441         }
442 
443         @Override
onDeleteComplete(int token, Object cookie, int result)444         protected void onDeleteComplete(int token, Object cookie, int result) {
445         }
446     }
447 
log(String msg)448     private void log(String msg) {
449         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
450     }
451 }
452