blob: c12074e679adf79ca96651206032d1cc597f3fad [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
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070019import static com.android.contacts.ContactEntryAdapter.CONTACT_CUSTOM_RINGTONE_COLUMN;
20import static com.android.contacts.ContactEntryAdapter.CONTACT_NAME_COLUMN;
21import static com.android.contacts.ContactEntryAdapter.CONTACT_NOTES_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
23import static com.android.contacts.ContactEntryAdapter.CONTACT_SEND_TO_VOICEMAIL_COLUMN;
The Android Open Source Project9cb63a52009-01-09 17:51:25 -080024import static com.android.contacts.ContactEntryAdapter.CONTACT_PHONETIC_NAME_COLUMN;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070025import static com.android.contacts.ContactEntryAdapter.METHODS_AUX_DATA_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.METHODS_DATA_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.METHODS_ID_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.METHODS_ISPRIMARY_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.METHODS_KIND_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.METHODS_LABEL_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.METHODS_PROJECTION;
32import static com.android.contacts.ContactEntryAdapter.METHODS_TYPE_COLUMN;
33import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_COMPANY_COLUMN;
34import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ID_COLUMN;
35import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ISPRIMARY_COLUMN;
36import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_LABEL_COLUMN;
37import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_PROJECTION;
38import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TITLE_COLUMN;
39import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TYPE_COLUMN;
40import static com.android.contacts.ContactEntryAdapter.PHONES_ID_COLUMN;
41import static com.android.contacts.ContactEntryAdapter.PHONES_ISPRIMARY_COLUMN;
42import static com.android.contacts.ContactEntryAdapter.PHONES_LABEL_COLUMN;
43import static com.android.contacts.ContactEntryAdapter.PHONES_NUMBER_COLUMN;
44import static com.android.contacts.ContactEntryAdapter.PHONES_PROJECTION;
45import static com.android.contacts.ContactEntryAdapter.PHONES_TYPE_COLUMN;
46
47import android.app.Activity;
48import android.app.AlertDialog;
49import android.app.Dialog;
50import android.content.ActivityNotFoundException;
51import android.content.ContentResolver;
52import android.content.ContentUris;
53import android.content.ContentValues;
54import android.content.Context;
55import android.content.DialogInterface;
56import android.content.Intent;
57import android.content.SharedPreferences;
The Android Open Source Project3b024222009-02-13 12:57:53 -080058import android.content.res.ColorStateList;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070059import android.content.res.Resources;
60import android.database.Cursor;
61import android.graphics.Bitmap;
62import android.media.Ringtone;
63import android.media.RingtoneManager;
64import android.net.Uri;
65import android.os.Bundle;
66import android.os.Parcel;
67import android.os.Parcelable;
68import android.preference.PreferenceManager;
69import android.provider.Contacts;
70import android.provider.Contacts.ContactMethods;
71import android.provider.Contacts.Intents.Insert;
72import android.provider.Contacts.Organizations;
73import android.provider.Contacts.People;
74import android.provider.Contacts.Phones;
75import android.telephony.PhoneNumberFormattingTextWatcher;
The Android Open Source Projectad5ad712009-01-15 16:12:13 -080076import android.text.Editable;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070077import android.text.TextUtils;
The Android Open Source Projectad5ad712009-01-15 16:12:13 -080078import android.text.TextWatcher;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070079import android.text.method.TextKeyListener;
80import android.text.method.TextKeyListener.Capitalize;
81import android.util.Log;
The Android Open Source Project3b024222009-02-13 12:57:53 -080082import android.util.SparseBooleanArray;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070083import 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;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070094import android.widget.ImageView;
95import android.widget.LinearLayout;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -070096import android.widget.TextView;
97import android.widget.Toast;
98
99import java.io.ByteArrayOutputStream;
100import java.util.ArrayList;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700101
102/**
103 * Activity for editing or inserting a contact. Note that if the contact data changes in the
104 * background while this activity is running, the updates will be overwritten.
105 */
106public final class EditContactActivity extends Activity implements View.OnClickListener,
The Android Open Source Project3b024222009-02-13 12:57:53 -0800107 TextWatcher, View.OnFocusChangeListener {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700108 private static final String TAG = "EditContactActivity";
109
110 private static final int STATE_UNKNOWN = 0;
111 /** Editing an existing contact */
112 private static final int STATE_EDIT = 1;
113 /** The full insert mode */
114 private static final int STATE_INSERT = 2;
115
116 /** The launch code when picking a photo and the raw data is returned */
117 private static final int PHOTO_PICKED_WITH_DATA = 3021;
118
119 /** The launch code when picking a ringtone */
120 private static final int RINGTONE_PICKED = 3023;
121
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700122 // These correspond to the string array in resources for picker "other" items
123 final static int OTHER_ORGANIZATION = 0;
124 final static int OTHER_NOTE = 1;
125
126 // Dialog IDs
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700127 final static int DELETE_CONFIRMATION_DIALOG = 2;
The Android Open Source Project3b024222009-02-13 12:57:53 -0800128
129 // Section IDs
130 final static int SECTION_PHONES = 3;
131 final static int SECTION_EMAIL = 4;
132 final static int SECTION_IM = 5;
133 final static int SECTION_POSTAL = 6;
134 final static int SECTION_ORG = 7;
135 final static int SECTION_NOTE = 8;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700136
137 // Menu item IDs
138 public static final int MENU_ITEM_SAVE = 1;
139 public static final int MENU_ITEM_DONT_SAVE = 2;
140 public static final int MENU_ITEM_DELETE = 3;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700141 public static final int MENU_ITEM_PHOTO = 6;
142
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700143 /** Used to represent an invalid type for a contact entry */
144 private static final int INVALID_TYPE = -1;
145
146 /** The default type for a phone that is added via an intent */
147 private static final int DEFAULT_PHONE_TYPE = Phones.TYPE_MOBILE;
148
149 /** The default type for an email that is added via an intent */
150 private static final int DEFAULT_EMAIL_TYPE = ContactMethods.TYPE_HOME;
151
152 /** The default type for a postal address that is added via an intent */
153 private static final int DEFAULT_POSTAL_TYPE = ContactMethods.TYPE_HOME;
154
155 private int mState; // saved across instances
156 private boolean mInsert; // saved across instances
157 private Uri mUri; // saved across instances
158 /** In insert mode this is the photo */
159 private Bitmap mPhoto; // saved across instances
160 private boolean mPhotoChanged = false; // saved across instances
161
162 private EditText mNameView;
163 private ImageView mPhotoImageView;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700164 private LinearLayout mLayout;
165 private LayoutInflater mInflater;
166 private MenuItem mPhotoMenuItem;
167 private boolean mPhotoPresent = false;
The Android Open Source Project42b987b2009-01-22 00:13:44 -0800168 private EditText mPhoneticNameView; // invisible in some locales, but always present
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700169
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800170 /** Flag marking this contact as changed, meaning we should write changes back. */
171 private boolean mContactChanged = false;
172
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700173 // These are accessed by inner classes. They're package scoped to make access more efficient.
174 /* package */ ContentResolver mResolver;
175 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
176 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
177 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
178 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
The Android Open Source Project3b024222009-02-13 12:57:53 -0800179 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
180 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700181 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
182 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
The Android Open Source Project3b024222009-02-13 12:57:53 -0800183
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700184 /* package */ static final int MSG_DELETE = 1;
185 /* package */ static final int MSG_CHANGE_LABEL = 2;
186 /* package */ static final int MSG_ADD_PHONE = 3;
187 /* package */ static final int MSG_ADD_EMAIL = 4;
188 /* package */ static final int MSG_ADD_POSTAL = 5;
189
The Android Open Source Project3b024222009-02-13 12:57:53 -0800190 private static final int[] TYPE_PRECEDENCE_PHONES = new int[] {
191 Phones.TYPE_MOBILE, Phones.TYPE_HOME, Phones.TYPE_WORK, Phones.TYPE_OTHER
192 };
193 private static final int[] TYPE_PRECEDENCE_METHODS = new int[] {
194 ContactMethods.TYPE_HOME, ContactMethods.TYPE_WORK, ContactMethods.TYPE_OTHER
195 };
196 private static final int[] TYPE_PRECEDENCE_IM = new int[] {
197 ContactMethods.PROTOCOL_GOOGLE_TALK, ContactMethods.PROTOCOL_AIM,
198 ContactMethods.PROTOCOL_MSN, ContactMethods.PROTOCOL_YAHOO,
199 ContactMethods.PROTOCOL_JABBER
200 };
201 private static final int[] TYPE_PRECEDENCE_ORG = new int[] {
202 Organizations.TYPE_WORK, Organizations.TYPE_OTHER
203 };
204
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700205 public void onClick(View v) {
206 switch (v.getId()) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700207 case R.id.photoImage: {
208 doPickPhotoAction();
209 break;
210 }
The Android Open Source Project3b024222009-02-13 12:57:53 -0800211
212 case R.id.checkable: {
213 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
214 checkBox.toggle();
215
216 EditEntry entry = findEntryForView(v);
217 entry.data = checkBox.isChecked() ? "1" : "0";
218
219 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700220 break;
The Android Open Source Project3b024222009-02-13 12:57:53 -0800221 }
222
223 case R.id.entry_ringtone: {
224 EditEntry entry = findEntryForView(v);
225 doPickRingtone(entry);
226 break;
227 }
228
229 case R.id.separator: {
230 // Someone clicked on a section header, so handle add action
231 int sectionType = (Integer) v.getTag();
232 doAddAction(sectionType);
233 break;
234 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700235
236 case R.id.saveButton:
237 doSaveAction();
238 break;
239
240 case R.id.discardButton:
241 doRevertAction();
242 break;
243
The Android Open Source Project3b024222009-02-13 12:57:53 -0800244 case R.id.delete: {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700245 EditEntry entry = findEntryForView(v);
246 if (entry != null) {
247 // Clear the text and hide the view so it gets saved properly
248 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
249 entry.view.setVisibility(View.GONE);
250 entry.isDeleted = true;
251 }
The Android Open Source Project3b024222009-02-13 12:57:53 -0800252
253 // Force rebuild of views because section headers might need to change
254 buildViews();
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700255 break;
256 }
257
258 case R.id.label: {
259 EditEntry entry = findEntryForView(v);
260 if (entry != null) {
261 String[] labels = getLabelsForKind(this, entry.kind);
262 LabelPickedListener listener = new LabelPickedListener(entry, labels);
263 new AlertDialog.Builder(EditContactActivity.this)
264 .setItems(labels, listener)
265 .setTitle(R.string.selectLabel)
266 .show();
267 }
268 break;
269 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700270 }
271 }
272
273 private void setPhotoPresent(boolean present) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700274 mPhotoPresent = present;
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800275
276 // Correctly scale the contact photo if present, otherwise just center
277 // the photo placeholder icon.
278 if (mPhotoPresent) {
279 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
280 } else {
281 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
282 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
283 }
284
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700285 if (mPhotoMenuItem != null) {
286 if (present) {
287 mPhotoMenuItem.setTitle(R.string.removePicture);
288 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
289 } else {
290 mPhotoMenuItem.setTitle(R.string.addPicture);
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800291 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700292 }
293 }
294 }
295
296 private EditEntry findEntryForView(View v) {
297 // Try to find the entry for this view
298 EditEntry entry = null;
299 do {
300 Object tag = v.getTag();
301 if (tag != null && tag instanceof EditEntry) {
302 entry = (EditEntry) tag;
303 break;
304 } else {
305 ViewParent parent = v.getParent();
306 if (parent != null && parent instanceof View) {
307 v = (View) parent;
308 } else {
309 v = null;
310 }
311 }
312 } while (v != null);
313 return entry;
314 }
315
316 private DialogInterface.OnClickListener mDeleteContactDialogListener =
317 new DialogInterface.OnClickListener() {
318 public void onClick(DialogInterface dialog, int button) {
319 mResolver.delete(mUri, null, null);
320 finish();
321 }
322 };
323
324 private boolean mMobilePhoneAdded = false;
325 private boolean mPrimaryEmailAdded = false;
326
327 @Override
328 protected void onCreate(Bundle icicle) {
329 super.onCreate(icicle);
330
331 mResolver = getContentResolver();
332
333 // Build the list of sections
334 setupSections();
335
336 // Load the UI
337 setContentView(R.layout.edit_contact);
338 mLayout = (LinearLayout) findViewById(R.id.list);
339 mNameView = (EditText) findViewById(R.id.name);
340 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
341 mPhotoImageView.setOnClickListener(this);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800342 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
The Android Open Source Project3b024222009-02-13 12:57:53 -0800343
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800344 // Setup the bottom buttons
The Android Open Source Project3b024222009-02-13 12:57:53 -0800345 View view = findViewById(R.id.saveButton);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700346 view.setOnClickListener(this);
347 view = findViewById(R.id.discardButton);
348 view.setOnClickListener(this);
349
350 mInflater = getLayoutInflater();
351
352 // Resolve the intent
353 mState = STATE_UNKNOWN;
354 Intent intent = getIntent();
355 String action = intent.getAction();
356 mUri = intent.getData();
357 if (mUri != null) {
358 if (action.equals(Intent.ACTION_EDIT)) {
359 if (icicle == null) {
360 // Build the entries & views
361 buildEntriesForEdit(getIntent().getExtras());
362 buildViews();
363 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800364 setTitle(R.string.editContact_title_edit);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700365 mState = STATE_EDIT;
366 } else if (action.equals(Intent.ACTION_INSERT)) {
367 if (icicle == null) {
368 // Build the entries & views
369 buildEntriesForInsert(getIntent().getExtras());
370 buildViews();
371 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800372 setTitle(R.string.editContact_title_insert);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700373 mState = STATE_INSERT;
374 mInsert = true;
375 }
376 }
377
378 if (mState == STATE_UNKNOWN) {
379 Log.e(TAG, "Cannot resolve intent: " + intent);
380 finish();
381 return;
382 }
383
384 if (mState == STATE_EDIT) {
385 setTitle(getResources().getText(R.string.editContact_title_edit));
386 } else {
387 setTitle(getResources().getText(R.string.editContact_title_insert));
388 }
389 }
390
391 private void setupSections() {
392 mSections.add(mPhoneEntries);
393 mSections.add(mEmailEntries);
394 mSections.add(mImEntries);
395 mSections.add(mPostalEntries);
The Android Open Source Project3b024222009-02-13 12:57:53 -0800396 mSections.add(mOrgEntries);
397 mSections.add(mNoteEntries);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700398 mSections.add(mOtherEntries);
399 }
400
401 @Override
402 protected void onSaveInstanceState(Bundle outState) {
403 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
404 outState.putParcelableArrayList("emailEntries", mEmailEntries);
405 outState.putParcelableArrayList("imEntries", mImEntries);
406 outState.putParcelableArrayList("postalEntries", mPostalEntries);
The Android Open Source Project3b024222009-02-13 12:57:53 -0800407 outState.putParcelableArrayList("orgEntries", mOrgEntries);
408 outState.putParcelableArrayList("noteEntries", mNoteEntries);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700409 outState.putParcelableArrayList("otherEntries", mOtherEntries);
410 outState.putInt("state", mState);
411 outState.putBoolean("insert", mInsert);
412 outState.putParcelable("uri", mUri);
413 outState.putString("name", mNameView.getText().toString());
414 outState.putParcelable("photo", mPhoto);
415 outState.putBoolean("photoChanged", mPhotoChanged);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800416 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800417 outState.putBoolean("contactChanged", mContactChanged);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700418 }
419
420 @Override
421 protected void onRestoreInstanceState(Bundle inState) {
422 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
423 mEmailEntries = inState.getParcelableArrayList("emailEntries");
424 mImEntries = inState.getParcelableArrayList("imEntries");
425 mPostalEntries = inState.getParcelableArrayList("postalEntries");
The Android Open Source Project3b024222009-02-13 12:57:53 -0800426 mOrgEntries = inState.getParcelableArrayList("orgEntries");
427 mNoteEntries = inState.getParcelableArrayList("noteEntries");
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700428 mOtherEntries = inState.getParcelableArrayList("otherEntries");
429 setupSections();
430
431 mState = inState.getInt("state");
432 mInsert = inState.getBoolean("insert");
433 mUri = inState.getParcelable("uri");
434 mNameView.setText(inState.getString("name"));
435 mPhoto = inState.getParcelable("photo");
436 if (mPhoto != null) {
437 mPhotoImageView.setImageBitmap(mPhoto);
438 setPhotoPresent(true);
439 } else {
440 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
441 setPhotoPresent(false);
442 }
443 mPhotoChanged = inState.getBoolean("photoChanged");
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800444 mPhoneticNameView.setText(inState.getString("phoneticName"));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800445 mContactChanged = inState.getBoolean("contactChanged");
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800446
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700447 // Now that everything is restored, build the view
448 buildViews();
449 }
450
451 @Override
452 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
453 if (resultCode != RESULT_OK) {
454 return;
455 }
456
457 switch (requestCode) {
458 case PHOTO_PICKED_WITH_DATA: {
459 final Bundle extras = data.getExtras();
460 if (extras != null) {
461 Bitmap photo = extras.getParcelable("data");
462 mPhoto = photo;
463 mPhotoChanged = true;
464 mPhotoImageView.setImageBitmap(photo);
465 setPhotoPresent(true);
466 }
467 break;
468 }
469
470 case RINGTONE_PICKED: {
471 Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
472 handleRingtonePicked(pickedUri);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800473 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700474 break;
475 }
476 }
477 }
478
479 @Override
480 public boolean onKeyDown(int keyCode, KeyEvent event) {
481 switch (keyCode) {
482 case KeyEvent.KEYCODE_BACK: {
483 doSaveAction();
484 return true;
485 }
486 }
487 return super.onKeyDown(keyCode, event);
488 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800489
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700490 @Override
491 public boolean onCreateOptionsMenu(Menu menu) {
492 super.onCreateOptionsMenu(menu);
493 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
494 .setIcon(android.R.drawable.ic_menu_save)
495 .setAlphabeticShortcut('\n');
496 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
497 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
498 .setAlphabeticShortcut('q');
499 if (!mInsert) {
500 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
501 .setIcon(android.R.drawable.ic_menu_delete);
502 }
503
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700504 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
505 // Updates the state of the menu item
506 setPhotoPresent(mPhotoPresent);
507
508 return true;
509 }
510
511 @Override
512 public boolean onOptionsItemSelected(MenuItem item) {
513 switch (item.getItemId()) {
514 case MENU_ITEM_SAVE:
515 doSaveAction();
516 return true;
517
518 case MENU_ITEM_DONT_SAVE:
519 doRevertAction();
520 return true;
521
522 case MENU_ITEM_DELETE:
523 // Get confirmation
524 showDialog(DELETE_CONFIRMATION_DIALOG);
525 return true;
526
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700527 case MENU_ITEM_PHOTO:
528 if (!mPhotoPresent) {
529 doPickPhotoAction();
530 } else {
531 doRemovePhotoAction();
532 }
533 return true;
534 }
535
536 return false;
537 }
The Android Open Source Project3b024222009-02-13 12:57:53 -0800538
539 /**
540 * Try guessing the next-best type of {@link EditEntry} to insert into the
541 * given list. We walk down the precedence list until we find a type that
542 * doesn't exist yet, or default to the lowest ranking type.
543 */
544 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
545 // Keep track of the types we've seen already
546 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
547 for (int i = entries.size() - 1; i >= 0; i--) {
548 EditEntry entry = entries.get(i);
549 if (!entry.isDeleted) {
550 existAlready.put(entry.type, true);
551 }
552 }
553
554 // Pick the first item we haven't seen
555 for (int type : precedenceList) {
556 if (!existAlready.get(type, false)) {
557 return type;
558 }
559 }
560
561 // Otherwise default to last item
562 return precedenceList[precedenceList.length - 1];
563 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700564
The Android Open Source Project3b024222009-02-13 12:57:53 -0800565 private void doAddAction(int sectionType) {
566 EditEntry entry = null;
567 switch (sectionType) {
568 case SECTION_PHONES: {
569 // Try figuring out which type to insert next
570 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
571 entry = EditEntry.newPhoneEntry(EditContactActivity.this,
572 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
573 nextType);
574 mPhoneEntries.add(entry);
575 break;
576 }
577 case SECTION_EMAIL: {
578 // Try figuring out which type to insert next
579 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_METHODS);
580 entry = EditEntry.newEmailEntry(EditContactActivity.this,
581 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
582 nextType);
583 mEmailEntries.add(entry);
584 break;
585 }
586 case SECTION_IM: {
587 // Try figuring out which type to insert next
588 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
589 entry = EditEntry.newImEntry(EditContactActivity.this,
590 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
591 nextType);
592 mImEntries.add(entry);
593 break;
594 }
595 case SECTION_POSTAL: {
596 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_METHODS);
597 entry = EditEntry.newPostalEntry(EditContactActivity.this,
598 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
599 nextType);
600 mPostalEntries.add(entry);
601 break;
602 }
603 case SECTION_ORG: {
604 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
605 entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
606 Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
607 nextType);
608 mOrgEntries.add(entry);
609 break;
610 }
611 case SECTION_NOTE: {
612 entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
613 mNoteEntries.add(entry);
614 break;
615 }
616 }
617
618 // Rebuild the views if needed
619 if (entry != null) {
620 buildViews();
621 mContactChanged = true;
622
623 View dataView = entry.view.findViewById(R.id.data);
624 if (dataView == null) {
625 entry.view.requestFocus();
626 } else {
627 dataView.requestFocus();
628 }
629 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700630 }
631
632 private void doRevertAction() {
633 finish();
634 }
635
636 private void doPickPhotoAction() {
637 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
638 // TODO: get these values from constants somewhere
639 intent.setType("image/*");
640 intent.putExtra("crop", "true");
641 intent.putExtra("aspectX", 1);
642 intent.putExtra("aspectY", 1);
643 intent.putExtra("outputX", 96);
644 intent.putExtra("outputY", 96);
645 try {
646 intent.putExtra("return-data", true);
647 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
648 } catch (ActivityNotFoundException e) {
649 new AlertDialog.Builder(EditContactActivity.this)
650 .setTitle(R.string.errorDialogTitle)
651 .setMessage(R.string.photoPickerNotFoundText)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800652 .setPositiveButton(android.R.string.ok, null)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700653 .show();
654 }
655 }
656
657 private void doRemovePhotoAction() {
658 mPhoto = null;
659 mPhotoChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700660 setPhotoPresent(false);
661 }
662
663 private void doPickRingtone(EditEntry entry) {
664 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
665 // Allow user to pick 'Default'
666 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
667 // Show only ringtones
668 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
669 // Don't show 'Silent'
670 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800671
672 Uri ringtoneUri;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700673 if (entry.data != null) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800674 ringtoneUri = Uri.parse(entry.data);
675 } else {
676 // Otherwise pick default ringtone Uri so that something is selected.
677 ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700678 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800679
680 // Put checkmark next to the current ringtone for this contact
681 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700682 // Launch!
683 startActivityForResult(intent, RINGTONE_PICKED);
684 }
685
686 private void handleRingtonePicked(Uri pickedUri) {
The Android Open Source Project3b024222009-02-13 12:57:53 -0800687 EditEntry entry = getOtherEntry(People.CUSTOM_RINGTONE);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700688 if (entry == null) {
689 Log.w(TAG, "Ringtone picked but could not find ringtone entry");
690 return;
691 }
692
693 if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
694 entry.data = null;
695 } else {
696 entry.data = pickedUri.toString();
697 }
698
699 updateRingtoneView(entry);
700 }
701
702 private void updateRingtoneView(EditEntry entry) {
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800703 String ringtoneName;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700704 if (entry.data == null) {
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800705 ringtoneName = getString(R.string.default_ringtone);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700706 } else {
707 Uri ringtoneUri = Uri.parse(entry.data);
708 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
709 if (ringtone == null) {
710 Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
711 return;
712 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800713 ringtoneName = ringtone.getTitle(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700714 }
The Android Open Source Project42e29b72009-02-10 15:44:04 -0800715
The Android Open Source Project3b024222009-02-13 12:57:53 -0800716 updateDataView(entry, ringtoneName);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700717 }
718
719 private void updateDataView(EditEntry entry, String text) {
720 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
721 dataView.setText(text);
722 }
723
724 @Override
725 protected Dialog onCreateDialog(int id) {
726 switch (id) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700727 case DELETE_CONFIRMATION_DIALOG:
728 return new AlertDialog.Builder(EditContactActivity.this)
729 .setTitle(R.string.deleteConfirmation_title)
730 .setIcon(android.R.drawable.ic_dialog_alert)
731 .setMessage(R.string.deleteConfirmation)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800732 .setNegativeButton(android.R.string.cancel, null)
733 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700734 .setCancelable(false)
735 .create();
736 }
737 return super.onCreateDialog(id);
738 }
739
740 static String[] getLabelsForKind(Context context, int kind) {
741 final Resources resources = context.getResources();
742 switch (kind) {
743 case Contacts.KIND_PHONE:
744 return resources.getStringArray(android.R.array.phoneTypes);
745 case Contacts.KIND_EMAIL:
746 return resources.getStringArray(android.R.array.emailAddressTypes);
747 case Contacts.KIND_POSTAL:
748 return resources.getStringArray(android.R.array.postalAddressTypes);
749 case Contacts.KIND_IM:
750 return resources.getStringArray(android.R.array.imProtocols);
751 case Contacts.KIND_ORGANIZATION:
752 return resources.getStringArray(android.R.array.organizationTypes);
753 case EditEntry.KIND_CONTACT:
754 return resources.getStringArray(R.array.otherLabels);
755 }
756 return null;
757 }
758
759 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
760 // In the UI Custom... comes last, but it is uses the constant 0
761 // so it is in the same location across the various kinds. Fix up the
762 // position to a valid type here.
763 if (labelPosition == labels.length - 1) {
764 return ContactMethods.TYPE_CUSTOM;
765 } else {
766 return labelPosition + 1;
767 }
768 }
The Android Open Source Project3b024222009-02-13 12:57:53 -0800769
770 private EditEntry getOtherEntry(String column) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700771 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
772 EditEntry entry = mOtherEntries.get(i);
The Android Open Source Project3b024222009-02-13 12:57:53 -0800773 if (isOtherEntry(entry, column)) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700774 return entry;
775 }
776 }
777 return null;
778 }
779
The Android Open Source Project3b024222009-02-13 12:57:53 -0800780 private static boolean isOtherEntry(EditEntry entry, String column) {
781 return entry != null && entry.column != null && entry.column.equals(column);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700782 }
783
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700784 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
785 final EditText label = new EditText(this);
786 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
787 label.requestFocus();
788 new AlertDialog.Builder(this)
789 .setView(label)
790 .setTitle(R.string.customLabelPickerTitle)
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800791 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700792 public void onClick(DialogInterface dialog, int which) {
793 entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
794 label.getText().toString());
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800795 mContactChanged = true;
796
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700797 if (addTo != null) {
798 addTo.add(entry);
799 buildViews();
800 entry.view.requestFocus(View.FOCUS_DOWN);
801 }
802 }
803 })
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800804 .setNegativeButton(android.R.string.cancel, null)
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700805 .show();
806 }
807
808 /**
809 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
810 */
811 private void doSaveAction() {
812 // Save or create the contact if needed
813 switch (mState) {
814 case STATE_EDIT:
815 save();
816 break;
817
818 case STATE_INSERT:
819 create();
820 break;
821
822 default:
823 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
824 break;
825 }
826 finish();
827 }
828
829 /**
830 * Save the various fields to the existing contact.
831 */
832 private void save() {
833 ContentValues values = new ContentValues();
834 String data;
835 int numValues = 0;
836
837 // Handle the name and send to voicemail specially
838 final String name = mNameView.getText().toString();
839 if (name != null && TextUtils.isGraphic(name)) {
840 numValues++;
841 }
842 values.put(People.NAME, name);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800843 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700844 mResolver.update(mUri, values, null, null);
845
846 if (mPhotoChanged) {
847 // Only write the photo if it's changed, since we don't initially load mPhoto
848 if (mPhoto != null) {
849 ByteArrayOutputStream stream = new ByteArrayOutputStream();
850 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
851 Contacts.People.setPhotoData(mResolver, mUri, stream.toByteArray());
852 } else {
853 Contacts.People.setPhotoData(mResolver, mUri, null);
854 }
855 }
856
857 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
858 for (int i = 0; i < entryCount; i++) {
859 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
860 int kind = entry.kind;
861 data = entry.getData();
862 boolean empty = data == null || !TextUtils.isGraphic(data);
863 if (kind == EditEntry.KIND_CONTACT) {
864 values.clear();
865 if (!empty) {
866 values.put(entry.column, data);
867 mResolver.update(entry.uri, values, null, null);
868 numValues++;
869 } else {
870 values.put(entry.column, (String) null);
871 mResolver.update(entry.uri, values, null, null);
872 }
873 } else {
874 if (!empty) {
875 values.clear();
876 entry.toValues(values);
877 if (entry.id != 0) {
878 mResolver.update(entry.uri, values, null, null);
879 } else {
880 mResolver.insert(entry.uri, values);
881 }
882 numValues++;
883 } else if (entry.id != 0) {
884 mResolver.delete(entry.uri, null, null);
885 }
886 }
887 }
888
889 if (numValues == 0) {
890 // The contact is completely empty, delete it
891 mResolver.delete(mUri, null, null);
892 mUri = null;
893 setResult(RESULT_CANCELED);
894 } else {
895 // Add the entry to the my contacts group if it isn't there already
896 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
897 setResult(RESULT_OK, new Intent().setData(mUri));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -0800898
899 // Only notify user if we actually changed contact
900 if (mContactChanged || mPhotoChanged) {
901 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
902 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700903 }
904 }
905
906 /**
907 * Takes the entered data and saves it to a new contact.
908 */
909 private void create() {
910 ContentValues values = new ContentValues();
911 String data;
912 int numValues = 0;
913
914 // Create the contact itself
915 final String name = mNameView.getText().toString();
916 if (name != null && TextUtils.isGraphic(name)) {
917 numValues++;
918 }
919 values.put(People.NAME, name);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -0800920 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700921
922 // Add the contact to the My Contacts group
923 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
924
925 // Add the contact to the group that is being displayed in the contact list
926 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
The Android Open Source Projectd9351702008-12-17 18:05:55 -0800927 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
928 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
929 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
930 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -0700931 null);
932 if (!TextUtils.isEmpty(displayGroup)) {
933 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
934 }
935 }
936
937 // Handle the photo
938 if (mPhoto != null) {
939 ByteArrayOutputStream stream = new ByteArrayOutputStream();
940 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
941 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
942 }
943
944 // Create the contact methods
945 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
946 for (int i = 0; i < entryCount; i++) {
947 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
948 if (entry.kind != EditEntry.KIND_CONTACT) {
949 values.clear();
950 if (entry.toValues(values)) {
951 // Only create the entry if there is data
952 entry.uri = mResolver.insert(
953 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
954 entry.id = ContentUris.parseId(entry.uri);
955 numValues++;
956 }
957 } else {
958 // Update the contact with any straggling data, like notes
959 data = entry.getData();
960 values.clear();
961 if (data != null && TextUtils.isGraphic(data)) {
962 values.put(entry.column, data);
963 mResolver.update(contactUri, values, null, null);
964 numValues++;
965 }
966 }
967 }
968
969 if (numValues == 0) {
970 mResolver.delete(contactUri, null, null);
971 setResult(RESULT_CANCELED);
972 } else {
973 mUri = contactUri;
974 Intent resultIntent = new Intent()
975 .setData(mUri)
976 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
977 setResult(RESULT_OK, resultIntent);
978 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
979 }
980 }
981
982 /**
983 * Build up the entries to display on the screen.
984 *
985 * @param extras the extras used to start this activity, may be null
986 */
987 private void buildEntriesForEdit(Bundle extras) {
988 Cursor personCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
989 if (personCursor == null) {
990 Log.e(TAG, "invalid contact uri: " + mUri);
991 finish();
992 return;
993 } else if (!personCursor.moveToFirst()) {
994 Log.e(TAG, "invalid contact uri: " + mUri);
995 finish();
996 personCursor.close();
997 return;
998 }
999
1000 // Clear out the old entries
1001 int numSections = mSections.size();
1002 for (int i = 0; i < numSections; i++) {
1003 mSections.get(i).clear();
1004 }
1005
1006 EditEntry entry;
1007
1008 // Name
1009 mNameView.setText(personCursor.getString(CONTACT_NAME_COLUMN));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001010 mNameView.addTextChangedListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001011
1012 // Photo
1013 mPhoto = People.loadContactPhoto(this, mUri, 0, null);
1014 if (mPhoto == null) {
1015 setPhotoPresent(false);
1016 } else {
1017 setPhotoPresent(true);
1018 mPhotoImageView.setImageBitmap(mPhoto);
1019 }
The Android Open Source Project3b024222009-02-13 12:57:53 -08001020
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001021 // Organizations
1022 Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
1023 Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
1024 null, null, null);
1025
1026 if (organizationsCursor != null) {
1027 while (organizationsCursor.moveToNext()) {
1028 int type = organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
1029 String label = organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN);
1030 String company = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
1031 String title = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
1032 long id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
1033 Uri uri = ContentUris.withAppendedId(Organizations.CONTENT_URI, id);
1034
1035 // Add an organization entry
1036 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1037 entry.isPrimary = organizationsCursor.getLong(ORGANIZATIONS_ISPRIMARY_COLUMN) != 0;
The Android Open Source Project3b024222009-02-13 12:57:53 -08001038 mOrgEntries.add(entry);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001039 }
1040 organizationsCursor.close();
1041 }
1042
1043 // Notes
1044 if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
1045 entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
1046 mUri);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001047 mNoteEntries.add(entry);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001048 }
1049
1050 // Ringtone
1051 entry = EditEntry.newRingtoneEntry(this,
1052 personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
1053 mOtherEntries.add(entry);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001054
1055 // Send to voicemail
1056 entry = EditEntry.newSendToVoicemailEntry(this,
1057 personCursor.getString(CONTACT_SEND_TO_VOICEMAIL_COLUMN), mUri);
1058 mOtherEntries.add(entry);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001059
1060 // Phonetic name
1061 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001062 mPhoneticNameView.addTextChangedListener(this);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001063
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001064 personCursor.close();
1065
1066 // Build up the phone entries
1067 Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
1068 Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION,
1069 null, null, null);
1070
1071 if (phonesCursor != null) {
1072 while (phonesCursor.moveToNext()) {
1073 int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
1074 String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
1075 String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
1076 long id = phonesCursor.getLong(PHONES_ID_COLUMN);
1077 boolean isPrimary = phonesCursor.getLong(PHONES_ISPRIMARY_COLUMN) != 0;
1078 Uri uri = ContentUris.withAppendedId(phonesUri, id);
1079
1080 // Add a phone number entry
1081 entry = EditEntry.newPhoneEntry(this, label, type, number, uri, id);
1082 entry.isPrimary = isPrimary;
1083 mPhoneEntries.add(entry);
1084
1085 // Keep track of which primary types have been added
1086 if (type == Phones.TYPE_MOBILE) {
1087 mMobilePhoneAdded = true;
1088 }
1089 }
1090
1091 phonesCursor.close();
1092 }
1093
1094 // Build the contact method entries
1095 Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
1096 Cursor methodsCursor = mResolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
1097
1098 if (methodsCursor != null) {
1099 while (methodsCursor.moveToNext()) {
1100 int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
1101 String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
1102 String data = methodsCursor.getString(METHODS_DATA_COLUMN);
1103 String auxData = methodsCursor.getString(METHODS_AUX_DATA_COLUMN);
1104 int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
1105 long id = methodsCursor.getLong(METHODS_ID_COLUMN);
1106 boolean isPrimary = methodsCursor.getLong(METHODS_ISPRIMARY_COLUMN) != 0;
1107 Uri uri = ContentUris.withAppendedId(methodsUri, id);
1108
1109 switch (kind) {
1110 case Contacts.KIND_EMAIL: {
1111 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1112 entry.isPrimary = isPrimary;
1113 mEmailEntries.add(entry);
1114
1115 if (isPrimary) {
1116 mPrimaryEmailAdded = true;
1117 }
1118 break;
1119 }
1120
1121 case Contacts.KIND_POSTAL: {
1122 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1123 entry.isPrimary = isPrimary;
1124 mPostalEntries.add(entry);
1125 break;
1126 }
1127
1128 case Contacts.KIND_IM: {
1129 Object protocolObj = ContactMethods.decodeImProtocol(auxData);
1130 if (protocolObj == null) {
1131 // Invalid IM protocol, log it then ignore.
1132 Log.e(TAG, "Couldn't decode IM protocol: " + auxData);
1133 continue;
1134 } else {
1135 if (protocolObj instanceof Number) {
1136 int protocol = ((Number) protocolObj).intValue();
1137 entry = EditEntry.newImEntry(this,
1138 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1139 data, uri, id);
1140 } else {
1141 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1142 uri, id);
1143 }
1144 mImEntries.add(entry);
1145 }
1146 break;
1147 }
1148 }
1149 }
1150
1151 methodsCursor.close();
1152 }
1153
1154 // Add values from the extras, if there are any
1155 if (extras != null) {
1156 addFromExtras(extras, phonesUri, methodsUri);
1157 }
1158
1159 // Add the base types if needed
1160 if (!mMobilePhoneAdded) {
1161 entry = EditEntry.newPhoneEntry(this,
1162 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1163 DEFAULT_PHONE_TYPE);
1164 mPhoneEntries.add(entry);
1165 }
1166
1167 if (!mPrimaryEmailAdded) {
1168 entry = EditEntry.newEmailEntry(this,
1169 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1170 DEFAULT_EMAIL_TYPE);
1171 entry.isPrimary = true;
1172 mEmailEntries.add(entry);
1173 }
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001174
1175 mContactChanged = false;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001176 }
1177
1178 /**
1179 * Build the list of EditEntries for full mode insertions.
1180 *
1181 * @param extras the extras used to start this activity, may be null
1182 */
1183 private void buildEntriesForInsert(Bundle extras) {
1184 // Clear out the old entries
1185 int numSections = mSections.size();
1186 for (int i = 0; i < numSections; i++) {
1187 mSections.get(i).clear();
1188 }
1189
1190 EditEntry entry;
1191
1192 // Check the intent extras
1193 if (extras != null) {
1194 addFromExtras(extras, null, null);
1195 }
1196
1197 // Photo
1198 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1199
1200 // Add the base entries if they're not already present
1201 if (!mMobilePhoneAdded) {
1202 entry = EditEntry.newPhoneEntry(this, null, Phones.TYPE_MOBILE);
1203 entry.isPrimary = true;
1204 mPhoneEntries.add(entry);
1205 }
1206
1207 if (!mPrimaryEmailAdded) {
1208 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1209 entry.isPrimary = true;
1210 mEmailEntries.add(entry);
1211 }
1212
1213 // Ringtone
1214 entry = EditEntry.newRingtoneEntry(this, null, mUri);
1215 mOtherEntries.add(entry);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001216
1217 // Send to voicemail
1218 entry = EditEntry.newSendToVoicemailEntry(this, "0", mUri);
1219 mOtherEntries.add(entry);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001220 }
1221
1222 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1223 EditEntry entry;
1224
1225 // Read the name from the bundle
1226 CharSequence name = extras.getCharSequence(Insert.NAME);
1227 if (name != null && TextUtils.isGraphic(name)) {
1228 mNameView.setText(name);
1229 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001230
1231 // Read the phonetic name from the bundle
1232 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1233 if (!TextUtils.isEmpty(phoneticName)) {
1234 mPhoneticNameView.setText(phoneticName);
1235 }
1236
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001237 // Postal entries from extras
1238 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1239 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1240 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1241 postalType = DEFAULT_POSTAL_TYPE;
1242 }
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001243
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001244 if (postalType != INVALID_TYPE) {
1245 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1246 methodsUri, 0);
1247 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1248 mPostalEntries.add(entry);
1249 }
1250
1251 // Email entries from extras
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001252 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1253 Insert.EMAIL_ISPRIMARY);
1254 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1255 null);
1256 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1257 null);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001258
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001259 // Phone entries from extras
1260 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1261 Insert.PHONE_ISPRIMARY);
1262 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1263 null);
1264 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1265 null);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001266
1267 // IM entries from extras
1268 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1269 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
1270
1271 if (imHandle != null && imProtocol != null) {
1272 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1273 if (protocolObj instanceof Number) {
1274 int protocol = ((Number) protocolObj).intValue();
1275 entry = EditEntry.newImEntry(this,
1276 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
1277 imHandle.toString(), null, 0);
1278 } else {
1279 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
1280 null, 0);
1281 }
1282 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1283 mImEntries.add(entry);
1284 }
1285 }
1286
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001287 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1288 String typeField, String primaryField) {
1289 CharSequence email = extras.getCharSequence(emailField);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001290
1291 // Correctly handle String in typeField as TYPE_CUSTOM
1292 int emailType = INVALID_TYPE;
1293 String customLabel = null;
1294 if(extras.get(typeField) instanceof String) {
1295 emailType = ContactMethods.TYPE_CUSTOM;
1296 customLabel = extras.getString(typeField);
1297 } else {
1298 emailType = extras.getInt(typeField, INVALID_TYPE);
1299 }
1300
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001301 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1302 emailType = DEFAULT_EMAIL_TYPE;
1303 mPrimaryEmailAdded = true;
1304 }
1305
1306 if (emailType != INVALID_TYPE) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001307 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001308 methodsUri, 0);
1309 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1310 mEmailEntries.add(entry);
1311
1312 // Keep track of which primary types have been added
1313 if (entry.isPrimary) {
1314 mPrimaryEmailAdded = true;
1315 }
1316 }
1317 }
1318
1319 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1320 String typeField, String primaryField) {
1321 CharSequence phoneNumber = extras.getCharSequence(phoneField);
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001322
1323 // Correctly handle String in typeField as TYPE_CUSTOM
1324 int phoneType = INVALID_TYPE;
1325 String customLabel = null;
1326 if(extras.get(typeField) instanceof String) {
1327 phoneType = Phones.TYPE_CUSTOM;
1328 customLabel = extras.getString(typeField);
1329 } else {
1330 phoneType = extras.getInt(typeField, INVALID_TYPE);
1331 }
1332
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001333 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1334 phoneType = DEFAULT_PHONE_TYPE;
1335 }
1336
1337 if (phoneType != INVALID_TYPE) {
The Android Open Source Project9cb63a52009-01-09 17:51:25 -08001338 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001339 phoneNumber.toString(), phonesUri, 0);
1340 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1341 mPhoneEntries.add(entry);
1342
1343 // Keep track of which primary types have been added
1344 if (phoneType == Phones.TYPE_MOBILE) {
1345 mMobilePhoneAdded = true;
1346 }
1347 }
1348 }
1349
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001350 /**
1351 * Removes all existing views, builds new ones for all the entries, and adds them.
1352 */
1353 private void buildViews() {
1354 // Remove existing views
1355 final LinearLayout layout = mLayout;
1356 layout.removeAllViews();
The Android Open Source Project3b024222009-02-13 12:57:53 -08001357
1358 buildViewsForSection(layout, mPhoneEntries,
1359 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1360 buildViewsForSection(layout, mEmailEntries,
1361 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1362 buildViewsForSection(layout, mImEntries,
1363 R.string.listSeparatorSendIm_edit, SECTION_IM);
1364 buildViewsForSection(layout, mPostalEntries,
1365 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1366 buildViewsForSection(layout, mOrgEntries,
1367 R.string.listSeparatorOrganizations, SECTION_ORG);
1368 buildViewsForSection(layout, mNoteEntries,
1369 R.string.label_notes, SECTION_NOTE);
1370
1371 buildOtherViews(layout, mOtherEntries);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001372 }
1373
1374
1375 /**
1376 * Builds the views for a specific section.
1377 *
1378 * @param layout the container
1379 * @param section the section to build the views for
1380 */
1381 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
The Android Open Source Project3b024222009-02-13 12:57:53 -08001382 int separatorResource, int sectionType) {
1383
1384 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1385 layout.addView(divider);
1386
1387 // Count up undeleted children
1388 int activeChildren = 0;
1389 for (int i = section.size() - 1; i >= 0; i--) {
1390 EditEntry entry = section.get(i);
1391 if (!entry.isDeleted) {
1392 activeChildren++;
1393 }
1394 }
1395
1396 // Build the correct group header based on undeleted children
1397 ViewGroup header;
1398 if (activeChildren == 0) {
1399 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1400 } else {
1401 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001402 }
1403
The Android Open Source Project3b024222009-02-13 12:57:53 -08001404 // Because we're emulating a ListView, we need to handle focus changes
1405 // with some additional logic.
1406 header.setOnFocusChangeListener(this);
1407
1408 TextView text = (TextView) header.findViewById(R.id.text);
1409 text.setText(getText(separatorResource));
1410
1411 // Force TextView to always default color if we have children. This makes sure
1412 // we don't change color when parent is pressed.
1413 if (activeChildren > 0) {
1414 ColorStateList stateList = text.getTextColors();
1415 text.setTextColor(stateList.getDefaultColor());
1416 }
1417
1418 View addView = header.findViewById(R.id.separator);
1419 addView.setTag(Integer.valueOf(sectionType));
1420 addView.setOnClickListener(this);
1421
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001422 // Build views for the current section
1423 for (EditEntry entry : section) {
1424 entry.activity = this; // this could be null from when the state is restored
1425 if (!entry.isDeleted) {
1426 View view = buildViewForEntry(entry);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001427 header.addView(view);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001428 }
1429 }
The Android Open Source Project3b024222009-02-13 12:57:53 -08001430
1431 layout.addView(header);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001432 }
The Android Open Source Project3b024222009-02-13 12:57:53 -08001433
1434 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1435 // Build views for the current section, putting a divider between each one
1436 for (EditEntry entry : section) {
1437 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1438 layout.addView(divider);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001439
The Android Open Source Project3b024222009-02-13 12:57:53 -08001440 entry.activity = this; // this could be null from when the state is restored
1441 View view = buildViewForEntry(entry);
1442 view.setOnClickListener(this);
1443 layout.addView(view);
1444 }
1445
1446 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1447 layout.addView(divider);
1448 }
1449
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001450 /**
1451 * Builds a view to display an EditEntry.
1452 *
1453 * @param entry the entry to display
1454 * @return a view that will display the given entry
1455 */
1456 /* package */ View buildViewForEntry(final EditEntry entry) {
1457 // Look for any existing entered text, and save it if found
1458 if (entry.view != null && entry.syncDataWithView) {
1459 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1460 .getText().toString();
1461 if (!TextUtils.isEmpty(enteredText)) {
1462 entry.data = enteredText;
1463 }
1464 }
1465
1466 // Build a new view
1467 final ViewGroup parent = mLayout;
1468 View view;
1469
The Android Open Source Project3b024222009-02-13 12:57:53 -08001470 // Because we're emulating a ListView, we might need to handle focus changes
1471 // with some additional logic.
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001472 if (entry.kind == Contacts.KIND_ORGANIZATION) {
1473 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001474 } else if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001475 view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001476 view.setOnFocusChangeListener(this);
1477 } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
1478 view = mInflater.inflate(R.layout.edit_contact_entry_voicemail, parent, false);
1479 view.setOnFocusChangeListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001480 } else if (!entry.isStaticLabel) {
1481 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1482 } else {
1483 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1484 }
1485 entry.view = view;
1486
1487 // Set the entry as the tag so we can find it again later given just the view
1488 view.setTag(entry);
1489
1490 // Bind the label
1491 entry.bindLabel(this);
1492
1493 // Bind data
1494 TextView data = (TextView) view.findViewById(R.id.data);
1495 TextView data2 = (TextView) view.findViewById(R.id.data2);
The Android Open Source Project42e29b72009-02-10 15:44:04 -08001496
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001497 if (data instanceof Button) {
1498 data.setOnClickListener(this);
1499 }
1500 if (data.length() == 0) {
1501 if (entry.syncDataWithView) {
1502 // If there is already data entered don't overwrite it
1503 data.setText(entry.data);
1504 } else {
1505 fillViewData(entry);
1506 }
1507 }
1508 if (data2 != null && data2.length() == 0) {
1509 // If there is already data entered don't overwrite it
1510 data2.setText(entry.data2);
1511 }
1512 data.setHint(entry.hint);
1513 if (data2 != null) data2.setHint(entry.hint2);
1514 if (entry.lines > 1) {
1515 data.setLines(entry.lines);
1516 data.setMaxLines(entry.maxLines);
1517 if (data2 != null) {
1518 data2.setLines(entry.lines);
1519 data2.setMaxLines(entry.maxLines);
1520 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001521 }
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001522 int contentType = entry.contentType;
1523 if (contentType != EditorInfo.TYPE_NULL) {
1524 data.setInputType(contentType);
1525 if (data2 != null) {
1526 data2.setInputType(contentType);
1527 }
1528 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1529 == EditorInfo.TYPE_CLASS_PHONE) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001530 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1531 if (data2 != null) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001532 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1533 }
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001534 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001535 }
1536
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001537 // Connect listeners up to watch for changed values.
1538 if (data instanceof EditText) {
1539 data.addTextChangedListener(this);
1540 }
1541 if (data2 instanceof EditText) {
1542 data2.addTextChangedListener(this);
1543 }
1544
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001545 // Hook up the delete button
1546 View delete = view.findViewById(R.id.delete);
1547 if (delete != null) delete.setOnClickListener(this);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001548
1549 return view;
1550 }
1551
1552 private void fillViewData(final EditEntry entry) {
The Android Open Source Project3b024222009-02-13 12:57:53 -08001553 if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001554 updateRingtoneView(entry);
The Android Open Source Project3b024222009-02-13 12:57:53 -08001555 } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
1556 CheckBox checkBox = (CheckBox) entry.view.findViewById(R.id.checkbox);
1557 boolean sendToVoicemail = false;
1558 if (entry.data != null) {
1559 sendToVoicemail = (Integer.valueOf(entry.data) == 1);
1560 }
1561 checkBox.setChecked(sendToVoicemail);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001562 }
1563 }
1564
1565 /**
1566 * Handles the results from the label change picker.
1567 */
1568 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1569 EditEntry mEntry;
1570 String[] mLabels;
1571
1572 public LabelPickedListener(EditEntry entry, String[] labels) {
1573 mEntry = entry;
1574 mLabels = labels;
1575 }
1576
1577 public void onClick(DialogInterface dialog, int which) {
1578 // TODO: Use a managed dialog
1579 if (mEntry.kind != Contacts.KIND_IM) {
1580 final int type = getTypeFromLabelPosition(mLabels, which);
1581 if (type == ContactMethods.TYPE_CUSTOM) {
1582 createCustomPicker(mEntry, null);
1583 } else {
1584 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001585 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001586 }
1587 } else {
1588 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001589 mContactChanged = true;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001590 }
1591 }
1592 }
1593
1594 /**
1595 * A basic structure with the data for a contact entry in the list.
1596 */
1597 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1598 // These aren't stuffed into the parcel
1599 public EditContactActivity activity;
1600 public View view;
1601
1602 // These are stuffed into the parcel
1603 public String hint;
1604 public String hint2;
1605 public String column;
1606 public String contentDirectory;
1607 public String data2;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001608 public int contentType;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001609 public int type;
1610 /**
1611 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1612 * will not be called.
1613 */
1614 public int lines = 1;
1615 public boolean isPrimary;
1616 public boolean isDeleted = false;
1617 public boolean isStaticLabel = false;
1618 public boolean syncDataWithView = true;
1619
1620 private EditEntry() {
1621 // only used by CREATOR
1622 }
1623
1624 public EditEntry(EditContactActivity activity) {
1625 this.activity = activity;
1626 }
1627
1628 public EditEntry(EditContactActivity activity, String label,
1629 int type, String data, Uri uri, long id) {
1630 this.activity = activity;
1631 this.isPrimary = false;
1632 this.label = label;
1633 this.type = type;
1634 this.data = data;
1635 this.uri = uri;
1636 this.id = id;
1637 }
1638
1639 public int describeContents() {
1640 return 0;
1641 }
1642
1643 public void writeToParcel(Parcel parcel, int flags) {
1644 // Make sure to read data from the input field, if anything is entered
1645 data = getData();
1646
1647 // Write in our own fields.
1648 parcel.writeString(hint);
1649 parcel.writeString(hint2);
1650 parcel.writeString(column);
1651 parcel.writeString(contentDirectory);
1652 parcel.writeString(data2);
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001653 parcel.writeInt(contentType);
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001654 parcel.writeInt(type);
1655 parcel.writeInt(lines);
1656 parcel.writeInt(isPrimary ? 1 : 0);
1657 parcel.writeInt(isDeleted ? 1 : 0);
1658 parcel.writeInt(isStaticLabel ? 1 : 0);
1659 parcel.writeInt(syncDataWithView ? 1 : 0);
1660
1661 // Write in the fields from Entry
1662 super.writeToParcel(parcel);
1663 }
1664
1665 public static final Parcelable.Creator<EditEntry> CREATOR =
1666 new Parcelable.Creator<EditEntry>() {
1667 public EditEntry createFromParcel(Parcel in) {
1668 EditEntry entry = new EditEntry();
1669
1670 // Read out our own fields
1671 entry.hint = in.readString();
1672 entry.hint2 = in.readString();
1673 entry.column = in.readString();
1674 entry.contentDirectory = in.readString();
1675 entry.data2 = in.readString();
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001676 entry.contentType = in.readInt();
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001677 entry.type = in.readInt();
1678 entry.lines = in.readInt();
1679 entry.isPrimary = in.readInt() == 1;
1680 entry.isDeleted = in.readInt() == 1;
1681 entry.isStaticLabel = in.readInt() == 1;
1682 entry.syncDataWithView = in.readInt() == 1;
1683
1684 // Read out the fields from Entry
1685 entry.readFromParcel(in);
1686
1687 return entry;
1688 }
1689
1690 public EditEntry[] newArray(int size) {
1691 return new EditEntry[size];
1692 }
1693 };
1694
1695 public void setLabel(Context context, int typeIn, String labelIn) {
1696 type = typeIn;
1697 label = labelIn;
1698 if (view != null) {
1699 bindLabel(context);
1700 }
1701 }
1702
1703 public void bindLabel(Context context) {
1704 TextView v = (TextView) view.findViewById(R.id.label);
1705 if (isStaticLabel) {
1706 v.setText(label);
1707 return;
1708 }
1709
1710 switch (kind) {
1711 case Contacts.KIND_PHONE: {
1712 v.setText(Phones.getDisplayLabel(context, type, label));
1713 break;
1714 }
1715
1716 case Contacts.KIND_IM: {
1717 v.setText(getLabelsForKind(activity, kind)[type]);
1718 break;
1719 }
1720
1721 case Contacts.KIND_ORGANIZATION: {
1722 v.setText(Organizations.getDisplayLabel(activity, type, label));
1723 break;
1724 }
1725
1726 default: {
1727 v.setText(Contacts.ContactMethods.getDisplayLabel(context, kind, type, label));
1728 if (kind == Contacts.KIND_POSTAL) {
1729 v.setMaxLines(3);
1730 }
1731 break;
1732 }
1733 }
1734 v.setOnClickListener(activity);
1735 }
1736
1737 /**
1738 * Returns the data for the entry
1739 * @return the data for the entry
1740 */
1741 public String getData() {
1742 if (view != null && syncDataWithView) {
1743 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1744 if (text != null) {
1745 return text.toString();
1746 }
1747 }
1748
1749 if (data != null) {
1750 return data.toString();
1751 }
1752
1753 return null;
1754 }
1755
1756 /**
1757 * Dumps the entry into a HashMap suitable for passing to the database.
1758 *
1759 * @param values the HashMap to fill in.
1760 * @return true if the value should be saved, false otherwise
1761 */
1762 public boolean toValues(ContentValues values) {
1763 boolean success = false;
1764 String labelString = null;
1765 // Save the type and label
1766 if (view != null) {
1767 // Read the possibly updated label from the text field
1768 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1769 }
1770 switch (kind) {
1771 case Contacts.KIND_PHONE:
1772 if (type != Phones.TYPE_CUSTOM) {
1773 labelString = null;
1774 }
1775 values.put(Phones.LABEL, labelString);
1776 values.put(Phones.TYPE, type);
1777 break;
1778
1779 case Contacts.KIND_EMAIL:
1780 if (type != ContactMethods.TYPE_CUSTOM) {
1781 labelString = null;
1782 }
1783 values.put(ContactMethods.LABEL, labelString);
1784 values.put(ContactMethods.KIND, kind);
1785 values.put(ContactMethods.TYPE, type);
1786 break;
1787
1788 case Contacts.KIND_IM:
1789 values.put(ContactMethods.KIND, kind);
1790 values.put(ContactMethods.TYPE, ContactMethods.TYPE_OTHER);
1791 values.putNull(ContactMethods.LABEL);
1792 if (type != -1) {
1793 values.put(ContactMethods.AUX_DATA,
1794 ContactMethods.encodePredefinedImProtocol(type));
1795 } else {
1796 values.put(ContactMethods.AUX_DATA,
1797 ContactMethods.encodeCustomImProtocol(label.toString()));
1798 }
1799 break;
1800
1801 case Contacts.KIND_POSTAL:
1802 if (type != ContactMethods.TYPE_CUSTOM) {
1803 labelString = null;
1804 }
1805 values.put(ContactMethods.LABEL, labelString);
1806 values.put(ContactMethods.KIND, kind);
1807 values.put(ContactMethods.TYPE, type);
1808 break;
1809
1810 case Contacts.KIND_ORGANIZATION:
1811 if (type != ContactMethods.TYPE_CUSTOM) {
1812 labelString = null;
1813 }
1814 values.put(ContactMethods.LABEL, labelString);
1815 values.put(ContactMethods.TYPE, type);
1816 // Save the title
1817 if (view != null) {
1818 // Read the possibly updated data from the text field
1819 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
1820 }
1821 if (!TextUtils.isGraphic(data2)) {
1822 values.putNull(Organizations.TITLE);
1823 } else {
1824 values.put(Organizations.TITLE, data2.toString());
1825 success = true;
1826 }
1827 break;
1828
1829 default:
1830 Log.w(TAG, "unknown kind " + kind);
1831 values.put(ContactMethods.LABEL, labelString);
1832 values.put(ContactMethods.KIND, kind);
1833 values.put(ContactMethods.TYPE, type);
1834 break;
1835 }
1836
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08001837 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
1838 // ContentProvider will try finding a new primary when setting to false, meaning
1839 // it's possible to lose primary altogether as we walk down the list. If this editor
1840 // implements editing of primaries in the future, this will need to be revisited.
1841 if (isPrimary) {
1842 values.put(ContactMethods.ISPRIMARY, 1);
1843 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001844
1845 // Save the data
1846 if (view != null && syncDataWithView) {
1847 // Read the possibly updated data from the text field
1848 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
1849 }
1850 if (!TextUtils.isGraphic(data)) {
1851 values.putNull(column);
1852 return success;
1853 } else {
1854 values.put(column, data.toString());
1855 return true;
1856 }
1857 }
1858
1859 /**
1860 * Create a new empty organization entry
1861 */
1862 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1863 Uri uri, int type) {
1864 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
1865 }
1866
1867 /**
1868 * Create a new company entry with the given data.
1869 */
1870 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
1871 String label, int type, String company, String title, Uri uri, long id) {
1872 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
1873 entry.hint = activity.getString(R.string.ghostData_company);
1874 entry.hint2 = activity.getString(R.string.ghostData_title);
1875 entry.data2 = title;
1876 entry.column = Organizations.COMPANY;
1877 entry.contentDirectory = Organizations.CONTENT_DIRECTORY;
1878 entry.kind = Contacts.KIND_ORGANIZATION;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001879 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
1880 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001881 return entry;
1882 }
1883
1884 /**
1885 * Create a new notes entry with the given data.
1886 */
1887 public static final EditEntry newNotesEntry(EditContactActivity activity,
1888 String data, Uri uri) {
1889 EditEntry entry = new EditEntry(activity);
1890 entry.label = activity.getString(R.string.label_notes);
1891 entry.hint = activity.getString(R.string.ghostData_notes);
1892 entry.data = data;
1893 entry.uri = uri;
1894 entry.column = People.NOTES;
1895 entry.maxLines = 10;
1896 entry.lines = 2;
1897 entry.id = 0;
1898 entry.kind = KIND_CONTACT;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001899 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
1900 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
1901 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001902 entry.isStaticLabel = true;
1903 return entry;
1904 }
1905
1906 /**
1907 * Create a new ringtone entry with the given data.
1908 */
1909 public static final EditEntry newRingtoneEntry(EditContactActivity activity,
1910 String data, Uri uri) {
1911 EditEntry entry = new EditEntry(activity);
1912 entry.label = activity.getString(R.string.label_ringtone);
1913 entry.data = data;
1914 entry.uri = uri;
1915 entry.column = People.CUSTOM_RINGTONE;
1916 entry.kind = KIND_CONTACT;
1917 entry.isStaticLabel = true;
1918 entry.syncDataWithView = false;
1919 entry.lines = -1;
1920 return entry;
1921 }
1922
1923 /**
The Android Open Source Project3b024222009-02-13 12:57:53 -08001924 * Create a new send-to-voicemail entry with the given data.
1925 */
1926 public static final EditEntry newSendToVoicemailEntry(EditContactActivity activity,
1927 String data, Uri uri) {
1928 EditEntry entry = new EditEntry(activity);
1929 entry.label = activity.getString(R.string.actionIncomingCall);
1930 entry.data = data;
1931 entry.uri = uri;
1932 entry.column = People.SEND_TO_VOICEMAIL;
1933 entry.kind = KIND_CONTACT;
1934 entry.isStaticLabel = true;
1935 entry.syncDataWithView = false;
1936 entry.lines = -1;
1937 return entry;
1938 }
1939
1940 /**
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001941 * Create a new empty email entry
1942 */
1943 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1944 Uri uri, int type) {
1945 return newPhoneEntry(activity, null, type, null, uri, 0);
1946 }
1947
1948 /**
1949 * Create a new phone entry with the given data.
1950 */
1951 public static final EditEntry newPhoneEntry(EditContactActivity activity,
1952 String label, int type, String data, Uri uri,
1953 long id) {
1954 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1955 entry.hint = activity.getString(R.string.ghostData_phone);
1956 entry.column = People.Phones.NUMBER;
1957 entry.contentDirectory = People.Phones.CONTENT_DIRECTORY;
1958 entry.kind = Contacts.KIND_PHONE;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001959 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001960 return entry;
1961 }
1962
1963 /**
1964 * Create a new empty email entry
1965 */
1966 public static final EditEntry newEmailEntry(EditContactActivity activity,
1967 Uri uri, int type) {
1968 return newEmailEntry(activity, null, type, null, uri, 0);
1969 }
1970
1971 /**
1972 * Create a new email entry with the given data.
1973 */
1974 public static final EditEntry newEmailEntry(EditContactActivity activity,
1975 String label, int type, String data, Uri uri,
1976 long id) {
1977 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
1978 entry.hint = activity.getString(R.string.ghostData_email);
1979 entry.column = ContactMethods.DATA;
1980 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
1981 entry.kind = Contacts.KIND_EMAIL;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08001982 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
1983 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07001984 return entry;
1985 }
1986
1987 /**
1988 * Create a new empty postal address entry
1989 */
1990 public static final EditEntry newPostalEntry(EditContactActivity activity,
1991 Uri uri, int type) {
1992 return newPostalEntry(activity, null, type, null, uri, 0);
1993 }
1994
1995 /**
1996 * Create a new postal address entry with the given data.
1997 *
1998 * @param label label for the item, from the db not the display label
1999 * @param type the type of postal address
2000 * @param data the starting data for the entry, may be null
2001 * @param uri the uri for the entry if it already exists, may be null
2002 * @param id the id for the entry if it already exists, 0 it it doesn't
2003 * @return the new EditEntry
2004 */
2005 public static final EditEntry newPostalEntry(EditContactActivity activity,
2006 String label, int type, String data, Uri uri, long id) {
2007 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2008 entry.hint = activity.getString(R.string.ghostData_postal);
2009 entry.column = ContactMethods.DATA;
2010 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2011 entry.kind = Contacts.KIND_POSTAL;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08002012 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2013 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2014 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2015 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002016 entry.maxLines = 4;
2017 entry.lines = 2;
2018 return entry;
2019 }
2020
2021 /**
The Android Open Source Project3b024222009-02-13 12:57:53 -08002022 * Create a new IM address entry
2023 */
2024 public static final EditEntry newImEntry(EditContactActivity activity,
2025 Uri uri, int type) {
2026 return newImEntry(activity, null, type, null, uri, 0);
2027 }
2028
2029 /**
2030 * Create a new IM address entry with the given data.
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002031 *
2032 * @param label label for the item, from the db not the display label
2033 * @param protocol the type used
2034 * @param data the starting data for the entry, may be null
2035 * @param uri the uri for the entry if it already exists, may be null
2036 * @param id the id for the entry if it already exists, 0 it it doesn't
2037 * @return the new EditEntry
2038 */
2039 public static final EditEntry newImEntry(EditContactActivity activity,
2040 String label, int protocol, String data, Uri uri, long id) {
2041 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2042 entry.hint = activity.getString(R.string.ghostData_im);
2043 entry.column = ContactMethods.DATA;
2044 entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
2045 entry.kind = Contacts.KIND_IM;
The Android Open Source Projectd9351702008-12-17 18:05:55 -08002046 entry.contentType = EditorInfo.TYPE_CLASS_TEXT;
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002047 return entry;
2048 }
2049 }
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08002050
2051 public void afterTextChanged(Editable s) {
The Android Open Source Project3b024222009-02-13 12:57:53 -08002052 // Someone edited a text field, so assume this contact is changed
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08002053 mContactChanged = true;
2054 }
2055
2056 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2057 // Do nothing; editing handled by afterTextChanged()
2058 }
2059
2060 public void onTextChanged(CharSequence s, int start, int before, int count) {
2061 // Do nothing; editing handled by afterTextChanged()
2062 }
The Android Open Source Project3b024222009-02-13 12:57:53 -08002063
2064 public void onFocusChange(View v, boolean hasFocus) {
2065 // Because we're emulating a ListView, we need to setSelected() for
2066 // views as they are focused.
2067 v.setSelected(hasFocus);
The Android Open Source Projectad5ad712009-01-15 16:12:13 -08002068 }
The Android Open Source Project5dc3b4f2008-10-21 07:00:00 -07002069}