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