• 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 = PhoneApp.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 number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
299 
300         if (!isValidNumber(number)) {
301             handleResult(false, true);
302             return;
303         }
304         Uri uri = getContentURI();
305 
306         ContentValues bundle = new ContentValues();
307         bundle.put("tag", mName);
308         bundle.put("number", mNumber);
309         bundle.put("newTag", number);
310         bundle.put("newNumber", number);
311         bundle.put("pin2", mPin2);
312 
313         mQueryHandler = new QueryHandler(getContentResolver());
314         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
315         displayProgress(true);
316         showStatus(getResources().getText(R.string.updating_fdn_contact));
317     }
318 
319     /**
320      * Handle the delete command, based upon the state of the Activity.
321      */
deleteSelected()322     private void deleteSelected() {
323         // delete ONLY if this is NOT a new contact.
324         if (!mAddContact) {
325             Intent intent = new Intent();
326             intent.setClass(this, DeleteFdnContactScreen.class);
327             intent.putExtra(INTENT_EXTRA_NAME, mName);
328             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
329             startActivity(intent);
330         }
331         finish();
332     }
333 
authenticatePin2()334     private void authenticatePin2() {
335         Intent intent = new Intent();
336         intent.setClass(this, GetPin2Screen.class);
337         startActivityForResult(intent, PIN2_REQUEST_CODE);
338     }
339 
displayProgress(boolean flag)340     private void displayProgress(boolean flag) {
341         // indicate we are busy.
342         mDataBusy = flag;
343         getWindow().setFeatureInt(
344                 Window.FEATURE_INDETERMINATE_PROGRESS,
345                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
346         // make sure we don't allow calls to save when we're
347         // not ready for them.
348         mButton.setClickable(!mDataBusy);
349     }
350 
351     /**
352      * Removed the status field, with preference to displaying a toast
353      * to match the rest of settings UI.
354      */
showStatus(CharSequence statusMsg)355     private void showStatus(CharSequence statusMsg) {
356         if (statusMsg != null) {
357             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
358                     .show();
359         }
360     }
361 
handleResult(boolean success, boolean invalidNumber)362     private void handleResult(boolean success, boolean invalidNumber) {
363         if (success) {
364             if (DBG) log("handleResult: success!");
365             showStatus(getResources().getText(mAddContact ?
366                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
367         } else {
368             if (DBG) log("handleResult: failed!");
369             if (invalidNumber) {
370                 showStatus(getResources().getText(R.string.fdn_invalid_number));
371             } else {
372                 // There's no way to know whether the failure is due to incorrect PIN2 or
373                 // an inappropriate phone number.
374                 showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
375             }
376         }
377 
378         mHandler.postDelayed(new Runnable() {
379             @Override
380             public void run() {
381                 finish();
382             }
383         }, 2000);
384 
385     }
386 
387     private final View.OnClickListener mClicked = new View.OnClickListener() {
388         @Override
389         public void onClick(View v) {
390             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
391                 return;
392             }
393 
394             if (v == mNameField) {
395                 mNumberField.requestFocus();
396             } else if (v == mNumberField) {
397                 mButton.requestFocus();
398             } else if (v == mButton) {
399                 // Authenticate the pin AFTER the contact information
400                 // is entered, and if we're not busy.
401                 if (!mDataBusy) {
402                     authenticatePin2();
403                 }
404             }
405         }
406     };
407 
408     private final View.OnFocusChangeListener mOnFocusChangeHandler =
409             new View.OnFocusChangeListener() {
410         @Override
411         public void onFocusChange(View v, boolean hasFocus) {
412             if (hasFocus) {
413                 TextView textView = (TextView) v;
414                 Selection.selectAll((Spannable) textView.getText());
415             }
416         }
417     };
418 
419     private class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver cr)420         public QueryHandler(ContentResolver cr) {
421             super(cr);
422         }
423 
424         @Override
onQueryComplete(int token, Object cookie, Cursor c)425         protected void onQueryComplete(int token, Object cookie, Cursor c) {
426         }
427 
428         @Override
onInsertComplete(int token, Object cookie, Uri uri)429         protected void onInsertComplete(int token, Object cookie, Uri uri) {
430             if (DBG) log("onInsertComplete");
431             displayProgress(false);
432             handleResult(uri != null, false);
433         }
434 
435         @Override
onUpdateComplete(int token, Object cookie, int result)436         protected void onUpdateComplete(int token, Object cookie, int result) {
437             if (DBG) log("onUpdateComplete");
438             displayProgress(false);
439             handleResult(result > 0, false);
440         }
441 
442         @Override
onDeleteComplete(int token, Object cookie, int result)443         protected void onDeleteComplete(int token, Object cookie, int result) {
444         }
445     }
446 
log(String msg)447     private void log(String msg) {
448         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
449     }
450 }
451