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