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 import com.android.internal.telephony.Phone;
     51 import com.android.internal.telephony.PhoneFactory;
     52 
     53 /**
     54  * Activity to let the user add or edit an FDN contact.
     55  */
     56 public class EditFdnContactScreen extends Activity {
     57     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
     58     private static final boolean DBG = false;
     59 
     60     // Menu item codes
     61     private static final int MENU_IMPORT = 1;
     62     private static final int MENU_DELETE = 2;
     63 
     64     private static final String INTENT_EXTRA_NAME = "name";
     65     private static final String INTENT_EXTRA_NUMBER = "number";
     66 
     67     private static final int PIN2_REQUEST_CODE = 100;
     68 
     69     private String mName;
     70     private String mNumber;
     71     private String mPin2;
     72     private boolean mAddContact;
     73     private QueryHandler mQueryHandler;
     74 
     75     private EditText mNameField;
     76     private EditText mNumberField;
     77     private LinearLayout mPinFieldContainer;
     78     private Button mButton;
     79 
     80     private Handler mHandler = new Handler();
     81 
     82     /**
     83      * Constants used in importing from contacts
     84      */
     85     /** request code when invoking subactivity */
     86     private static final int CONTACTS_PICKER_CODE = 200;
     87     /** projection for phone number query */
     88     private static final String NUM_PROJECTION[] = {PeopleColumns.DISPLAY_NAME,
     89         PhonesColumns.NUMBER};
     90     /** static intent to invoke phone number picker */
     91     private static final Intent CONTACT_IMPORT_INTENT;
     92     static {
     93         CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_GET_CONTENT);
     94         CONTACT_IMPORT_INTENT.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
     95     }
     96     /** flag to track saving state */
     97     private boolean mDataBusy;
     98 
     99     @Override
    100     protected void onCreate(Bundle icicle) {
    101         super.onCreate(icicle);
    102 
    103         resolveIntent();
    104 
    105         getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    106         setContentView(R.layout.edit_fdn_contact_screen);
    107         setupView();
    108         setTitle(mAddContact ?
    109                 R.string.add_fdn_contact : R.string.edit_fdn_contact);
    110 
    111         displayProgress(false);
    112     }
    113 
    114     /**
    115      * We now want to bring up the pin request screen AFTER the
    116      * contact information is displayed, to help with user
    117      * experience.
    118      *
    119      * Also, process the results from the contact picker.
    120      */
    121     @Override
    122     protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    123         if (DBG) log("onActivityResult request:" + requestCode + " result:" + resultCode);
    124 
    125         switch (requestCode) {
    126             case PIN2_REQUEST_CODE:
    127                 Bundle extras = (intent != null) ? intent.getExtras() : null;
    128                 if (extras != null) {
    129                     mPin2 = extras.getString("pin2");
    130                     if (mAddContact) {
    131                         addContact();
    132                     } else {
    133                         updateContact();
    134                     }
    135                 } else if (resultCode != RESULT_OK) {
    136                     // if they cancelled, then we just cancel too.
    137                     if (DBG) log("onActivityResult: cancelled.");
    138                     finish();
    139                 }
    140                 break;
    141 
    142             // look for the data associated with this number, and update
    143             // the display with it.
    144             case CONTACTS_PICKER_CODE:
    145                 if (resultCode != RESULT_OK) {
    146                     if (DBG) log("onActivityResult: cancelled.");
    147                     return;
    148                 }
    149                 Cursor cursor = null;
    150                 try {
    151                     cursor = getContentResolver().query(intent.getData(),
    152                         NUM_PROJECTION, null, null, null);
    153                     if ((cursor == null) || (!cursor.moveToFirst())) {
    154                         Log.w(LOG_TAG,"onActivityResult: bad contact data, no results found.");
    155                         return;
    156                     }
    157                     mNameField.setText(cursor.getString(0));
    158                     mNumberField.setText(cursor.getString(1));
    159                 } finally {
    160                     if (cursor != null) {
    161                         cursor.close();
    162                     }
    163                 }
    164                 break;
    165         }
    166     }
    167 
    168     /**
    169      * Overridden to display the import and delete commands.
    170      */
    171     @Override
    172     public boolean onCreateOptionsMenu(Menu menu) {
    173         super.onCreateOptionsMenu(menu);
    174 
    175         Resources r = getResources();
    176 
    177         // Added the icons to the context menu
    178         menu.add(0, MENU_IMPORT, 0, r.getString(R.string.importToFDNfromContacts))
    179                 .setIcon(R.drawable.ic_menu_contact);
    180         menu.add(0, MENU_DELETE, 0, r.getString(R.string.menu_delete))
    181                 .setIcon(android.R.drawable.ic_menu_delete);
    182         return true;
    183     }
    184 
    185     /**
    186      * Allow the menu to be opened ONLY if we're not busy.
    187      */
    188     @Override
    189     public boolean onPrepareOptionsMenu(Menu menu) {
    190         boolean result = super.onPrepareOptionsMenu(menu);
    191         return mDataBusy ? false : result;
    192     }
    193 
    194     /**
    195      * Overridden to allow for handling of delete and import.
    196      */
    197     @Override
    198     public boolean onOptionsItemSelected(MenuItem item) {
    199         switch (item.getItemId()) {
    200             case MENU_IMPORT:
    201                 startActivityForResult(CONTACT_IMPORT_INTENT, CONTACTS_PICKER_CODE);
    202                 return true;
    203 
    204             case MENU_DELETE:
    205                 deleteSelected();
    206                 return true;
    207         }
    208 
    209         return super.onOptionsItemSelected(item);
    210     }
    211 
    212     private void resolveIntent() {
    213         Intent intent = getIntent();
    214 
    215         mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
    216         mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
    217 
    218         mAddContact = TextUtils.isEmpty(mNumber);
    219     }
    220 
    221     /**
    222      * We have multiple layouts, one to indicate that the user needs to
    223      * open the keyboard to enter information (if the keybord is hidden).
    224      * So, we need to make sure that the layout here matches that in the
    225      * layout file.
    226      */
    227     private void setupView() {
    228         mNameField = (EditText) findViewById(R.id.fdn_name);
    229         if (mNameField != null) {
    230             mNameField.setOnFocusChangeListener(mOnFocusChangeHandler);
    231             mNameField.setOnClickListener(mClicked);
    232         }
    233 
    234         mNumberField = (EditText) findViewById(R.id.fdn_number);
    235         if (mNumberField != null) {
    236             mNumberField.setKeyListener(DialerKeyListener.getInstance());
    237             mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler);
    238             mNumberField.setOnClickListener(mClicked);
    239         }
    240 
    241         if (!mAddContact) {
    242             if (mNameField != null) {
    243                 mNameField.setText(mName);
    244             }
    245             if (mNumberField != null) {
    246                 mNumberField.setText(mNumber);
    247             }
    248         }
    249 
    250         mButton = (Button) findViewById(R.id.button);
    251         if (mButton != null) {
    252             mButton.setOnClickListener(mClicked);
    253         }
    254 
    255         mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
    256 
    257     }
    258 
    259     private String getNameFromTextField() {
    260         return mNameField.getText().toString();
    261     }
    262 
    263     private String getNumberFromTextField() {
    264         return mNumberField.getText().toString();
    265     }
    266 
    267     private Uri getContentURI() {
    268         return Uri.parse("content://icc/fdn");
    269     }
    270 
    271     /**
    272       * @param number is voice mail number
    273       * @return true if number length is less than 20-digit limit
    274       *
    275       * TODO: Fix this logic.
    276       */
    277      private boolean isValidNumber(String number) {
    278          return (number.length() <= 20);
    279      }
    280 
    281 
    282     private void addContact() {
    283         if (DBG) log("addContact");
    284 
    285         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
    286 
    287         if (!isValidNumber(number)) {
    288             handleResult(false, true);
    289             return;
    290         }
    291 
    292         Uri uri = getContentURI();
    293 
    294         ContentValues bundle = new ContentValues(3);
    295         bundle.put("tag", getNameFromTextField());
    296         bundle.put("number", number);
    297         bundle.put("pin2", mPin2);
    298 
    299         mQueryHandler = new QueryHandler(getContentResolver());
    300         mQueryHandler.startInsert(0, null, uri, bundle);
    301         displayProgress(true);
    302         showStatus(getResources().getText(R.string.adding_fdn_contact));
    303     }
    304 
    305     private void updateContact() {
    306         if (DBG) log("updateContact");
    307 
    308         final String name = getNameFromTextField();
    309         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
    310 
    311         if (!isValidNumber(number)) {
    312             handleResult(false, true);
    313             return;
    314         }
    315         Uri uri = getContentURI();
    316 
    317         ContentValues bundle = new ContentValues();
    318         bundle.put("tag", mName);
    319         bundle.put("number", mNumber);
    320         bundle.put("newTag", name);
    321         bundle.put("newNumber", number);
    322         bundle.put("pin2", mPin2);
    323 
    324         mQueryHandler = new QueryHandler(getContentResolver());
    325         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
    326         displayProgress(true);
    327         showStatus(getResources().getText(R.string.updating_fdn_contact));
    328     }
    329 
    330     /**
    331      * Handle the delete command, based upon the state of the Activity.
    332      */
    333     private void deleteSelected() {
    334         // delete ONLY if this is NOT a new contact.
    335         if (!mAddContact) {
    336             Intent intent = new Intent();
    337             intent.setClass(this, DeleteFdnContactScreen.class);
    338             intent.putExtra(INTENT_EXTRA_NAME, mName);
    339             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
    340             startActivity(intent);
    341         }
    342         finish();
    343     }
    344 
    345     private void authenticatePin2() {
    346         Intent intent = new Intent();
    347         intent.setClass(this, GetPin2Screen.class);
    348         startActivityForResult(intent, PIN2_REQUEST_CODE);
    349     }
    350 
    351     private void displayProgress(boolean flag) {
    352         // indicate we are busy.
    353         mDataBusy = flag;
    354         getWindow().setFeatureInt(
    355                 Window.FEATURE_INDETERMINATE_PROGRESS,
    356                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
    357         // make sure we don't allow calls to save when we're
    358         // not ready for them.
    359         mButton.setClickable(!mDataBusy);
    360     }
    361 
    362     /**
    363      * Removed the status field, with preference to displaying a toast
    364      * to match the rest of settings UI.
    365      */
    366     private void showStatus(CharSequence statusMsg) {
    367         if (statusMsg != null) {
    368             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
    369                     .show();
    370         }
    371     }
    372 
    373     private void handleResult(boolean success, boolean invalidNumber) {
    374         if (success) {
    375             if (DBG) log("handleResult: success!");
    376             showStatus(getResources().getText(mAddContact ?
    377                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
    378         } else {
    379             if (DBG) log("handleResult: failed!");
    380             if (invalidNumber) {
    381                 showStatus(getResources().getText(R.string.fdn_invalid_number));
    382             } else {
    383                if (PhoneFactory.getDefaultPhone().getIccCard().getIccPin2Blocked()) {
    384                     showStatus(getResources().getText(R.string.fdn_enable_puk2_requested));
    385                 } else if (PhoneFactory.getDefaultPhone().getIccCard().getIccPuk2Blocked()) {
    386                     showStatus(getResources().getText(R.string.puk2_blocked));
    387                 } else {
    388                     // There's no way to know whether the failure is due to incorrect PIN2 or
    389                     // an inappropriate phone number.
    390                     showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
    391                 }
    392             }
    393         }
    394 
    395         mHandler.postDelayed(new Runnable() {
    396             @Override
    397             public void run() {
    398                 finish();
    399             }
    400         }, 2000);
    401 
    402     }
    403 
    404     private final View.OnClickListener mClicked = new View.OnClickListener() {
    405         @Override
    406         public void onClick(View v) {
    407             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
    408                 return;
    409             }
    410 
    411             if (v == mNameField) {
    412                 mNumberField.requestFocus();
    413             } else if (v == mNumberField) {
    414                 mButton.requestFocus();
    415             } else if (v == mButton) {
    416                 // Authenticate the pin AFTER the contact information
    417                 // is entered, and if we're not busy.
    418                 if (!mDataBusy) {
    419                     authenticatePin2();
    420                 }
    421             }
    422         }
    423     };
    424 
    425     private final View.OnFocusChangeListener mOnFocusChangeHandler =
    426             new View.OnFocusChangeListener() {
    427         @Override
    428         public void onFocusChange(View v, boolean hasFocus) {
    429             if (hasFocus) {
    430                 TextView textView = (TextView) v;
    431                 Selection.selectAll((Spannable) textView.getText());
    432             }
    433         }
    434     };
    435 
    436     private class QueryHandler extends AsyncQueryHandler {
    437         public QueryHandler(ContentResolver cr) {
    438             super(cr);
    439         }
    440 
    441         @Override
    442         protected void onQueryComplete(int token, Object cookie, Cursor c) {
    443         }
    444 
    445         @Override
    446         protected void onInsertComplete(int token, Object cookie, Uri uri) {
    447             if (DBG) log("onInsertComplete");
    448             displayProgress(false);
    449             handleResult(uri != null, false);
    450         }
    451 
    452         @Override
    453         protected void onUpdateComplete(int token, Object cookie, int result) {
    454             if (DBG) log("onUpdateComplete");
    455             displayProgress(false);
    456             handleResult(result > 0, false);
    457         }
    458 
    459         @Override
    460         protected void onDeleteComplete(int token, Object cookie, int result) {
    461         }
    462     }
    463 
    464     private void log(String msg) {
    465         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
    466     }
    467 }
    468