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