EditFdnContactScreen.java revision 1f6403fc897d0a334588380b63f47f8ef080d155
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
17package com.android.phone;
18
19import static android.view.Window.PROGRESS_VISIBILITY_OFF;
20import static android.view.Window.PROGRESS_VISIBILITY_ON;
21
22import android.app.Activity;
23import android.content.AsyncQueryHandler;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Intent;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.net.Uri;
30import android.os.Bundle;
31import android.os.Handler;
32import android.provider.Contacts.PeopleColumns;
33import android.provider.Contacts.PhonesColumns;
34import android.telephony.PhoneNumberUtils;
35import android.text.Selection;
36import android.text.Spannable;
37import android.text.TextUtils;
38import android.text.method.DialerKeyListener;
39import android.util.Log;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.View;
43import android.view.Window;
44import android.widget.Button;
45import android.widget.EditText;
46import android.widget.LinearLayout;
47import android.widget.TextView;
48import android.widget.Toast;
49
50/**
51 * Activity to let the user add or edit an FDN contact.
52 */
53public class EditFdnContactScreen extends Activity {
54    private static final String LOG_TAG = PhoneApp.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 number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
299
300        if (!isValidNumber(number)) {
301            handleResult(false, true);
302            return;
303        }
304        Uri uri = getContentURI();
305
306        ContentValues bundle = new ContentValues();
307        bundle.put("tag", mName);
308        bundle.put("number", mNumber);
309        bundle.put("newTag", number);
310        bundle.put("newNumber", number);
311        bundle.put("pin2", mPin2);
312
313        mQueryHandler = new QueryHandler(getContentResolver());
314        mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
315        displayProgress(true);
316        showStatus(getResources().getText(R.string.updating_fdn_contact));
317    }
318
319    /**
320     * Handle the delete command, based upon the state of the Activity.
321     */
322    private void deleteSelected() {
323        // delete ONLY if this is NOT a new contact.
324        if (!mAddContact) {
325            Intent intent = new Intent();
326            intent.setClass(this, DeleteFdnContactScreen.class);
327            intent.putExtra(INTENT_EXTRA_NAME, mName);
328            intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
329            startActivity(intent);
330        }
331        finish();
332    }
333
334    private void authenticatePin2() {
335        Intent intent = new Intent();
336        intent.setClass(this, GetPin2Screen.class);
337        startActivityForResult(intent, PIN2_REQUEST_CODE);
338    }
339
340    private void displayProgress(boolean flag) {
341        // indicate we are busy.
342        mDataBusy = flag;
343        getWindow().setFeatureInt(
344                Window.FEATURE_INDETERMINATE_PROGRESS,
345                mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
346        // make sure we don't allow calls to save when we're
347        // not ready for them.
348        mButton.setClickable(!mDataBusy);
349    }
350
351    /**
352     * Removed the status field, with preference to displaying a toast
353     * to match the rest of settings UI.
354     */
355    private void showStatus(CharSequence statusMsg) {
356        if (statusMsg != null) {
357            Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
358                    .show();
359        }
360    }
361
362    private void handleResult(boolean success, boolean invalidNumber) {
363        if (success) {
364            if (DBG) log("handleResult: success!");
365            showStatus(getResources().getText(mAddContact ?
366                    R.string.fdn_contact_added : R.string.fdn_contact_updated));
367        } else {
368            if (DBG) log("handleResult: failed!");
369            if (invalidNumber) {
370                showStatus(getResources().getText(R.string.fdn_invalid_number));
371            } else {
372                // There's no way to know whether the failure is due to incorrect PIN2 or
373                // an inappropriate phone number.
374                showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
375            }
376        }
377
378        mHandler.postDelayed(new Runnable() {
379            @Override
380            public void run() {
381                finish();
382            }
383        }, 2000);
384
385    }
386
387    private final View.OnClickListener mClicked = new View.OnClickListener() {
388        @Override
389        public void onClick(View v) {
390            if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
391                return;
392            }
393
394            if (v == mNameField) {
395                mNumberField.requestFocus();
396            } else if (v == mNumberField) {
397                mButton.requestFocus();
398            } else if (v == mButton) {
399                // Authenticate the pin AFTER the contact information
400                // is entered, and if we're not busy.
401                if (!mDataBusy) {
402                    authenticatePin2();
403                }
404            }
405        }
406    };
407
408    private final View.OnFocusChangeListener mOnFocusChangeHandler =
409            new View.OnFocusChangeListener() {
410        @Override
411        public void onFocusChange(View v, boolean hasFocus) {
412            if (hasFocus) {
413                TextView textView = (TextView) v;
414                Selection.selectAll((Spannable) textView.getText());
415            }
416        }
417    };
418
419    private class QueryHandler extends AsyncQueryHandler {
420        public QueryHandler(ContentResolver cr) {
421            super(cr);
422        }
423
424        @Override
425        protected void onQueryComplete(int token, Object cookie, Cursor c) {
426        }
427
428        @Override
429        protected void onInsertComplete(int token, Object cookie, Uri uri) {
430            if (DBG) log("onInsertComplete");
431            displayProgress(false);
432            handleResult(uri != null, false);
433        }
434
435        @Override
436        protected void onUpdateComplete(int token, Object cookie, int result) {
437            if (DBG) log("onUpdateComplete");
438            displayProgress(false);
439            handleResult(result > 0, false);
440        }
441
442        @Override
443        protected void onDeleteComplete(int token, Object cookie, int result) {
444        }
445    }
446
447    private void log(String msg) {
448        Log.d(LOG_TAG, "[EditFdnContact] " + msg);
449    }
450}
451