blob: 4d5a669958d3bbbd4dcbd921c920a9fdf6e61f37 [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
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 static com.android.contacts.ContactEntryAdapter.CONTACT_CUSTOM_RINGTONE_COLUMN;
20import static com.android.contacts.ContactEntryAdapter.CONTACT_NAME_COLUMN;
21import static com.android.contacts.ContactEntryAdapter.CONTACT_NOTES_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
23import static com.android.contacts.ContactEntryAdapter.CONTACT_SEND_TO_VOICEMAIL_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.CONTACT_PHONETIC_NAME_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.METHODS_AUX_DATA_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.METHODS_DATA_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.METHODS_ID_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.METHODS_ISPRIMARY_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.METHODS_KIND_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.METHODS_LABEL_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.METHODS_PROJECTION;
32import static com.android.contacts.ContactEntryAdapter.METHODS_TYPE_COLUMN;
33import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_COMPANY_COLUMN;
34import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ID_COLUMN;
35import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ISPRIMARY_COLUMN;
36import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_LABEL_COLUMN;
37import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_PROJECTION;
38import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TITLE_COLUMN;
39import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TYPE_COLUMN;
40import static com.android.contacts.ContactEntryAdapter.PHONES_ID_COLUMN;
41import static com.android.contacts.ContactEntryAdapter.PHONES_ISPRIMARY_COLUMN;
42import static com.android.contacts.ContactEntryAdapter.PHONES_LABEL_COLUMN;
43import static com.android.contacts.ContactEntryAdapter.PHONES_NUMBER_COLUMN;
44import static com.android.contacts.ContactEntryAdapter.PHONES_PROJECTION;
45import static com.android.contacts.ContactEntryAdapter.PHONES_TYPE_COLUMN;
46
Alex Kennberg87fc3172009-03-28 06:43:06 -070047import com.android.contacts.ViewContactActivity.ViewEntry;
48
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080049import android.app.Activity;
50import android.app.AlertDialog;
51import android.app.Dialog;
52import android.content.ActivityNotFoundException;
53import android.content.ContentResolver;
54import android.content.ContentUris;
55import android.content.ContentValues;
56import android.content.Context;
57import android.content.DialogInterface;
58import android.content.Intent;
59import android.content.SharedPreferences;
60import android.content.res.ColorStateList;
61import android.content.res.Resources;
62import android.database.Cursor;
63import android.graphics.Bitmap;
64import android.media.Ringtone;
65import android.media.RingtoneManager;
66import android.net.Uri;
67import android.os.Bundle;
68import android.os.Parcel;
69import android.os.Parcelable;
70import android.preference.PreferenceManager;
71import android.provider.Contacts;
72import android.provider.Contacts.ContactMethods;
73import android.provider.Contacts.Intents.Insert;
Alex Kennberg87fc3172009-03-28 06:43:06 -070074import android.provider.Contacts.GroupMembership;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080075import android.provider.Contacts.Groups;
76import android.provider.Contacts.Organizations;
77import android.provider.Contacts.People;
78import android.provider.Contacts.Phones;
79import android.telephony.PhoneNumberFormattingTextWatcher;
80import android.text.Editable;
81import android.text.TextUtils;
82import android.text.TextWatcher;
83import android.text.method.TextKeyListener;
84import android.text.method.TextKeyListener.Capitalize;
85import android.util.Log;
86import android.util.SparseBooleanArray;
87import android.view.KeyEvent;
88import android.view.LayoutInflater;
89import android.view.Menu;
90import android.view.MenuItem;
91import android.view.View;
92import android.view.ViewGroup;
93import android.view.ViewParent;
94import android.view.inputmethod.EditorInfo;
95import android.widget.Button;
96import android.widget.CheckBox;
97import android.widget.EditText;
98import android.widget.ImageView;
99import android.widget.LinearLayout;
100import android.widget.TextView;
101import android.widget.Toast;
102
103import java.io.ByteArrayOutputStream;
104import java.util.ArrayList;
105
106/**
107 * Activity for editing or inserting a contact. Note that if the contact data changes in the
108 * background while this activity is running, the updates will be overwritten.
109 */
110public final class EditContactActivity extends Activity implements View.OnClickListener,
111 TextWatcher, View.OnFocusChangeListener {
112 private static final String TAG = "EditContactActivity";
113
114 private static final int STATE_UNKNOWN = 0;
115 /** Editing an existing contact */
116 private static final int STATE_EDIT = 1;
117 /** The full insert mode */
118 private static final int STATE_INSERT = 2;
119
120 /** The launch code when picking a photo and the raw data is returned */
121 private static final int PHOTO_PICKED_WITH_DATA = 3021;
122
123 /** The launch code when picking a ringtone */
124 private static final int RINGTONE_PICKED = 3023;
125
126 // These correspond to the string array in resources for picker "other" items
127 final static int OTHER_ORGANIZATION = 0;
128 final static int OTHER_NOTE = 1;
129
130 // Dialog IDs
131 final static int DELETE_CONFIRMATION_DIALOG = 2;
132
133 // Section IDs
134 final static int SECTION_PHONES = 3;
135 final static int SECTION_EMAIL = 4;
136 final static int SECTION_IM = 5;
137 final static int SECTION_POSTAL = 6;
138 final static int SECTION_ORG = 7;
139 final static int SECTION_NOTE = 8;
140
141 // Menu item IDs
142 public static final int MENU_ITEM_SAVE = 1;
143 public static final int MENU_ITEM_DONT_SAVE = 2;
144 public static final int MENU_ITEM_DELETE = 3;
145 public static final int MENU_ITEM_PHOTO = 6;
146
147 /** Used to represent an invalid type for a contact entry */
148 private static final int INVALID_TYPE = -1;
149
150 /** The default type for a phone that is added via an intent */
151 private static final int DEFAULT_PHONE_TYPE = Phones.TYPE_MOBILE;
152
153 /** The default type for an email that is added via an intent */
154 private static final int DEFAULT_EMAIL_TYPE = ContactMethods.TYPE_HOME;
155
156 /** The default type for a postal address that is added via an intent */
157 private static final int DEFAULT_POSTAL_TYPE = ContactMethods.TYPE_HOME;
158
159 private int mState; // saved across instances
160 private boolean mInsert; // saved across instances
161 private Uri mUri; // saved across instances
162 /** In insert mode this is the photo */
163 private Bitmap mPhoto; // saved across instances
164 private boolean mPhotoChanged = false; // saved across instances
165
166 private EditText mNameView;
167 private ImageView mPhotoImageView;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800168 private ViewGroup mContentView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800169 private LinearLayout mLayout;
170 private LayoutInflater mInflater;
171 private MenuItem mPhotoMenuItem;
172 private boolean mPhotoPresent = false;
173 private EditText mPhoneticNameView; // invisible in some locales, but always present
174
175 /** Flag marking this contact as changed, meaning we should write changes back. */
176 private boolean mContactChanged = false;
Alex Kennberg87fc3172009-03-28 06:43:06 -0700177
178 /** List of all the group names */
179 private CharSequence[] mGroups;
180
181 /** Is this contact part of the group */
182 private boolean[] mInTheGroup;
183
184 private static final String[] GROUP_ID_PROJECTION = new String[] {
185 Groups._ID,
186 };
187
188 private static final String[] GROUPMEMBERSHIP_ID_PROJECTION = new String[] {
189 GroupMembership._ID,
190 };
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800191
192 // These are accessed by inner classes. They're package scoped to make access more efficient.
193 /* package */ ContentResolver mResolver;
194 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
195 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
196 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
197 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
198 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
199 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
200 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
201 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
202
203 /* package */ static final int MSG_DELETE = 1;
204 /* package */ static final int MSG_CHANGE_LABEL = 2;
205 /* package */ static final int MSG_ADD_PHONE = 3;
206 /* package */ static final int MSG_ADD_EMAIL = 4;
207 /* package */ static final int MSG_ADD_POSTAL = 5;
Alex Kennberg87fc3172009-03-28 06:43:06 -0700208
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800209 private static final int[] TYPE_PRECEDENCE_PHONES = new int[] {
210 Phones.TYPE_MOBILE, Phones.TYPE_HOME, Phones.TYPE_WORK, Phones.TYPE_OTHER
211 };
212 private static final int[] TYPE_PRECEDENCE_METHODS = new int[] {
213 ContactMethods.TYPE_HOME, ContactMethods.TYPE_WORK, ContactMethods.TYPE_OTHER
214 };
215 private static final int[] TYPE_PRECEDENCE_IM = new int[] {
216 ContactMethods.PROTOCOL_GOOGLE_TALK, ContactMethods.PROTOCOL_AIM,
217 ContactMethods.PROTOCOL_MSN, ContactMethods.PROTOCOL_YAHOO,
218 ContactMethods.PROTOCOL_JABBER
219 };
220 private static final int[] TYPE_PRECEDENCE_ORG = new int[] {
221 Organizations.TYPE_WORK, Organizations.TYPE_OTHER
222 };
223
224 public void onClick(View v) {
225 switch (v.getId()) {
226 case R.id.photoImage: {
227 doPickPhotoAction();
228 break;
229 }
230
231 case R.id.checkable: {
232 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
233 checkBox.toggle();
234
235 EditEntry entry = findEntryForView(v);
236 entry.data = checkBox.isChecked() ? "1" : "0";
237
238 mContactChanged = true;
239 break;
240 }
241
Alex Kennberg87fc3172009-03-28 06:43:06 -0700242 case R.id.entry_group: {
243 EditEntry entry = findEntryForView(v);
244 doPickGroup(entry);
245 break;
246 }
247
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800248 case R.id.entry_ringtone: {
249 EditEntry entry = findEntryForView(v);
250 doPickRingtone(entry);
251 break;
252 }
253
254 case R.id.separator: {
255 // Someone clicked on a section header, so handle add action
256 int sectionType = (Integer) v.getTag();
257 doAddAction(sectionType);
258 break;
259 }
260
261 case R.id.saveButton:
262 doSaveAction();
263 break;
264
265 case R.id.discardButton:
266 doRevertAction();
267 break;
268
269 case R.id.delete: {
270 EditEntry entry = findEntryForView(v);
271 if (entry != null) {
272 // Clear the text and hide the view so it gets saved properly
273 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
274 entry.view.setVisibility(View.GONE);
275 entry.isDeleted = true;
276 }
277
278 // Force rebuild of views because section headers might need to change
279 buildViews();
280 break;
281 }
282
283 case R.id.label: {
284 EditEntry entry = findEntryForView(v);
285 if (entry != null) {
286 String[] labels = getLabelsForKind(this, entry.kind);
287 LabelPickedListener listener = new LabelPickedListener(entry, labels);
288 new AlertDialog.Builder(EditContactActivity.this)
289 .setItems(labels, listener)
290 .setTitle(R.string.selectLabel)
291 .show();
292 }
293 break;
294 }
295 }
296 }
297
298 private void setPhotoPresent(boolean present) {
299 mPhotoPresent = present;
300
301 // Correctly scale the contact photo if present, otherwise just center
302 // the photo placeholder icon.
303 if (mPhotoPresent) {
304 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
305 } else {
306 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
307 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
308 }
309
310 if (mPhotoMenuItem != null) {
311 if (present) {
312 mPhotoMenuItem.setTitle(R.string.removePicture);
313 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
314 } else {
315 mPhotoMenuItem.setTitle(R.string.addPicture);
316 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
317 }
318 }
319 }
320
321 private EditEntry findEntryForView(View v) {
322 // Try to find the entry for this view
323 EditEntry entry = null;
324 do {
325 Object tag = v.getTag();
326 if (tag != null && tag instanceof EditEntry) {
327 entry = (EditEntry) tag;
328 break;
329 } else {
330 ViewParent parent = v.getParent();
331 if (parent != null && parent instanceof View) {
332 v = (View) parent;
333 } else {
334 v = null;
335 }
336 }
337 } while (v != null);
338 return entry;
339 }
340
341 private DialogInterface.OnClickListener mDeleteContactDialogListener =
342 new DialogInterface.OnClickListener() {
343 public void onClick(DialogInterface dialog, int button) {
344 mResolver.delete(mUri, null, null);
345 finish();
346 }
347 };
348
349 private boolean mMobilePhoneAdded = false;
350 private boolean mPrimaryEmailAdded = false;
351
352 @Override
353 protected void onCreate(Bundle icicle) {
354 super.onCreate(icicle);
355
356 mResolver = getContentResolver();
357
358 // Build the list of sections
359 setupSections();
360
361 // Load the UI
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800362 mInflater = getLayoutInflater();
363 mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
364 setContentView(mContentView);
365
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800366 mLayout = (LinearLayout) findViewById(R.id.list);
367 mNameView = (EditText) findViewById(R.id.name);
368 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
369 mPhotoImageView.setOnClickListener(this);
370 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
371
372 // Setup the bottom buttons
373 View view = findViewById(R.id.saveButton);
374 view.setOnClickListener(this);
375 view = findViewById(R.id.discardButton);
376 view.setOnClickListener(this);
377
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800378 // Resolve the intent
379 mState = STATE_UNKNOWN;
380 Intent intent = getIntent();
381 String action = intent.getAction();
382 mUri = intent.getData();
383 if (mUri != null) {
384 if (action.equals(Intent.ACTION_EDIT)) {
385 if (icicle == null) {
386 // Build the entries & views
387 buildEntriesForEdit(getIntent().getExtras());
388 buildViews();
389 }
390 setTitle(R.string.editContact_title_edit);
391 mState = STATE_EDIT;
392 } else if (action.equals(Intent.ACTION_INSERT)) {
393 if (icicle == null) {
394 // Build the entries & views
395 buildEntriesForInsert(getIntent().getExtras());
396 buildViews();
397 }
398 setTitle(R.string.editContact_title_insert);
399 mState = STATE_INSERT;
400 mInsert = true;
401 }
402 }
403
404 if (mState == STATE_UNKNOWN) {
405 Log.e(TAG, "Cannot resolve intent: " + intent);
406 finish();
407 return;
408 }
409
410 if (mState == STATE_EDIT) {
411 setTitle(getResources().getText(R.string.editContact_title_edit));
412 } else {
413 setTitle(getResources().getText(R.string.editContact_title_insert));
414 }
415 }
416
417 private void setupSections() {
418 mSections.add(mPhoneEntries);
419 mSections.add(mEmailEntries);
420 mSections.add(mImEntries);
421 mSections.add(mPostalEntries);
422 mSections.add(mOrgEntries);
423 mSections.add(mNoteEntries);
424 mSections.add(mOtherEntries);
425 }
426
427 @Override
428 protected void onSaveInstanceState(Bundle outState) {
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800429
430 // To store current focus between config changes, follow focus down the
431 // view tree, keeping track of any parents with EditEntry tags
432 View focusedChild = mContentView.getFocusedChild();
433 EditEntry focusedEntry = null;
434 while (focusedChild != null) {
435 Object tag = focusedChild.getTag();
436 if (tag instanceof EditEntry) {
437 focusedEntry = (EditEntry) tag;
438 }
439
440 // Keep going deeper until child isn't a group
441 if (focusedChild instanceof ViewGroup) {
442 View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
443 if (deeperFocus != null) {
444 focusedChild = deeperFocus;
445 } else {
446 break;
447 }
448 } else {
449 break;
450 }
451 }
452
453 if (focusedChild != null) {
454 int requestFocusId = focusedChild.getId();
455 int requestCursor = 0;
456 if (focusedChild instanceof EditText) {
457 requestCursor = ((EditText) focusedChild).getSelectionStart();
458 }
459
460 // Store focus values in EditEntry if found, otherwise store as
461 // generic values
462 if (focusedEntry != null) {
463 focusedEntry.requestFocusId = requestFocusId;
464 focusedEntry.requestCursor = requestCursor;
465 } else {
466 outState.putInt("requestFocusId", requestFocusId);
467 outState.putInt("requestCursor", requestCursor);
468 }
469 }
470
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800471 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
472 outState.putParcelableArrayList("emailEntries", mEmailEntries);
473 outState.putParcelableArrayList("imEntries", mImEntries);
474 outState.putParcelableArrayList("postalEntries", mPostalEntries);
475 outState.putParcelableArrayList("orgEntries", mOrgEntries);
476 outState.putParcelableArrayList("noteEntries", mNoteEntries);
477 outState.putParcelableArrayList("otherEntries", mOtherEntries);
478 outState.putInt("state", mState);
479 outState.putBoolean("insert", mInsert);
480 outState.putParcelable("uri", mUri);
481 outState.putString("name", mNameView.getText().toString());
482 outState.putParcelable("photo", mPhoto);
483 outState.putBoolean("photoChanged", mPhotoChanged);
484 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
485 outState.putBoolean("contactChanged", mContactChanged);
486 }
487
488 @Override
489 protected void onRestoreInstanceState(Bundle inState) {
490 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
491 mEmailEntries = inState.getParcelableArrayList("emailEntries");
492 mImEntries = inState.getParcelableArrayList("imEntries");
493 mPostalEntries = inState.getParcelableArrayList("postalEntries");
494 mOrgEntries = inState.getParcelableArrayList("orgEntries");
495 mNoteEntries = inState.getParcelableArrayList("noteEntries");
496 mOtherEntries = inState.getParcelableArrayList("otherEntries");
497 setupSections();
498
499 mState = inState.getInt("state");
500 mInsert = inState.getBoolean("insert");
501 mUri = inState.getParcelable("uri");
502 mNameView.setText(inState.getString("name"));
503 mPhoto = inState.getParcelable("photo");
504 if (mPhoto != null) {
505 mPhotoImageView.setImageBitmap(mPhoto);
506 setPhotoPresent(true);
507 } else {
508 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
509 setPhotoPresent(false);
510 }
511 mPhotoChanged = inState.getBoolean("photoChanged");
512 mPhoneticNameView.setText(inState.getString("phoneticName"));
513 mContactChanged = inState.getBoolean("contactChanged");
514
515 // Now that everything is restored, build the view
516 buildViews();
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800517
518 // Try restoring any generally requested focus
519 int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
520 View focusedChild = mContentView.findViewById(requestFocusId);
521 if (focusedChild != null) {
522 focusedChild.requestFocus();
523 if (focusedChild instanceof EditText) {
524 int requestCursor = inState.getInt("requestCursor", 0);
525 ((EditText) focusedChild).setSelection(requestCursor);
526 }
527 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800528 }
529
530 @Override
531 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
532 if (resultCode != RESULT_OK) {
533 return;
534 }
535
536 switch (requestCode) {
537 case PHOTO_PICKED_WITH_DATA: {
538 final Bundle extras = data.getExtras();
539 if (extras != null) {
540 Bitmap photo = extras.getParcelable("data");
541 mPhoto = photo;
542 mPhotoChanged = true;
543 mPhotoImageView.setImageBitmap(photo);
544 setPhotoPresent(true);
545 }
546 break;
547 }
548
549 case RINGTONE_PICKED: {
550 Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
551 handleRingtonePicked(pickedUri);
552 mContactChanged = true;
553 break;
554 }
555 }
556 }
557
558 @Override
559 public boolean onKeyDown(int keyCode, KeyEvent event) {
560 switch (keyCode) {
561 case KeyEvent.KEYCODE_BACK: {
562 doSaveAction();
563 return true;
564 }
565 }
566 return super.onKeyDown(keyCode, event);
567 }
568
569 @Override
570 public boolean onCreateOptionsMenu(Menu menu) {
571 super.onCreateOptionsMenu(menu);
572 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
573 .setIcon(android.R.drawable.ic_menu_save)
574 .setAlphabeticShortcut('\n');
575 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
576 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
577 .setAlphabeticShortcut('q');
578 if (!mInsert) {
579 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
580 .setIcon(android.R.drawable.ic_menu_delete);
581 }
582
583 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
584 // Updates the state of the menu item
585 setPhotoPresent(mPhotoPresent);
586
587 return true;
588 }
589
590 @Override
591 public boolean onOptionsItemSelected(MenuItem item) {
592 switch (item.getItemId()) {
593 case MENU_ITEM_SAVE:
594 doSaveAction();
595 return true;
596
597 case MENU_ITEM_DONT_SAVE:
598 doRevertAction();
599 return true;
600
601 case MENU_ITEM_DELETE:
602 // Get confirmation
603 showDialog(DELETE_CONFIRMATION_DIALOG);
604 return true;
605
606 case MENU_ITEM_PHOTO:
607 if (!mPhotoPresent) {
608 doPickPhotoAction();
609 } else {
610 doRemovePhotoAction();
611 }
612 return true;
613 }
614
615 return false;
616 }
617
618 /**
619 * Try guessing the next-best type of {@link EditEntry} to insert into the
620 * given list. We walk down the precedence list until we find a type that
621 * doesn't exist yet, or default to the lowest ranking type.
622 */
623 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
624 // Keep track of the types we've seen already
625 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
626 for (int i = entries.size() - 1; i >= 0; i--) {
627 EditEntry entry = entries.get(i);
628 if (!entry.isDeleted) {
629 existAlready.put(entry.type, true);
630 }
631 }
632
633 // Pick the first item we haven't seen
634 for (int type : precedenceList) {
635 if (!existAlready.get(type, false)) {
636 return type;
637 }
638 }
639
640 // Otherwise default to last item
641 return precedenceList[precedenceList.length - 1];
642 }
643
644 private void doAddAction(int sectionType) {
645 EditEntry entry = null;
646 switch (sectionType) {
647 case SECTION_PHONES: {
648 // Try figuring out which type to insert next
649 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
650 entry = EditEntry.newPhoneEntry(EditContactActivity.this,
651 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
652 nextType);
653 mPhoneEntries.add(entry);
654 break;
655 }
656 case SECTION_EMAIL: {
657 // Try figuring out which type to insert next
658 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_METHODS);
659 entry = EditEntry.newEmailEntry(EditContactActivity.this,
660 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
661 nextType);
662 mEmailEntries.add(entry);
663 break;
664 }
665 case SECTION_IM: {
666 // Try figuring out which type to insert next
667 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
668 entry = EditEntry.newImEntry(EditContactActivity.this,
669 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
670 nextType);
671 mImEntries.add(entry);
672 break;
673 }
674 case SECTION_POSTAL: {
675 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_METHODS);
676 entry = EditEntry.newPostalEntry(EditContactActivity.this,
677 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
678 nextType);
679 mPostalEntries.add(entry);
680 break;
681 }
682 case SECTION_ORG: {
683 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
684 entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
685 Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
686 nextType);
687 mOrgEntries.add(entry);
688 break;
689 }
690 case SECTION_NOTE: {
691 entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
692 mNoteEntries.add(entry);
693 break;
694 }
695 }
696
697 // Rebuild the views if needed
698 if (entry != null) {
699 buildViews();
700 mContactChanged = true;
701
702 View dataView = entry.view.findViewById(R.id.data);
703 if (dataView == null) {
704 entry.view.requestFocus();
705 } else {
706 dataView.requestFocus();
707 }
708 }
709 }
710
711 private void doRevertAction() {
712 finish();
713 }
714
715 private void doPickPhotoAction() {
716 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
717 // TODO: get these values from constants somewhere
718 intent.setType("image/*");
719 intent.putExtra("crop", "true");
720 intent.putExtra("aspectX", 1);
721 intent.putExtra("aspectY", 1);
722 intent.putExtra("outputX", 96);
723 intent.putExtra("outputY", 96);
724 try {
725 intent.putExtra("return-data", true);
726 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
727 } catch (ActivityNotFoundException e) {
728 new AlertDialog.Builder(EditContactActivity.this)
729 .setTitle(R.string.errorDialogTitle)
730 .setMessage(R.string.photoPickerNotFoundText)
731 .setPositiveButton(android.R.string.ok, null)
732 .show();
733 }
734 }
735
736 private void doRemovePhotoAction() {
737 mPhoto = null;
738 mPhotoChanged = true;
739 setPhotoPresent(false);
740 }
741
Alex Kennberg87fc3172009-03-28 06:43:06 -0700742 private void populateGroups() {
743 // Create a list of all the groups
744 Cursor cursor = mResolver.query(Groups.CONTENT_URI, ContactsListActivity.GROUPS_PROJECTION,
745 null, null, Groups.DEFAULT_SORT_ORDER);
746 try {
747 ArrayList<Long> ids = new ArrayList<Long>();
748 ArrayList<String> items = new ArrayList<String>();
749
750 while (cursor.moveToNext()) {
751 String systemId = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
752 String name = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
753
754 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
755 continue;
756 }
757
758 if (!TextUtils.isEmpty(name)) {
759 ids.add(new Long(cursor.getLong(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID)));
760 items.add(name);
761 }
762 }
763
764 mGroups = items.toArray(new CharSequence[items.size()]);
765 mInTheGroup = new boolean[items.size()];
766 } finally {
767 cursor.close();
768 }
769
770 if (mGroups != null) {
771
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700772 // Go through the groups for this member and update the list
Alex Kennberg87fc3172009-03-28 06:43:06 -0700773 final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700774 Cursor groupCursor = null;
775 try {
776 groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
777 null, null, Groups.DEFAULT_SORT_ORDER);
778 } catch (IllegalArgumentException e) {
779 // Contact is new, so we don't need to do any work.
780 }
781
Alex Kennberg87fc3172009-03-28 06:43:06 -0700782 if (groupCursor != null) {
783 try {
784 while (groupCursor.moveToNext()) {
785 String systemId = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
786 String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
787
788 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
789 continue;
790 }
791
792 if (!TextUtils.isEmpty(name)) {
793 for (int i = 0; i < mGroups.length; i++) {
794 if (name.equals(mGroups[i])) {
795 mInTheGroup[i] = true;
796 break;
797 }
798 }
799 }
800 }
801 } finally {
802 groupCursor.close();
803 }
804 }
805 }
806 }
807
808 private String generateGroupList() {
809 StringBuilder groupList = new StringBuilder();
810 for (int i = 0; mGroups != null && i < mGroups.length; i++) {
811 if (mInTheGroup[i]) {
812 if (groupList.length() == 0) {
813 groupList.append(mGroups[i]);
814 } else {
815 groupList.append(getString(R.string.group_list, mGroups[i]));
816 }
817 }
818 }
819 return groupList.length() > 0 ? groupList.toString() : null;
820 }
821
822 private void doPickGroup(EditEntry entry) {
823 if (mGroups != null) {
824 GroupDialogListener listener = new GroupDialogListener(this, entry);
825
826 new AlertDialog.Builder(EditContactActivity.this)
827 .setTitle(R.string.label_groups)
828 .setMultiChoiceItems(mGroups, mInTheGroup, listener)
829 .setPositiveButton(android.R.string.ok, listener)
830 .setNegativeButton(android.R.string.cancel, null)
831 .show();
832 }
833 }
834
835 /** Handles the clicks in the groups dialog */
836 private static final class GroupDialogListener implements DialogInterface.OnClickListener,
837 DialogInterface.OnMultiChoiceClickListener {
838
839 private EditContactActivity mEditContactActivity;
840 private EditEntry mEntry;
841 private boolean[] mInTheGroup;
842
843 public GroupDialogListener(EditContactActivity editContactActivity, EditEntry entry) {
844 mEditContactActivity = editContactActivity;
845 mEntry = entry;
846 mInTheGroup = editContactActivity.mInTheGroup.clone();
847 }
848
849 /** Called when the dialog's ok button is clicked */
850 public void onClick(DialogInterface dialog, int which) {
851 mEditContactActivity.mInTheGroup = mInTheGroup;
852 mEntry.data = mEditContactActivity.generateGroupList();
853 mEditContactActivity.updateDataView(mEntry, mEntry.data);
854 }
855
856 /** Called when each group is clicked */
857 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
858 mInTheGroup[which] = isChecked;
859 }
860 }
861
862
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800863 private void doPickRingtone(EditEntry entry) {
864 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
865 // Allow user to pick 'Default'
866 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
867 // Show only ringtones
868 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
869 // Don't show 'Silent'
870 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
871
872 Uri ringtoneUri;
873 if (entry.data != null) {
874 ringtoneUri = Uri.parse(entry.data);
875 } else {
876 // Otherwise pick default ringtone Uri so that something is selected.
877 ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
878 }
879
880 // Put checkmark next to the current ringtone for this contact
881 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
882 // Launch!
883 startActivityForResult(intent, RINGTONE_PICKED);
884 }
885
886 private void handleRingtonePicked(Uri pickedUri) {
887 EditEntry entry = getOtherEntry(People.CUSTOM_RINGTONE);
888 if (entry == null) {
889 Log.w(TAG, "Ringtone picked but could not find ringtone entry");
890 return;
891 }
892
893 if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
894 entry.data = null;
895 } else {
896 entry.data = pickedUri.toString();
897 }
898
899 updateRingtoneView(entry);
900 }
901
902 private void updateRingtoneView(EditEntry entry) {
903 String ringtoneName;
904 if (entry.data == null) {
905 ringtoneName = getString(R.string.default_ringtone);
906 } else {
907 Uri ringtoneUri = Uri.parse(entry.data);
908 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
909 if (ringtone == null) {
910 Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
911 return;
912 }
913 ringtoneName = ringtone.getTitle(this);
914 }
915
916 updateDataView(entry, ringtoneName);
917 }
918
919 private void updateDataView(EditEntry entry, String text) {
920 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
921 dataView.setText(text);
922 }
923
924 @Override
925 protected Dialog onCreateDialog(int id) {
926 switch (id) {
927 case DELETE_CONFIRMATION_DIALOG:
928 return new AlertDialog.Builder(EditContactActivity.this)
929 .setTitle(R.string.deleteConfirmation_title)
930 .setIcon(android.R.drawable.ic_dialog_alert)
931 .setMessage(R.string.deleteConfirmation)
932 .setNegativeButton(android.R.string.cancel, null)
933 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
934 .setCancelable(false)
935 .create();
936 }
937 return super.onCreateDialog(id);
938 }
939
940 static String[] getLabelsForKind(Context context, int kind) {
941 final Resources resources = context.getResources();
942 switch (kind) {
943 case Contacts.KIND_PHONE:
944 return resources.getStringArray(android.R.array.phoneTypes);
945 case Contacts.KIND_EMAIL:
946 return resources.getStringArray(android.R.array.emailAddressTypes);
947 case Contacts.KIND_POSTAL:
948 return resources.getStringArray(android.R.array.postalAddressTypes);
949 case Contacts.KIND_IM:
950 return resources.getStringArray(android.R.array.imProtocols);
951 case Contacts.KIND_ORGANIZATION:
952 return resources.getStringArray(android.R.array.organizationTypes);
953 case EditEntry.KIND_CONTACT:
954 return resources.getStringArray(R.array.otherLabels);
955 }
956 return null;
957 }
958
959 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
960 // In the UI Custom... comes last, but it is uses the constant 0
961 // so it is in the same location across the various kinds. Fix up the
962 // position to a valid type here.
963 if (labelPosition == labels.length - 1) {
964 return ContactMethods.TYPE_CUSTOM;
965 } else {
966 return labelPosition + 1;
967 }
968 }
969
970 private EditEntry getOtherEntry(String column) {
971 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
972 EditEntry entry = mOtherEntries.get(i);
973 if (isOtherEntry(entry, column)) {
974 return entry;
975 }
976 }
977 return null;
978 }
979
980 private static boolean isOtherEntry(EditEntry entry, String column) {
981 return entry != null && entry.column != null && entry.column.equals(column);
982 }
983
984 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
985 final EditText label = new EditText(this);
986 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
987 label.requestFocus();
988 new AlertDialog.Builder(this)
989 .setView(label)
990 .setTitle(R.string.customLabelPickerTitle)
991 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
992 public void onClick(DialogInterface dialog, int which) {
993 entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
994 label.getText().toString());
995 mContactChanged = true;
996
997 if (addTo != null) {
998 addTo.add(entry);
999 buildViews();
1000 entry.view.requestFocus(View.FOCUS_DOWN);
1001 }
1002 }
1003 })
1004 .setNegativeButton(android.R.string.cancel, null)
1005 .show();
1006 }
1007
1008 /**
1009 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
1010 */
1011 private void doSaveAction() {
1012 // Save or create the contact if needed
1013 switch (mState) {
1014 case STATE_EDIT:
1015 save();
1016 break;
1017
1018 case STATE_INSERT:
1019 create();
1020 break;
1021
1022 default:
1023 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
1024 break;
1025 }
1026 finish();
1027 }
Alex Kennberg87fc3172009-03-28 06:43:06 -07001028
1029 /**
1030 * Gets the group id based on group name.
1031 *
1032 * @param resolver the resolver to use
1033 * @param groupName the name of the group to add the contact to
1034 * @return the id of the group
1035 * @throws IllegalStateException if the group can't be found
1036 */
1037 private long getGroupId(ContentResolver resolver, String groupName) {
1038 long groupId = 0;
1039 Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUP_ID_PROJECTION,
1040 Groups.NAME + "=?", new String[] { groupName }, null);
1041 if (groupsCursor != null) {
1042 try {
1043 if (groupsCursor.moveToFirst()) {
1044 groupId = groupsCursor.getLong(0);
1045 }
1046 } finally {
1047 groupsCursor.close();
1048 }
1049 }
1050
1051 if (groupId == 0) {
1052 throw new IllegalStateException("Failed to find the " + groupName + "group");
1053 }
1054
1055 return groupId;
1056 }
1057
1058 /**
1059 * Deletes group membership based on person and group ids.
1060 *
1061 * @param personId the person id
1062 * @param groupId the group id
1063 * @return the id of the group membership
1064 */
1065 private void deleteGroupMembership(long personId, long groupId) {
1066 long groupMembershipId = 0;
1067 Cursor groupsCursor = mResolver.query(GroupMembership.CONTENT_URI, GROUPMEMBERSHIP_ID_PROJECTION,
1068 GroupMembership.PERSON_ID + "=? AND " + GroupMembership.GROUP_ID + "=?",
1069 new String[] {String.valueOf(personId), String.valueOf(groupId)}, null);
1070 if (groupsCursor != null) {
1071 try {
1072 if (groupsCursor.moveToFirst()) {
1073 groupMembershipId = groupsCursor.getLong(0);
1074 }
1075 } finally {
1076 groupsCursor.close();
1077 }
1078 }
1079
1080 if (groupMembershipId != 0) {
1081 final Uri groupsUri = ContentUris.withAppendedId(
1082 GroupMembership.CONTENT_URI,groupMembershipId);
1083 mResolver.delete(groupsUri, null, null);
1084 }
1085 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001086
1087 /**
1088 * Save the various fields to the existing contact.
1089 */
1090 private void save() {
1091 ContentValues values = new ContentValues();
1092 String data;
1093 int numValues = 0;
1094
1095 // Handle the name and send to voicemail specially
1096 final String name = mNameView.getText().toString();
1097 if (name != null && TextUtils.isGraphic(name)) {
1098 numValues++;
1099 }
1100 values.put(People.NAME, name);
1101 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1102 mResolver.update(mUri, values, null, null);
1103
1104 if (mPhotoChanged) {
1105 // Only write the photo if it's changed, since we don't initially load mPhoto
1106 if (mPhoto != null) {
1107 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1108 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1109 Contacts.People.setPhotoData(mResolver, mUri, stream.toByteArray());
1110 } else {
1111 Contacts.People.setPhotoData(mResolver, mUri, null);
1112 }
1113 }
1114
1115 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1116 for (int i = 0; i < entryCount; i++) {
1117 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
1118 int kind = entry.kind;
1119 data = entry.getData();
1120 boolean empty = data == null || !TextUtils.isGraphic(data);
1121 if (kind == EditEntry.KIND_CONTACT) {
1122 values.clear();
1123 if (!empty) {
1124 values.put(entry.column, data);
1125 mResolver.update(entry.uri, values, null, null);
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001126 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1127 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1128 numValues++;
1129 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001130 } else {
1131 values.put(entry.column, (String) null);
1132 mResolver.update(entry.uri, values, null, null);
1133 }
Alex Kennberg87fc3172009-03-28 06:43:06 -07001134 } else if (kind == EditEntry.KIND_GROUP) {
1135 if (entry.id != 0) {
1136 for (int g = 0; g < mGroups.length; g++) {
1137 long groupId = getGroupId(mResolver, mGroups[g].toString());
1138 if (mInTheGroup[g]) {
1139 Contacts.People.addToGroup(mResolver, entry.id, groupId);
1140 numValues++;
1141 } else {
1142 deleteGroupMembership(entry.id, groupId);
1143 }
1144 }
1145 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001146 } else {
1147 if (!empty) {
1148 values.clear();
1149 entry.toValues(values);
1150 if (entry.id != 0) {
1151 mResolver.update(entry.uri, values, null, null);
1152 } else {
1153 mResolver.insert(entry.uri, values);
1154 }
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001155 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1156 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1157 numValues++;
1158 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001159 } else if (entry.id != 0) {
1160 mResolver.delete(entry.uri, null, null);
1161 }
1162 }
1163 }
1164
1165 if (numValues == 0) {
1166 // The contact is completely empty, delete it
1167 mResolver.delete(mUri, null, null);
1168 mUri = null;
1169 setResult(RESULT_CANCELED);
1170 } else {
1171 // Add the entry to the my contacts group if it isn't there already
1172 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1173 setResult(RESULT_OK, new Intent().setData(mUri));
1174
1175 // Only notify user if we actually changed contact
1176 if (mContactChanged || mPhotoChanged) {
1177 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1178 }
1179 }
1180 }
1181
1182 /**
1183 * Takes the entered data and saves it to a new contact.
1184 */
1185 private void create() {
1186 ContentValues values = new ContentValues();
1187 String data;
1188 int numValues = 0;
1189
1190 // Create the contact itself
1191 final String name = mNameView.getText().toString();
1192 if (name != null && TextUtils.isGraphic(name)) {
1193 numValues++;
1194 }
1195 values.put(People.NAME, name);
1196 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1197
1198 // Add the contact to the My Contacts group
1199 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1200
1201 // Add the contact to the group that is being displayed in the contact list
1202 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1203 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1204 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1205 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1206 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
1207 null);
1208 if (!TextUtils.isEmpty(displayGroup)) {
1209 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1210 }
1211 } else {
1212 // Check to see if we're not syncing everything and if so if My Contacts is synced.
1213 // If it isn't then the created contact can end up not in any groups that are
1214 // currently synced and end up getting removed from the phone, which is really bad.
1215 boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
1216 Contacts.Settings.SYNC_EVERYTHING));
1217 if (!syncingEverything) {
1218 boolean syncingMyContacts = false;
1219 Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
1220 Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
1221 if (c != null) {
1222 try {
1223 if (c.moveToFirst()) {
1224 syncingMyContacts = !"0".equals(c.getString(0));
1225 }
1226 } finally {
1227 c.close();
1228 }
1229 }
1230
1231 if (!syncingMyContacts) {
1232 // Not syncing My Contacts, so find a group that is being synced and stick
1233 // the contact in there. We sort the list so at least all contacts
1234 // will appear in the same group.
1235 c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
1236 Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
1237 if (c != null) {
1238 try {
1239 if (c.moveToFirst()) {
1240 People.addToGroup(mResolver, ContentUris.parseId(contactUri),
1241 c.getLong(0));
1242 }
1243 } finally {
1244 c.close();
1245 }
1246 }
1247 }
1248 }
1249 }
1250
1251 // Handle the photo
1252 if (mPhoto != null) {
1253 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1254 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1255 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1256 }
1257
1258 // Create the contact methods
1259 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1260 for (int i = 0; i < entryCount; i++) {
1261 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
Alex Kennbergfb0386a2009-04-02 09:59:10 -07001262 if (entry.kind == EditEntry.KIND_GROUP) {
1263 long contactId = ContentUris.parseId(contactUri);
1264 for (int g = 0; g < mGroups.length; g++) {
1265 if (mInTheGroup[g]) {
1266 long groupId = getGroupId(mResolver, mGroups[g].toString());
1267 People.addToGroup(mResolver, contactId, groupId);
1268 numValues++;
1269 }
1270 }
1271 } else if (entry.kind != EditEntry.KIND_CONTACT) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001272 values.clear();
1273 if (entry.toValues(values)) {
1274 // Only create the entry if there is data
1275 entry.uri = mResolver.insert(
1276 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1277 entry.id = ContentUris.parseId(entry.uri);
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001278 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1279 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1280 numValues++;
1281 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001282 }
1283 } else {
1284 // Update the contact with any straggling data, like notes
1285 data = entry.getData();
1286 values.clear();
1287 if (data != null && TextUtils.isGraphic(data)) {
1288 values.put(entry.column, data);
1289 mResolver.update(contactUri, values, null, null);
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001290 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1291 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1292 numValues++;
1293 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001294 }
1295 }
1296 }
1297
1298 if (numValues == 0) {
1299 mResolver.delete(contactUri, null, null);
1300 setResult(RESULT_CANCELED);
1301 } else {
1302 mUri = contactUri;
1303 Intent resultIntent = new Intent()
1304 .setData(mUri)
1305 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1306 setResult(RESULT_OK, resultIntent);
1307 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1308 }
1309 }
1310
1311 /**
1312 * Build up the entries to display on the screen.
1313 *
1314 * @param extras the extras used to start this activity, may be null
1315 */
1316 private void buildEntriesForEdit(Bundle extras) {
1317 Cursor personCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
1318 if (personCursor == null) {
1319 Log.e(TAG, "invalid contact uri: " + mUri);
1320 finish();
1321 return;
1322 } else if (!personCursor.moveToFirst()) {
1323 Log.e(TAG, "invalid contact uri: " + mUri);
1324 finish();
1325 personCursor.close();
1326 return;
1327 }
1328
1329 // Clear out the old entries
1330 int numSections = mSections.size();
1331 for (int i = 0; i < numSections; i++) {
1332 mSections.get(i).clear();
1333 }
1334
1335 EditEntry entry;
1336
1337 // Name
1338 mNameView.setText(personCursor.getString(CONTACT_NAME_COLUMN));
1339 mNameView.addTextChangedListener(this);
1340
1341 // Photo
1342 mPhoto = People.loadContactPhoto(this, mUri, 0, null);
1343 if (mPhoto == null) {
1344 setPhotoPresent(false);
1345 } else {
1346 setPhotoPresent(true);
1347 mPhotoImageView.setImageBitmap(mPhoto);
1348 }
1349
1350 // Organizations
1351 Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
1352 Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
1353 null, null, null);
1354
1355 if (organizationsCursor != null) {
1356 while (organizationsCursor.moveToNext()) {
1357 int type = organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
1358 String label = organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN);
1359 String company = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
1360 String title = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
1361 long id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
1362 Uri uri = ContentUris.withAppendedId(Organizations.CONTENT_URI, id);
1363
1364 // Add an organization entry
1365 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1366 entry.isPrimary = organizationsCursor.getLong(ORGANIZATIONS_ISPRIMARY_COLUMN) != 0;
1367 mOrgEntries.add(entry);
1368 }
1369 organizationsCursor.close();
1370 }
1371
1372 // Notes
1373 if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
1374 entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
1375 mUri);
1376 mNoteEntries.add(entry);
1377 }
Alex Kennberg87fc3172009-03-28 06:43:06 -07001378
1379 // Groups
1380 populateGroups();
1381 if (mGroups != null) {
1382 entry = EditEntry.newGroupEntry(this, generateGroupList(), mUri,
1383 personCursor.getLong(0));
1384 mOtherEntries.add(entry);
1385 }
1386
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001387 // Ringtone
1388 entry = EditEntry.newRingtoneEntry(this,
1389 personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
1390 mOtherEntries.add(entry);
1391
1392 // Send to voicemail
1393 entry = EditEntry.newSendToVoicemailEntry(this,
1394 personCursor.getString(CONTACT_SEND_TO_VOICEMAIL_COLUMN), mUri);
1395 mOtherEntries.add(entry);
1396
1397 // Phonetic name
1398 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
1399 mPhoneticNameView.addTextChangedListener(this);
1400
1401 personCursor.close();
1402
1403 // Build up the phone entries
1404 Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
1405 Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION,
1406 null, null, null);
1407
1408 if (phonesCursor != null) {
1409 while (phonesCursor.moveToNext()) {
1410 int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
1411 String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
1412 String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
1413 long id = phonesCursor.getLong(PHONES_ID_COLUMN);
1414 boolean isPrimary = phonesCursor.getLong(PHONES_ISPRIMARY_COLUMN) != 0;
1415 Uri uri = ContentUris.withAppendedId(phonesUri, id);
1416
1417 // Add a phone number entry
1418 entry = EditEntry.newPhoneEntry(this, label, type, number, uri, id);
1419 entry.isPrimary = isPrimary;
1420 mPhoneEntries.add(entry);
1421
1422 // Keep track of which primary types have been added
1423 if (type == Phones.TYPE_MOBILE) {
1424 mMobilePhoneAdded = true;
1425 }
1426 }
1427
1428 phonesCursor.close();
1429 }
1430
1431 // Build the contact method entries
1432 Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
1433 Cursor methodsCursor = mResolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
1434
1435 if (methodsCursor != null) {
1436 while (methodsCursor.moveToNext()) {
1437 int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
1438 String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
1439 String data = methodsCursor.getString(METHODS_DATA_COLUMN);
1440 String auxData = methodsCursor.getString(METHODS_AUX_DATA_COLUMN);
1441 int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
1442 long id = methodsCursor.getLong(METHODS_ID_COLUMN);
1443 boolean isPrimary = methodsCursor.getLong(METHODS_ISPRIMARY_COLUMN) != 0;
1444 Uri uri = ContentUris.withAppendedId(methodsUri, id);
1445
1446 switch (kind) {
1447 case Contacts.KIND_EMAIL: {
1448 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1449 entry.isPrimary = isPrimary;
1450 mEmailEntries.add(entry);
1451
1452 if (isPrimary) {
1453 mPrimaryEmailAdded = true;
1454 }
1455 break;
1456 }
1457
1458 case Contacts.KIND_POSTAL: {
1459 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1460 entry.isPrimary = isPrimary;
1461 mPostalEntries.add(entry);
1462 break;
1463 }
1464
1465 case Contacts.KIND_IM: {
1466 Object protocolObj = ContactMethods.decodeImProtocol(auxData);
1467 if (protocolObj == null) {
1468 // Invalid IM protocol, log it then ignore.
1469 Log.e(TAG, "Couldn't decode IM protocol: " + auxData);
1470 continue;
1471 } else {
1472 if (protocolObj instanceof Number) {
1473 int protocol = ((Number) protocolObj).intValue();
1474 entry = EditEntry.newImEntry(this,
1475 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1476 data, uri, id);
1477 } else {
1478 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1479 uri, id);
1480 }
1481 mImEntries.add(entry);
1482 }
1483 break;
1484 }
1485 }
1486 }
1487
1488 methodsCursor.close();
1489 }
1490
1491 // Add values from the extras, if there are any
1492 if (extras != null) {
1493 addFromExtras(extras, phonesUri, methodsUri);
1494 }
1495
1496 // Add the base types if needed
1497 if (!mMobilePhoneAdded) {
1498 entry = EditEntry.newPhoneEntry(this,
1499 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1500 DEFAULT_PHONE_TYPE);
1501 mPhoneEntries.add(entry);
1502 }
1503
1504 if (!mPrimaryEmailAdded) {
1505 entry = EditEntry.newEmailEntry(this,
1506 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1507 DEFAULT_EMAIL_TYPE);
1508 entry.isPrimary = true;
1509 mEmailEntries.add(entry);
1510 }
1511
1512 mContactChanged = false;
1513 }
1514
1515 /**
1516 * Build the list of EditEntries for full mode insertions.
1517 *
1518 * @param extras the extras used to start this activity, may be null
1519 */
1520 private void buildEntriesForInsert(Bundle extras) {
1521 // Clear out the old entries
1522 int numSections = mSections.size();
1523 for (int i = 0; i < numSections; i++) {
1524 mSections.get(i).clear();
1525 }
1526
1527 EditEntry entry;
1528
1529 // Check the intent extras
1530 if (extras != null) {
1531 addFromExtras(extras, null, null);
1532 }
1533
1534 // Photo
1535 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1536
1537 // Add the base entries if they're not already present
1538 if (!mMobilePhoneAdded) {
1539 entry = EditEntry.newPhoneEntry(this, null, Phones.TYPE_MOBILE);
1540 entry.isPrimary = true;
1541 mPhoneEntries.add(entry);
1542 }
1543
1544 if (!mPrimaryEmailAdded) {
1545 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1546 entry.isPrimary = true;
1547 mEmailEntries.add(entry);
1548 }
1549
Alex Kennberg87fc3172009-03-28 06:43:06 -07001550 // Group
1551 populateGroups();
1552 if (mGroups != null) {
1553 entry = EditEntry.newGroupEntry(this, null, mUri, 0);
1554 mOtherEntries.add(entry);
1555 }
1556
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001557 // Ringtone
1558 entry = EditEntry.newRingtoneEntry(this, null, mUri);
1559 mOtherEntries.add(entry);
1560
1561 // Send to voicemail
1562 entry = EditEntry.newSendToVoicemailEntry(this, "0", mUri);
1563 mOtherEntries.add(entry);
1564 }
1565
1566 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1567 EditEntry entry;
1568
1569 // Read the name from the bundle
1570 CharSequence name = extras.getCharSequence(Insert.NAME);
1571 if (name != null && TextUtils.isGraphic(name)) {
1572 mNameView.setText(name);
1573 }
1574
1575 // Read the phonetic name from the bundle
1576 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1577 if (!TextUtils.isEmpty(phoneticName)) {
1578 mPhoneticNameView.setText(phoneticName);
1579 }
1580
1581 // Postal entries from extras
1582 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1583 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1584 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1585 postalType = DEFAULT_POSTAL_TYPE;
1586 }
1587
1588 if (postalType != INVALID_TYPE) {
1589 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1590 methodsUri, 0);
1591 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1592 mPostalEntries.add(entry);
1593 }
1594
1595 // Email entries from extras
1596 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1597 Insert.EMAIL_ISPRIMARY);
1598 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1599 null);
1600 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1601 null);
1602
1603 // Phone entries from extras
1604 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1605 Insert.PHONE_ISPRIMARY);
1606 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1607 null);
1608 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1609 null);
1610
1611 // IM entries from extras
1612 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1613 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
1614
1615 if (imHandle != null && imProtocol != null) {
1616 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1617 if (protocolObj instanceof Number) {
1618 int protocol = ((Number) protocolObj).intValue();
1619 entry = EditEntry.newImEntry(this,
1620 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001621 imHandle.toString(), methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001622 } else {
1623 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001624 methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001625 }
1626 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1627 mImEntries.add(entry);
1628 }
1629 }
1630
1631 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1632 String typeField, String primaryField) {
1633 CharSequence email = extras.getCharSequence(emailField);
1634
1635 // Correctly handle String in typeField as TYPE_CUSTOM
1636 int emailType = INVALID_TYPE;
1637 String customLabel = null;
1638 if(extras.get(typeField) instanceof String) {
1639 emailType = ContactMethods.TYPE_CUSTOM;
1640 customLabel = extras.getString(typeField);
1641 } else {
1642 emailType = extras.getInt(typeField, INVALID_TYPE);
1643 }
1644
1645 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1646 emailType = DEFAULT_EMAIL_TYPE;
1647 mPrimaryEmailAdded = true;
1648 }
1649
1650 if (emailType != INVALID_TYPE) {
1651 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
1652 methodsUri, 0);
1653 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1654 mEmailEntries.add(entry);
1655
1656 // Keep track of which primary types have been added
1657 if (entry.isPrimary) {
1658 mPrimaryEmailAdded = true;
1659 }
1660 }
1661 }
1662
1663 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1664 String typeField, String primaryField) {
1665 CharSequence phoneNumber = extras.getCharSequence(phoneField);
1666
1667 // Correctly handle String in typeField as TYPE_CUSTOM
1668 int phoneType = INVALID_TYPE;
1669 String customLabel = null;
1670 if(extras.get(typeField) instanceof String) {
1671 phoneType = Phones.TYPE_CUSTOM;
1672 customLabel = extras.getString(typeField);
1673 } else {
1674 phoneType = extras.getInt(typeField, INVALID_TYPE);
1675 }
1676
1677 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1678 phoneType = DEFAULT_PHONE_TYPE;
1679 }
1680
1681 if (phoneType != INVALID_TYPE) {
1682 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
1683 phoneNumber.toString(), phonesUri, 0);
1684 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1685 mPhoneEntries.add(entry);
1686
1687 // Keep track of which primary types have been added
1688 if (phoneType == Phones.TYPE_MOBILE) {
1689 mMobilePhoneAdded = true;
1690 }
1691 }
1692 }
1693
1694 /**
1695 * Removes all existing views, builds new ones for all the entries, and adds them.
1696 */
1697 private void buildViews() {
1698 // Remove existing views
1699 final LinearLayout layout = mLayout;
1700 layout.removeAllViews();
1701
1702 buildViewsForSection(layout, mPhoneEntries,
1703 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1704 buildViewsForSection(layout, mEmailEntries,
1705 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1706 buildViewsForSection(layout, mImEntries,
1707 R.string.listSeparatorSendIm_edit, SECTION_IM);
1708 buildViewsForSection(layout, mPostalEntries,
1709 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1710 buildViewsForSection(layout, mOrgEntries,
1711 R.string.listSeparatorOrganizations, SECTION_ORG);
1712 buildViewsForSection(layout, mNoteEntries,
1713 R.string.label_notes, SECTION_NOTE);
1714
1715 buildOtherViews(layout, mOtherEntries);
1716 }
1717
1718
1719 /**
1720 * Builds the views for a specific section.
1721 *
1722 * @param layout the container
1723 * @param section the section to build the views for
1724 */
1725 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1726 int separatorResource, int sectionType) {
1727
1728 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1729 layout.addView(divider);
1730
1731 // Count up undeleted children
1732 int activeChildren = 0;
1733 for (int i = section.size() - 1; i >= 0; i--) {
1734 EditEntry entry = section.get(i);
1735 if (!entry.isDeleted) {
1736 activeChildren++;
1737 }
1738 }
1739
1740 // Build the correct group header based on undeleted children
1741 ViewGroup header;
1742 if (activeChildren == 0) {
1743 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1744 } else {
1745 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
1746 }
1747
1748 // Because we're emulating a ListView, we need to handle focus changes
1749 // with some additional logic.
1750 header.setOnFocusChangeListener(this);
1751
1752 TextView text = (TextView) header.findViewById(R.id.text);
1753 text.setText(getText(separatorResource));
1754
1755 // Force TextView to always default color if we have children. This makes sure
1756 // we don't change color when parent is pressed.
1757 if (activeChildren > 0) {
1758 ColorStateList stateList = text.getTextColors();
1759 text.setTextColor(stateList.getDefaultColor());
1760 }
1761
1762 View addView = header.findViewById(R.id.separator);
1763 addView.setTag(Integer.valueOf(sectionType));
1764 addView.setOnClickListener(this);
1765
1766 // Build views for the current section
1767 for (EditEntry entry : section) {
1768 entry.activity = this; // this could be null from when the state is restored
1769 if (!entry.isDeleted) {
1770 View view = buildViewForEntry(entry);
1771 header.addView(view);
1772 }
1773 }
1774
1775 layout.addView(header);
1776 }
1777
1778 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1779 // Build views for the current section, putting a divider between each one
1780 for (EditEntry entry : section) {
1781 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1782 layout.addView(divider);
1783
1784 entry.activity = this; // this could be null from when the state is restored
1785 View view = buildViewForEntry(entry);
1786 view.setOnClickListener(this);
1787 layout.addView(view);
1788 }
1789
1790 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1791 layout.addView(divider);
1792 }
1793
1794 /**
1795 * Builds a view to display an EditEntry.
1796 *
1797 * @param entry the entry to display
1798 * @return a view that will display the given entry
1799 */
1800 /* package */ View buildViewForEntry(final EditEntry entry) {
1801 // Look for any existing entered text, and save it if found
1802 if (entry.view != null && entry.syncDataWithView) {
1803 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1804 .getText().toString();
1805 if (!TextUtils.isEmpty(enteredText)) {
1806 entry.data = enteredText;
1807 }
1808 }
1809
1810 // Build a new view
1811 final ViewGroup parent = mLayout;
1812 View view;
1813
1814 // Because we're emulating a ListView, we might need to handle focus changes
1815 // with some additional logic.
1816 if (entry.kind == Contacts.KIND_ORGANIZATION) {
1817 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
Alex Kennberg87fc3172009-03-28 06:43:06 -07001818 } else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
1819 view = mInflater.inflate(R.layout.edit_contact_entry_group, parent, false);
1820 view.setOnFocusChangeListener(this);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001821 } else if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
1822 view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
1823 view.setOnFocusChangeListener(this);
1824 } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
1825 view = mInflater.inflate(R.layout.edit_contact_entry_voicemail, parent, false);
1826 view.setOnFocusChangeListener(this);
1827 } else if (!entry.isStaticLabel) {
1828 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1829 } else {
1830 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1831 }
1832 entry.view = view;
1833
1834 // Set the entry as the tag so we can find it again later given just the view
1835 view.setTag(entry);
1836
1837 // Bind the label
1838 entry.bindLabel(this);
1839
1840 // Bind data
1841 TextView data = (TextView) view.findViewById(R.id.data);
1842 TextView data2 = (TextView) view.findViewById(R.id.data2);
1843
1844 if (data instanceof Button) {
1845 data.setOnClickListener(this);
1846 }
1847 if (data.length() == 0) {
1848 if (entry.syncDataWithView) {
1849 // If there is already data entered don't overwrite it
1850 data.setText(entry.data);
1851 } else {
1852 fillViewData(entry);
1853 }
1854 }
1855 if (data2 != null && data2.length() == 0) {
1856 // If there is already data entered don't overwrite it
1857 data2.setText(entry.data2);
1858 }
1859 data.setHint(entry.hint);
1860 if (data2 != null) data2.setHint(entry.hint2);
1861 if (entry.lines > 1) {
1862 data.setLines(entry.lines);
1863 data.setMaxLines(entry.maxLines);
1864 if (data2 != null) {
1865 data2.setLines(entry.lines);
1866 data2.setMaxLines(entry.maxLines);
1867 }
1868 }
1869 int contentType = entry.contentType;
1870 if (contentType != EditorInfo.TYPE_NULL) {
1871 data.setInputType(contentType);
1872 if (data2 != null) {
1873 data2.setInputType(contentType);
1874 }
1875 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1876 == EditorInfo.TYPE_CLASS_PHONE) {
1877 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1878 if (data2 != null) {
1879 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1880 }
1881 }
1882 }
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001883
1884 // Give focus to children as requested, possibly after a configuration change
1885 View focusChild = view.findViewById(entry.requestFocusId);
1886 if (focusChild != null) {
1887 focusChild.requestFocus();
1888 if (focusChild instanceof EditText) {
1889 ((EditText) focusChild).setSelection(entry.requestCursor);
1890 }
1891 }
1892
1893 // Reset requested focus values
1894 entry.requestFocusId = View.NO_ID;
1895 entry.requestCursor = 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001896
1897 // Connect listeners up to watch for changed values.
1898 if (data instanceof EditText) {
1899 data.addTextChangedListener(this);
1900 }
1901 if (data2 instanceof EditText) {
1902 data2.addTextChangedListener(this);
1903 }
1904
1905 // Hook up the delete button
1906 View delete = view.findViewById(R.id.delete);
1907 if (delete != null) delete.setOnClickListener(this);
1908
1909 return view;
1910 }
1911
1912 private void fillViewData(final EditEntry entry) {
1913 if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
1914 updateRingtoneView(entry);
Alex Kennberg87fc3172009-03-28 06:43:06 -07001915 } else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
1916 if (entry.data != null) {
1917 updateDataView(entry, entry.data);
1918 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001919 } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
1920 CheckBox checkBox = (CheckBox) entry.view.findViewById(R.id.checkbox);
1921 boolean sendToVoicemail = false;
1922 if (entry.data != null) {
1923 sendToVoicemail = (Integer.valueOf(entry.data) == 1);
1924 }
1925 checkBox.setChecked(sendToVoicemail);
1926 }
1927 }
1928
1929 /**
1930 * Handles the results from the label change picker.
1931 */
1932 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1933 EditEntry mEntry;
1934 String[] mLabels;
1935
1936 public LabelPickedListener(EditEntry entry, String[] labels) {
1937 mEntry = entry;
1938 mLabels = labels;
1939 }
1940
1941 public void onClick(DialogInterface dialog, int which) {
1942 // TODO: Use a managed dialog
1943 if (mEntry.kind != Contacts.KIND_IM) {
1944 final int type = getTypeFromLabelPosition(mLabels, which);
1945 if (type == ContactMethods.TYPE_CUSTOM) {
1946 createCustomPicker(mEntry, null);
1947 } else {
1948 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1949 mContactChanged = true;
1950 }
1951 } else {
1952 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1953 mContactChanged = true;
1954 }
1955 }
1956 }
1957
1958 /**
1959 * A basic structure with the data for a contact entry in the list.
1960 */
1961 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1962 // These aren't stuffed into the parcel
1963 public EditContactActivity activity;
1964 public View view;
1965
1966 // These are stuffed into the parcel
1967 public String hint;
1968 public String hint2;
1969 public String column;
1970 public String contentDirectory;
1971 public String data2;
1972 public int contentType;
1973 public int type;
1974 /**
1975 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1976 * will not be called.
1977 */
1978 public int lines = 1;
1979 public boolean isPrimary;
1980 public boolean isDeleted = false;
1981 public boolean isStaticLabel = false;
1982 public boolean syncDataWithView = true;
1983
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001984 /**
1985 * Request focus on the child of this {@link EditEntry} found using
1986 * {@link View#findViewById(int)}. This value should be reset to
1987 * {@link View#NO_ID} after each use.
1988 */
1989 public int requestFocusId = View.NO_ID;
1990
1991 /**
1992 * If the {@link #requestFocusId} is an {@link EditText}, this value
1993 * indicates the requested cursor position placement.
1994 */
1995 public int requestCursor = 0;
1996
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001997 private EditEntry() {
1998 // only used by CREATOR
1999 }
2000
2001 public EditEntry(EditContactActivity activity) {
2002 this.activity = activity;
2003 }
2004
2005 public EditEntry(EditContactActivity activity, String label,
2006 int type, String data, Uri uri, long id) {
2007 this.activity = activity;
2008 this.isPrimary = false;
2009 this.label = label;
2010 this.type = type;
2011 this.data = data;
2012 this.uri = uri;
2013 this.id = id;
2014 }
2015
2016 public int describeContents() {
2017 return 0;
2018 }
2019
2020 public void writeToParcel(Parcel parcel, int flags) {
2021 // Make sure to read data from the input field, if anything is entered
2022 data = getData();
2023
2024 // Write in our own fields.
2025 parcel.writeString(hint);
2026 parcel.writeString(hint2);
2027 parcel.writeString(column);
2028 parcel.writeString(contentDirectory);
2029 parcel.writeString(data2);
2030 parcel.writeInt(contentType);
2031 parcel.writeInt(type);
2032 parcel.writeInt(lines);
2033 parcel.writeInt(isPrimary ? 1 : 0);
2034 parcel.writeInt(isDeleted ? 1 : 0);
2035 parcel.writeInt(isStaticLabel ? 1 : 0);
2036 parcel.writeInt(syncDataWithView ? 1 : 0);
2037
2038 // Write in the fields from Entry
2039 super.writeToParcel(parcel);
2040 }
2041
2042 public static final Parcelable.Creator<EditEntry> CREATOR =
2043 new Parcelable.Creator<EditEntry>() {
2044 public EditEntry createFromParcel(Parcel in) {
2045 EditEntry entry = new EditEntry();
2046
2047 // Read out our own fields
2048 entry.hint = in.readString();
2049 entry.hint2 = in.readString();
2050 entry.column = in.readString();
2051 entry.contentDirectory = in.readString();
2052 entry.data2 = in.readString();
2053 entry.contentType = in.readInt();
2054 entry.type = in.readInt();
2055 entry.lines = in.readInt();
2056 entry.isPrimary = in.readInt() == 1;
2057 entry.isDeleted = in.readInt() == 1;
2058 entry.isStaticLabel = in.readInt() == 1;
2059 entry.syncDataWithView = in.readInt() == 1;
2060
2061 // Read out the fields from Entry
2062 entry.readFromParcel(in);
2063
2064 return entry;
2065 }
2066
2067 public EditEntry[] newArray(int size) {
2068 return new EditEntry[size];
2069 }
2070 };
2071
2072 public void setLabel(Context context, int typeIn, String labelIn) {
2073 type = typeIn;
2074 label = labelIn;
2075 if (view != null) {
2076 bindLabel(context);
2077 }
2078 }
2079
2080 public void bindLabel(Context context) {
2081 TextView v = (TextView) view.findViewById(R.id.label);
2082 if (isStaticLabel) {
2083 v.setText(label);
2084 return;
2085 }
2086
2087 switch (kind) {
2088 case Contacts.KIND_PHONE: {
2089 v.setText(Phones.getDisplayLabel(context, type, label));
2090 break;
2091 }
2092
2093 case Contacts.KIND_IM: {
Alex Kennberg87fc3172009-03-28 06:43:06 -07002094 if (type >= 0) {
2095 v.setText(getLabelsForKind(activity, kind)[type]);
2096 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002097 break;
2098 }
2099
2100 case Contacts.KIND_ORGANIZATION: {
2101 v.setText(Organizations.getDisplayLabel(activity, type, label));
2102 break;
2103 }
2104
2105 default: {
2106 v.setText(Contacts.ContactMethods.getDisplayLabel(context, kind, type, label));
2107 if (kind == Contacts.KIND_POSTAL) {
2108 v.setMaxLines(3);
2109 }
2110 break;
2111 }
2112 }
2113 v.setOnClickListener(activity);
2114 }
2115
2116 /**
2117 * Returns the data for the entry
2118 * @return the data for the entry
2119 */
2120 public String getData() {
2121 if (view != null && syncDataWithView) {
2122 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
2123 if (text != null) {
2124 return text.toString();
2125 }
2126 }
2127
2128 if (data != null) {
2129 return data.toString();
2130 }
2131
2132 return null;
2133 }
2134
2135 /**
2136 * Dumps the entry into a HashMap suitable for passing to the database.
2137 *
2138 * @param values the HashMap to fill in.
2139 * @return true if the value should be saved, false otherwise
2140 */
2141 public boolean toValues(ContentValues values) {
2142 boolean success = false;
2143 String labelString = null;
2144 // Save the type and label
2145 if (view != null) {
2146 // Read the possibly updated label from the text field
2147 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
2148 }
2149 switch (kind) {
2150 case Contacts.KIND_PHONE:
2151 if (type != Phones.TYPE_CUSTOM) {
2152 labelString = null;
2153 }
2154 values.put(Phones.LABEL, labelString);
2155 values.put(Phones.TYPE, type);
2156 break;
2157
2158 case Contacts.KIND_EMAIL:
2159 if (type != ContactMethods.TYPE_CUSTOM) {
2160 labelString = null;
2161 }
2162 values.put(ContactMethods.LABEL, labelString);
2163 values.put(ContactMethods.KIND, kind);
2164 values.put(ContactMethods.TYPE, type);
2165 break;
2166
2167 case Contacts.KIND_IM:
2168 values.put(ContactMethods.KIND, kind);
2169 values.put(ContactMethods.TYPE, ContactMethods.TYPE_OTHER);
2170 values.putNull(ContactMethods.LABEL);
2171 if (type != -1) {
2172 values.put(ContactMethods.AUX_DATA,
2173 ContactMethods.encodePredefinedImProtocol(type));
2174 } else {
2175 values.put(ContactMethods.AUX_DATA,
2176 ContactMethods.encodeCustomImProtocol(label.toString()));
2177 }
2178 break;
2179
2180 case Contacts.KIND_POSTAL:
2181 if (type != ContactMethods.TYPE_CUSTOM) {
2182 labelString = null;
2183 }
2184 values.put(ContactMethods.LABEL, labelString);
2185 values.put(ContactMethods.KIND, kind);
2186 values.put(ContactMethods.TYPE, type);
2187 break;
2188
2189 case Contacts.KIND_ORGANIZATION:
2190 if (type != ContactMethods.TYPE_CUSTOM) {
2191 labelString = null;
2192 }
2193 values.put(ContactMethods.LABEL, labelString);
2194 values.put(ContactMethods.TYPE, type);
2195 // Save the title
2196 if (view != null) {
2197 // Read the possibly updated data from the text field
2198 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
2199 }
2200 if (!TextUtils.isGraphic(data2)) {
2201 values.putNull(Organizations.TITLE);
2202 } else {
2203 values.put(Organizations.TITLE, data2.toString());
2204 success = true;
2205 }
2206 break;
2207
2208 default:
2209 Log.w(TAG, "unknown kind " + kind);
2210 values.put(ContactMethods.LABEL, labelString);
2211 values.put(ContactMethods.KIND, kind);
2212 values.put(ContactMethods.TYPE, type);
2213 break;
2214 }
2215
2216 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
2217 // ContentProvider will try finding a new primary when setting to false, meaning
2218 // it's possible to lose primary altogether as we walk down the list. If this editor
2219 // implements editing of primaries in the future, this will need to be revisited.
2220 if (isPrimary) {
2221 values.put(ContactMethods.ISPRIMARY, 1);
2222 }
2223
2224 // Save the data
2225 if (view != null && syncDataWithView) {
2226 // Read the possibly updated data from the text field
2227 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
2228 }
2229 if (!TextUtils.isGraphic(data)) {
2230 values.putNull(column);
2231 return success;
2232 } else {
2233 values.put(column, data.toString());
2234 return true;
2235 }
2236 }
2237
2238 /**
2239 * Create a new empty organization entry
2240 */
2241 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2242 Uri uri, int type) {
2243 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
2244 }
2245
2246 /**
2247 * Create a new company entry with the given data.
2248 */
2249 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2250 String label, int type, String company, String title, Uri uri, long id) {
2251 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
2252 entry.hint = activity.getString(R.string.ghostData_company);
2253 entry.hint2 = activity.getString(R.string.ghostData_title);
2254 entry.data2 = title;
2255 entry.column = Organizations.COMPANY;
2256 entry.contentDirectory = Organizations.CONTENT_DIRECTORY;
2257 entry.kind = Contacts.KIND_ORGANIZATION;
2258 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2259 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
2260 return entry;
2261 }
2262
2263 /**
2264 * Create a new notes entry with the given data.
2265 */
2266 public static final EditEntry newNotesEntry(EditContactActivity activity,
2267 String data, Uri uri) {
2268 EditEntry entry = new EditEntry(activity);
2269 entry.label = activity.getString(R.string.label_notes);
2270 entry.hint = activity.getString(R.string.ghostData_notes);
2271 entry.data = data;
2272 entry.uri = uri;
2273 entry.column = People.NOTES;
2274 entry.maxLines = 10;
2275 entry.lines = 2;
2276 entry.id = 0;
2277 entry.kind = KIND_CONTACT;
2278 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2279 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
2280 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2281 entry.isStaticLabel = true;
2282 return entry;
2283 }
2284
2285 /**
Alex Kennberg87fc3172009-03-28 06:43:06 -07002286 * Create a new group entry with the given data.
2287 */
2288 public static final EditEntry newGroupEntry(EditContactActivity activity,
2289 String data, Uri uri, long personId) {
2290 EditEntry entry = new EditEntry(activity);
2291 entry.label = activity.getString(R.string.label_groups);
2292 entry.data = data;
2293 entry.uri = uri;
2294 entry.id = personId;
2295 entry.column = GroupMembership.GROUP_ID;
2296 entry.kind = KIND_GROUP;
2297 entry.isStaticLabel = true;
2298 entry.syncDataWithView = false;
2299 entry.lines = -1;
2300 return entry;
2301 }
2302
2303 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002304 * Create a new ringtone entry with the given data.
2305 */
2306 public static final EditEntry newRingtoneEntry(EditContactActivity activity,
2307 String data, Uri uri) {
2308 EditEntry entry = new EditEntry(activity);
2309 entry.label = activity.getString(R.string.label_ringtone);
2310 entry.data = data;
2311 entry.uri = uri;
2312 entry.column = People.CUSTOM_RINGTONE;
2313 entry.kind = KIND_CONTACT;
2314 entry.isStaticLabel = true;
2315 entry.syncDataWithView = false;
2316 entry.lines = -1;
2317 return entry;
2318 }
2319
2320 /**
2321 * Create a new send-to-voicemail entry with the given data.
2322 */
2323 public static final EditEntry newSendToVoicemailEntry(EditContactActivity activity,
2324 String data, Uri uri) {
2325 EditEntry entry = new EditEntry(activity);
2326 entry.label = activity.getString(R.string.actionIncomingCall);
2327 entry.data = data;
2328 entry.uri = uri;
2329 entry.column = People.SEND_TO_VOICEMAIL;
2330 entry.kind = KIND_CONTACT;
2331 entry.isStaticLabel = true;
2332 entry.syncDataWithView = false;
2333 entry.lines = -1;
2334 return entry;
2335 }
2336
2337 /**
2338 * Create a new empty email entry
2339 */
2340 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2341 Uri uri, int type) {
2342 return newPhoneEntry(activity, null, type, null, uri, 0);
2343 }
2344
2345 /**
2346 * Create a new phone entry with the given data.
2347 */
2348 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2349 String label, int type, String data, Uri uri,
2350 long id) {
2351 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2352 entry.hint = activity.getString(R.string.ghostData_phone);
2353 entry.column = People.Phones.NUMBER;
2354 entry.contentDirectory = People.Phones.CONTENT_DIRECTORY;
2355 entry.kind = Contacts.KIND_PHONE;
2356 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
2357 return entry;
2358 }
2359
2360 /**
2361 * Create a new empty email entry
2362 */
2363 public static final EditEntry newEmailEntry(EditContactActivity activity,
2364 Uri uri, int type) {
2365 return newEmailEntry(activity, null, type, null, uri, 0);
2366 }
2367
2368 /**
2369 * Create a new email entry with the given data.
2370 */
2371 public static final EditEntry newEmailEntry(EditContactActivity activity,
2372 String label, int type, String data, Uri uri,
2373 long id) {
2374 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2375 entry.hint = activity.getString(R.string.ghostData_email);
2376 entry.column = ContactMethods.DATA;
2377 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2378 entry.kind = Contacts.KIND_EMAIL;
2379 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2380 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
2381 return entry;
2382 }
2383
2384 /**
2385 * Create a new empty postal address entry
2386 */
2387 public static final EditEntry newPostalEntry(EditContactActivity activity,
2388 Uri uri, int type) {
2389 return newPostalEntry(activity, null, type, null, uri, 0);
2390 }
2391
2392 /**
2393 * Create a new postal address entry with the given data.
2394 *
2395 * @param label label for the item, from the db not the display label
2396 * @param type the type of postal address
2397 * @param data the starting data for the entry, may be null
2398 * @param uri the uri for the entry if it already exists, may be null
2399 * @param id the id for the entry if it already exists, 0 it it doesn't
2400 * @return the new EditEntry
2401 */
2402 public static final EditEntry newPostalEntry(EditContactActivity activity,
2403 String label, int type, String data, Uri uri, long id) {
2404 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2405 entry.hint = activity.getString(R.string.ghostData_postal);
2406 entry.column = ContactMethods.DATA;
2407 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2408 entry.kind = Contacts.KIND_POSTAL;
2409 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2410 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2411 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2412 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2413 entry.maxLines = 4;
2414 entry.lines = 2;
2415 return entry;
2416 }
2417
2418 /**
2419 * Create a new IM address entry
2420 */
2421 public static final EditEntry newImEntry(EditContactActivity activity,
2422 Uri uri, int type) {
2423 return newImEntry(activity, null, type, null, uri, 0);
2424 }
2425
2426 /**
2427 * Create a new IM address entry with the given data.
2428 *
2429 * @param label label for the item, from the db not the display label
2430 * @param protocol the type used
2431 * @param data the starting data for the entry, may be null
2432 * @param uri the uri for the entry if it already exists, may be null
2433 * @param id the id for the entry if it already exists, 0 it it doesn't
2434 * @return the new EditEntry
2435 */
2436 public static final EditEntry newImEntry(EditContactActivity activity,
2437 String label, int protocol, String data, Uri uri, long id) {
2438 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2439 entry.hint = activity.getString(R.string.ghostData_im);
2440 entry.column = ContactMethods.DATA;
2441 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2442 entry.kind = Contacts.KIND_IM;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08002443 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2444 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002445 return entry;
2446 }
2447 }
2448
2449 public void afterTextChanged(Editable s) {
2450 // Someone edited a text field, so assume this contact is changed
2451 mContactChanged = true;
2452 }
2453
2454 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2455 // Do nothing; editing handled by afterTextChanged()
2456 }
2457
2458 public void onTextChanged(CharSequence s, int start, int before, int count) {
2459 // Do nothing; editing handled by afterTextChanged()
2460 }
2461
2462 public void onFocusChange(View v, boolean hasFocus) {
2463 // Because we're emulating a ListView, we need to setSelected() for
2464 // views as they are focused.
2465 v.setSelected(hasFocus);
2466 }
2467}