| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.phone; |
| |
| import static android.view.Window.PROGRESS_VISIBILITY_OFF; |
| import static android.view.Window.PROGRESS_VISIBILITY_ON; |
| |
| import android.app.Activity; |
| import android.content.AsyncQueryHandler; |
| import android.content.ContentResolver; |
| import android.content.ContentValues; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.provider.Contacts.PeopleColumns; |
| import android.provider.Contacts.PhonesColumns; |
| import android.telephony.PhoneNumberUtils; |
| import android.text.Selection; |
| import android.text.Spannable; |
| import android.text.TextUtils; |
| import android.text.method.DialerKeyListener; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.Window; |
| import android.widget.Button; |
| import android.widget.EditText; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| import android.widget.Toast; |
| |
| /** |
| * Activity to let the user add or edit an FDN contact. |
| */ |
| public class EditFdnContactScreen extends Activity { |
| private static final String LOG_TAG = PhoneGlobals.LOG_TAG; |
| private static final boolean DBG = false; |
| |
| // Menu item codes |
| private static final int MENU_IMPORT = 1; |
| private static final int MENU_DELETE = 2; |
| |
| private static final String INTENT_EXTRA_NAME = "name"; |
| private static final String INTENT_EXTRA_NUMBER = "number"; |
| |
| private static final int PIN2_REQUEST_CODE = 100; |
| |
| private String mName; |
| private String mNumber; |
| private String mPin2; |
| private boolean mAddContact; |
| private QueryHandler mQueryHandler; |
| |
| private EditText mNameField; |
| private EditText mNumberField; |
| private LinearLayout mPinFieldContainer; |
| private Button mButton; |
| |
| private Handler mHandler = new Handler(); |
| |
| /** |
| * Constants used in importing from contacts |
| */ |
| /** request code when invoking subactivity */ |
| private static final int CONTACTS_PICKER_CODE = 200; |
| /** projection for phone number query */ |
| private static final String NUM_PROJECTION[] = {PeopleColumns.DISPLAY_NAME, |
| PhonesColumns.NUMBER}; |
| /** static intent to invoke phone number picker */ |
| private static final Intent CONTACT_IMPORT_INTENT; |
| static { |
| CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_GET_CONTENT); |
| CONTACT_IMPORT_INTENT.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE); |
| } |
| /** flag to track saving state */ |
| private boolean mDataBusy; |
| |
| @Override |
| protected void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| resolveIntent(); |
| |
| getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS); |
| setContentView(R.layout.edit_fdn_contact_screen); |
| setupView(); |
| setTitle(mAddContact ? |
| R.string.add_fdn_contact : R.string.edit_fdn_contact); |
| |
| displayProgress(false); |
| } |
| |
| /** |
| * We now want to bring up the pin request screen AFTER the |
| * contact information is displayed, to help with user |
| * experience. |
| * |
| * Also, process the results from the contact picker. |
| */ |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent intent) { |
| if (DBG) log("onActivityResult request:" + requestCode + " result:" + resultCode); |
| |
| switch (requestCode) { |
| case PIN2_REQUEST_CODE: |
| Bundle extras = (intent != null) ? intent.getExtras() : null; |
| if (extras != null) { |
| mPin2 = extras.getString("pin2"); |
| if (mAddContact) { |
| addContact(); |
| } else { |
| updateContact(); |
| } |
| } else if (resultCode != RESULT_OK) { |
| // if they cancelled, then we just cancel too. |
| if (DBG) log("onActivityResult: cancelled."); |
| finish(); |
| } |
| break; |
| |
| // look for the data associated with this number, and update |
| // the display with it. |
| case CONTACTS_PICKER_CODE: |
| if (resultCode != RESULT_OK) { |
| if (DBG) log("onActivityResult: cancelled."); |
| return; |
| } |
| Cursor cursor = getContentResolver().query(intent.getData(), |
| NUM_PROJECTION, null, null, null); |
| if ((cursor == null) || (!cursor.moveToFirst())) { |
| Log.w(LOG_TAG,"onActivityResult: bad contact data, no results found."); |
| return; |
| } |
| mNameField.setText(cursor.getString(0)); |
| mNumberField.setText(cursor.getString(1)); |
| break; |
| } |
| } |
| |
| /** |
| * Overridden to display the import and delete commands. |
| */ |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| |
| Resources r = getResources(); |
| |
| // Added the icons to the context menu |
| menu.add(0, MENU_IMPORT, 0, r.getString(R.string.importToFDNfromContacts)) |
| .setIcon(R.drawable.ic_menu_contact); |
| menu.add(0, MENU_DELETE, 0, r.getString(R.string.menu_delete)) |
| .setIcon(android.R.drawable.ic_menu_delete); |
| return true; |
| } |
| |
| /** |
| * Allow the menu to be opened ONLY if we're not busy. |
| */ |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| boolean result = super.onPrepareOptionsMenu(menu); |
| return mDataBusy ? false : result; |
| } |
| |
| /** |
| * Overridden to allow for handling of delete and import. |
| */ |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case MENU_IMPORT: |
| startActivityForResult(CONTACT_IMPORT_INTENT, CONTACTS_PICKER_CODE); |
| return true; |
| |
| case MENU_DELETE: |
| deleteSelected(); |
| return true; |
| } |
| |
| return super.onOptionsItemSelected(item); |
| } |
| |
| private void resolveIntent() { |
| Intent intent = getIntent(); |
| |
| mName = intent.getStringExtra(INTENT_EXTRA_NAME); |
| mNumber = intent.getStringExtra(INTENT_EXTRA_NUMBER); |
| |
| mAddContact = TextUtils.isEmpty(mNumber); |
| } |
| |
| /** |
| * We have multiple layouts, one to indicate that the user needs to |
| * open the keyboard to enter information (if the keybord is hidden). |
| * So, we need to make sure that the layout here matches that in the |
| * layout file. |
| */ |
| private void setupView() { |
| mNameField = (EditText) findViewById(R.id.fdn_name); |
| if (mNameField != null) { |
| mNameField.setOnFocusChangeListener(mOnFocusChangeHandler); |
| mNameField.setOnClickListener(mClicked); |
| } |
| |
| mNumberField = (EditText) findViewById(R.id.fdn_number); |
| if (mNumberField != null) { |
| mNumberField.setKeyListener(DialerKeyListener.getInstance()); |
| mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler); |
| mNumberField.setOnClickListener(mClicked); |
| } |
| |
| if (!mAddContact) { |
| if (mNameField != null) { |
| mNameField.setText(mName); |
| } |
| if (mNumberField != null) { |
| mNumberField.setText(mNumber); |
| } |
| } |
| |
| mButton = (Button) findViewById(R.id.button); |
| if (mButton != null) { |
| mButton.setOnClickListener(mClicked); |
| } |
| |
| mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc); |
| |
| } |
| |
| private String getNameFromTextField() { |
| return mNameField.getText().toString(); |
| } |
| |
| private String getNumberFromTextField() { |
| return mNumberField.getText().toString(); |
| } |
| |
| private Uri getContentURI() { |
| return Uri.parse("content://icc/fdn"); |
| } |
| |
| /** |
| * @param number is voice mail number |
| * @return true if number length is less than 20-digit limit |
| * |
| * TODO: Fix this logic. |
| */ |
| private boolean isValidNumber(String number) { |
| return (number.length() <= 20); |
| } |
| |
| |
| private void addContact() { |
| if (DBG) log("addContact"); |
| |
| final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField()); |
| |
| if (!isValidNumber(number)) { |
| handleResult(false, true); |
| return; |
| } |
| |
| Uri uri = getContentURI(); |
| |
| ContentValues bundle = new ContentValues(3); |
| bundle.put("tag", getNameFromTextField()); |
| bundle.put("number", number); |
| bundle.put("pin2", mPin2); |
| |
| mQueryHandler = new QueryHandler(getContentResolver()); |
| mQueryHandler.startInsert(0, null, uri, bundle); |
| displayProgress(true); |
| showStatus(getResources().getText(R.string.adding_fdn_contact)); |
| } |
| |
| private void updateContact() { |
| if (DBG) log("updateContact"); |
| |
| final String name = getNameFromTextField(); |
| final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField()); |
| |
| if (!isValidNumber(number)) { |
| handleResult(false, true); |
| return; |
| } |
| Uri uri = getContentURI(); |
| |
| ContentValues bundle = new ContentValues(); |
| bundle.put("tag", mName); |
| bundle.put("number", mNumber); |
| bundle.put("newTag", name); |
| bundle.put("newNumber", number); |
| bundle.put("pin2", mPin2); |
| |
| mQueryHandler = new QueryHandler(getContentResolver()); |
| mQueryHandler.startUpdate(0, null, uri, bundle, null, null); |
| displayProgress(true); |
| showStatus(getResources().getText(R.string.updating_fdn_contact)); |
| } |
| |
| /** |
| * Handle the delete command, based upon the state of the Activity. |
| */ |
| private void deleteSelected() { |
| // delete ONLY if this is NOT a new contact. |
| if (!mAddContact) { |
| Intent intent = new Intent(); |
| intent.setClass(this, DeleteFdnContactScreen.class); |
| intent.putExtra(INTENT_EXTRA_NAME, mName); |
| intent.putExtra(INTENT_EXTRA_NUMBER, mNumber); |
| startActivity(intent); |
| } |
| finish(); |
| } |
| |
| private void authenticatePin2() { |
| Intent intent = new Intent(); |
| intent.setClass(this, GetPin2Screen.class); |
| startActivityForResult(intent, PIN2_REQUEST_CODE); |
| } |
| |
| private void displayProgress(boolean flag) { |
| // indicate we are busy. |
| mDataBusy = flag; |
| getWindow().setFeatureInt( |
| Window.FEATURE_INDETERMINATE_PROGRESS, |
| mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF); |
| // make sure we don't allow calls to save when we're |
| // not ready for them. |
| mButton.setClickable(!mDataBusy); |
| } |
| |
| /** |
| * Removed the status field, with preference to displaying a toast |
| * to match the rest of settings UI. |
| */ |
| private void showStatus(CharSequence statusMsg) { |
| if (statusMsg != null) { |
| Toast.makeText(this, statusMsg, Toast.LENGTH_LONG) |
| .show(); |
| } |
| } |
| |
| private void handleResult(boolean success, boolean invalidNumber) { |
| if (success) { |
| if (DBG) log("handleResult: success!"); |
| showStatus(getResources().getText(mAddContact ? |
| R.string.fdn_contact_added : R.string.fdn_contact_updated)); |
| } else { |
| if (DBG) log("handleResult: failed!"); |
| if (invalidNumber) { |
| showStatus(getResources().getText(R.string.fdn_invalid_number)); |
| } else { |
| // There's no way to know whether the failure is due to incorrect PIN2 or |
| // an inappropriate phone number. |
| showStatus(getResources().getText(R.string.pin2_or_fdn_invalid)); |
| } |
| } |
| |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| finish(); |
| } |
| }, 2000); |
| |
| } |
| |
| private final View.OnClickListener mClicked = new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| if (mPinFieldContainer.getVisibility() != View.VISIBLE) { |
| return; |
| } |
| |
| if (v == mNameField) { |
| mNumberField.requestFocus(); |
| } else if (v == mNumberField) { |
| mButton.requestFocus(); |
| } else if (v == mButton) { |
| // Authenticate the pin AFTER the contact information |
| // is entered, and if we're not busy. |
| if (!mDataBusy) { |
| authenticatePin2(); |
| } |
| } |
| } |
| }; |
| |
| private final View.OnFocusChangeListener mOnFocusChangeHandler = |
| new View.OnFocusChangeListener() { |
| @Override |
| public void onFocusChange(View v, boolean hasFocus) { |
| if (hasFocus) { |
| TextView textView = (TextView) v; |
| Selection.selectAll((Spannable) textView.getText()); |
| } |
| } |
| }; |
| |
| private class QueryHandler extends AsyncQueryHandler { |
| public QueryHandler(ContentResolver cr) { |
| super(cr); |
| } |
| |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor c) { |
| } |
| |
| @Override |
| protected void onInsertComplete(int token, Object cookie, Uri uri) { |
| if (DBG) log("onInsertComplete"); |
| displayProgress(false); |
| handleResult(uri != null, false); |
| } |
| |
| @Override |
| protected void onUpdateComplete(int token, Object cookie, int result) { |
| if (DBG) log("onUpdateComplete"); |
| displayProgress(false); |
| handleResult(result > 0, false); |
| } |
| |
| @Override |
| protected void onDeleteComplete(int token, Object cookie, int result) { |
| } |
| } |
| |
| private void log(String msg) { |
| Log.d(LOG_TAG, "[EditFdnContact] " + msg); |
| } |
| } |