• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle icicle)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
onActivityResult(int requestCode, int resultCode, Intent intent)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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 
resolveIntent()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      */
setupView()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 
getNameFromTextField()275     private String getNameFromTextField() {
276         return mNameField.getText().toString();
277     }
278 
getNumberFromTextField()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      */
setButtonEnabled()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       */
isValidNumber(String number)298      private boolean isValidNumber(String number) {
299          return (number.length() <= 20) && (number.length() > 0);
300      }
301 
302 
addContact()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 
updateContact()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      */
deleteSelected()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 
authenticatePin2()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 
displayProgress(boolean flag)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      */
showStatus(CharSequence statusMsg)387     private void showStatus(CharSequence statusMsg) {
388         if (statusMsg != null) {
389             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
390                     .show();
391         }
392     }
393 
handleResult(boolean success, boolean invalidNumber)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 {
QueryHandler(ContentResolver cr)477         public QueryHandler(ContentResolver cr) {
478             super(cr);
479         }
480 
481         @Override
onQueryComplete(int token, Object cookie, Cursor c)482         protected void onQueryComplete(int token, Object cookie, Cursor c) {
483         }
484 
485         @Override
onInsertComplete(int token, Object cookie, Uri uri)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
onUpdateComplete(int token, Object cookie, int result)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
onDeleteComplete(int token, Object cookie, int result)500         protected void onDeleteComplete(int token, Object cookie, int result) {
501         }
502     }
503 
log(String msg)504     private void log(String msg) {
505         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
506     }
507 }
508