• 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.text.Selection;
35 import android.text.Spannable;
36 import android.text.TextUtils;
37 import android.text.method.DialerKeyListener;
38 import android.util.Log;
39 import android.view.Menu;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.view.Window;
43 import android.widget.Button;
44 import android.widget.EditText;
45 import android.widget.LinearLayout;
46 import android.widget.TextView;
47 import android.widget.Toast;
48 
49 /**
50  * Activity to let the user add or edit an FDN contact.
51  */
52 public class EditFdnContactScreen extends Activity {
53     private static final String LOG_TAG = PhoneApp.LOG_TAG;
54     private static final boolean DBG = false;
55 
56     // Menu item codes
57     private static final int MENU_IMPORT = 1;
58     private static final int MENU_DELETE = 2;
59 
60     private static final String INTENT_EXTRA_NAME = "name";
61     private static final String INTENT_EXTRA_NUMBER = "number";
62 
63     private static final int PIN2_REQUEST_CODE = 100;
64 
65     private String mName;
66     private String mNumber;
67     private String mPin2;
68     private boolean mAddContact;
69     private QueryHandler mQueryHandler;
70 
71     private EditText mNameField;
72     private EditText mNumberField;
73     private LinearLayout mPinFieldContainer;
74     private Button mButton;
75 
76     private Handler mHandler = new Handler();
77 
78     /**
79      * Constants used in importing from contacts
80      */
81     /** request code when invoking subactivity */
82     private static final int CONTACTS_PICKER_CODE = 200;
83     /** projection for phone number query */
84     private static final String NUM_PROJECTION[] = {PeopleColumns.DISPLAY_NAME,
85         PhonesColumns.NUMBER};
86     /** static intent to invoke phone number picker */
87     private static final Intent CONTACT_IMPORT_INTENT;
88     static {
89         CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_GET_CONTENT);
90         CONTACT_IMPORT_INTENT.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
91     }
92     /** flag to track saving state */
93     private boolean mDataBusy;
94 
95     @Override
onCreate(Bundle icicle)96     protected void onCreate(Bundle icicle) {
97         super.onCreate(icicle);
98 
99         resolveIntent();
100 
101         getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
102         setContentView(R.layout.edit_fdn_contact_screen);
103         setupView();
104         setTitle(mAddContact ?
105                 R.string.add_fdn_contact : R.string.edit_fdn_contact);
106 
107         mDataBusy = false;
108     }
109 
110     /**
111      * We now want to bring up the pin request screen AFTER the
112      * contact information is displayed, to help with user
113      * experience.
114      *
115      * Also, process the results from the contact picker.
116      */
117     @Override
onActivityResult(int requestCode, int resultCode, Intent intent)118     protected void onActivityResult(int requestCode, int resultCode,
119                                     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         if (TextUtils.isEmpty(mName)) {
209             mAddContact = true;
210         }
211     }
212 
213     /**
214      * We have multiple layouts, one to indicate that the user needs to
215      * open the keyboard to enter information (if the keybord is hidden).
216      * So, we need to make sure that the layout here matches that in the
217      * layout file.
218      */
setupView()219     private void setupView() {
220         mNameField = (EditText) findViewById(R.id.fdn_name);
221         if (mNameField != null) {
222             mNameField.setOnFocusChangeListener(mOnFocusChangeHandler);
223             mNameField.setOnClickListener(mClicked);
224         }
225 
226         mNumberField = (EditText) findViewById(R.id.fdn_number);
227         if (mNumberField != null) {
228             mNumberField.setKeyListener(DialerKeyListener.getInstance());
229             mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler);
230             mNumberField.setOnClickListener(mClicked);
231         }
232 
233         if (!mAddContact) {
234             if (mNameField != null) {
235                 mNameField.setText(mName);
236             }
237             if (mNumberField != null) {
238                 mNumberField.setText(mNumber);
239             }
240         }
241 
242         mButton = (Button) findViewById(R.id.button);
243         if (mButton != null) {
244             mButton.setOnClickListener(mClicked);
245         }
246 
247         mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
248 
249     }
250 
getNameFromTextField()251     private String getNameFromTextField() {
252         return mNameField.getText().toString();
253     }
254 
getNumberFromTextField()255     private String getNumberFromTextField() {
256         return mNumberField.getText().toString();
257     }
258 
getContentURI()259     private Uri getContentURI() {
260         return Uri.parse("content://icc/fdn");
261     }
262 
263     /**
264       * @param number is voice mail number
265       * @return true if number length is less than 20-digit limit
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         if (!isValidNumber(getNumberFromTextField())) {
276             handleResult(false, true);
277             return;
278         }
279 
280         Uri uri = getContentURI();
281 
282         ContentValues bundle = new ContentValues(3);
283         bundle.put("tag", getNameFromTextField());
284         bundle.put("number", getNumberFromTextField());
285         bundle.put("pin2", mPin2);
286 
287 
288         mQueryHandler = new QueryHandler(getContentResolver());
289         mQueryHandler.startInsert(0, null, uri, bundle);
290         displayProgress(true);
291         showStatus(getResources().getText(R.string.adding_fdn_contact));
292     }
293 
updateContact()294     private void updateContact() {
295         if (DBG) log("updateContact");
296 
297         if (!isValidNumber(getNumberFromTextField())) {
298             handleResult(false, true);
299             return;
300         }
301         Uri uri = getContentURI();
302 
303         ContentValues bundle = new ContentValues();
304         bundle.put("tag", mName);
305         bundle.put("number", mNumber);
306         bundle.put("newTag", getNameFromTextField());
307         bundle.put("newNumber", getNumberFromTextField());
308         bundle.put("pin2", mPin2);
309 
310         mQueryHandler = new QueryHandler(getContentResolver());
311         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
312         displayProgress(true);
313         showStatus(getResources().getText(R.string.updating_fdn_contact));
314     }
315 
316     /**
317      * Handle the delete command, based upon the state of the Activity.
318      */
deleteSelected()319     private void deleteSelected() {
320         // delete ONLY if this is NOT a new contact.
321         if (!mAddContact) {
322             Intent intent = new Intent();
323             intent.setClass(this, DeleteFdnContactScreen.class);
324             intent.putExtra(INTENT_EXTRA_NAME, mName);
325             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
326             startActivity(intent);
327         }
328         finish();
329     }
330 
authenticatePin2()331     private void authenticatePin2() {
332         Intent intent = new Intent();
333         intent.setClass(this, GetPin2Screen.class);
334         startActivityForResult(intent, PIN2_REQUEST_CODE);
335     }
336 
displayProgress(boolean flag)337     private void displayProgress(boolean flag) {
338         // indicate we are busy.
339         mDataBusy = flag;
340         getWindow().setFeatureInt(
341                 Window.FEATURE_INDETERMINATE_PROGRESS,
342                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
343         // make sure we don't allow calls to save when we're
344         // not ready for them.
345         mButton.setClickable(!mDataBusy);
346     }
347 
348     /**
349      * Removed the status field, with preference to displaying a toast
350      * to match the rest of settings UI.
351      */
showStatus(CharSequence statusMsg)352     private void showStatus(CharSequence statusMsg) {
353         if (statusMsg != null) {
354             Toast.makeText(this, statusMsg, Toast.LENGTH_SHORT)
355             .show();
356         }
357     }
358 
handleResult(boolean success, boolean invalidNumber)359     private void handleResult(boolean success, boolean invalidNumber) {
360         if (success) {
361             if (DBG) log("handleResult: success!");
362             showStatus(getResources().getText(mAddContact ?
363                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
364         } else {
365             if (DBG) log("handleResult: failed!");
366             if (invalidNumber)
367                 showStatus(getResources().getText(R.string.fdn_invalid_number));
368             else
369                 showStatus(getResources().getText(R.string.pin2_invalid));
370         }
371 
372         mHandler.postDelayed(new Runnable() {
373             public void run() {
374                 finish();
375             }
376         }, 2000);
377 
378     }
379 
380     private View.OnClickListener mClicked = new View.OnClickListener() {
381         public void onClick(View v) {
382             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
383                 return;
384             }
385 
386             if (v == mNameField) {
387                 mNumberField.requestFocus();
388             } else if (v == mNumberField) {
389                 mButton.requestFocus();
390             } else if (v == mButton) {
391                 // Autheticate the pin AFTER the contact information
392                 // is entered, and if we're not busy.
393                 if (!mDataBusy) {
394                     authenticatePin2();
395                 }
396             }
397         }
398     };
399 
400     View.OnFocusChangeListener mOnFocusChangeHandler =
401             new View.OnFocusChangeListener() {
402         public void onFocusChange(View v, boolean hasFocus) {
403             if (hasFocus) {
404                 TextView textView = (TextView) v;
405                 Selection.selectAll((Spannable) textView.getText());
406             }
407         }
408     };
409 
410     private class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver cr)411         public QueryHandler(ContentResolver cr) {
412             super(cr);
413         }
414 
415         @Override
onQueryComplete(int token, Object cookie, Cursor c)416         protected void onQueryComplete(int token, Object cookie, Cursor c) {
417         }
418 
419         @Override
onInsertComplete(int token, Object cookie, Uri uri)420         protected void onInsertComplete(int token, Object cookie,
421                                         Uri uri) {
422             if (DBG) log("onInsertComplete");
423             displayProgress(false);
424             handleResult(uri != null, false);
425         }
426 
427         @Override
onUpdateComplete(int token, Object cookie, int result)428         protected void onUpdateComplete(int token, Object cookie, int result) {
429             if (DBG) log("onUpdateComplete");
430             displayProgress(false);
431             handleResult(result > 0, false);
432         }
433 
434         @Override
onDeleteComplete(int token, Object cookie, int result)435         protected void onDeleteComplete(int token, Object cookie, int result) {
436         }
437     }
438 
log(String msg)439     private void log(String msg) {
440         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
441     }
442 }
443