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.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
     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 = 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       * TODO: Fix this logic.
    266       */
    267      private boolean isValidNumber(String number) {
    268          return (number.length() <= 20);
    269      }
    270 
    271 
    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 
    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      */
    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 
    335     private void authenticatePin2() {
    336         Intent intent = new Intent();
    337         intent.setClass(this, GetPin2Screen.class);
    338         startActivityForResult(intent, PIN2_REQUEST_CODE);
    339     }
    340 
    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      */
    356     private void showStatus(CharSequence statusMsg) {
    357         if (statusMsg != null) {
    358             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
    359                     .show();
    360         }
    361     }
    362 
    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 {
    421         public QueryHandler(ContentResolver cr) {
    422             super(cr);
    423         }
    424 
    425         @Override
    426         protected void onQueryComplete(int token, Object cookie, Cursor c) {
    427         }
    428 
    429         @Override
    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
    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
    444         protected void onDeleteComplete(int token, Object cookie, int result) {
    445         }
    446     }
    447 
    448     private void log(String msg) {
    449         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
    450     }
    451 }
    452