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