• 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.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
onCreate(Bundle icicle)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
onActivityResult(int requestCode, int resultCode, Intent intent)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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 
resolveIntent()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      */
setupView()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.setTextDirection(View.TEXT_DIRECTION_LTR);
247             mNumberField.setKeyListener(DialerKeyListener.getInstance());
248             mNumberField.setOnFocusChangeListener(mOnFocusChangeHandler);
249             mNumberField.setOnClickListener(mClicked);
250         }
251 
252         if (!mAddContact) {
253             if (mNameField != null) {
254                 mNameField.setText(mName);
255             }
256             if (mNumberField != null) {
257                 mNumberField.setText(mNumber);
258             }
259         }
260 
261         mButton = (Button) findViewById(R.id.button);
262         if (mButton != null) {
263             mButton.setOnClickListener(mClicked);
264         }
265 
266         mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
267 
268     }
269 
getNameFromTextField()270     private String getNameFromTextField() {
271         return mNameField.getText().toString();
272     }
273 
getNumberFromTextField()274     private String getNumberFromTextField() {
275         return mNumberField.getText().toString();
276     }
277 
278     /**
279       * @param number is voice mail number
280       * @return true if number length is less than 20-digit limit
281       *
282       * TODO: Fix this logic.
283       */
isValidNumber(String number)284      private boolean isValidNumber(String number) {
285          return (number.length() <= 20) && (number.length() > 0);
286      }
287 
288 
addContact()289     private void addContact() {
290         if (DBG) log("addContact");
291 
292         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
293 
294         if (!isValidNumber(number)) {
295             handleResult(false, true);
296             return;
297         }
298 
299         Uri uri = FdnList.getContentUri(mSubscriptionInfoHelper);
300 
301         ContentValues bundle = new ContentValues(3);
302         bundle.put("tag", getNameFromTextField());
303         bundle.put("number", number);
304         bundle.put("pin2", mPin2);
305 
306         mQueryHandler = new QueryHandler(getContentResolver());
307         mQueryHandler.startInsert(0, null, uri, bundle);
308         displayProgress(true);
309         showStatus(getResources().getText(R.string.adding_fdn_contact));
310     }
311 
updateContact()312     private void updateContact() {
313         if (DBG) log("updateContact");
314 
315         final String name = getNameFromTextField();
316         final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
317 
318         if (!isValidNumber(number)) {
319             handleResult(false, true);
320             return;
321         }
322         Uri uri = FdnList.getContentUri(mSubscriptionInfoHelper);
323 
324         ContentValues bundle = new ContentValues();
325         bundle.put("tag", mName);
326         bundle.put("number", mNumber);
327         bundle.put("newTag", name);
328         bundle.put("newNumber", number);
329         bundle.put("pin2", mPin2);
330 
331         mQueryHandler = new QueryHandler(getContentResolver());
332         mQueryHandler.startUpdate(0, null, uri, bundle, null, null);
333         displayProgress(true);
334         showStatus(getResources().getText(R.string.updating_fdn_contact));
335     }
336 
337     /**
338      * Handle the delete command, based upon the state of the Activity.
339      */
deleteSelected()340     private void deleteSelected() {
341         // delete ONLY if this is NOT a new contact.
342         if (!mAddContact) {
343             Intent intent = mSubscriptionInfoHelper.getIntent(DeleteFdnContactScreen.class);
344             intent.putExtra(INTENT_EXTRA_NAME, mName);
345             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
346             startActivity(intent);
347         }
348         finish();
349     }
350 
authenticatePin2()351     private void authenticatePin2() {
352         Intent intent = new Intent();
353         intent.setClass(this, GetPin2Screen.class);
354         intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
355         startActivityForResult(intent, PIN2_REQUEST_CODE);
356     }
357 
displayProgress(boolean flag)358     private void displayProgress(boolean flag) {
359         // indicate we are busy.
360         mDataBusy = flag;
361         getWindow().setFeatureInt(
362                 Window.FEATURE_INDETERMINATE_PROGRESS,
363                 mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
364         // make sure we don't allow calls to save when we're
365         // not ready for them.
366         mButton.setClickable(!mDataBusy);
367     }
368 
369     /**
370      * Removed the status field, with preference to displaying a toast
371      * to match the rest of settings UI.
372      */
showStatus(CharSequence statusMsg)373     private void showStatus(CharSequence statusMsg) {
374         if (statusMsg != null) {
375             Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
376                     .show();
377         }
378     }
379 
handleResult(boolean success, boolean invalidNumber)380     private void handleResult(boolean success, boolean invalidNumber) {
381         if (success) {
382             if (DBG) log("handleResult: success!");
383             showStatus(getResources().getText(mAddContact ?
384                     R.string.fdn_contact_added : R.string.fdn_contact_updated));
385         } else {
386             if (DBG) log("handleResult: failed!");
387             if (invalidNumber) {
388                 showStatus(getResources().getText(R.string.fdn_invalid_number));
389             } else {
390                if (PhoneFactory.getDefaultPhone().getIccCard().getIccPin2Blocked()) {
391                     showStatus(getResources().getText(R.string.fdn_enable_puk2_requested));
392                 } else if (PhoneFactory.getDefaultPhone().getIccCard().getIccPuk2Blocked()) {
393                     showStatus(getResources().getText(R.string.puk2_blocked));
394                 } else {
395                     // There's no way to know whether the failure is due to incorrect PIN2 or
396                     // an inappropriate phone number.
397                     showStatus(getResources().getText(R.string.pin2_or_fdn_invalid));
398                 }
399             }
400         }
401 
402         mHandler.postDelayed(new Runnable() {
403             @Override
404             public void run() {
405                 finish();
406             }
407         }, 2000);
408 
409     }
410 
411     private final View.OnClickListener mClicked = new View.OnClickListener() {
412         @Override
413         public void onClick(View v) {
414             if (mPinFieldContainer.getVisibility() != View.VISIBLE) {
415                 return;
416             }
417 
418             if (v == mNameField) {
419                 mNumberField.requestFocus();
420             } else if (v == mNumberField) {
421                 mButton.requestFocus();
422             } else if (v == mButton) {
423                 final String number = PhoneNumberUtils.convertAndStrip(getNumberFromTextField());
424 
425                 if (!isValidNumber(number)) {
426                     handleResult(false, true);
427                     return;
428                 }
429                 // Authenticate the pin AFTER the contact information
430                 // is entered, and if we're not busy.
431                 if (!mDataBusy) {
432                     authenticatePin2();
433                 }
434             }
435         }
436     };
437 
438     private final View.OnFocusChangeListener mOnFocusChangeHandler =
439             new View.OnFocusChangeListener() {
440         @Override
441         public void onFocusChange(View v, boolean hasFocus) {
442             if (hasFocus) {
443                 TextView textView = (TextView) v;
444                 Selection.selectAll((Spannable) textView.getText());
445             }
446         }
447     };
448 
449     private class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver cr)450         public QueryHandler(ContentResolver cr) {
451             super(cr);
452         }
453 
454         @Override
onQueryComplete(int token, Object cookie, Cursor c)455         protected void onQueryComplete(int token, Object cookie, Cursor c) {
456         }
457 
458         @Override
onInsertComplete(int token, Object cookie, Uri uri)459         protected void onInsertComplete(int token, Object cookie, Uri uri) {
460             if (DBG) log("onInsertComplete");
461             displayProgress(false);
462             handleResult(uri != null, false);
463         }
464 
465         @Override
onUpdateComplete(int token, Object cookie, int result)466         protected void onUpdateComplete(int token, Object cookie, int result) {
467             if (DBG) log("onUpdateComplete");
468             displayProgress(false);
469             handleResult(result > 0, false);
470         }
471 
472         @Override
onDeleteComplete(int token, Object cookie, int result)473         protected void onDeleteComplete(int token, Object cookie, int result) {
474         }
475     }
476 
log(String msg)477     private void log(String msg) {
478         Log.d(LOG_TAG, "[EditFdnContact] " + msg);
479     }
480 }
481