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