Home | History | Annotate | Download | only in phone
      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
     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
    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
    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
    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
    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 
    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      */
    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 
    249     private String getNameFromTextField() {
    250         return mNameField.getText().toString();
    251     }
    252 
    253     private String getNumberFromTextField() {
    254         return mNumberField.getText().toString();
    255     }
    256 
    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      private boolean isValidNumber(String number) {
    266          return (number.length() <= 20);
    267      }
    268 
    269 
    270     private void addContact() {
    271         if (DBG) log("addContact");
    272 
    273         if (!isValidNumber(getNumberFromTextField())) {
    274             handleResult(false, true);
    275             return;
    276         }
    277 
    278         Uri uri = getContentURI();
    279 
    280         ContentValues bundle = new ContentValues(3);
    281         bundle.put("tag", getNameFromTextField());
    282         bundle.put("number", getNumberFromTextField());
    283         bundle.put("pin2", mPin2);
    284 
    285 
    286         mQueryHandler = new QueryHandler(getContentResolver());
    287         mQueryHandler.startInsert(0, null, uri, bundle);
    288         displayProgress(true);
    289         showStatus(getResources().getText(R.string.adding_fdn_contact));
    290     }
    291 
    292     private void updateContact() {
    293         if (DBG) log("updateContact");
    294 
    295         if (!isValidNumber(getNumberFromTextField())) {
    296             handleResult(false, true);
    297             return;
    298         }
    299         Uri uri = getContentURI();
    300 
    301         ContentValues bundle = new ContentValues();
    302         bundle.put("tag", mName);
    303         bundle.put("number", mNumber);
    304         bundle.put("newTag", getNameFromTextField());
    305         bundle.put("newNumber", getNumberFromTextField());
    306         bundle.put("pin2", mPin2);
    307 
    308         mQueryHandler = new QueryHandler(getContentResolver());
    309         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
    310         displayProgress(true);
    311         showStatus(getResources().getText(R.string.updating_fdn_contact));
    312     }
    313 
    314     /**
    315      * Handle the delete command, based upon the state of the Activity.
    316      */
    317     private void deleteSelected() {
    318         // delete ONLY if this is NOT a new contact.
    319         if (!mAddContact) {
    320             Intent intent = new Intent();
    321             intent.setClass(this, DeleteFdnContactScreen.class);
    322             intent.putExtra(INTENT_EXTRA_NAME, mName);
    323             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
    324             startActivity(intent);
    325         }
    326         finish();
    327     }
    328 
    329     private void authenticatePin2() {
    330         Intent intent = new Intent();
    331         intent.setClass(this, GetPin2Screen.class);
    332         startActivityForResult(intent, PIN2_REQUEST_CODE);
    333     }
    334 
    335     private void displayProgress(boolean flag) {
    336         // indicate we are busy.
    337         mDataBusy = flag;
    338         getWindow().setFeatureInt(
    339                 Window.FEATURE_INDETERMINATE_PROGRESS,
    340                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
    341         // make sure we don't allow calls to save when we're
    342         // not ready for them.
    343         mButton.setClickable(!mDataBusy);
    344     }
    345 
    346     /**
    347      * Removed the status field, with preference to displaying a toast
    348      * to match the rest of settings UI.
    349      */
    350     private void showStatus(CharSequence statusMsg) {
    351         if (statusMsg != null) {
    352             Toast.makeText(this, statusMsg, Toast.LENGTH_SHORT)
    353             .show();
    354         }
    355     }
    356 
    357     private void handleResult(boolean success, boolean invalidNumber) {
    358         if (success) {
    359             if (DBG) log("handleResult: success!");
    360             showStatus(getResources().getText(mAddContact ?
    361                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
    362         } else {
    363             if (DBG) log("handleResult: failed!");
    364             if (invalidNumber)
    365                 showStatus(getResources().getText(R.string.fdn_invalid_number));
    366             else
    367                 showStatus(getResources().getText(R.string.pin2_invalid));
    368         }
    369 
    370         mHandler.postDelayed(new Runnable() {
    371             public void run() {
    372                 finish();
    373             }
    374         }, 2000);
    375 
    376     }
    377 
    378     private View.OnClickListener mClicked = new View.OnClickListener() {
    379         public void onClick(View v) {
    380             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
    381                 return;
    382             }
    383 
    384             if (v == mNameField) {
    385                 mNumberField.requestFocus();
    386             } else if (v == mNumberField) {
    387                 mButton.requestFocus();
    388             } else if (v == mButton) {
    389                 // Authenticate the pin AFTER the contact information
    390                 // is entered, and if we're not busy.
    391                 if (!mDataBusy) {
    392                     authenticatePin2();
    393                 }
    394             }
    395         }
    396     };
    397 
    398     View.OnFocusChangeListener mOnFocusChangeHandler =
    399             new View.OnFocusChangeListener() {
    400         public void onFocusChange(View v, boolean hasFocus) {
    401             if (hasFocus) {
    402                 TextView textView = (TextView) v;
    403                 Selection.selectAll((Spannable) textView.getText());
    404             }
    405         }
    406     };
    407 
    408     private class QueryHandler extends AsyncQueryHandler {
    409         public QueryHandler(ContentResolver cr) {
    410             super(cr);
    411         }
    412 
    413         @Override
    414         protected void onQueryComplete(int token, Object cookie, Cursor c) {
    415         }
    416 
    417         @Override
    418         protected void onInsertComplete(int token, Object cookie,
    419                                         Uri uri) {
    420             if (DBG) log("onInsertComplete");
    421             displayProgress(false);
    422             handleResult(uri != null, false);
    423         }
    424 
    425         @Override
    426         protected void onUpdateComplete(int token, Object cookie, int result) {
    427             if (DBG) log("onUpdateComplete");
    428             displayProgress(false);
    429             handleResult(result > 0, false);
    430         }
    431 
    432         @Override
    433         protected void onDeleteComplete(int token, Object cookie, int result) {
    434         }
    435     }
    436 
    437     private void log(String msg) {
    438         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
    439     }
    440 }
    441