blob: c301473415be073942950b410b727c225c25ebed [file] [log] [blame]
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 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.contacts;
18
19import com.google.android.collect.Lists;
20
21import static com.android.contacts.ContactEntryAdapter.CONTACT_CUSTOM_RINGTONE_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.CONTACT_NAME_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.CONTACT_NOTES_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
25import static com.android.contacts.ContactEntryAdapter.CONTACT_SEND_TO_VOICEMAIL_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.METHODS_AUX_DATA_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.METHODS_DATA_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.METHODS_ID_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.METHODS_ISPRIMARY_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.METHODS_KIND_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.METHODS_LABEL_COLUMN;
32import static com.android.contacts.ContactEntryAdapter.METHODS_PROJECTION;
33import static com.android.contacts.ContactEntryAdapter.METHODS_TYPE_COLUMN;
34import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_COMPANY_COLUMN;
35import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ID_COLUMN;
36import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ISPRIMARY_COLUMN;
37import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_LABEL_COLUMN;
38import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_PROJECTION;
39import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TITLE_COLUMN;
40import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TYPE_COLUMN;
41import static com.android.contacts.ContactEntryAdapter.PHONES_ID_COLUMN;
42import static com.android.contacts.ContactEntryAdapter.PHONES_ISPRIMARY_COLUMN;
43import static com.android.contacts.ContactEntryAdapter.PHONES_LABEL_COLUMN;
44import static com.android.contacts.ContactEntryAdapter.PHONES_NUMBER_COLUMN;
45import static com.android.contacts.ContactEntryAdapter.PHONES_PROJECTION;
46import static com.android.contacts.ContactEntryAdapter.PHONES_TYPE_COLUMN;
47
48import android.app.Activity;
49import android.app.AlertDialog;
50import android.app.Dialog;
51import android.content.ActivityNotFoundException;
52import android.content.ContentResolver;
53import android.content.ContentUris;
54import android.content.ContentValues;
55import android.content.Context;
56import android.content.DialogInterface;
57import android.content.Intent;
58import android.content.SharedPreferences;
59import android.content.res.Resources;
60import android.database.Cursor;
61import android.graphics.Bitmap;
62import android.media.Ringtone;
63import android.media.RingtoneManager;
64import android.net.Uri;
65import android.os.Bundle;
66import android.os.Parcel;
67import android.os.Parcelable;
68import android.preference.PreferenceManager;
69import android.provider.Contacts;
70import android.provider.Contacts.ContactMethods;
71import android.provider.Contacts.Intents.Insert;
72import android.provider.Contacts.Organizations;
73import android.provider.Contacts.People;
74import android.provider.Contacts.Phones;
75import android.telephony.PhoneNumberFormattingTextWatcher;
76import android.text.TextUtils;
77import android.text.method.DialerKeyListener;
78import android.text.method.TextKeyListener;
79import android.text.method.TextKeyListener.Capitalize;
80import android.util.Log;
81import android.view.ContextThemeWrapper;
82import android.view.KeyEvent;
83import android.view.LayoutInflater;
84import android.view.Menu;
85import android.view.MenuItem;
86import android.view.View;
87import android.view.ViewGroup;
88import android.view.ViewParent;
89import android.widget.Button;
90import android.widget.CheckBox;
91import android.widget.EditText;
92import android.widget.ExpandableListView;
93import android.widget.ImageView;
94import android.widget.LinearLayout;
95import android.widget.SimpleExpandableListAdapter;
96import android.widget.TextView;
97import android.widget.Toast;
98
99import java.io.ByteArrayOutputStream;
100import java.util.ArrayList;
101import java.util.HashMap;
102import java.util.List;
103import java.util.Map;
104
105/**
106 * Activity for editing or inserting a contact. Note that if the contact data changes in the
107 * background while this activity is running, the updates will be overwritten.
108 */
109public final class EditContactActivity extends Activity implements View.OnClickListener,
110 ExpandableListView.OnChildClickListener {
111 private static final String TAG = "EditContactActivity";
112
113 private static final int STATE_UNKNOWN = 0;
114 /** Editing an existing contact */
115 private static final int STATE_EDIT = 1;
116 /** The full insert mode */
117 private static final int STATE_INSERT = 2;
118
119 /** The launch code when picking a photo and the raw data is returned */
120 private static final int PHOTO_PICKED_WITH_DATA = 3021;
121
122 /** The launch code when picking a ringtone */
123 private static final int RINGTONE_PICKED = 3023;
124
125 // Label picker position info
126 final static int LABEL_PICKER_PHONES_POSITION = 0;
127 final static int LABEL_PICKER_EMAIL_POSITION = 1;
128 final static int LABEL_PICKER_IM_POSITION = 2;
129 final static int LABEL_PICKER_POSTAL_POSITION = 3;
130 final static int LABEL_PICKER_OTHER_POSITION = 4;
131
132 // These correspond to the string array in resources for picker "other" items
133 final static int OTHER_ORGANIZATION = 0;
134 final static int OTHER_NOTE = 1;
135
136 // Dialog IDs
137 final static int LABEL_PICKER_ALL_TYPES_DIALOG = 1;
138 final static int DELETE_CONFIRMATION_DIALOG = 2;
139
140 // Menu item IDs
141 public static final int MENU_ITEM_SAVE = 1;
142 public static final int MENU_ITEM_DONT_SAVE = 2;
143 public static final int MENU_ITEM_DELETE = 3;
144 public static final int MENU_ITEM_ADD = 5;
145 public static final int MENU_ITEM_PHOTO = 6;
146
147 // Key listener types
148 final static int INPUT_TEXT = 1;
149 final static int INPUT_TEXT_WORDS = 2;
150 final static int INPUT_TEXT_SENTENCES = 3;
151 final static int INPUT_DIALER = 4;
152
153 /** Used to represent an invalid type for a contact entry */
154 private static final int INVALID_TYPE = -1;
155
156 /** The default type for a phone that is added via an intent */
157 private static final int DEFAULT_PHONE_TYPE = Phones.TYPE_MOBILE;
158
159 /** The default type for an email that is added via an intent */
160 private static final int DEFAULT_EMAIL_TYPE = ContactMethods.TYPE_HOME;
161
162 /** The default type for a postal address that is added via an intent */
163 private static final int DEFAULT_POSTAL_TYPE = ContactMethods.TYPE_HOME;
164
165 private int mState; // saved across instances
166 private boolean mInsert; // saved across instances
167 private Uri mUri; // saved across instances
168 /** In insert mode this is the photo */
169 private Bitmap mPhoto; // saved across instances
170 private boolean mPhotoChanged = false; // saved across instances
171
172 private EditText mNameView;
173 private ImageView mPhotoImageView;
174 private Button mPhotoButton;
175 private CheckBox mSendToVoicemailCheckBox;
176 private LinearLayout mLayout;
177 private LayoutInflater mInflater;
178 private MenuItem mPhotoMenuItem;
179 private boolean mPhotoPresent = false;
180
181 // These are accessed by inner classes. They're package scoped to make access more efficient.
182 /* package */ ContentResolver mResolver;
183 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
184 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
185 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
186 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
187 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
188 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
189
190 /* package */ static final int MSG_DELETE = 1;
191 /* package */ static final int MSG_CHANGE_LABEL = 2;
192 /* package */ static final int MSG_ADD_PHONE = 3;
193 /* package */ static final int MSG_ADD_EMAIL = 4;
194 /* package */ static final int MSG_ADD_POSTAL = 5;
195
196 public void onClick(View v) {
197 switch (v.getId()) {
198 case R.id.photoButton:
199 case R.id.photoImage: {
200 doPickPhotoAction();
201 break;
202 }
203
204 case R.id.addMore:
205 doAddAction();
206 break;
207
208 case R.id.saveButton:
209 doSaveAction();
210 break;
211
212 case R.id.discardButton:
213 doRevertAction();
214 break;
215
216 case R.id.delete:
217 case R.id.delete2: {
218 EditEntry entry = findEntryForView(v);
219 if (entry != null) {
220 // Clear the text and hide the view so it gets saved properly
221 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
222 entry.view.setVisibility(View.GONE);
223 entry.isDeleted = true;
224 }
225 break;
226 }
227
228 case R.id.label: {
229 EditEntry entry = findEntryForView(v);
230 if (entry != null) {
231 String[] labels = getLabelsForKind(this, entry.kind);
232 LabelPickedListener listener = new LabelPickedListener(entry, labels);
233 new AlertDialog.Builder(EditContactActivity.this)
234 .setItems(labels, listener)
235 .setTitle(R.string.selectLabel)
236 .show();
237 }
238 break;
239 }
240
241 case R.id.data: {
242 EditEntry entry = findEntryForView(v);
243 if (isRingtoneEntry(entry)) {
244 doPickRingtone(entry);
245 }
246 break;
247 }
248 }
249 }
250
251 private void setPhotoPresent(boolean present) {
252 mPhotoImageView.setVisibility(present ? View.VISIBLE : View.GONE);
253 mPhotoButton.setVisibility(present ? View.GONE : View.VISIBLE);
254 mPhotoPresent = present;
255 if (mPhotoMenuItem != null) {
256 if (present) {
257 mPhotoMenuItem.setTitle(R.string.removePicture);
258 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
259 } else {
260 mPhotoMenuItem.setTitle(R.string.addPicture);
261 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_add);
262 }
263 }
264 }
265
266 private EditEntry findEntryForView(View v) {
267 // Try to find the entry for this view
268 EditEntry entry = null;
269 do {
270 Object tag = v.getTag();
271 if (tag != null && tag instanceof EditEntry) {
272 entry = (EditEntry) tag;
273 break;
274 } else {
275 ViewParent parent = v.getParent();
276 if (parent != null && parent instanceof View) {
277 v = (View) parent;
278 } else {
279 v = null;
280 }
281 }
282 } while (v != null);
283 return entry;
284 }
285
286 private DialogInterface.OnClickListener mDeleteContactDialogListener =
287 new DialogInterface.OnClickListener() {
288 public void onClick(DialogInterface dialog, int button) {
289 mResolver.delete(mUri, null, null);
290 finish();
291 }
292 };
293
294 private boolean mMobilePhoneAdded = false;
295 private boolean mPrimaryEmailAdded = false;
296
297 @Override
298 protected void onCreate(Bundle icicle) {
299 super.onCreate(icicle);
300
301 mResolver = getContentResolver();
302
303 // Build the list of sections
304 setupSections();
305
306 // Load the UI
307 setContentView(R.layout.edit_contact);
308 mLayout = (LinearLayout) findViewById(R.id.list);
309 mNameView = (EditText) findViewById(R.id.name);
310 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
311 mPhotoImageView.setOnClickListener(this);
312 mPhotoImageView.setVisibility(View.GONE);
313 mPhotoButton = (Button) findViewById(R.id.photoButton);
314 mPhotoButton.setOnClickListener(this);
315 mSendToVoicemailCheckBox = (CheckBox) findViewById(R.id.send_to_voicemail);
316
317 // Setup the bottom buttons
318 View view = findViewById(R.id.addMore);
319 view.setOnClickListener(this);
320 view = findViewById(R.id.saveButton);
321 view.setOnClickListener(this);
322 view = findViewById(R.id.discardButton);
323 view.setOnClickListener(this);
324
325 mInflater = getLayoutInflater();
326
327 // Resolve the intent
328 mState = STATE_UNKNOWN;
329 Intent intent = getIntent();
330 String action = intent.getAction();
331 mUri = intent.getData();
332 if (mUri != null) {
333 if (action.equals(Intent.ACTION_EDIT)) {
334 if (icicle == null) {
335 // Build the entries & views
336 buildEntriesForEdit(getIntent().getExtras());
337 buildViews();
338 }
339 mState = STATE_EDIT;
340 } else if (action.equals(Intent.ACTION_INSERT)) {
341 if (icicle == null) {
342 // Build the entries & views
343 buildEntriesForInsert(getIntent().getExtras());
344 buildViews();
345 }
346 mState = STATE_INSERT;
347 mInsert = true;
348 }
349 }
350
351 if (mState == STATE_UNKNOWN) {
352 Log.e(TAG, "Cannot resolve intent: " + intent);
353 finish();
354 return;
355 }
356
357 if (mState == STATE_EDIT) {
358 setTitle(getResources().getText(R.string.editContact_title_edit));
359 } else {
360 setTitle(getResources().getText(R.string.editContact_title_insert));
361 }
362 }
363
364 private void setupSections() {
365 mSections.add(mPhoneEntries);
366 mSections.add(mEmailEntries);
367 mSections.add(mImEntries);
368 mSections.add(mPostalEntries);
369 mSections.add(mOtherEntries);
370 }
371
372 @Override
373 protected void onSaveInstanceState(Bundle outState) {
374 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
375 outState.putParcelableArrayList("emailEntries", mEmailEntries);
376 outState.putParcelableArrayList("imEntries", mImEntries);
377 outState.putParcelableArrayList("postalEntries", mPostalEntries);
378 outState.putParcelableArrayList("otherEntries", mOtherEntries);
379 outState.putInt("state", mState);
380 outState.putBoolean("insert", mInsert);
381 outState.putParcelable("uri", mUri);
382 outState.putString("name", mNameView.getText().toString());
383 outState.putParcelable("photo", mPhoto);
384 outState.putBoolean("photoChanged", mPhotoChanged);
385 outState.putBoolean("sendToVoicemail", mSendToVoicemailCheckBox.isChecked());
386 }
387
388 @Override
389 protected void onRestoreInstanceState(Bundle inState) {
390 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
391 mEmailEntries = inState.getParcelableArrayList("emailEntries");
392 mImEntries = inState.getParcelableArrayList("imEntries");
393 mPostalEntries = inState.getParcelableArrayList("postalEntries");
394 mOtherEntries = inState.getParcelableArrayList("otherEntries");
395 setupSections();
396
397 mState = inState.getInt("state");
398 mInsert = inState.getBoolean("insert");
399 mUri = inState.getParcelable("uri");
400 mNameView.setText(inState.getString("name"));
401 mPhoto = inState.getParcelable("photo");
402 if (mPhoto != null) {
403 mPhotoImageView.setImageBitmap(mPhoto);
404 setPhotoPresent(true);
405 } else {
406 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
407 setPhotoPresent(false);
408 }
409 mPhotoChanged = inState.getBoolean("photoChanged");
410 mSendToVoicemailCheckBox.setChecked(inState.getBoolean("sendToVoicemail"));
411
412 // Now that everything is restored, build the view
413 buildViews();
414 }
415
416 @Override
417 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
418 if (resultCode != RESULT_OK) {
419 return;
420 }
421
422 switch (requestCode) {
423 case PHOTO_PICKED_WITH_DATA: {
424 final Bundle extras = data.getExtras();
425 if (extras != null) {
426 Bitmap photo = extras.getParcelable("data");
427 mPhoto = photo;
428 mPhotoChanged = true;
429 mPhotoImageView.setImageBitmap(photo);
430 setPhotoPresent(true);
431 }
432 break;
433 }
434
435 case RINGTONE_PICKED: {
436 Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
437 handleRingtonePicked(pickedUri);
438 break;
439 }
440 }
441 }
442
443 @Override
444 public boolean onKeyDown(int keyCode, KeyEvent event) {
445 switch (keyCode) {
446 case KeyEvent.KEYCODE_BACK: {
447 doSaveAction();
448 return true;
449 }
450 }
451 return super.onKeyDown(keyCode, event);
452 }
453
454 @Override
455 public boolean onCreateOptionsMenu(Menu menu) {
456 super.onCreateOptionsMenu(menu);
457 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
458 .setIcon(android.R.drawable.ic_menu_save)
459 .setAlphabeticShortcut('\n');
460 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
461 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
462 .setAlphabeticShortcut('q');
463 if (!mInsert) {
464 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
465 .setIcon(android.R.drawable.ic_menu_delete);
466 }
467
468 menu.add(0, MENU_ITEM_ADD, 0, R.string.menu_addItem)
469 .setIcon(android.R.drawable.ic_menu_add)
470 .setAlphabeticShortcut('n');
471
472 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
473 // Updates the state of the menu item
474 setPhotoPresent(mPhotoPresent);
475
476 return true;
477 }
478
479 @Override
480 public boolean onOptionsItemSelected(MenuItem item) {
481 switch (item.getItemId()) {
482 case MENU_ITEM_SAVE:
483 doSaveAction();
484 return true;
485
486 case MENU_ITEM_DONT_SAVE:
487 doRevertAction();
488 return true;
489
490 case MENU_ITEM_DELETE:
491 // Get confirmation
492 showDialog(DELETE_CONFIRMATION_DIALOG);
493 return true;
494
495 case MENU_ITEM_ADD:
496 doAddAction();
497 return true;
498
499 case MENU_ITEM_PHOTO:
500 if (!mPhotoPresent) {
501 doPickPhotoAction();
502 } else {
503 doRemovePhotoAction();
504 }
505 return true;
506 }
507
508 return false;
509 }
510
511 private void doAddAction() {
512 showDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
513 }
514
515 private void doRevertAction() {
516 finish();
517 }
518
519 private void doPickPhotoAction() {
520 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
521 // TODO: get these values from constants somewhere
522 intent.setType("image/*");
523 intent.putExtra("crop", "true");
524 intent.putExtra("aspectX", 1);
525 intent.putExtra("aspectY", 1);
526 intent.putExtra("outputX", 96);
527 intent.putExtra("outputY", 96);
528 try {
529 intent.putExtra("return-data", true);
530 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
531 } catch (ActivityNotFoundException e) {
532 new AlertDialog.Builder(EditContactActivity.this)
533 .setTitle(R.string.errorDialogTitle)
534 .setMessage(R.string.photoPickerNotFoundText)
535 .setPositiveButton(R.string.okButtonText, null)
536 .show();
537 }
538 }
539
540 private void doRemovePhotoAction() {
541 mPhoto = null;
542 mPhotoChanged = true;
543 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
544 setPhotoPresent(false);
545 }
546
547 private void doPickRingtone(EditEntry entry) {
548 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
549 // Allow user to pick 'Default'
550 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
551 // Show only ringtones
552 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
553 // Don't show 'Silent'
554 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
555 if (entry.data != null) {
556 Uri ringtoneUri = Uri.parse(entry.data);
557 // Put checkmark next to the current ringtone for this contact
558 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
559 }
560 // Launch!
561 startActivityForResult(intent, RINGTONE_PICKED);
562 }
563
564 private void handleRingtonePicked(Uri pickedUri) {
565 EditEntry entry = getRingtoneEntry();
566 if (entry == null) {
567 Log.w(TAG, "Ringtone picked but could not find ringtone entry");
568 return;
569 }
570
571 if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
572 entry.data = null;
573 } else {
574 entry.data = pickedUri.toString();
575 }
576
577 updateRingtoneView(entry);
578 }
579
580 private void updateRingtoneView(EditEntry entry) {
581 if (entry.data == null) {
582 updateDataView(entry, getString(R.string.default_ringtone));
583 } else {
584 Uri ringtoneUri = Uri.parse(entry.data);
585 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
586 if (ringtone == null) {
587 Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
588 return;
589 }
590 updateDataView(entry, ringtone.getTitle(this));
591 }
592 }
593
594 private void updateDataView(EditEntry entry, String text) {
595 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
596 dataView.setText(text);
597 }
598
599 @Override
600 protected Dialog onCreateDialog(int id) {
601 switch (id) {
602 case LABEL_PICKER_ALL_TYPES_DIALOG:
603 return createAllTypesPicker();
604
605 case DELETE_CONFIRMATION_DIALOG:
606 return new AlertDialog.Builder(EditContactActivity.this)
607 .setTitle(R.string.deleteConfirmation_title)
608 .setIcon(android.R.drawable.ic_dialog_alert)
609 .setMessage(R.string.deleteConfirmation)
610 .setNegativeButton(R.string.noButton, null)
611 .setPositiveButton(R.string.yesButton, mDeleteContactDialogListener)
612 .setCancelable(false)
613 .create();
614 }
615 return super.onCreateDialog(id);
616 }
617
618 static String[] getLabelsForKind(Context context, int kind) {
619 final Resources resources = context.getResources();
620 switch (kind) {
621 case Contacts.KIND_PHONE:
622 return resources.getStringArray(android.R.array.phoneTypes);
623 case Contacts.KIND_EMAIL:
624 return resources.getStringArray(android.R.array.emailAddressTypes);
625 case Contacts.KIND_POSTAL:
626 return resources.getStringArray(android.R.array.postalAddressTypes);
627 case Contacts.KIND_IM:
628 return resources.getStringArray(android.R.array.imProtocols);
629 case Contacts.KIND_ORGANIZATION:
630 return resources.getStringArray(android.R.array.organizationTypes);
631 case EditEntry.KIND_CONTACT:
632 return resources.getStringArray(R.array.otherLabels);
633 }
634 return null;
635 }
636
637 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
638 // In the UI Custom... comes last, but it is uses the constant 0
639 // so it is in the same location across the various kinds. Fix up the
640 // position to a valid type here.
641 if (labelPosition == labels.length - 1) {
642 return ContactMethods.TYPE_CUSTOM;
643 } else {
644 return labelPosition + 1;
645 }
646 }
647
648 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
649 int childPosition, long id) {
650 EditEntry entry = null;
651
652 // Make the dialog go away
653 dismissDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
654
655 // Create the new entry
656 switch (groupPosition) {
657 case LABEL_PICKER_PHONES_POSITION: {
658 String[] labels = getLabelsForKind(this, Contacts.KIND_PHONE);
659 final int type = getTypeFromLabelPosition(labels, childPosition);
660 entry = EditEntry.newPhoneEntry(EditContactActivity.this,
661 labels[childPosition], type,
662 null, Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY), 0);
663 if (type == Phones.TYPE_CUSTOM) {
664 createCustomPicker(entry, mPhoneEntries);
665 return true;
666 } else {
667 mPhoneEntries.add(entry);
668 }
669 break;
670 }
671
672 case LABEL_PICKER_EMAIL_POSITION: {
673 String[] labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
674 final int type = getTypeFromLabelPosition(labels, childPosition);
675 entry = EditEntry.newEmailEntry(EditContactActivity.this,
676 labels[childPosition], type, null,
677 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
678 if (type == ContactMethods.TYPE_CUSTOM) {
679 createCustomPicker(entry, mEmailEntries);
680 return true;
681 } else {
682 mEmailEntries.add(entry);
683 }
684 break;
685 }
686
687 case LABEL_PICKER_IM_POSITION: {
688 String[] labels = getLabelsForKind(this, Contacts.KIND_IM);
689 entry = EditEntry.newImEntry(EditContactActivity.this,
690 labels[childPosition], childPosition, null,
691 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
692 mImEntries.add(entry);
693 break;
694 }
695
696 case LABEL_PICKER_POSTAL_POSITION: {
697 String[] labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
698 final int type = getTypeFromLabelPosition(labels, childPosition);
699 entry = EditEntry.newPostalEntry(EditContactActivity.this,
700 labels[childPosition], type, null,
701 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
702 if (type == ContactMethods.TYPE_CUSTOM) {
703 createCustomPicker(entry, mPostalEntries);
704 return true;
705 } else {
706 mPostalEntries.add(entry);
707 }
708 break;
709 }
710
711 case LABEL_PICKER_OTHER_POSITION: {
712 switch (childPosition) {
713 case OTHER_ORGANIZATION:
714 entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
715 Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
716 ContactMethods.TYPE_WORK);
717 mOtherEntries.add(entry);
718 break;
719
720 case OTHER_NOTE:
721 entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
722 mOtherEntries.add(entry);
723 break;
724
725 default:
726 entry = null;
727 }
728 break;
729 }
730
731 default:
732 entry = null;
733 }
734
735 // Rebuild the views if needed
736 if (entry != null) {
737 buildViews();
738
739 View dataView = entry.view.findViewById(R.id.data);
740 if (dataView == null) {
741 entry.view.requestFocus();
742 } else {
743 dataView.requestFocus();
744 }
745 }
746 return true;
747 }
748
749 private EditEntry getRingtoneEntry() {
750 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
751 EditEntry entry = mOtherEntries.get(i);
752 if (isRingtoneEntry(entry)) {
753 return entry;
754 }
755 }
756 return null;
757 }
758
759 private static boolean isRingtoneEntry(EditEntry entry) {
760 return entry != null && entry.column != null && entry.column.equals(People.CUSTOM_RINGTONE);
761 }
762
763 private Dialog createAllTypesPicker() {
764 // Setup the adapter
765 List<Map<String, ?>> groupData = Lists.newArrayList();
766 List<List<Map<String, ?>>> childData = Lists.newArrayList();
767 List<Map<String, ?>> children;
768 HashMap<String, CharSequence> curGroupMap;
769 CharSequence[] labels;
770 int labelsSize;
771
772 // Phones
773 curGroupMap = new HashMap<String, CharSequence>();
774 groupData.add(curGroupMap);
775 curGroupMap.put("data", getText(R.string.phoneLabelsGroup));
776
777 labels = getLabelsForKind(this, Contacts.KIND_PHONE);
778 labelsSize = labels.length;
779 children = Lists.newArrayList();
780 for (int i = 0; i < labelsSize; i++) {
781 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
782 children.add(curChildMap);
783 curChildMap.put("data", labels[i]);
784 }
785 childData.add(LABEL_PICKER_PHONES_POSITION, children);
786
787 // Email
788 curGroupMap = new HashMap<String, CharSequence>();
789 groupData.add(curGroupMap);
790 curGroupMap.put("data", getText(R.string.emailLabelsGroup));
791
792 labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
793 labelsSize = labels.length;
794 children = Lists.newArrayList();
795 for (int i = 0; i < labelsSize; i++) {
796 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
797 children.add(curChildMap);
798 curChildMap.put("data", labels[i]);
799 }
800 childData.add(LABEL_PICKER_EMAIL_POSITION, children);
801
802 // IM
803 curGroupMap = new HashMap<String, CharSequence>();
804 groupData.add(curGroupMap);
805 curGroupMap.put("data", getText(R.string.imLabelsGroup));
806
807 labels = getLabelsForKind(this, Contacts.KIND_IM);
808 labelsSize = labels.length;
809 children = Lists.newArrayList();
810 for (int i = 0; i < labelsSize; i++) {
811 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
812 children.add(curChildMap);
813 curChildMap.put("data", labels[i]);
814 }
815 childData.add(LABEL_PICKER_IM_POSITION, children);
816
817 // Postal
818 curGroupMap = new HashMap<String, CharSequence>();
819 groupData.add(curGroupMap);
820 curGroupMap.put("data", getText(R.string.postalLabelsGroup));
821
822 labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
823 labelsSize = labels.length;
824 children = Lists.newArrayList();
825 for (int i = 0; i < labelsSize; i++) {
826 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
827 children.add(curChildMap);
828 curChildMap.put("data", labels[i]);
829 }
830 childData.add(LABEL_PICKER_POSTAL_POSITION, children);
831
832 // Other
833 curGroupMap = new HashMap<String, CharSequence>();
834 groupData.add(curGroupMap);
835 curGroupMap.put("data", getText(R.string.otherLabelsGroup));
836
837 labels = getLabelsForKind(this, EditEntry.KIND_CONTACT);
838 labelsSize = labels.length;
839 children = Lists.newArrayList();
840 for (int i = 0; i < labelsSize; i++) {
841 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
842 children.add(curChildMap);
843 curChildMap.put("data", labels[i]);
844 }
845 childData.add(LABEL_PICKER_OTHER_POSITION, children);
846
847 // Create the expandable list view
848 ExpandableListView list = new ExpandableListView(new ContextThemeWrapper(this,
849 android.R.style.Theme_Light));
850 list.setOnChildClickListener(this);
851 list.setAdapter(new SimpleExpandableListAdapter(
852 new ContextThemeWrapper(this, android.R.style.Theme_Light),
853 groupData,
854 android.R.layout.simple_expandable_list_item_1,
855 new String[] { "data" },
856 new int[] { android.R.id.text1 },
857 childData,
858 android.R.layout.simple_expandable_list_item_1,
859 new String[] { "data" },
860 new int[] { android.R.id.text1 }
861 ));
862 // This list shouldn't have a color hint since the dialog may be transparent
863 list.setCacheColorHint(0);
864
865 // Create the dialog
866 return new AlertDialog.Builder(this).setView(list).setInverseBackgroundForced(true)
867 .setTitle(R.string.selectLabel).create();
868 }
869
870 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
871 final EditText label = new EditText(this);
872 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
873 label.requestFocus();
874 new AlertDialog.Builder(this)
875 .setView(label)
876 .setTitle(R.string.customLabelPickerTitle)
877 .setPositiveButton(R.string.okButtonText, new DialogInterface.OnClickListener() {
878 public void onClick(DialogInterface dialog, int which) {
879 entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
880 label.getText().toString());
881 if (addTo != null) {
882 addTo.add(entry);
883 buildViews();
884 entry.view.requestFocus(View.FOCUS_DOWN);
885 }
886 }
887 })
888 .setNegativeButton(R.string.cancelButtonText, null)
889 .show();
890 }
891
892 /**
893 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
894 */
895 private void doSaveAction() {
896 // Save or create the contact if needed
897 switch (mState) {
898 case STATE_EDIT:
899 save();
900 break;
901
902 case STATE_INSERT:
903 create();
904 break;
905
906 default:
907 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
908 break;
909 }
910 finish();
911 }
912
913 /**
914 * Save the various fields to the existing contact.
915 */
916 private void save() {
917 ContentValues values = new ContentValues();
918 String data;
919 int numValues = 0;
920
921 // Handle the name and send to voicemail specially
922 final String name = mNameView.getText().toString();
923 if (name != null && TextUtils.isGraphic(name)) {
924 numValues++;
925 }
926 values.put(People.NAME, name);
927 values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
928 mResolver.update(mUri, values, null, null);
929
930 if (mPhotoChanged) {
931 // Only write the photo if it's changed, since we don't initially load mPhoto
932 if (mPhoto != null) {
933 ByteArrayOutputStream stream = new ByteArrayOutputStream();
934 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
935 Contacts.People.setPhotoData(mResolver, mUri, stream.toByteArray());
936 } else {
937 Contacts.People.setPhotoData(mResolver, mUri, null);
938 }
939 }
940
941 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
942 for (int i = 0; i < entryCount; i++) {
943 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
944 int kind = entry.kind;
945 data = entry.getData();
946 boolean empty = data == null || !TextUtils.isGraphic(data);
947 if (kind == EditEntry.KIND_CONTACT) {
948 values.clear();
949 if (!empty) {
950 values.put(entry.column, data);
951 mResolver.update(entry.uri, values, null, null);
952 numValues++;
953 } else {
954 values.put(entry.column, (String) null);
955 mResolver.update(entry.uri, values, null, null);
956 }
957 } else {
958 if (!empty) {
959 values.clear();
960 entry.toValues(values);
961 if (entry.id != 0) {
962 mResolver.update(entry.uri, values, null, null);
963 } else {
964 mResolver.insert(entry.uri, values);
965 }
966 numValues++;
967 } else if (entry.id != 0) {
968 mResolver.delete(entry.uri, null, null);
969 }
970 }
971 }
972
973 if (numValues == 0) {
974 // The contact is completely empty, delete it
975 mResolver.delete(mUri, null, null);
976 mUri = null;
977 setResult(RESULT_CANCELED);
978 } else {
979 // Add the entry to the my contacts group if it isn't there already
980 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
981 setResult(RESULT_OK, new Intent().setData(mUri));
982 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
983 }
984 }
985
986 /**
987 * Takes the entered data and saves it to a new contact.
988 */
989 private void create() {
990 ContentValues values = new ContentValues();
991 String data;
992 int numValues = 0;
993
994 // Create the contact itself
995 final String name = mNameView.getText().toString();
996 if (name != null && TextUtils.isGraphic(name)) {
997 numValues++;
998 }
999 values.put(People.NAME, name);
1000 values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
1001
1002 // Add the contact to the My Contacts group
1003 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1004
1005 // Add the contact to the group that is being displayed in the contact list
1006 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1007 int displayType = prefs.getInt(ContactsPreferenceActivity.PREF_DISPLAY_TYPE,
1008 ContactsPreferenceActivity.DISPLAY_TYPE_UNKNOWN);
1009 if (displayType == ContactsPreferenceActivity.DISPLAY_TYPE_USER_GROUP) {
1010 String displayGroup = prefs.getString(ContactsPreferenceActivity.PREF_DISPLAY_INFO,
1011 null);
1012 if (!TextUtils.isEmpty(displayGroup)) {
1013 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1014 }
1015 }
1016
1017 // Handle the photo
1018 if (mPhoto != null) {
1019 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1020 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1021 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1022 }
1023
1024 // Create the contact methods
1025 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1026 for (int i = 0; i < entryCount; i++) {
1027 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
1028 if (entry.kind != EditEntry.KIND_CONTACT) {
1029 values.clear();
1030 if (entry.toValues(values)) {
1031 // Only create the entry if there is data
1032 entry.uri = mResolver.insert(
1033 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1034 entry.id = ContentUris.parseId(entry.uri);
1035 numValues++;
1036 }
1037 } else {
1038 // Update the contact with any straggling data, like notes
1039 data = entry.getData();
1040 values.clear();
1041 if (data != null && TextUtils.isGraphic(data)) {
1042 values.put(entry.column, data);
1043 mResolver.update(contactUri, values, null, null);
1044 numValues++;
1045 }
1046 }
1047 }
1048
1049 if (numValues == 0) {
1050 mResolver.delete(contactUri, null, null);
1051 setResult(RESULT_CANCELED);
1052 } else {
1053 mUri = contactUri;
1054 Intent resultIntent = new Intent()
1055 .setData(mUri)
1056 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1057 setResult(RESULT_OK, resultIntent);
1058 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1059 }
1060 }
1061
1062 /**
1063 * Build up the entries to display on the screen.
1064 *
1065 * @param extras the extras used to start this activity, may be null
1066 */
1067 private void buildEntriesForEdit(Bundle extras) {
1068 Cursor personCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
1069 if (personCursor == null) {
1070 Log.e(TAG, "invalid contact uri: " + mUri);
1071 finish();
1072 return;
1073 } else if (!personCursor.moveToFirst()) {
1074 Log.e(TAG, "invalid contact uri: " + mUri);
1075 finish();
1076 personCursor.close();
1077 return;
1078 }
1079
1080 // Clear out the old entries
1081 int numSections = mSections.size();
1082 for (int i = 0; i < numSections; i++) {
1083 mSections.get(i).clear();
1084 }
1085
1086 EditEntry entry;
1087
1088 // Name
1089 mNameView.setText(personCursor.getString(CONTACT_NAME_COLUMN));
1090
1091 // Photo
1092 mPhoto = People.loadContactPhoto(this, mUri, 0, null);
1093 if (mPhoto == null) {
1094 setPhotoPresent(false);
1095 } else {
1096 setPhotoPresent(true);
1097 mPhotoImageView.setImageBitmap(mPhoto);
1098 }
1099
1100 // Send to voicemail
1101 mSendToVoicemailCheckBox
1102 .setChecked(personCursor.getInt(CONTACT_SEND_TO_VOICEMAIL_COLUMN) == 1);
1103
1104 // Organizations
1105 Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
1106 Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
1107 null, null, null);
1108
1109 if (organizationsCursor != null) {
1110 while (organizationsCursor.moveToNext()) {
1111 int type = organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
1112 String label = organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN);
1113 String company = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
1114 String title = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
1115 long id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
1116 Uri uri = ContentUris.withAppendedId(Organizations.CONTENT_URI, id);
1117
1118 // Add an organization entry
1119 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1120 entry.isPrimary = organizationsCursor.getLong(ORGANIZATIONS_ISPRIMARY_COLUMN) != 0;
1121 mOtherEntries.add(entry);
1122 }
1123 organizationsCursor.close();
1124 }
1125
1126 // Notes
1127 if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
1128 entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
1129 mUri);
1130 mOtherEntries.add(entry);
1131 }
1132
1133 // Ringtone
1134 entry = EditEntry.newRingtoneEntry(this,
1135 personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
1136 mOtherEntries.add(entry);
1137 personCursor.close();
1138
1139 // Build up the phone entries
1140 Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
1141 Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION,
1142 null, null, null);
1143
1144 if (phonesCursor != null) {
1145 while (phonesCursor.moveToNext()) {
1146 int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
1147 String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
1148 String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
1149 long id = phonesCursor.getLong(PHONES_ID_COLUMN);
1150 boolean isPrimary = phonesCursor.getLong(PHONES_ISPRIMARY_COLUMN) != 0;
1151 Uri uri = ContentUris.withAppendedId(phonesUri, id);
1152
1153 // Add a phone number entry
1154 entry = EditEntry.newPhoneEntry(this, label, type, number, uri, id);
1155 entry.isPrimary = isPrimary;
1156 mPhoneEntries.add(entry);
1157
1158 // Keep track of which primary types have been added
1159 if (type == Phones.TYPE_MOBILE) {
1160 mMobilePhoneAdded = true;
1161 }
1162 }
1163
1164 phonesCursor.close();
1165 }
1166
1167 // Build the contact method entries
1168 Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
1169 Cursor methodsCursor = mResolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
1170
1171 if (methodsCursor != null) {
1172 while (methodsCursor.moveToNext()) {
1173 int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
1174 String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
1175 String data = methodsCursor.getString(METHODS_DATA_COLUMN);
1176 String auxData = methodsCursor.getString(METHODS_AUX_DATA_COLUMN);
1177 int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
1178 long id = methodsCursor.getLong(METHODS_ID_COLUMN);
1179 boolean isPrimary = methodsCursor.getLong(METHODS_ISPRIMARY_COLUMN) != 0;
1180 Uri uri = ContentUris.withAppendedId(methodsUri, id);
1181
1182 switch (kind) {
1183 case Contacts.KIND_EMAIL: {
1184 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1185 entry.isPrimary = isPrimary;
1186 mEmailEntries.add(entry);
1187
1188 if (isPrimary) {
1189 mPrimaryEmailAdded = true;
1190 }
1191 break;
1192 }
1193
1194 case Contacts.KIND_POSTAL: {
1195 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1196 entry.isPrimary = isPrimary;
1197 mPostalEntries.add(entry);
1198 break;
1199 }
1200
1201 case Contacts.KIND_IM: {
1202 Object protocolObj = ContactMethods.decodeImProtocol(auxData);
1203 if (protocolObj == null) {
1204 // Invalid IM protocol, log it then ignore.
1205 Log.e(TAG, "Couldn't decode IM protocol: " + auxData);
1206 continue;
1207 } else {
1208 if (protocolObj instanceof Number) {
1209 int protocol = ((Number) protocolObj).intValue();
1210 entry = EditEntry.newImEntry(this,
1211 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1212 data, uri, id);
1213 } else {
1214 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1215 uri, id);
1216 }
1217 mImEntries.add(entry);
1218 }
1219 break;
1220 }
1221 }
1222 }
1223
1224 methodsCursor.close();
1225 }
1226
1227 // Add values from the extras, if there are any
1228 if (extras != null) {
1229 addFromExtras(extras, phonesUri, methodsUri);
1230 }
1231
1232 // Add the base types if needed
1233 if (!mMobilePhoneAdded) {
1234 entry = EditEntry.newPhoneEntry(this,
1235 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1236 DEFAULT_PHONE_TYPE);
1237 mPhoneEntries.add(entry);
1238 }
1239
1240 if (!mPrimaryEmailAdded) {
1241 entry = EditEntry.newEmailEntry(this,
1242 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1243 DEFAULT_EMAIL_TYPE);
1244 entry.isPrimary = true;
1245 mEmailEntries.add(entry);
1246 }
1247 }
1248
1249 /**
1250 * Build the list of EditEntries for full mode insertions.
1251 *
1252 * @param extras the extras used to start this activity, may be null
1253 */
1254 private void buildEntriesForInsert(Bundle extras) {
1255 // Clear out the old entries
1256 int numSections = mSections.size();
1257 for (int i = 0; i < numSections; i++) {
1258 mSections.get(i).clear();
1259 }
1260
1261 EditEntry entry;
1262
1263 // Check the intent extras
1264 if (extras != null) {
1265 addFromExtras(extras, null, null);
1266 }
1267
1268 // Photo
1269 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1270
1271 // Add the base entries if they're not already present
1272 if (!mMobilePhoneAdded) {
1273 entry = EditEntry.newPhoneEntry(this, null, Phones.TYPE_MOBILE);
1274 entry.isPrimary = true;
1275 mPhoneEntries.add(entry);
1276 }
1277
1278 if (!mPrimaryEmailAdded) {
1279 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1280 entry.isPrimary = true;
1281 mEmailEntries.add(entry);
1282 }
1283
1284 // Ringtone
1285 entry = EditEntry.newRingtoneEntry(this, null, mUri);
1286 mOtherEntries.add(entry);
1287
1288 }
1289
1290 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1291 EditEntry entry;
1292
1293 // Read the name from the bundle
1294 CharSequence name = extras.getCharSequence(Insert.NAME);
1295 if (name != null && TextUtils.isGraphic(name)) {
1296 mNameView.setText(name);
1297 }
1298
1299 // Postal entries from extras
1300 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1301 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1302 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1303 postalType = DEFAULT_POSTAL_TYPE;
1304 }
1305
1306 if (postalType != INVALID_TYPE) {
1307 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1308 methodsUri, 0);
1309 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1310 mPostalEntries.add(entry);
1311 }
1312
1313 // Email entries from extras
1314 CharSequence email = extras.getCharSequence(Insert.EMAIL);
1315 int emailType = extras.getInt(Insert.EMAIL_TYPE, INVALID_TYPE);
1316 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1317 emailType = DEFAULT_EMAIL_TYPE;
1318 mPrimaryEmailAdded = true;
1319 }
1320
1321 if (emailType != INVALID_TYPE) {
1322 entry = EditEntry.newEmailEntry(this, null, emailType, email.toString(), methodsUri, 0);
1323 entry.isPrimary = extras.getBoolean(Insert.EMAIL_ISPRIMARY);
1324 mEmailEntries.add(entry);
1325
1326 // Keep track of which primary types have been added
1327 if (entry.isPrimary) {
1328 mPrimaryEmailAdded = true;
1329 }
1330 }
1331
1332 // Phone entries from extras
1333 CharSequence phoneNumber = extras.getCharSequence(Insert.PHONE);
1334 int phoneType = extras.getInt(Insert.PHONE_TYPE, INVALID_TYPE);
1335 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1336 phoneType = DEFAULT_PHONE_TYPE;
1337 }
1338
1339 if (phoneType != INVALID_TYPE) {
1340 entry = EditEntry.newPhoneEntry(this, null, phoneType,
1341 phoneNumber.toString(), phonesUri, 0);
1342 entry.isPrimary = extras.getBoolean(Insert.PHONE_ISPRIMARY);
1343 mPhoneEntries.add(entry);
1344
1345 // Keep track of which primary types have been added
1346 if (phoneType == Phones.TYPE_MOBILE) {
1347 mMobilePhoneAdded = true;
1348 }
1349 }
1350
1351 // IM entries from extras
1352 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1353 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
1354
1355 if (imHandle != null && imProtocol != null) {
1356 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1357 if (protocolObj instanceof Number) {
1358 int protocol = ((Number) protocolObj).intValue();
1359 entry = EditEntry.newImEntry(this,
1360 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1361 imHandle.toString(), null, 0);
1362 } else {
1363 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
1364 null, 0);
1365 }
1366 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1367 mImEntries.add(entry);
1368 }
1369 }
1370
1371 /**
1372 * Removes all existing views, builds new ones for all the entries, and adds them.
1373 */
1374 private void buildViews() {
1375 // Remove existing views
1376 final LinearLayout layout = mLayout;
1377 layout.removeAllViews();
1378
1379 buildViewsForSection(layout, mPhoneEntries, R.string.listSeparatorCallNumber);
1380 buildViewsForSection(layout, mEmailEntries, R.string.listSeparatorSendEmail);
1381 buildViewsForSection(layout, mImEntries, R.string.listSeparatorSendIm);
1382 buildViewsForSection(layout, mPostalEntries, R.string.listSeparatorMapAddress);
1383 buildViewsForSection(layout, mOtherEntries, R.string.listSeparatorOtherInformation);
1384 }
1385
1386
1387 /**
1388 * Builds the views for a specific section.
1389 *
1390 * @param layout the container
1391 * @param section the section to build the views for
1392 */
1393 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1394 int separatorResource) {
1395 // Build the separator if the section isn't empty
1396 if (section.size() > 0) {
1397 View separator = mInflater.inflate(R.layout.edit_separator, layout, false);
1398 TextView text = (TextView) separator.findViewById(R.id.text);
1399 text.setText(getText(separatorResource));
1400 layout.addView(separator);
1401 }
1402
1403 // Build views for the current section
1404 for (EditEntry entry : section) {
1405 entry.activity = this; // this could be null from when the state is restored
1406 if (!entry.isDeleted) {
1407 View view = buildViewForEntry(entry);
1408 layout.addView(view);
1409 }
1410 }
1411 }
1412
1413 /**
1414 * Builds a view to display an EditEntry.
1415 *
1416 * @param entry the entry to display
1417 * @return a view that will display the given entry
1418 */
1419 /* package */ View buildViewForEntry(final EditEntry entry) {
1420 // Look for any existing entered text, and save it if found
1421 if (entry.view != null && entry.syncDataWithView) {
1422 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1423 .getText().toString();
1424 if (!TextUtils.isEmpty(enteredText)) {
1425 entry.data = enteredText;
1426 }
1427 }
1428
1429 // Build a new view
1430 final ViewGroup parent = mLayout;
1431 View view;
1432
1433 if (entry.kind == Contacts.KIND_ORGANIZATION) {
1434 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
1435 } else if (isRingtoneEntry(entry)) {
1436 view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
1437 } else if (!entry.isStaticLabel) {
1438 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1439 } else {
1440 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1441 }
1442 entry.view = view;
1443
1444 // Set the entry as the tag so we can find it again later given just the view
1445 view.setTag(entry);
1446
1447 // Bind the label
1448 entry.bindLabel(this);
1449
1450 // Bind data
1451 TextView data = (TextView) view.findViewById(R.id.data);
1452 TextView data2 = (TextView) view.findViewById(R.id.data2);
1453
1454 if (data instanceof Button) {
1455 data.setOnClickListener(this);
1456 }
1457 if (data.length() == 0) {
1458 if (entry.syncDataWithView) {
1459 // If there is already data entered don't overwrite it
1460 data.setText(entry.data);
1461 } else {
1462 fillViewData(entry);
1463 }
1464 }
1465 if (data2 != null && data2.length() == 0) {
1466 // If there is already data entered don't overwrite it
1467 data2.setText(entry.data2);
1468 }
1469 data.setHint(entry.hint);
1470 if (data2 != null) data2.setHint(entry.hint2);
1471 if (entry.lines > 1) {
1472 data.setLines(entry.lines);
1473 data.setMaxLines(entry.maxLines);
1474 if (data2 != null) {
1475 data2.setLines(entry.lines);
1476 data2.setMaxLines(entry.maxLines);
1477 }
1478 } else if (entry.lines >= 0) {
1479 data.setSingleLine();
1480 if (data2 != null) {
1481 data2.setSingleLine();
1482 }
1483 }
1484 switch (entry.keyListener) {
1485 case INPUT_TEXT:
1486 data.setKeyListener(TextKeyListener.getInstance());
1487 if (data2 != null) {
1488 data2.setKeyListener(TextKeyListener.getInstance());
1489 }
1490 break;
1491
1492 case INPUT_TEXT_WORDS:
1493 data.setKeyListener(TextKeyListener.getInstance(true, Capitalize.WORDS));
1494 if (data2 != null) {
1495 data2.setKeyListener(TextKeyListener.getInstance(true, Capitalize.WORDS));
1496 }
1497 break;
1498
1499 case INPUT_TEXT_SENTENCES:
1500 data.setKeyListener(TextKeyListener.getInstance(true, Capitalize.SENTENCES));
1501 if (data2 != null) {
1502 data2.setKeyListener(TextKeyListener.getInstance(true, Capitalize.SENTENCES));
1503 }
1504 break;
1505
1506 case INPUT_DIALER:
1507 data.setKeyListener(DialerKeyListener.getInstance());
1508 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1509 if (data2 != null) {
1510 data2.setKeyListener(DialerKeyListener.getInstance());
1511 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1512 }
1513 break;
1514 }
1515
1516 // Hook up the delete button
1517 View delete = view.findViewById(R.id.delete);
1518 if (delete != null) delete.setOnClickListener(this);
1519 View delete2 = view.findViewById(R.id.delete2);
1520 if (delete2 != null) delete2.setOnClickListener(this);
1521
1522 return view;
1523 }
1524
1525 private void fillViewData(final EditEntry entry) {
1526 if (isRingtoneEntry(entry)) {
1527 updateRingtoneView(entry);
1528 }
1529 }
1530
1531 /**
1532 * Handles the results from the label change picker.
1533 */
1534 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1535 EditEntry mEntry;
1536 String[] mLabels;
1537
1538 public LabelPickedListener(EditEntry entry, String[] labels) {
1539 mEntry = entry;
1540 mLabels = labels;
1541 }
1542
1543 public void onClick(DialogInterface dialog, int which) {
1544 // TODO: Use a managed dialog
1545 if (mEntry.kind != Contacts.KIND_IM) {
1546 final int type = getTypeFromLabelPosition(mLabels, which);
1547 if (type == ContactMethods.TYPE_CUSTOM) {
1548 createCustomPicker(mEntry, null);
1549 } else {
1550 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1551 }
1552 } else {
1553 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1554 }
1555 }
1556 }
1557
1558 /**
1559 * A basic structure with the data for a contact entry in the list.
1560 */
1561 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1562 // These aren't stuffed into the parcel
1563 public EditContactActivity activity;
1564 public View view;
1565
1566 // These are stuffed into the parcel
1567 public String hint;
1568 public String hint2;
1569 public String column;
1570 public String contentDirectory;
1571 public String data2;
1572 public int keyListener;
1573 public int type;
1574 /**
1575 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1576 * will not be called.
1577 */
1578 public int lines = 1;
1579 public boolean isPrimary;
1580 public boolean isDeleted = false;
1581 public boolean isStaticLabel = false;
1582 public boolean syncDataWithView = true;
1583
1584 private EditEntry() {
1585 // only used by CREATOR
1586 }
1587
1588 public EditEntry(EditContactActivity activity) {
1589 this.activity = activity;
1590 }
1591
1592 public EditEntry(EditContactActivity activity, String label,
1593 int type, String data, Uri uri, long id) {
1594 this.activity = activity;
1595 this.isPrimary = false;
1596 this.label = label;
1597 this.type = type;
1598 this.data = data;
1599 this.uri = uri;
1600 this.id = id;
1601 }
1602
1603 public int describeContents() {
1604 return 0;
1605 }
1606
1607 public void writeToParcel(Parcel parcel, int flags) {
1608 // Make sure to read data from the input field, if anything is entered
1609 data = getData();
1610
1611 // Write in our own fields.
1612 parcel.writeString(hint);
1613 parcel.writeString(hint2);
1614 parcel.writeString(column);
1615 parcel.writeString(contentDirectory);
1616 parcel.writeString(data2);
1617 parcel.writeInt(keyListener);
1618 parcel.writeInt(type);
1619 parcel.writeInt(lines);
1620 parcel.writeInt(isPrimary ? 1 : 0);
1621 parcel.writeInt(isDeleted ? 1 : 0);
1622 parcel.writeInt(isStaticLabel ? 1 : 0);
1623 parcel.writeInt(syncDataWithView ? 1 : 0);
1624
1625 // Write in the fields from Entry
1626 super.writeToParcel(parcel);
1627 }
1628
1629 public static final Parcelable.Creator<EditEntry> CREATOR =
1630 new Parcelable.Creator<EditEntry>() {
1631 public EditEntry createFromParcel(Parcel in) {
1632 EditEntry entry = new EditEntry();
1633
1634 // Read out our own fields
1635 entry.hint = in.readString();
1636 entry.hint2 = in.readString();
1637 entry.column = in.readString();
1638 entry.contentDirectory = in.readString();
1639 entry.data2 = in.readString();
1640 entry.keyListener = in.readInt();
1641 entry.type = in.readInt();
1642 entry.lines = in.readInt();
1643 entry.isPrimary = in.readInt() == 1;
1644 entry.isDeleted = in.readInt() == 1;
1645 entry.isStaticLabel = in.readInt() == 1;
1646 entry.syncDataWithView = in.readInt() == 1;
1647
1648 // Read out the fields from Entry
1649 entry.readFromParcel(in);
1650
1651 return entry;
1652 }
1653
1654 public EditEntry[] newArray(int size) {
1655 return new EditEntry[size];
1656 }
1657 };
1658
1659 public void setLabel(Context context, int typeIn, String labelIn) {
1660 type = typeIn;
1661 label = labelIn;
1662 if (view != null) {
1663 bindLabel(context);
1664 }
1665 }
1666
1667 public void bindLabel(Context context) {
1668 TextView v = (TextView) view.findViewById(R.id.label);
1669 if (isStaticLabel) {
1670 v.setText(label);
1671 return;
1672 }
1673
1674 switch (kind) {
1675 case Contacts.KIND_PHONE: {
1676 v.setText(Phones.getDisplayLabel(context, type, label));
1677 break;
1678 }
1679
1680 case Contacts.KIND_IM: {
1681 v.setText(getLabelsForKind(activity, kind)[type]);
1682 break;
1683 }
1684
1685 case Contacts.KIND_ORGANIZATION: {
1686 v.setText(Organizations.getDisplayLabel(activity, type, label));
1687 break;
1688 }
1689
1690 default: {
1691 v.setText(Contacts.ContactMethods.getDisplayLabel(context, kind, type, label));
1692 if (kind == Contacts.KIND_POSTAL) {
1693 v.setMaxLines(3);
1694 }
1695 break;
1696 }
1697 }
1698 v.setOnClickListener(activity);
1699 }
1700
1701 /**
1702 * Returns the data for the entry
1703 * @return the data for the entry
1704 */
1705 public String getData() {
1706 if (view != null && syncDataWithView) {
1707 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1708 if (text != null) {
1709 return text.toString();
1710 }
1711 }
1712
1713 if (data != null) {
1714 return data.toString();
1715 }
1716
1717 return null;
1718 }
1719
1720 /**
1721 * Dumps the entry into a HashMap suitable for passing to the database.
1722 *
1723 * @param values the HashMap to fill in.
1724 * @return true if the value should be saved, false otherwise
1725 */
1726 public boolean toValues(ContentValues values) {
1727 boolean success = false;
1728 String labelString = null;
1729 // Save the type and label
1730 if (view != null) {
1731 // Read the possibly updated label from the text field
1732 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1733 }
1734 switch (kind) {
1735 case Contacts.KIND_PHONE:
1736 if (type != Phones.TYPE_CUSTOM) {
1737 labelString = null;
1738 }
1739 values.put(Phones.LABEL, labelString);
1740 values.put(Phones.TYPE, type);
1741 break;
1742
1743 case Contacts.KIND_EMAIL:
1744 if (type != ContactMethods.TYPE_CUSTOM) {
1745 labelString = null;
1746 }
1747 values.put(ContactMethods.LABEL, labelString);
1748 values.put(ContactMethods.KIND, kind);
1749 values.put(ContactMethods.TYPE, type);
1750 break;
1751
1752 case Contacts.KIND_IM:
1753 values.put(ContactMethods.KIND, kind);
1754 values.put(ContactMethods.TYPE, ContactMethods.TYPE_OTHER);
1755 values.putNull(ContactMethods.LABEL);
1756 if (type != -1) {
1757 values.put(ContactMethods.AUX_DATA,
1758 ContactMethods.encodePredefinedImProtocol(type));
1759 } else {
1760 values.put(ContactMethods.AUX_DATA,
1761 ContactMethods.encodeCustomImProtocol(label.toString()));
1762 }
1763 break;
1764
1765 case Contacts.KIND_POSTAL:
1766 if (type != ContactMethods.TYPE_CUSTOM) {
1767 labelString = null;
1768 }
1769 values.put(ContactMethods.LABEL, labelString);
1770 values.put(ContactMethods.KIND, kind);
1771 values.put(ContactMethods.TYPE, type);
1772 break;
1773
1774 case Contacts.KIND_ORGANIZATION:
1775 if (type != ContactMethods.TYPE_CUSTOM) {
1776 labelString = null;
1777 }
1778 values.put(ContactMethods.LABEL, labelString);
1779 values.put(ContactMethods.TYPE, type);
1780 // Save the title
1781 if (view != null) {
1782 // Read the possibly updated data from the text field
1783 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
1784 }
1785 if (!TextUtils.isGraphic(data2)) {
1786 values.putNull(Organizations.TITLE);
1787 } else {
1788 values.put(Organizations.TITLE, data2.toString());
1789 success = true;
1790 }
1791 break;
1792
1793 default:
1794 Log.w(TAG, "unknown kind " + kind);
1795 values.put(ContactMethods.LABEL, labelString);
1796 values.put(ContactMethods.KIND, kind);
1797 values.put(ContactMethods.TYPE, type);
1798 break;
1799 }
1800
1801 values.put(ContactMethods.ISPRIMARY, isPrimary ? "1" : "0");
1802
1803 // Save the data
1804 if (view != null && syncDataWithView) {
1805 // Read the possibly updated data from the text field
1806 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
1807 }
1808 if (!TextUtils.isGraphic(data)) {
1809 values.putNull(column);
1810 return success;
1811 } else {
1812 values.put(column, data.toString());
1813 return true;
1814 }
1815 }
1816
1817 /**
1818 * Create a new empty organization entry
1819 */
1820 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1821 Uri uri, int type) {
1822 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
1823 }
1824
1825 /**
1826 * Create a new company entry with the given data.
1827 */
1828 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1829 String label, int type, String company, String title, Uri uri, long id) {
1830 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
1831 entry.hint = activity.getString(R.string.ghostData_company);
1832 entry.hint2 = activity.getString(R.string.ghostData_title);
1833 entry.data2 = title;
1834 entry.column = Organizations.COMPANY;
1835 entry.contentDirectory = Organizations.CONTENT_DIRECTORY;
1836 entry.kind = Contacts.KIND_ORGANIZATION;
1837 entry.keyListener = INPUT_TEXT_WORDS;
1838 return entry;
1839 }
1840
1841 /**
1842 * Create a new notes entry with the given data.
1843 */
1844 public static final EditEntry newNotesEntry(EditContactActivity activity,
1845 String data, Uri uri) {
1846 EditEntry entry = new EditEntry(activity);
1847 entry.label = activity.getString(R.string.label_notes);
1848 entry.hint = activity.getString(R.string.ghostData_notes);
1849 entry.data = data;
1850 entry.uri = uri;
1851 entry.column = People.NOTES;
1852 entry.maxLines = 10;
1853 entry.lines = 2;
1854 entry.id = 0;
1855 entry.kind = KIND_CONTACT;
1856 entry.keyListener = INPUT_TEXT_SENTENCES;
1857 entry.isStaticLabel = true;
1858 return entry;
1859 }
1860
1861 /**
1862 * Create a new ringtone entry with the given data.
1863 */
1864 public static final EditEntry newRingtoneEntry(EditContactActivity activity,
1865 String data, Uri uri) {
1866 EditEntry entry = new EditEntry(activity);
1867 entry.label = activity.getString(R.string.label_ringtone);
1868 entry.data = data;
1869 entry.uri = uri;
1870 entry.column = People.CUSTOM_RINGTONE;
1871 entry.kind = KIND_CONTACT;
1872 entry.isStaticLabel = true;
1873 entry.syncDataWithView = false;
1874 entry.lines = -1;
1875 return entry;
1876 }
1877
1878 /**
1879 * Create a new empty email entry
1880 */
1881 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1882 Uri uri, int type) {
1883 return newPhoneEntry(activity, null, type, null, uri, 0);
1884 }
1885
1886 /**
1887 * Create a new phone entry with the given data.
1888 */
1889 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1890 String label, int type, String data, Uri uri,
1891 long id) {
1892 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1893 entry.hint = activity.getString(R.string.ghostData_phone);
1894 entry.column = People.Phones.NUMBER;
1895 entry.contentDirectory = People.Phones.CONTENT_DIRECTORY;
1896 entry.kind = Contacts.KIND_PHONE;
1897 entry.keyListener = INPUT_DIALER;
1898 return entry;
1899 }
1900
1901 /**
1902 * Create a new empty email entry
1903 */
1904 public static final EditEntry newEmailEntry(EditContactActivity activity,
1905 Uri uri, int type) {
1906 return newEmailEntry(activity, null, type, null, uri, 0);
1907 }
1908
1909 /**
1910 * Create a new email entry with the given data.
1911 */
1912 public static final EditEntry newEmailEntry(EditContactActivity activity,
1913 String label, int type, String data, Uri uri,
1914 long id) {
1915 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1916 entry.hint = activity.getString(R.string.ghostData_email);
1917 entry.column = ContactMethods.DATA;
1918 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
1919 entry.kind = Contacts.KIND_EMAIL;
1920 entry.keyListener = INPUT_TEXT;
1921 return entry;
1922 }
1923
1924 /**
1925 * Create a new empty postal address entry
1926 */
1927 public static final EditEntry newPostalEntry(EditContactActivity activity,
1928 Uri uri, int type) {
1929 return newPostalEntry(activity, null, type, null, uri, 0);
1930 }
1931
1932 /**
1933 * Create a new postal address entry with the given data.
1934 *
1935 * @param label label for the item, from the db not the display label
1936 * @param type the type of postal address
1937 * @param data the starting data for the entry, may be null
1938 * @param uri the uri for the entry if it already exists, may be null
1939 * @param id the id for the entry if it already exists, 0 it it doesn't
1940 * @return the new EditEntry
1941 */
1942 public static final EditEntry newPostalEntry(EditContactActivity activity,
1943 String label, int type, String data, Uri uri, long id) {
1944 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1945 entry.hint = activity.getString(R.string.ghostData_postal);
1946 entry.column = ContactMethods.DATA;
1947 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
1948 entry.kind = Contacts.KIND_POSTAL;
1949 entry.keyListener = INPUT_TEXT_WORDS;
1950 entry.maxLines = 4;
1951 entry.lines = 2;
1952 return entry;
1953 }
1954
1955 /**
1956 * Create a new postal address entry with the given data.
1957 *
1958 * @param label label for the item, from the db not the display label
1959 * @param protocol the type used
1960 * @param data the starting data for the entry, may be null
1961 * @param uri the uri for the entry if it already exists, may be null
1962 * @param id the id for the entry if it already exists, 0 it it doesn't
1963 * @return the new EditEntry
1964 */
1965 public static final EditEntry newImEntry(EditContactActivity activity,
1966 String label, int protocol, String data, Uri uri, long id) {
1967 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
1968 entry.hint = activity.getString(R.string.ghostData_im);
1969 entry.column = ContactMethods.DATA;
1970 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
1971 entry.kind = Contacts.KIND_IM;
1972 entry.keyListener = INPUT_TEXT;
1973 return entry;
1974 }
1975 }
1976}