blob: d34713d6f7069e5cee1883cf332181d8f8bf6f40 [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 Project5dc3b4f2008-10-21 07:00:00 -0700173 private CheckBox mSendToVoicemailCheckBox;
174 private LinearLayout mLayout;
175 private LayoutInflater mInflater;
176 private MenuItem mPhotoMenuItem;
177 private boolean mPhotoPresent = false;
The Android Open Source Project42b987b2009-01-22 00:13:44 -0800178 private EditText mPhoneticNameView; // invisible in some locales, but always present
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700179
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800180 /** Flag marking this contact as changed, meaning we should write changes back. */
181 private boolean mContactChanged = false;
182
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700183 // These are accessed by inner classes. They're package scoped to make access more efficient.
184 /* package */ ContentResolver mResolver;
185 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
186 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
187 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
188 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
189 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
190 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
191
192 /* package */ static final int MSG_DELETE = 1;
193 /* package */ static final int MSG_CHANGE_LABEL = 2;
194 /* package */ static final int MSG_ADD_PHONE = 3;
195 /* package */ static final int MSG_ADD_EMAIL = 4;
196 /* package */ static final int MSG_ADD_POSTAL = 5;
197
198 public void onClick(View v) {
199 switch (v.getId()) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700200 case R.id.photoImage: {
201 doPickPhotoAction();
202 break;
203 }
204
205 case R.id.addMore:
206 doAddAction();
207 break;
208
209 case R.id.saveButton:
210 doSaveAction();
211 break;
212
213 case R.id.discardButton:
214 doRevertAction();
215 break;
216
217 case R.id.delete:
218 case R.id.delete2: {
219 EditEntry entry = findEntryForView(v);
220 if (entry != null) {
221 // Clear the text and hide the view so it gets saved properly
222 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
223 entry.view.setVisibility(View.GONE);
224 entry.isDeleted = true;
225 }
226 break;
227 }
228
229 case R.id.label: {
230 EditEntry entry = findEntryForView(v);
231 if (entry != null) {
232 String[] labels = getLabelsForKind(this, entry.kind);
233 LabelPickedListener listener = new LabelPickedListener(entry, labels);
234 new AlertDialog.Builder(EditContactActivity.this)
235 .setItems(labels, listener)
236 .setTitle(R.string.selectLabel)
237 .show();
238 }
239 break;
240 }
241
242 case R.id.data: {
243 EditEntry entry = findEntryForView(v);
244 if (isRingtoneEntry(entry)) {
245 doPickRingtone(entry);
246 }
247 break;
248 }
249 }
250 }
251
252 private void setPhotoPresent(boolean present) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700253 mPhotoPresent = present;
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800254
255 // Correctly scale the contact photo if present, otherwise just center
256 // the photo placeholder icon.
257 if (mPhotoPresent) {
258 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
259 } else {
260 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
261 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
262 }
263
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700264 if (mPhotoMenuItem != null) {
265 if (present) {
266 mPhotoMenuItem.setTitle(R.string.removePicture);
267 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
268 } else {
269 mPhotoMenuItem.setTitle(R.string.addPicture);
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800270 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700271 }
272 }
273 }
274
275 private EditEntry findEntryForView(View v) {
276 // Try to find the entry for this view
277 EditEntry entry = null;
278 do {
279 Object tag = v.getTag();
280 if (tag != null && tag instanceof EditEntry) {
281 entry = (EditEntry) tag;
282 break;
283 } else {
284 ViewParent parent = v.getParent();
285 if (parent != null && parent instanceof View) {
286 v = (View) parent;
287 } else {
288 v = null;
289 }
290 }
291 } while (v != null);
292 return entry;
293 }
294
295 private DialogInterface.OnClickListener mDeleteContactDialogListener =
296 new DialogInterface.OnClickListener() {
297 public void onClick(DialogInterface dialog, int button) {
298 mResolver.delete(mUri, null, null);
299 finish();
300 }
301 };
302
303 private boolean mMobilePhoneAdded = false;
304 private boolean mPrimaryEmailAdded = false;
305
306 @Override
307 protected void onCreate(Bundle icicle) {
308 super.onCreate(icicle);
309
310 mResolver = getContentResolver();
311
312 // Build the list of sections
313 setupSections();
314
315 // Load the UI
316 setContentView(R.layout.edit_contact);
317 mLayout = (LinearLayout) findViewById(R.id.list);
318 mNameView = (EditText) findViewById(R.id.name);
319 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
320 mPhotoImageView.setOnClickListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700321 mSendToVoicemailCheckBox = (CheckBox) findViewById(R.id.send_to_voicemail);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800322 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800323
324 // Setup the bottom buttons
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700325 View view = findViewById(R.id.addMore);
326 view.setOnClickListener(this);
327 view = findViewById(R.id.saveButton);
328 view.setOnClickListener(this);
329 view = findViewById(R.id.discardButton);
330 view.setOnClickListener(this);
331
332 mInflater = getLayoutInflater();
333
334 // Resolve the intent
335 mState = STATE_UNKNOWN;
336 Intent intent = getIntent();
337 String action = intent.getAction();
338 mUri = intent.getData();
339 if (mUri != null) {
340 if (action.equals(Intent.ACTION_EDIT)) {
341 if (icicle == null) {
342 // Build the entries & views
343 buildEntriesForEdit(getIntent().getExtras());
344 buildViews();
345 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800346 setTitle(R.string.editContact_title_edit);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700347 mState = STATE_EDIT;
348 } else if (action.equals(Intent.ACTION_INSERT)) {
349 if (icicle == null) {
350 // Build the entries & views
351 buildEntriesForInsert(getIntent().getExtras());
352 buildViews();
353 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800354 setTitle(R.string.editContact_title_insert);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700355 mState = STATE_INSERT;
356 mInsert = true;
357 }
358 }
359
360 if (mState == STATE_UNKNOWN) {
361 Log.e(TAG, "Cannot resolve intent: " + intent);
362 finish();
363 return;
364 }
365
366 if (mState == STATE_EDIT) {
367 setTitle(getResources().getText(R.string.editContact_title_edit));
368 } else {
369 setTitle(getResources().getText(R.string.editContact_title_insert));
370 }
371 }
372
373 private void setupSections() {
374 mSections.add(mPhoneEntries);
375 mSections.add(mEmailEntries);
376 mSections.add(mImEntries);
377 mSections.add(mPostalEntries);
378 mSections.add(mOtherEntries);
379 }
380
381 @Override
382 protected void onSaveInstanceState(Bundle outState) {
383 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
384 outState.putParcelableArrayList("emailEntries", mEmailEntries);
385 outState.putParcelableArrayList("imEntries", mImEntries);
386 outState.putParcelableArrayList("postalEntries", mPostalEntries);
387 outState.putParcelableArrayList("otherEntries", mOtherEntries);
388 outState.putInt("state", mState);
389 outState.putBoolean("insert", mInsert);
390 outState.putParcelable("uri", mUri);
391 outState.putString("name", mNameView.getText().toString());
392 outState.putParcelable("photo", mPhoto);
393 outState.putBoolean("photoChanged", mPhotoChanged);
394 outState.putBoolean("sendToVoicemail", mSendToVoicemailCheckBox.isChecked());
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800395 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800396 outState.putBoolean("contactChanged", mContactChanged);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700397 }
398
399 @Override
400 protected void onRestoreInstanceState(Bundle inState) {
401 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
402 mEmailEntries = inState.getParcelableArrayList("emailEntries");
403 mImEntries = inState.getParcelableArrayList("imEntries");
404 mPostalEntries = inState.getParcelableArrayList("postalEntries");
405 mOtherEntries = inState.getParcelableArrayList("otherEntries");
406 setupSections();
407
408 mState = inState.getInt("state");
409 mInsert = inState.getBoolean("insert");
410 mUri = inState.getParcelable("uri");
411 mNameView.setText(inState.getString("name"));
412 mPhoto = inState.getParcelable("photo");
413 if (mPhoto != null) {
414 mPhotoImageView.setImageBitmap(mPhoto);
415 setPhotoPresent(true);
416 } else {
417 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
418 setPhotoPresent(false);
419 }
420 mPhotoChanged = inState.getBoolean("photoChanged");
421 mSendToVoicemailCheckBox.setChecked(inState.getBoolean("sendToVoicemail"));
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800422 mPhoneticNameView.setText(inState.getString("phoneticName"));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800423 mContactChanged = inState.getBoolean("contactChanged");
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800424
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700425 // Now that everything is restored, build the view
426 buildViews();
427 }
428
429 @Override
430 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
431 if (resultCode != RESULT_OK) {
432 return;
433 }
434
435 switch (requestCode) {
436 case PHOTO_PICKED_WITH_DATA: {
437 final Bundle extras = data.getExtras();
438 if (extras != null) {
439 Bitmap photo = extras.getParcelable("data");
440 mPhoto = photo;
441 mPhotoChanged = true;
442 mPhotoImageView.setImageBitmap(photo);
443 setPhotoPresent(true);
444 }
445 break;
446 }
447
448 case RINGTONE_PICKED: {
449 Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
450 handleRingtonePicked(pickedUri);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800451 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700452 break;
453 }
454 }
455 }
456
457 @Override
458 public boolean onKeyDown(int keyCode, KeyEvent event) {
459 switch (keyCode) {
460 case KeyEvent.KEYCODE_BACK: {
461 doSaveAction();
462 return true;
463 }
464 }
465 return super.onKeyDown(keyCode, event);
466 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800467
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700468 @Override
469 public boolean onCreateOptionsMenu(Menu menu) {
470 super.onCreateOptionsMenu(menu);
471 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
472 .setIcon(android.R.drawable.ic_menu_save)
473 .setAlphabeticShortcut('\n');
474 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
475 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
476 .setAlphabeticShortcut('q');
477 if (!mInsert) {
478 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
479 .setIcon(android.R.drawable.ic_menu_delete);
480 }
481
482 menu.add(0, MENU_ITEM_ADD, 0, R.string.menu_addItem)
483 .setIcon(android.R.drawable.ic_menu_add)
484 .setAlphabeticShortcut('n');
485
486 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
487 // Updates the state of the menu item
488 setPhotoPresent(mPhotoPresent);
489
490 return true;
491 }
492
493 @Override
494 public boolean onOptionsItemSelected(MenuItem item) {
495 switch (item.getItemId()) {
496 case MENU_ITEM_SAVE:
497 doSaveAction();
498 return true;
499
500 case MENU_ITEM_DONT_SAVE:
501 doRevertAction();
502 return true;
503
504 case MENU_ITEM_DELETE:
505 // Get confirmation
506 showDialog(DELETE_CONFIRMATION_DIALOG);
507 return true;
508
509 case MENU_ITEM_ADD:
510 doAddAction();
511 return true;
512
513 case MENU_ITEM_PHOTO:
514 if (!mPhotoPresent) {
515 doPickPhotoAction();
516 } else {
517 doRemovePhotoAction();
518 }
519 return true;
520 }
521
522 return false;
523 }
524
525 private void doAddAction() {
526 showDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
527 }
528
529 private void doRevertAction() {
530 finish();
531 }
532
533 private void doPickPhotoAction() {
534 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
535 // TODO: get these values from constants somewhere
536 intent.setType("image/*");
537 intent.putExtra("crop", "true");
538 intent.putExtra("aspectX", 1);
539 intent.putExtra("aspectY", 1);
540 intent.putExtra("outputX", 96);
541 intent.putExtra("outputY", 96);
542 try {
543 intent.putExtra("return-data", true);
544 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
545 } catch (ActivityNotFoundException e) {
546 new AlertDialog.Builder(EditContactActivity.this)
547 .setTitle(R.string.errorDialogTitle)
548 .setMessage(R.string.photoPickerNotFoundText)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800549 .setPositiveButton(android.R.string.ok, null)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700550 .show();
551 }
552 }
553
554 private void doRemovePhotoAction() {
555 mPhoto = null;
556 mPhotoChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700557 setPhotoPresent(false);
558 }
559
560 private void doPickRingtone(EditEntry entry) {
561 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
562 // Allow user to pick 'Default'
563 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
564 // Show only ringtones
565 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
566 // Don't show 'Silent'
567 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800568
569 Uri ringtoneUri;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700570 if (entry.data != null) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800571 ringtoneUri = Uri.parse(entry.data);
572 } else {
573 // Otherwise pick default ringtone Uri so that something is selected.
574 ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700575 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800576
577 // Put checkmark next to the current ringtone for this contact
578 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700579 // Launch!
580 startActivityForResult(intent, RINGTONE_PICKED);
581 }
582
583 private void handleRingtonePicked(Uri pickedUri) {
584 EditEntry entry = getRingtoneEntry();
585 if (entry == null) {
586 Log.w(TAG, "Ringtone picked but could not find ringtone entry");
587 return;
588 }
589
590 if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
591 entry.data = null;
592 } else {
593 entry.data = pickedUri.toString();
594 }
595
596 updateRingtoneView(entry);
597 }
598
599 private void updateRingtoneView(EditEntry entry) {
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800600 String ringtoneName;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700601 if (entry.data == null) {
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800602 ringtoneName = getString(R.string.default_ringtone);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700603 } else {
604 Uri ringtoneUri = Uri.parse(entry.data);
605 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
606 if (ringtone == null) {
607 Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
608 return;
609 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800610 ringtoneName = ringtone.getTitle(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700611 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800612
613 // Build actual "Ringtone: Default" string that is assigned to spinner.
614 String text = getString(R.string.ringtone_spinner, ringtoneName);
615 updateDataView(entry, text);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700616 }
617
618 private void updateDataView(EditEntry entry, String text) {
619 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
620 dataView.setText(text);
621 }
622
623 @Override
624 protected Dialog onCreateDialog(int id) {
625 switch (id) {
626 case LABEL_PICKER_ALL_TYPES_DIALOG:
627 return createAllTypesPicker();
628
629 case DELETE_CONFIRMATION_DIALOG:
630 return new AlertDialog.Builder(EditContactActivity.this)
631 .setTitle(R.string.deleteConfirmation_title)
632 .setIcon(android.R.drawable.ic_dialog_alert)
633 .setMessage(R.string.deleteConfirmation)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800634 .setNegativeButton(android.R.string.cancel, null)
635 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700636 .setCancelable(false)
637 .create();
638 }
639 return super.onCreateDialog(id);
640 }
641
642 static String[] getLabelsForKind(Context context, int kind) {
643 final Resources resources = context.getResources();
644 switch (kind) {
645 case Contacts.KIND_PHONE:
646 return resources.getStringArray(android.R.array.phoneTypes);
647 case Contacts.KIND_EMAIL:
648 return resources.getStringArray(android.R.array.emailAddressTypes);
649 case Contacts.KIND_POSTAL:
650 return resources.getStringArray(android.R.array.postalAddressTypes);
651 case Contacts.KIND_IM:
652 return resources.getStringArray(android.R.array.imProtocols);
653 case Contacts.KIND_ORGANIZATION:
654 return resources.getStringArray(android.R.array.organizationTypes);
655 case EditEntry.KIND_CONTACT:
656 return resources.getStringArray(R.array.otherLabels);
657 }
658 return null;
659 }
660
661 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
662 // In the UI Custom... comes last, but it is uses the constant 0
663 // so it is in the same location across the various kinds. Fix up the
664 // position to a valid type here.
665 if (labelPosition == labels.length - 1) {
666 return ContactMethods.TYPE_CUSTOM;
667 } else {
668 return labelPosition + 1;
669 }
670 }
671
672 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
673 int childPosition, long id) {
674 EditEntry entry = null;
675
676 // Make the dialog go away
677 dismissDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
678
679 // Create the new entry
680 switch (groupPosition) {
681 case LABEL_PICKER_PHONES_POSITION: {
682 String[] labels = getLabelsForKind(this, Contacts.KIND_PHONE);
683 final int type = getTypeFromLabelPosition(labels, childPosition);
684 entry = EditEntry.newPhoneEntry(EditContactActivity.this,
685 labels[childPosition], type,
686 null, Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY), 0);
687 if (type == Phones.TYPE_CUSTOM) {
688 createCustomPicker(entry, mPhoneEntries);
689 return true;
690 } else {
691 mPhoneEntries.add(entry);
692 }
693 break;
694 }
695
696 case LABEL_PICKER_EMAIL_POSITION: {
697 String[] labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
698 final int type = getTypeFromLabelPosition(labels, childPosition);
699 entry = EditEntry.newEmailEntry(EditContactActivity.this,
700 labels[childPosition], type, null,
701 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
702 if (type == ContactMethods.TYPE_CUSTOM) {
703 createCustomPicker(entry, mEmailEntries);
704 return true;
705 } else {
706 mEmailEntries.add(entry);
707 }
708 break;
709 }
710
711 case LABEL_PICKER_IM_POSITION: {
712 String[] labels = getLabelsForKind(this, Contacts.KIND_IM);
713 entry = EditEntry.newImEntry(EditContactActivity.this,
714 labels[childPosition], childPosition, null,
715 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
716 mImEntries.add(entry);
717 break;
718 }
719
720 case LABEL_PICKER_POSTAL_POSITION: {
721 String[] labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
722 final int type = getTypeFromLabelPosition(labels, childPosition);
723 entry = EditEntry.newPostalEntry(EditContactActivity.this,
724 labels[childPosition], type, null,
725 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
726 if (type == ContactMethods.TYPE_CUSTOM) {
727 createCustomPicker(entry, mPostalEntries);
728 return true;
729 } else {
730 mPostalEntries.add(entry);
731 }
732 break;
733 }
734
735 case LABEL_PICKER_OTHER_POSITION: {
736 switch (childPosition) {
737 case OTHER_ORGANIZATION:
738 entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
739 Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800740 Organizations.TYPE_WORK);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700741 mOtherEntries.add(entry);
742 break;
743
744 case OTHER_NOTE:
745 entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
746 mOtherEntries.add(entry);
747 break;
748
749 default:
750 entry = null;
751 }
752 break;
753 }
754
755 default:
756 entry = null;
757 }
758
759 // Rebuild the views if needed
760 if (entry != null) {
761 buildViews();
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800762 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700763
764 View dataView = entry.view.findViewById(R.id.data);
765 if (dataView == null) {
766 entry.view.requestFocus();
767 } else {
768 dataView.requestFocus();
769 }
770 }
771 return true;
772 }
773
774 private EditEntry getRingtoneEntry() {
775 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
776 EditEntry entry = mOtherEntries.get(i);
777 if (isRingtoneEntry(entry)) {
778 return entry;
779 }
780 }
781 return null;
782 }
783
784 private static boolean isRingtoneEntry(EditEntry entry) {
785 return entry != null && entry.column != null && entry.column.equals(People.CUSTOM_RINGTONE);
786 }
787
788 private Dialog createAllTypesPicker() {
789 // Setup the adapter
790 List<Map<String, ?>> groupData = Lists.newArrayList();
791 List<List<Map<String, ?>>> childData = Lists.newArrayList();
792 List<Map<String, ?>> children;
793 HashMap<String, CharSequence> curGroupMap;
794 CharSequence[] labels;
795 int labelsSize;
796
797 // Phones
798 curGroupMap = new HashMap<String, CharSequence>();
799 groupData.add(curGroupMap);
800 curGroupMap.put("data", getText(R.string.phoneLabelsGroup));
801
802 labels = getLabelsForKind(this, Contacts.KIND_PHONE);
803 labelsSize = labels.length;
804 children = Lists.newArrayList();
805 for (int i = 0; i < labelsSize; i++) {
806 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
807 children.add(curChildMap);
808 curChildMap.put("data", labels[i]);
809 }
810 childData.add(LABEL_PICKER_PHONES_POSITION, children);
811
812 // Email
813 curGroupMap = new HashMap<String, CharSequence>();
814 groupData.add(curGroupMap);
815 curGroupMap.put("data", getText(R.string.emailLabelsGroup));
816
817 labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
818 labelsSize = labels.length;
819 children = Lists.newArrayList();
820 for (int i = 0; i < labelsSize; i++) {
821 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
822 children.add(curChildMap);
823 curChildMap.put("data", labels[i]);
824 }
825 childData.add(LABEL_PICKER_EMAIL_POSITION, children);
826
827 // IM
828 curGroupMap = new HashMap<String, CharSequence>();
829 groupData.add(curGroupMap);
830 curGroupMap.put("data", getText(R.string.imLabelsGroup));
831
832 labels = getLabelsForKind(this, Contacts.KIND_IM);
833 labelsSize = labels.length;
834 children = Lists.newArrayList();
835 for (int i = 0; i < labelsSize; i++) {
836 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
837 children.add(curChildMap);
838 curChildMap.put("data", labels[i]);
839 }
840 childData.add(LABEL_PICKER_IM_POSITION, children);
841
842 // Postal
843 curGroupMap = new HashMap<String, CharSequence>();
844 groupData.add(curGroupMap);
845 curGroupMap.put("data", getText(R.string.postalLabelsGroup));
846
847 labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
848 labelsSize = labels.length;
849 children = Lists.newArrayList();
850 for (int i = 0; i < labelsSize; i++) {
851 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
852 children.add(curChildMap);
853 curChildMap.put("data", labels[i]);
854 }
855 childData.add(LABEL_PICKER_POSTAL_POSITION, children);
856
857 // Other
858 curGroupMap = new HashMap<String, CharSequence>();
859 groupData.add(curGroupMap);
860 curGroupMap.put("data", getText(R.string.otherLabelsGroup));
861
862 labels = getLabelsForKind(this, EditEntry.KIND_CONTACT);
863 labelsSize = labels.length;
864 children = Lists.newArrayList();
865 for (int i = 0; i < labelsSize; i++) {
866 HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
867 children.add(curChildMap);
868 curChildMap.put("data", labels[i]);
869 }
870 childData.add(LABEL_PICKER_OTHER_POSITION, children);
871
872 // Create the expandable list view
873 ExpandableListView list = new ExpandableListView(new ContextThemeWrapper(this,
874 android.R.style.Theme_Light));
875 list.setOnChildClickListener(this);
876 list.setAdapter(new SimpleExpandableListAdapter(
877 new ContextThemeWrapper(this, android.R.style.Theme_Light),
878 groupData,
879 android.R.layout.simple_expandable_list_item_1,
880 new String[] { "data" },
881 new int[] { android.R.id.text1 },
882 childData,
883 android.R.layout.simple_expandable_list_item_1,
884 new String[] { "data" },
885 new int[] { android.R.id.text1 }
886 ));
887 // This list shouldn't have a color hint since the dialog may be transparent
888 list.setCacheColorHint(0);
889
890 // Create the dialog
891 return new AlertDialog.Builder(this).setView(list).setInverseBackgroundForced(true)
892 .setTitle(R.string.selectLabel).create();
893 }
894
895 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
896 final EditText label = new EditText(this);
897 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
898 label.requestFocus();
899 new AlertDialog.Builder(this)
900 .setView(label)
901 .setTitle(R.string.customLabelPickerTitle)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800902 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700903 public void onClick(DialogInterface dialog, int which) {
904 entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
905 label.getText().toString());
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800906 mContactChanged = true;
907
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700908 if (addTo != null) {
909 addTo.add(entry);
910 buildViews();
911 entry.view.requestFocus(View.FOCUS_DOWN);
912 }
913 }
914 })
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800915 .setNegativeButton(android.R.string.cancel, null)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700916 .show();
917 }
918
919 /**
920 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
921 */
922 private void doSaveAction() {
923 // Save or create the contact if needed
924 switch (mState) {
925 case STATE_EDIT:
926 save();
927 break;
928
929 case STATE_INSERT:
930 create();
931 break;
932
933 default:
934 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
935 break;
936 }
937 finish();
938 }
939
940 /**
941 * Save the various fields to the existing contact.
942 */
943 private void save() {
944 ContentValues values = new ContentValues();
945 String data;
946 int numValues = 0;
947
948 // Handle the name and send to voicemail specially
949 final String name = mNameView.getText().toString();
950 if (name != null && TextUtils.isGraphic(name)) {
951 numValues++;
952 }
953 values.put(People.NAME, name);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800954 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700955 values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
956 mResolver.update(mUri, values, null, null);
957
958 if (mPhotoChanged) {
959 // Only write the photo if it's changed, since we don't initially load mPhoto
960 if (mPhoto != null) {
961 ByteArrayOutputStream stream = new ByteArrayOutputStream();
962 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
963 Contacts.People.setPhotoData(mResolver, mUri, stream.toByteArray());
964 } else {
965 Contacts.People.setPhotoData(mResolver, mUri, null);
966 }
967 }
968
969 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
970 for (int i = 0; i < entryCount; i++) {
971 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
972 int kind = entry.kind;
973 data = entry.getData();
974 boolean empty = data == null || !TextUtils.isGraphic(data);
975 if (kind == EditEntry.KIND_CONTACT) {
976 values.clear();
977 if (!empty) {
978 values.put(entry.column, data);
979 mResolver.update(entry.uri, values, null, null);
980 numValues++;
981 } else {
982 values.put(entry.column, (String) null);
983 mResolver.update(entry.uri, values, null, null);
984 }
985 } else {
986 if (!empty) {
987 values.clear();
988 entry.toValues(values);
989 if (entry.id != 0) {
990 mResolver.update(entry.uri, values, null, null);
991 } else {
992 mResolver.insert(entry.uri, values);
993 }
994 numValues++;
995 } else if (entry.id != 0) {
996 mResolver.delete(entry.uri, null, null);
997 }
998 }
999 }
1000
1001 if (numValues == 0) {
1002 // The contact is completely empty, delete it
1003 mResolver.delete(mUri, null, null);
1004 mUri = null;
1005 setResult(RESULT_CANCELED);
1006 } else {
1007 // Add the entry to the my contacts group if it isn't there already
1008 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1009 setResult(RESULT_OK, new Intent().setData(mUri));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001010
1011 // Only notify user if we actually changed contact
1012 if (mContactChanged || mPhotoChanged) {
1013 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1014 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001015 }
1016 }
1017
1018 /**
1019 * Takes the entered data and saves it to a new contact.
1020 */
1021 private void create() {
1022 ContentValues values = new ContentValues();
1023 String data;
1024 int numValues = 0;
1025
1026 // Create the contact itself
1027 final String name = mNameView.getText().toString();
1028 if (name != null && TextUtils.isGraphic(name)) {
1029 numValues++;
1030 }
1031 values.put(People.NAME, name);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001032 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001033 values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
1034
1035 // Add the contact to the My Contacts group
1036 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1037
1038 // Add the contact to the group that is being displayed in the contact list
1039 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001040 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1041 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1042 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1043 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001044 null);
1045 if (!TextUtils.isEmpty(displayGroup)) {
1046 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1047 }
1048 }
1049
1050 // Handle the photo
1051 if (mPhoto != null) {
1052 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1053 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1054 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1055 }
1056
1057 // Create the contact methods
1058 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1059 for (int i = 0; i < entryCount; i++) {
1060 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
1061 if (entry.kind != EditEntry.KIND_CONTACT) {
1062 values.clear();
1063 if (entry.toValues(values)) {
1064 // Only create the entry if there is data
1065 entry.uri = mResolver.insert(
1066 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1067 entry.id = ContentUris.parseId(entry.uri);
1068 numValues++;
1069 }
1070 } else {
1071 // Update the contact with any straggling data, like notes
1072 data = entry.getData();
1073 values.clear();
1074 if (data != null && TextUtils.isGraphic(data)) {
1075 values.put(entry.column, data);
1076 mResolver.update(contactUri, values, null, null);
1077 numValues++;
1078 }
1079 }
1080 }
1081
1082 if (numValues == 0) {
1083 mResolver.delete(contactUri, null, null);
1084 setResult(RESULT_CANCELED);
1085 } else {
1086 mUri = contactUri;
1087 Intent resultIntent = new Intent()
1088 .setData(mUri)
1089 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1090 setResult(RESULT_OK, resultIntent);
1091 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1092 }
1093 }
1094
1095 /**
1096 * Build up the entries to display on the screen.
1097 *
1098 * @param extras the extras used to start this activity, may be null
1099 */
1100 private void buildEntriesForEdit(Bundle extras) {
1101 Cursor personCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
1102 if (personCursor == null) {
1103 Log.e(TAG, "invalid contact uri: " + mUri);
1104 finish();
1105 return;
1106 } else if (!personCursor.moveToFirst()) {
1107 Log.e(TAG, "invalid contact uri: " + mUri);
1108 finish();
1109 personCursor.close();
1110 return;
1111 }
1112
1113 // Clear out the old entries
1114 int numSections = mSections.size();
1115 for (int i = 0; i < numSections; i++) {
1116 mSections.get(i).clear();
1117 }
1118
1119 EditEntry entry;
1120
1121 // Name
1122 mNameView.setText(personCursor.getString(CONTACT_NAME_COLUMN));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001123 mNameView.addTextChangedListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001124
1125 // Photo
1126 mPhoto = People.loadContactPhoto(this, mUri, 0, null);
1127 if (mPhoto == null) {
1128 setPhotoPresent(false);
1129 } else {
1130 setPhotoPresent(true);
1131 mPhotoImageView.setImageBitmap(mPhoto);
1132 }
1133
1134 // Send to voicemail
1135 mSendToVoicemailCheckBox
1136 .setChecked(personCursor.getInt(CONTACT_SEND_TO_VOICEMAIL_COLUMN) == 1);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001137 mSendToVoicemailCheckBox
1138 .setOnCheckedChangeListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001139
1140 // Organizations
1141 Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
1142 Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
1143 null, null, null);
1144
1145 if (organizationsCursor != null) {
1146 while (organizationsCursor.moveToNext()) {
1147 int type = organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
1148 String label = organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN);
1149 String company = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
1150 String title = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
1151 long id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
1152 Uri uri = ContentUris.withAppendedId(Organizations.CONTENT_URI, id);
1153
1154 // Add an organization entry
1155 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1156 entry.isPrimary = organizationsCursor.getLong(ORGANIZATIONS_ISPRIMARY_COLUMN) != 0;
1157 mOtherEntries.add(entry);
1158 }
1159 organizationsCursor.close();
1160 }
1161
1162 // Notes
1163 if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
1164 entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
1165 mUri);
1166 mOtherEntries.add(entry);
1167 }
1168
1169 // Ringtone
1170 entry = EditEntry.newRingtoneEntry(this,
1171 personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
1172 mOtherEntries.add(entry);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001173
1174 // Phonetic name
1175 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001176 mPhoneticNameView.addTextChangedListener(this);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001177
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001178 personCursor.close();
1179
1180 // Build up the phone entries
1181 Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
1182 Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION,
1183 null, null, null);
1184
1185 if (phonesCursor != null) {
1186 while (phonesCursor.moveToNext()) {
1187 int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
1188 String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
1189 String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
1190 long id = phonesCursor.getLong(PHONES_ID_COLUMN);
1191 boolean isPrimary = phonesCursor.getLong(PHONES_ISPRIMARY_COLUMN) != 0;
1192 Uri uri = ContentUris.withAppendedId(phonesUri, id);
1193
1194 // Add a phone number entry
1195 entry = EditEntry.newPhoneEntry(this, label, type, number, uri, id);
1196 entry.isPrimary = isPrimary;
1197 mPhoneEntries.add(entry);
1198
1199 // Keep track of which primary types have been added
1200 if (type == Phones.TYPE_MOBILE) {
1201 mMobilePhoneAdded = true;
1202 }
1203 }
1204
1205 phonesCursor.close();
1206 }
1207
1208 // Build the contact method entries
1209 Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
1210 Cursor methodsCursor = mResolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
1211
1212 if (methodsCursor != null) {
1213 while (methodsCursor.moveToNext()) {
1214 int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
1215 String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
1216 String data = methodsCursor.getString(METHODS_DATA_COLUMN);
1217 String auxData = methodsCursor.getString(METHODS_AUX_DATA_COLUMN);
1218 int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
1219 long id = methodsCursor.getLong(METHODS_ID_COLUMN);
1220 boolean isPrimary = methodsCursor.getLong(METHODS_ISPRIMARY_COLUMN) != 0;
1221 Uri uri = ContentUris.withAppendedId(methodsUri, id);
1222
1223 switch (kind) {
1224 case Contacts.KIND_EMAIL: {
1225 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1226 entry.isPrimary = isPrimary;
1227 mEmailEntries.add(entry);
1228
1229 if (isPrimary) {
1230 mPrimaryEmailAdded = true;
1231 }
1232 break;
1233 }
1234
1235 case Contacts.KIND_POSTAL: {
1236 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1237 entry.isPrimary = isPrimary;
1238 mPostalEntries.add(entry);
1239 break;
1240 }
1241
1242 case Contacts.KIND_IM: {
1243 Object protocolObj = ContactMethods.decodeImProtocol(auxData);
1244 if (protocolObj == null) {
1245 // Invalid IM protocol, log it then ignore.
1246 Log.e(TAG, "Couldn't decode IM protocol: " + auxData);
1247 continue;
1248 } else {
1249 if (protocolObj instanceof Number) {
1250 int protocol = ((Number) protocolObj).intValue();
1251 entry = EditEntry.newImEntry(this,
1252 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1253 data, uri, id);
1254 } else {
1255 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1256 uri, id);
1257 }
1258 mImEntries.add(entry);
1259 }
1260 break;
1261 }
1262 }
1263 }
1264
1265 methodsCursor.close();
1266 }
1267
1268 // Add values from the extras, if there are any
1269 if (extras != null) {
1270 addFromExtras(extras, phonesUri, methodsUri);
1271 }
1272
1273 // Add the base types if needed
1274 if (!mMobilePhoneAdded) {
1275 entry = EditEntry.newPhoneEntry(this,
1276 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1277 DEFAULT_PHONE_TYPE);
1278 mPhoneEntries.add(entry);
1279 }
1280
1281 if (!mPrimaryEmailAdded) {
1282 entry = EditEntry.newEmailEntry(this,
1283 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1284 DEFAULT_EMAIL_TYPE);
1285 entry.isPrimary = true;
1286 mEmailEntries.add(entry);
1287 }
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001288
1289 mContactChanged = false;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001290 }
1291
1292 /**
1293 * Build the list of EditEntries for full mode insertions.
1294 *
1295 * @param extras the extras used to start this activity, may be null
1296 */
1297 private void buildEntriesForInsert(Bundle extras) {
1298 // Clear out the old entries
1299 int numSections = mSections.size();
1300 for (int i = 0; i < numSections; i++) {
1301 mSections.get(i).clear();
1302 }
1303
1304 EditEntry entry;
1305
1306 // Check the intent extras
1307 if (extras != null) {
1308 addFromExtras(extras, null, null);
1309 }
1310
1311 // Photo
1312 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1313
1314 // Add the base entries if they're not already present
1315 if (!mMobilePhoneAdded) {
1316 entry = EditEntry.newPhoneEntry(this, null, Phones.TYPE_MOBILE);
1317 entry.isPrimary = true;
1318 mPhoneEntries.add(entry);
1319 }
1320
1321 if (!mPrimaryEmailAdded) {
1322 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1323 entry.isPrimary = true;
1324 mEmailEntries.add(entry);
1325 }
1326
1327 // Ringtone
1328 entry = EditEntry.newRingtoneEntry(this, null, mUri);
1329 mOtherEntries.add(entry);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001330 }
1331
1332 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1333 EditEntry entry;
1334
1335 // Read the name from the bundle
1336 CharSequence name = extras.getCharSequence(Insert.NAME);
1337 if (name != null && TextUtils.isGraphic(name)) {
1338 mNameView.setText(name);
1339 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001340
1341 // Read the phonetic name from the bundle
1342 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1343 if (!TextUtils.isEmpty(phoneticName)) {
1344 mPhoneticNameView.setText(phoneticName);
1345 }
1346
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001347 // Postal entries from extras
1348 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1349 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1350 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1351 postalType = DEFAULT_POSTAL_TYPE;
1352 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001353
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001354 if (postalType != INVALID_TYPE) {
1355 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1356 methodsUri, 0);
1357 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1358 mPostalEntries.add(entry);
1359 }
1360
1361 // Email entries from extras
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001362 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1363 Insert.EMAIL_ISPRIMARY);
1364 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1365 null);
1366 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1367 null);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001368
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001369 // Phone entries from extras
1370 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1371 Insert.PHONE_ISPRIMARY);
1372 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1373 null);
1374 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1375 null);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001376
1377 // IM entries from extras
1378 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1379 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
1380
1381 if (imHandle != null && imProtocol != null) {
1382 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1383 if (protocolObj instanceof Number) {
1384 int protocol = ((Number) protocolObj).intValue();
1385 entry = EditEntry.newImEntry(this,
1386 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1387 imHandle.toString(), null, 0);
1388 } else {
1389 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
1390 null, 0);
1391 }
1392 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1393 mImEntries.add(entry);
1394 }
1395 }
1396
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001397 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1398 String typeField, String primaryField) {
1399 CharSequence email = extras.getCharSequence(emailField);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001400
1401 // Correctly handle String in typeField as TYPE_CUSTOM
1402 int emailType = INVALID_TYPE;
1403 String customLabel = null;
1404 if(extras.get(typeField) instanceof String) {
1405 emailType = ContactMethods.TYPE_CUSTOM;
1406 customLabel = extras.getString(typeField);
1407 } else {
1408 emailType = extras.getInt(typeField, INVALID_TYPE);
1409 }
1410
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001411 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1412 emailType = DEFAULT_EMAIL_TYPE;
1413 mPrimaryEmailAdded = true;
1414 }
1415
1416 if (emailType != INVALID_TYPE) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001417 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001418 methodsUri, 0);
1419 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1420 mEmailEntries.add(entry);
1421
1422 // Keep track of which primary types have been added
1423 if (entry.isPrimary) {
1424 mPrimaryEmailAdded = true;
1425 }
1426 }
1427 }
1428
1429 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1430 String typeField, String primaryField) {
1431 CharSequence phoneNumber = extras.getCharSequence(phoneField);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001432
1433 // Correctly handle String in typeField as TYPE_CUSTOM
1434 int phoneType = INVALID_TYPE;
1435 String customLabel = null;
1436 if(extras.get(typeField) instanceof String) {
1437 phoneType = Phones.TYPE_CUSTOM;
1438 customLabel = extras.getString(typeField);
1439 } else {
1440 phoneType = extras.getInt(typeField, INVALID_TYPE);
1441 }
1442
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001443 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1444 phoneType = DEFAULT_PHONE_TYPE;
1445 }
1446
1447 if (phoneType != INVALID_TYPE) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001448 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001449 phoneNumber.toString(), phonesUri, 0);
1450 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1451 mPhoneEntries.add(entry);
1452
1453 // Keep track of which primary types have been added
1454 if (phoneType == Phones.TYPE_MOBILE) {
1455 mMobilePhoneAdded = true;
1456 }
1457 }
1458 }
1459
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001460 /**
1461 * Removes all existing views, builds new ones for all the entries, and adds them.
1462 */
1463 private void buildViews() {
1464 // Remove existing views
1465 final LinearLayout layout = mLayout;
1466 layout.removeAllViews();
1467
The Android Open Source Project42e29b72009-02-10 15:44:04 -08001468 buildViewsForSection(layout, mPhoneEntries, R.string.listSeparatorCallNumber_edit);
1469 buildViewsForSection(layout, mEmailEntries, R.string.listSeparatorSendEmail_edit);
1470 buildViewsForSection(layout, mImEntries, R.string.listSeparatorSendIm_edit);
1471 buildViewsForSection(layout, mPostalEntries, R.string.listSeparatorMapAddress_edit);
1472 buildViewsForSection(layout, mOtherEntries, R.string.listSeparatorOtherInformation_edit);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001473 }
1474
1475
1476 /**
1477 * Builds the views for a specific section.
1478 *
1479 * @param layout the container
1480 * @param section the section to build the views for
1481 */
1482 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1483 int separatorResource) {
1484 // Build the separator if the section isn't empty
1485 if (section.size() > 0) {
1486 View separator = mInflater.inflate(R.layout.edit_separator, layout, false);
1487 TextView text = (TextView) separator.findViewById(R.id.text);
1488 text.setText(getText(separatorResource));
1489 layout.addView(separator);
1490 }
1491
1492 // Build views for the current section
1493 for (EditEntry entry : section) {
1494 entry.activity = this; // this could be null from when the state is restored
1495 if (!entry.isDeleted) {
1496 View view = buildViewForEntry(entry);
1497 layout.addView(view);
1498 }
1499 }
1500 }
1501
1502 /**
1503 * Builds a view to display an EditEntry.
1504 *
1505 * @param entry the entry to display
1506 * @return a view that will display the given entry
1507 */
1508 /* package */ View buildViewForEntry(final EditEntry entry) {
1509 // Look for any existing entered text, and save it if found
1510 if (entry.view != null && entry.syncDataWithView) {
1511 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1512 .getText().toString();
1513 if (!TextUtils.isEmpty(enteredText)) {
1514 entry.data = enteredText;
1515 }
1516 }
1517
1518 // Build a new view
1519 final ViewGroup parent = mLayout;
1520 View view;
1521
1522 if (entry.kind == Contacts.KIND_ORGANIZATION) {
1523 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
1524 } else if (isRingtoneEntry(entry)) {
1525 view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
1526 } else if (!entry.isStaticLabel) {
1527 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1528 } else {
1529 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1530 }
1531 entry.view = view;
1532
1533 // Set the entry as the tag so we can find it again later given just the view
1534 view.setTag(entry);
1535
1536 // Bind the label
1537 entry.bindLabel(this);
1538
1539 // Bind data
1540 TextView data = (TextView) view.findViewById(R.id.data);
1541 TextView data2 = (TextView) view.findViewById(R.id.data2);
The Android Open Source Project42e29b72009-02-10 15:44:04 -08001542
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001543 if (data instanceof Button) {
1544 data.setOnClickListener(this);
1545 }
1546 if (data.length() == 0) {
1547 if (entry.syncDataWithView) {
1548 // If there is already data entered don't overwrite it
1549 data.setText(entry.data);
1550 } else {
1551 fillViewData(entry);
1552 }
1553 }
1554 if (data2 != null && data2.length() == 0) {
1555 // If there is already data entered don't overwrite it
1556 data2.setText(entry.data2);
1557 }
1558 data.setHint(entry.hint);
1559 if (data2 != null) data2.setHint(entry.hint2);
1560 if (entry.lines > 1) {
1561 data.setLines(entry.lines);
1562 data.setMaxLines(entry.maxLines);
1563 if (data2 != null) {
1564 data2.setLines(entry.lines);
1565 data2.setMaxLines(entry.maxLines);
1566 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001567 }
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001568 int contentType = entry.contentType;
1569 if (contentType != EditorInfo.TYPE_NULL) {
1570 data.setInputType(contentType);
1571 if (data2 != null) {
1572 data2.setInputType(contentType);
1573 }
1574 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1575 == EditorInfo.TYPE_CLASS_PHONE) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001576 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1577 if (data2 != null) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001578 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1579 }
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001580 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001581 }
1582
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001583 // Connect listeners up to watch for changed values.
1584 if (data instanceof EditText) {
1585 data.addTextChangedListener(this);
1586 }
1587 if (data2 instanceof EditText) {
1588 data2.addTextChangedListener(this);
1589 }
1590
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001591 // Hook up the delete button
1592 View delete = view.findViewById(R.id.delete);
1593 if (delete != null) delete.setOnClickListener(this);
1594 View delete2 = view.findViewById(R.id.delete2);
1595 if (delete2 != null) delete2.setOnClickListener(this);
1596
1597 return view;
1598 }
1599
1600 private void fillViewData(final EditEntry entry) {
1601 if (isRingtoneEntry(entry)) {
1602 updateRingtoneView(entry);
1603 }
1604 }
1605
1606 /**
1607 * Handles the results from the label change picker.
1608 */
1609 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1610 EditEntry mEntry;
1611 String[] mLabels;
1612
1613 public LabelPickedListener(EditEntry entry, String[] labels) {
1614 mEntry = entry;
1615 mLabels = labels;
1616 }
1617
1618 public void onClick(DialogInterface dialog, int which) {
1619 // TODO: Use a managed dialog
1620 if (mEntry.kind != Contacts.KIND_IM) {
1621 final int type = getTypeFromLabelPosition(mLabels, which);
1622 if (type == ContactMethods.TYPE_CUSTOM) {
1623 createCustomPicker(mEntry, null);
1624 } else {
1625 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001626 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001627 }
1628 } else {
1629 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001630 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001631 }
1632 }
1633 }
1634
1635 /**
1636 * A basic structure with the data for a contact entry in the list.
1637 */
1638 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1639 // These aren't stuffed into the parcel
1640 public EditContactActivity activity;
1641 public View view;
1642
1643 // These are stuffed into the parcel
1644 public String hint;
1645 public String hint2;
1646 public String column;
1647 public String contentDirectory;
1648 public String data2;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001649 public int contentType;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001650 public int type;
1651 /**
1652 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1653 * will not be called.
1654 */
1655 public int lines = 1;
1656 public boolean isPrimary;
1657 public boolean isDeleted = false;
1658 public boolean isStaticLabel = false;
1659 public boolean syncDataWithView = true;
1660
1661 private EditEntry() {
1662 // only used by CREATOR
1663 }
1664
1665 public EditEntry(EditContactActivity activity) {
1666 this.activity = activity;
1667 }
1668
1669 public EditEntry(EditContactActivity activity, String label,
1670 int type, String data, Uri uri, long id) {
1671 this.activity = activity;
1672 this.isPrimary = false;
1673 this.label = label;
1674 this.type = type;
1675 this.data = data;
1676 this.uri = uri;
1677 this.id = id;
1678 }
1679
1680 public int describeContents() {
1681 return 0;
1682 }
1683
1684 public void writeToParcel(Parcel parcel, int flags) {
1685 // Make sure to read data from the input field, if anything is entered
1686 data = getData();
1687
1688 // Write in our own fields.
1689 parcel.writeString(hint);
1690 parcel.writeString(hint2);
1691 parcel.writeString(column);
1692 parcel.writeString(contentDirectory);
1693 parcel.writeString(data2);
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001694 parcel.writeInt(contentType);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001695 parcel.writeInt(type);
1696 parcel.writeInt(lines);
1697 parcel.writeInt(isPrimary ? 1 : 0);
1698 parcel.writeInt(isDeleted ? 1 : 0);
1699 parcel.writeInt(isStaticLabel ? 1 : 0);
1700 parcel.writeInt(syncDataWithView ? 1 : 0);
1701
1702 // Write in the fields from Entry
1703 super.writeToParcel(parcel);
1704 }
1705
1706 public static final Parcelable.Creator<EditEntry> CREATOR =
1707 new Parcelable.Creator<EditEntry>() {
1708 public EditEntry createFromParcel(Parcel in) {
1709 EditEntry entry = new EditEntry();
1710
1711 // Read out our own fields
1712 entry.hint = in.readString();
1713 entry.hint2 = in.readString();
1714 entry.column = in.readString();
1715 entry.contentDirectory = in.readString();
1716 entry.data2 = in.readString();
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001717 entry.contentType = in.readInt();
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001718 entry.type = in.readInt();
1719 entry.lines = in.readInt();
1720 entry.isPrimary = in.readInt() == 1;
1721 entry.isDeleted = in.readInt() == 1;
1722 entry.isStaticLabel = in.readInt() == 1;
1723 entry.syncDataWithView = in.readInt() == 1;
1724
1725 // Read out the fields from Entry
1726 entry.readFromParcel(in);
1727
1728 return entry;
1729 }
1730
1731 public EditEntry[] newArray(int size) {
1732 return new EditEntry[size];
1733 }
1734 };
1735
1736 public void setLabel(Context context, int typeIn, String labelIn) {
1737 type = typeIn;
1738 label = labelIn;
1739 if (view != null) {
1740 bindLabel(context);
1741 }
1742 }
1743
1744 public void bindLabel(Context context) {
1745 TextView v = (TextView) view.findViewById(R.id.label);
1746 if (isStaticLabel) {
1747 v.setText(label);
1748 return;
1749 }
1750
1751 switch (kind) {
1752 case Contacts.KIND_PHONE: {
1753 v.setText(Phones.getDisplayLabel(context, type, label));
1754 break;
1755 }
1756
1757 case Contacts.KIND_IM: {
1758 v.setText(getLabelsForKind(activity, kind)[type]);
1759 break;
1760 }
1761
1762 case Contacts.KIND_ORGANIZATION: {
1763 v.setText(Organizations.getDisplayLabel(activity, type, label));
1764 break;
1765 }
1766
1767 default: {
1768 v.setText(Contacts.ContactMethods.getDisplayLabel(context, kind, type, label));
1769 if (kind == Contacts.KIND_POSTAL) {
1770 v.setMaxLines(3);
1771 }
1772 break;
1773 }
1774 }
1775 v.setOnClickListener(activity);
1776 }
1777
1778 /**
1779 * Returns the data for the entry
1780 * @return the data for the entry
1781 */
1782 public String getData() {
1783 if (view != null && syncDataWithView) {
1784 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1785 if (text != null) {
1786 return text.toString();
1787 }
1788 }
1789
1790 if (data != null) {
1791 return data.toString();
1792 }
1793
1794 return null;
1795 }
1796
1797 /**
1798 * Dumps the entry into a HashMap suitable for passing to the database.
1799 *
1800 * @param values the HashMap to fill in.
1801 * @return true if the value should be saved, false otherwise
1802 */
1803 public boolean toValues(ContentValues values) {
1804 boolean success = false;
1805 String labelString = null;
1806 // Save the type and label
1807 if (view != null) {
1808 // Read the possibly updated label from the text field
1809 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1810 }
1811 switch (kind) {
1812 case Contacts.KIND_PHONE:
1813 if (type != Phones.TYPE_CUSTOM) {
1814 labelString = null;
1815 }
1816 values.put(Phones.LABEL, labelString);
1817 values.put(Phones.TYPE, type);
1818 break;
1819
1820 case Contacts.KIND_EMAIL:
1821 if (type != ContactMethods.TYPE_CUSTOM) {
1822 labelString = null;
1823 }
1824 values.put(ContactMethods.LABEL, labelString);
1825 values.put(ContactMethods.KIND, kind);
1826 values.put(ContactMethods.TYPE, type);
1827 break;
1828
1829 case Contacts.KIND_IM:
1830 values.put(ContactMethods.KIND, kind);
1831 values.put(ContactMethods.TYPE, ContactMethods.TYPE_OTHER);
1832 values.putNull(ContactMethods.LABEL);
1833 if (type != -1) {
1834 values.put(ContactMethods.AUX_DATA,
1835 ContactMethods.encodePredefinedImProtocol(type));
1836 } else {
1837 values.put(ContactMethods.AUX_DATA,
1838 ContactMethods.encodeCustomImProtocol(label.toString()));
1839 }
1840 break;
1841
1842 case Contacts.KIND_POSTAL:
1843 if (type != ContactMethods.TYPE_CUSTOM) {
1844 labelString = null;
1845 }
1846 values.put(ContactMethods.LABEL, labelString);
1847 values.put(ContactMethods.KIND, kind);
1848 values.put(ContactMethods.TYPE, type);
1849 break;
1850
1851 case Contacts.KIND_ORGANIZATION:
1852 if (type != ContactMethods.TYPE_CUSTOM) {
1853 labelString = null;
1854 }
1855 values.put(ContactMethods.LABEL, labelString);
1856 values.put(ContactMethods.TYPE, type);
1857 // Save the title
1858 if (view != null) {
1859 // Read the possibly updated data from the text field
1860 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
1861 }
1862 if (!TextUtils.isGraphic(data2)) {
1863 values.putNull(Organizations.TITLE);
1864 } else {
1865 values.put(Organizations.TITLE, data2.toString());
1866 success = true;
1867 }
1868 break;
1869
1870 default:
1871 Log.w(TAG, "unknown kind " + kind);
1872 values.put(ContactMethods.LABEL, labelString);
1873 values.put(ContactMethods.KIND, kind);
1874 values.put(ContactMethods.TYPE, type);
1875 break;
1876 }
1877
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001878 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
1879 // ContentProvider will try finding a new primary when setting to false, meaning
1880 // it's possible to lose primary altogether as we walk down the list. If this editor
1881 // implements editing of primaries in the future, this will need to be revisited.
1882 if (isPrimary) {
1883 values.put(ContactMethods.ISPRIMARY, 1);
1884 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001885
1886 // Save the data
1887 if (view != null && syncDataWithView) {
1888 // Read the possibly updated data from the text field
1889 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
1890 }
1891 if (!TextUtils.isGraphic(data)) {
1892 values.putNull(column);
1893 return success;
1894 } else {
1895 values.put(column, data.toString());
1896 return true;
1897 }
1898 }
1899
1900 /**
1901 * Create a new empty organization entry
1902 */
1903 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1904 Uri uri, int type) {
1905 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
1906 }
1907
1908 /**
1909 * Create a new company entry with the given data.
1910 */
1911 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1912 String label, int type, String company, String title, Uri uri, long id) {
1913 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
1914 entry.hint = activity.getString(R.string.ghostData_company);
1915 entry.hint2 = activity.getString(R.string.ghostData_title);
1916 entry.data2 = title;
1917 entry.column = Organizations.COMPANY;
1918 entry.contentDirectory = Organizations.CONTENT_DIRECTORY;
1919 entry.kind = Contacts.KIND_ORGANIZATION;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001920 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
1921 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001922 return entry;
1923 }
1924
1925 /**
1926 * Create a new notes entry with the given data.
1927 */
1928 public static final EditEntry newNotesEntry(EditContactActivity activity,
1929 String data, Uri uri) {
1930 EditEntry entry = new EditEntry(activity);
1931 entry.label = activity.getString(R.string.label_notes);
1932 entry.hint = activity.getString(R.string.ghostData_notes);
1933 entry.data = data;
1934 entry.uri = uri;
1935 entry.column = People.NOTES;
1936 entry.maxLines = 10;
1937 entry.lines = 2;
1938 entry.id = 0;
1939 entry.kind = KIND_CONTACT;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001940 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
1941 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
1942 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001943 entry.isStaticLabel = true;
1944 return entry;
1945 }
1946
1947 /**
1948 * Create a new ringtone entry with the given data.
1949 */
1950 public static final EditEntry newRingtoneEntry(EditContactActivity activity,
1951 String data, Uri uri) {
1952 EditEntry entry = new EditEntry(activity);
1953 entry.label = activity.getString(R.string.label_ringtone);
1954 entry.data = data;
1955 entry.uri = uri;
1956 entry.column = People.CUSTOM_RINGTONE;
1957 entry.kind = KIND_CONTACT;
1958 entry.isStaticLabel = true;
1959 entry.syncDataWithView = false;
1960 entry.lines = -1;
1961 return entry;
1962 }
1963
1964 /**
1965 * Create a new empty email entry
1966 */
1967 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1968 Uri uri, int type) {
1969 return newPhoneEntry(activity, null, type, null, uri, 0);
1970 }
1971
1972 /**
1973 * Create a new phone entry with the given data.
1974 */
1975 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1976 String label, int type, String data, Uri uri,
1977 long id) {
1978 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1979 entry.hint = activity.getString(R.string.ghostData_phone);
1980 entry.column = People.Phones.NUMBER;
1981 entry.contentDirectory = People.Phones.CONTENT_DIRECTORY;
1982 entry.kind = Contacts.KIND_PHONE;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001983 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001984 return entry;
1985 }
1986
1987 /**
1988 * Create a new empty email entry
1989 */
1990 public static final EditEntry newEmailEntry(EditContactActivity activity,
1991 Uri uri, int type) {
1992 return newEmailEntry(activity, null, type, null, uri, 0);
1993 }
1994
1995 /**
1996 * Create a new email entry with the given data.
1997 */
1998 public static final EditEntry newEmailEntry(EditContactActivity activity,
1999 String label, int type, String data, Uri uri,
2000 long id) {
2001 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2002 entry.hint = activity.getString(R.string.ghostData_email);
2003 entry.column = ContactMethods.DATA;
2004 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2005 entry.kind = Contacts.KIND_EMAIL;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08002006 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2007 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002008 return entry;
2009 }
2010
2011 /**
2012 * Create a new empty postal address entry
2013 */
2014 public static final EditEntry newPostalEntry(EditContactActivity activity,
2015 Uri uri, int type) {
2016 return newPostalEntry(activity, null, type, null, uri, 0);
2017 }
2018
2019 /**
2020 * Create a new postal address entry with the given data.
2021 *
2022 * @param label label for the item, from the db not the display label
2023 * @param type the type of postal address
2024 * @param data the starting data for the entry, may be null
2025 * @param uri the uri for the entry if it already exists, may be null
2026 * @param id the id for the entry if it already exists, 0 it it doesn't
2027 * @return the new EditEntry
2028 */
2029 public static final EditEntry newPostalEntry(EditContactActivity activity,
2030 String label, int type, String data, Uri uri, long id) {
2031 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2032 entry.hint = activity.getString(R.string.ghostData_postal);
2033 entry.column = ContactMethods.DATA;
2034 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2035 entry.kind = Contacts.KIND_POSTAL;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08002036 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2037 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2038 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2039 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002040 entry.maxLines = 4;
2041 entry.lines = 2;
2042 return entry;
2043 }
2044
2045 /**
2046 * Create a new postal address entry with the given data.
2047 *
2048 * @param label label for the item, from the db not the display label
2049 * @param protocol the type used
2050 * @param data the starting data for the entry, may be null
2051 * @param uri the uri for the entry if it already exists, may be null
2052 * @param id the id for the entry if it already exists, 0 it it doesn't
2053 * @return the new EditEntry
2054 */
2055 public static final EditEntry newImEntry(EditContactActivity activity,
2056 String label, int protocol, String data, Uri uri, long id) {
2057 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2058 entry.hint = activity.getString(R.string.ghostData_im);
2059 entry.column = ContactMethods.DATA;
2060 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2061 entry.kind = Contacts.KIND_IM;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08002062 entry.contentType = EditorInfo.TYPE_CLASS_TEXT;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002063 return entry;
2064 }
2065 }
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08002066
2067 public void afterTextChanged(Editable s) {
2068 // Someone edited a text field, so assume this contact is dirty.
2069 mContactChanged = true;
2070 }
2071
2072 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2073 // Do nothing; editing handled by afterTextChanged()
2074 }
2075
2076 public void onTextChanged(CharSequence s, int start, int before, int count) {
2077 // Do nothing; editing handled by afterTextChanged()
2078 }
2079
2080 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
2081 // Someone changed a checkbox, so assume this contact is dirty.
2082 mContactChanged = true;
2083 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002084}