blob: 32b55ef64ff2dfab8e261efcded12ac7f324450e [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts;
18
Evan Millar66388be2009-05-28 15:41:07 -070019import static com.android.contacts.ContactEntryAdapter.AGGREGATE_DISPLAY_NAME_COLUMN;
20import static com.android.contacts.ContactEntryAdapter.AGGREGATE_PROJECTION;
Evan Millar7e4accf2009-06-08 10:43:26 -070021import static com.android.contacts.ContactEntryAdapter.AGGREGATE_STARRED_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.DATA_PACKAGE_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.DATA_IS_PRIMARY_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
32import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
Alex Kennberg87fc3172009-03-28 06:43:06 -070033
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080034import android.app.Activity;
35import android.app.AlertDialog;
36import android.app.Dialog;
37import android.content.ActivityNotFoundException;
38import android.content.ContentResolver;
39import android.content.ContentUris;
40import android.content.ContentValues;
41import android.content.Context;
42import android.content.DialogInterface;
43import android.content.Intent;
44import android.content.SharedPreferences;
45import android.content.res.ColorStateList;
46import android.content.res.Resources;
47import android.database.Cursor;
48import android.graphics.Bitmap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080049import android.net.Uri;
50import android.os.Bundle;
51import android.os.Parcel;
52import android.os.Parcelable;
53import android.preference.PreferenceManager;
Evan Millar7e4accf2009-06-08 10:43:26 -070054import android.provider.ContactsContract;
55import android.provider.ContactsContract.Aggregates;
56import android.provider.ContactsContract.CommonDataKinds;
57import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
Evan Millar7e4accf2009-06-08 10:43:26 -070058import android.provider.ContactsContract.CommonDataKinds.Email;
59import android.provider.ContactsContract.CommonDataKinds.Im;
60import android.provider.ContactsContract.CommonDataKinds.Note;
61import android.provider.ContactsContract.CommonDataKinds.Organization;
62import android.provider.ContactsContract.CommonDataKinds.Phone;
63import android.provider.ContactsContract.CommonDataKinds.Photo;
64import android.provider.ContactsContract.CommonDataKinds.Postal;
65import android.provider.ContactsContract.CommonDataKinds.StructuredName;
66import android.provider.ContactsContract.Data;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080067import android.telephony.PhoneNumberFormattingTextWatcher;
68import android.text.Editable;
69import android.text.TextUtils;
70import android.text.TextWatcher;
71import android.text.method.TextKeyListener;
72import android.text.method.TextKeyListener.Capitalize;
73import android.util.Log;
74import android.util.SparseBooleanArray;
75import android.view.KeyEvent;
76import android.view.LayoutInflater;
77import android.view.Menu;
78import android.view.MenuItem;
79import android.view.View;
80import android.view.ViewGroup;
81import android.view.ViewParent;
82import android.view.inputmethod.EditorInfo;
83import android.widget.Button;
84import android.widget.CheckBox;
85import android.widget.EditText;
86import android.widget.ImageView;
87import android.widget.LinearLayout;
88import android.widget.TextView;
89import android.widget.Toast;
90
91import java.io.ByteArrayOutputStream;
92import java.util.ArrayList;
93
Evan Millar7e4accf2009-06-08 10:43:26 -070094// TODO: Much of this class has been commented out as a starting place for transition to new data
95// model. It will be added back as we progress.
96
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080097/**
98 * Activity for editing or inserting a contact. Note that if the contact data changes in the
99 * background while this activity is running, the updates will be overwritten.
100 */
101public final class EditContactActivity extends Activity implements View.OnClickListener,
102 TextWatcher, View.OnFocusChangeListener {
103 private static final String TAG = "EditContactActivity";
104
105 private static final int STATE_UNKNOWN = 0;
106 /** Editing an existing contact */
107 private static final int STATE_EDIT = 1;
108 /** The full insert mode */
109 private static final int STATE_INSERT = 2;
110
111 /** The launch code when picking a photo and the raw data is returned */
112 private static final int PHOTO_PICKED_WITH_DATA = 3021;
113
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800114 // These correspond to the string array in resources for picker "other" items
115 final static int OTHER_ORGANIZATION = 0;
116 final static int OTHER_NOTE = 1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700117
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800118 // Dialog IDs
119 final static int DELETE_CONFIRMATION_DIALOG = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -0700120
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800121 // Section IDs
122 final static int SECTION_PHONES = 3;
123 final static int SECTION_EMAIL = 4;
124 final static int SECTION_IM = 5;
125 final static int SECTION_POSTAL = 6;
126 final static int SECTION_ORG = 7;
127 final static int SECTION_NOTE = 8;
128
129 // Menu item IDs
130 public static final int MENU_ITEM_SAVE = 1;
131 public static final int MENU_ITEM_DONT_SAVE = 2;
132 public static final int MENU_ITEM_DELETE = 3;
133 public static final int MENU_ITEM_PHOTO = 6;
Evan Millar7e4accf2009-06-08 10:43:26 -0700134
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800135 /** Used to represent an invalid type for a contact entry */
136 private static final int INVALID_TYPE = -1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700137
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800138 /** The default type for a phone that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700139 private static final int DEFAULT_PHONE_TYPE = Phone.TYPE_MOBILE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800140
141 /** The default type for an email that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700142 private static final int DEFAULT_EMAIL_TYPE = Email.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800143
144 /** The default type for a postal address that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700145 private static final int DEFAULT_POSTAL_TYPE = Postal.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800146
147 private int mState; // saved across instances
148 private boolean mInsert; // saved across instances
149 private Uri mUri; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700150 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800151 /** In insert mode this is the photo */
152 private Bitmap mPhoto; // saved across instances
153 private boolean mPhotoChanged = false; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700154
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800155 private EditText mNameView;
Evan Millar7e4accf2009-06-08 10:43:26 -0700156 private Uri mStructuredNameUri;
157 private Uri mPhotoDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800158 private ImageView mPhotoImageView;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800159 private ViewGroup mContentView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800160 private LinearLayout mLayout;
161 private LayoutInflater mInflater;
162 private MenuItem mPhotoMenuItem;
163 private boolean mPhotoPresent = false;
164 private EditText mPhoneticNameView; // invisible in some locales, but always present
165
166 /** Flag marking this contact as changed, meaning we should write changes back. */
167 private boolean mContactChanged = false;
Evan Millar7e4accf2009-06-08 10:43:26 -0700168
Alex Kennberg87fc3172009-03-28 06:43:06 -0700169 /** List of all the group names */
170 private CharSequence[] mGroups;
Evan Millar7e4accf2009-06-08 10:43:26 -0700171
Alex Kennberg87fc3172009-03-28 06:43:06 -0700172 /** Is this contact part of the group */
173 private boolean[] mInTheGroup;
174
Evan Millar7e4accf2009-06-08 10:43:26 -0700175 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700176 private static final String[] GROUP_ID_PROJECTION = new String[] {
177 Groups._ID,
178 };
179
180 private static final String[] GROUPMEMBERSHIP_ID_PROJECTION = new String[] {
181 GroupMembership._ID,
182 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700183 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800184
185 // These are accessed by inner classes. They're package scoped to make access more efficient.
186 /* package */ ContentResolver mResolver;
187 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
188 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
189 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
190 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
191 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
192 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
193 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
194 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
Evan Millar7e4accf2009-06-08 10:43:26 -0700195
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800196 /* package */ static final int MSG_DELETE = 1;
197 /* package */ static final int MSG_CHANGE_LABEL = 2;
198 /* package */ static final int MSG_ADD_PHONE = 3;
199 /* package */ static final int MSG_ADD_EMAIL = 4;
200 /* package */ static final int MSG_ADD_POSTAL = 5;
Evan Millar7e4accf2009-06-08 10:43:26 -0700201
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800202 public void onClick(View v) {
203 switch (v.getId()) {
204 case R.id.photoImage: {
205 doPickPhotoAction();
206 break;
207 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700208
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800209 case R.id.checkable: {
210 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
211 checkBox.toggle();
Evan Millar7e4accf2009-06-08 10:43:26 -0700212
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800213 EditEntry entry = findEntryForView(v);
214 entry.data = checkBox.isChecked() ? "1" : "0";
Evan Millar7e4accf2009-06-08 10:43:26 -0700215
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800216 mContactChanged = true;
217 break;
218 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700219
220 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700221 case R.id.entry_group: {
222 EditEntry entry = findEntryForView(v);
223 doPickGroup(entry);
224 break;
225 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700226 */
227
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800228 case R.id.separator: {
229 // Someone clicked on a section header, so handle add action
Evan Millar7e4accf2009-06-08 10:43:26 -0700230 // TODO: Data addition is still being hashed out.
231 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800232 int sectionType = (Integer) v.getTag();
233 doAddAction(sectionType);
Evan Millar7e4accf2009-06-08 10:43:26 -0700234 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800235 break;
236 }
237
238 case R.id.saveButton:
239 doSaveAction();
240 break;
241
242 case R.id.discardButton:
243 doRevertAction();
244 break;
245
246 case R.id.delete: {
247 EditEntry entry = findEntryForView(v);
248 if (entry != null) {
249 // Clear the text and hide the view so it gets saved properly
250 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
251 entry.view.setVisibility(View.GONE);
252 entry.isDeleted = true;
253 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700254
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800255 // Force rebuild of views because section headers might need to change
256 buildViews();
257 break;
258 }
259
260 case R.id.label: {
261 EditEntry entry = findEntryForView(v);
262 if (entry != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700263 String[] labels = getLabelsForMimetype(this, entry.mimetype);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800264 LabelPickedListener listener = new LabelPickedListener(entry, labels);
265 new AlertDialog.Builder(EditContactActivity.this)
266 .setItems(labels, listener)
267 .setTitle(R.string.selectLabel)
268 .show();
269 }
270 break;
271 }
272 }
273 }
274
275 private void setPhotoPresent(boolean present) {
276 mPhotoPresent = present;
Evan Millar7e4accf2009-06-08 10:43:26 -0700277
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800278 // Correctly scale the contact photo if present, otherwise just center
279 // the photo placeholder icon.
280 if (mPhotoPresent) {
281 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
282 } else {
283 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
284 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
285 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700286
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800287 if (mPhotoMenuItem != null) {
288 if (present) {
289 mPhotoMenuItem.setTitle(R.string.removePicture);
290 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
291 } else {
292 mPhotoMenuItem.setTitle(R.string.addPicture);
293 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
294 }
295 }
296 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700297
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800298 private EditEntry findEntryForView(View v) {
299 // Try to find the entry for this view
300 EditEntry entry = null;
301 do {
302 Object tag = v.getTag();
303 if (tag != null && tag instanceof EditEntry) {
304 entry = (EditEntry) tag;
305 break;
306 } else {
307 ViewParent parent = v.getParent();
308 if (parent != null && parent instanceof View) {
309 v = (View) parent;
310 } else {
311 v = null;
312 }
313 }
314 } while (v != null);
315 return entry;
316 }
317
318 private DialogInterface.OnClickListener mDeleteContactDialogListener =
319 new DialogInterface.OnClickListener() {
320 public void onClick(DialogInterface dialog, int button) {
321 mResolver.delete(mUri, null, null);
322 finish();
323 }
324 };
325
326 private boolean mMobilePhoneAdded = false;
327 private boolean mPrimaryEmailAdded = false;
328
329 @Override
330 protected void onCreate(Bundle icicle) {
331 super.onCreate(icicle);
332
333 mResolver = getContentResolver();
334
335 // Build the list of sections
336 setupSections();
337
338 // Load the UI
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800339 mInflater = getLayoutInflater();
340 mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
341 setContentView(mContentView);
Evan Millar7e4accf2009-06-08 10:43:26 -0700342
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800343 mLayout = (LinearLayout) findViewById(R.id.list);
344 mNameView = (EditText) findViewById(R.id.name);
345 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
346 mPhotoImageView.setOnClickListener(this);
347 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
Evan Millar7e4accf2009-06-08 10:43:26 -0700348
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800349 // Setup the bottom buttons
350 View view = findViewById(R.id.saveButton);
351 view.setOnClickListener(this);
352 view = findViewById(R.id.discardButton);
353 view.setOnClickListener(this);
354
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800355 // Resolve the intent
356 mState = STATE_UNKNOWN;
357 Intent intent = getIntent();
358 String action = intent.getAction();
359 mUri = intent.getData();
Evan Millar7e4accf2009-06-08 10:43:26 -0700360 mAggDataUri = Uri.withAppendedPath(mUri, "data");
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800361 if (mUri != null) {
362 if (action.equals(Intent.ACTION_EDIT)) {
363 if (icicle == null) {
364 // Build the entries & views
365 buildEntriesForEdit(getIntent().getExtras());
366 buildViews();
367 }
368 setTitle(R.string.editContact_title_edit);
369 mState = STATE_EDIT;
370 } else if (action.equals(Intent.ACTION_INSERT)) {
371 if (icicle == null) {
372 // Build the entries & views
Evan Millar7e4accf2009-06-08 10:43:26 -0700373 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800374 buildEntriesForInsert(getIntent().getExtras());
375 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700376 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800377 }
378 setTitle(R.string.editContact_title_insert);
379 mState = STATE_INSERT;
380 mInsert = true;
381 }
382 }
383
384 if (mState == STATE_UNKNOWN) {
385 Log.e(TAG, "Cannot resolve intent: " + intent);
386 finish();
387 return;
388 }
389
390 if (mState == STATE_EDIT) {
391 setTitle(getResources().getText(R.string.editContact_title_edit));
392 } else {
393 setTitle(getResources().getText(R.string.editContact_title_insert));
394 }
395 }
396
397 private void setupSections() {
398 mSections.add(mPhoneEntries);
399 mSections.add(mEmailEntries);
400 mSections.add(mImEntries);
401 mSections.add(mPostalEntries);
402 mSections.add(mOrgEntries);
403 mSections.add(mNoteEntries);
404 mSections.add(mOtherEntries);
405 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700406
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800407 @Override
408 protected void onSaveInstanceState(Bundle outState) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700409
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800410 // To store current focus between config changes, follow focus down the
411 // view tree, keeping track of any parents with EditEntry tags
412 View focusedChild = mContentView.getFocusedChild();
413 EditEntry focusedEntry = null;
414 while (focusedChild != null) {
415 Object tag = focusedChild.getTag();
416 if (tag instanceof EditEntry) {
417 focusedEntry = (EditEntry) tag;
418 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700419
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800420 // Keep going deeper until child isn't a group
421 if (focusedChild instanceof ViewGroup) {
422 View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
423 if (deeperFocus != null) {
424 focusedChild = deeperFocus;
425 } else {
426 break;
427 }
428 } else {
429 break;
430 }
431 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700432
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800433 if (focusedChild != null) {
434 int requestFocusId = focusedChild.getId();
435 int requestCursor = 0;
436 if (focusedChild instanceof EditText) {
437 requestCursor = ((EditText) focusedChild).getSelectionStart();
438 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700439
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800440 // Store focus values in EditEntry if found, otherwise store as
441 // generic values
442 if (focusedEntry != null) {
443 focusedEntry.requestFocusId = requestFocusId;
444 focusedEntry.requestCursor = requestCursor;
445 } else {
446 outState.putInt("requestFocusId", requestFocusId);
447 outState.putInt("requestCursor", requestCursor);
448 }
449 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700450
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800451 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
452 outState.putParcelableArrayList("emailEntries", mEmailEntries);
453 outState.putParcelableArrayList("imEntries", mImEntries);
454 outState.putParcelableArrayList("postalEntries", mPostalEntries);
455 outState.putParcelableArrayList("orgEntries", mOrgEntries);
456 outState.putParcelableArrayList("noteEntries", mNoteEntries);
457 outState.putParcelableArrayList("otherEntries", mOtherEntries);
458 outState.putInt("state", mState);
459 outState.putBoolean("insert", mInsert);
460 outState.putParcelable("uri", mUri);
461 outState.putString("name", mNameView.getText().toString());
462 outState.putParcelable("photo", mPhoto);
463 outState.putBoolean("photoChanged", mPhotoChanged);
464 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
465 outState.putBoolean("contactChanged", mContactChanged);
466 }
467
468 @Override
469 protected void onRestoreInstanceState(Bundle inState) {
470 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
471 mEmailEntries = inState.getParcelableArrayList("emailEntries");
472 mImEntries = inState.getParcelableArrayList("imEntries");
473 mPostalEntries = inState.getParcelableArrayList("postalEntries");
474 mOrgEntries = inState.getParcelableArrayList("orgEntries");
475 mNoteEntries = inState.getParcelableArrayList("noteEntries");
476 mOtherEntries = inState.getParcelableArrayList("otherEntries");
477 setupSections();
478
479 mState = inState.getInt("state");
480 mInsert = inState.getBoolean("insert");
481 mUri = inState.getParcelable("uri");
482 mNameView.setText(inState.getString("name"));
483 mPhoto = inState.getParcelable("photo");
484 if (mPhoto != null) {
485 mPhotoImageView.setImageBitmap(mPhoto);
486 setPhotoPresent(true);
487 } else {
488 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
489 setPhotoPresent(false);
490 }
491 mPhotoChanged = inState.getBoolean("photoChanged");
492 mPhoneticNameView.setText(inState.getString("phoneticName"));
493 mContactChanged = inState.getBoolean("contactChanged");
494
495 // Now that everything is restored, build the view
496 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700497
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800498 // Try restoring any generally requested focus
499 int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
500 View focusedChild = mContentView.findViewById(requestFocusId);
501 if (focusedChild != null) {
502 focusedChild.requestFocus();
503 if (focusedChild instanceof EditText) {
504 int requestCursor = inState.getInt("requestCursor", 0);
505 ((EditText) focusedChild).setSelection(requestCursor);
506 }
507 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800508 }
509
510 @Override
511 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
512 if (resultCode != RESULT_OK) {
513 return;
514 }
515
516 switch (requestCode) {
517 case PHOTO_PICKED_WITH_DATA: {
518 final Bundle extras = data.getExtras();
519 if (extras != null) {
520 Bitmap photo = extras.getParcelable("data");
521 mPhoto = photo;
522 mPhotoChanged = true;
523 mPhotoImageView.setImageBitmap(photo);
524 setPhotoPresent(true);
525 }
526 break;
527 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800528 }
529 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700530
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800531 @Override
532 public boolean onKeyDown(int keyCode, KeyEvent event) {
533 switch (keyCode) {
534 case KeyEvent.KEYCODE_BACK: {
535 doSaveAction();
536 return true;
537 }
538 }
539 return super.onKeyDown(keyCode, event);
540 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700541
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800542 @Override
543 public boolean onCreateOptionsMenu(Menu menu) {
544 super.onCreateOptionsMenu(menu);
545 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
546 .setIcon(android.R.drawable.ic_menu_save)
547 .setAlphabeticShortcut('\n');
548 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
549 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
550 .setAlphabeticShortcut('q');
551 if (!mInsert) {
552 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
553 .setIcon(android.R.drawable.ic_menu_delete);
554 }
555
556 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
557 // Updates the state of the menu item
558 setPhotoPresent(mPhotoPresent);
559
560 return true;
561 }
562
563 @Override
564 public boolean onOptionsItemSelected(MenuItem item) {
565 switch (item.getItemId()) {
566 case MENU_ITEM_SAVE:
567 doSaveAction();
568 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700569
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800570 case MENU_ITEM_DONT_SAVE:
571 doRevertAction();
572 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700573
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800574 case MENU_ITEM_DELETE:
575 // Get confirmation
576 showDialog(DELETE_CONFIRMATION_DIALOG);
577 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700578
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800579 case MENU_ITEM_PHOTO:
580 if (!mPhotoPresent) {
581 doPickPhotoAction();
582 } else {
583 doRemovePhotoAction();
584 }
585 return true;
586 }
587
588 return false;
589 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700590
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800591 /**
592 * Try guessing the next-best type of {@link EditEntry} to insert into the
593 * given list. We walk down the precedence list until we find a type that
594 * doesn't exist yet, or default to the lowest ranking type.
595 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700596 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800597 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
598 // Keep track of the types we've seen already
599 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
600 for (int i = entries.size() - 1; i >= 0; i--) {
601 EditEntry entry = entries.get(i);
602 if (!entry.isDeleted) {
603 existAlready.put(entry.type, true);
604 }
605 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700606
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800607 // Pick the first item we haven't seen
608 for (int type : precedenceList) {
609 if (!existAlready.get(type, false)) {
610 return type;
611 }
612 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700613
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 // Otherwise default to last item
615 return precedenceList[precedenceList.length - 1];
616 }
617
Evan Millar54a5c9f2009-06-23 17:41:09 -0700618 // TODO When this gets brought back we'll need to use the new TypePrecedence class instead of
619 // the older local TYPE_PRECEDENCE* contstants.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800620 private void doAddAction(int sectionType) {
621 EditEntry entry = null;
622 switch (sectionType) {
623 case SECTION_PHONES: {
624 // Try figuring out which type to insert next
625 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
Evan Millar7e4accf2009-06-08 10:43:26 -0700626 entry = EditEntry.newPhoneEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800627 nextType);
628 mPhoneEntries.add(entry);
629 break;
630 }
631 case SECTION_EMAIL: {
632 // Try figuring out which type to insert next
Evan Millar7e4accf2009-06-08 10:43:26 -0700633 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_EMAIL);
634 entry = EditEntry.newEmailEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800635 nextType);
636 mEmailEntries.add(entry);
637 break;
638 }
639 case SECTION_IM: {
640 // Try figuring out which type to insert next
641 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
Evan Millar7e4accf2009-06-08 10:43:26 -0700642 entry = EditEntry.newImEntry(EditContactActivity.this, Data.CONTENT_URI, nextType);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800643 mImEntries.add(entry);
644 break;
645 }
646 case SECTION_POSTAL: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700647 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_POSTAL);
648 entry = EditEntry.newPostalEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800649 nextType);
650 mPostalEntries.add(entry);
651 break;
652 }
653 case SECTION_ORG: {
654 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
Evan Millar7e4accf2009-06-08 10:43:26 -0700655 entry = EditEntry.newOrganizationEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800656 nextType);
657 mOrgEntries.add(entry);
658 break;
659 }
660 case SECTION_NOTE: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700661 entry = EditEntry.newNotesEntry(EditContactActivity.this, Data.CONTENT_URI);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800662 mNoteEntries.add(entry);
663 break;
664 }
665 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700666
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800667 // Rebuild the views if needed
668 if (entry != null) {
669 buildViews();
670 mContactChanged = true;
671
672 View dataView = entry.view.findViewById(R.id.data);
673 if (dataView == null) {
674 entry.view.requestFocus();
675 } else {
676 dataView.requestFocus();
677 }
678 }
679 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700680 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800681
682 private void doRevertAction() {
683 finish();
684 }
685
686 private void doPickPhotoAction() {
687 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
688 // TODO: get these values from constants somewhere
689 intent.setType("image/*");
690 intent.putExtra("crop", "true");
691 intent.putExtra("aspectX", 1);
692 intent.putExtra("aspectY", 1);
693 intent.putExtra("outputX", 96);
694 intent.putExtra("outputY", 96);
695 try {
696 intent.putExtra("return-data", true);
697 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
698 } catch (ActivityNotFoundException e) {
699 new AlertDialog.Builder(EditContactActivity.this)
700 .setTitle(R.string.errorDialogTitle)
701 .setMessage(R.string.photoPickerNotFoundText)
702 .setPositiveButton(android.R.string.ok, null)
703 .show();
704 }
705 }
706
707 private void doRemovePhotoAction() {
708 mPhoto = null;
709 mPhotoChanged = true;
710 setPhotoPresent(false);
711 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700712
713 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700714 private void populateGroups() {
715 // Create a list of all the groups
716 Cursor cursor = mResolver.query(Groups.CONTENT_URI, ContactsListActivity.GROUPS_PROJECTION,
717 null, null, Groups.DEFAULT_SORT_ORDER);
718 try {
719 ArrayList<Long> ids = new ArrayList<Long>();
720 ArrayList<String> items = new ArrayList<String>();
721
722 while (cursor.moveToNext()) {
723 String systemId = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
724 String name = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700725
Alex Kennberg87fc3172009-03-28 06:43:06 -0700726 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
727 continue;
728 }
729
730 if (!TextUtils.isEmpty(name)) {
731 ids.add(new Long(cursor.getLong(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID)));
732 items.add(name);
733 }
734 }
735
736 mGroups = items.toArray(new CharSequence[items.size()]);
737 mInTheGroup = new boolean[items.size()];
738 } finally {
739 cursor.close();
740 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700741
Alex Kennberg87fc3172009-03-28 06:43:06 -0700742 if (mGroups != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700743
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700744 // Go through the groups for this member and update the list
Alex Kennberg87fc3172009-03-28 06:43:06 -0700745 final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700746 Cursor groupCursor = null;
747 try {
748 groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
749 null, null, Groups.DEFAULT_SORT_ORDER);
750 } catch (IllegalArgumentException e) {
751 // Contact is new, so we don't need to do any work.
752 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700753
Alex Kennberg87fc3172009-03-28 06:43:06 -0700754 if (groupCursor != null) {
755 try {
756 while (groupCursor.moveToNext()) {
757 String systemId = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
758 String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700759
Alex Kennberg87fc3172009-03-28 06:43:06 -0700760 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
761 continue;
762 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700763
Alex Kennberg87fc3172009-03-28 06:43:06 -0700764 if (!TextUtils.isEmpty(name)) {
765 for (int i = 0; i < mGroups.length; i++) {
766 if (name.equals(mGroups[i])) {
767 mInTheGroup[i] = true;
768 break;
769 }
770 }
771 }
772 }
773 } finally {
774 groupCursor.close();
775 }
776 }
777 }
778 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700779
Alex Kennberg87fc3172009-03-28 06:43:06 -0700780 private String generateGroupList() {
781 StringBuilder groupList = new StringBuilder();
782 for (int i = 0; mGroups != null && i < mGroups.length; i++) {
783 if (mInTheGroup[i]) {
784 if (groupList.length() == 0) {
785 groupList.append(mGroups[i]);
786 } else {
787 groupList.append(getString(R.string.group_list, mGroups[i]));
788 }
789 }
790 }
791 return groupList.length() > 0 ? groupList.toString() : null;
792 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700793
Alex Kennberg87fc3172009-03-28 06:43:06 -0700794 private void doPickGroup(EditEntry entry) {
795 if (mGroups != null) {
796 GroupDialogListener listener = new GroupDialogListener(this, entry);
Evan Millar7e4accf2009-06-08 10:43:26 -0700797
Alex Kennberg87fc3172009-03-28 06:43:06 -0700798 new AlertDialog.Builder(EditContactActivity.this)
799 .setTitle(R.string.label_groups)
800 .setMultiChoiceItems(mGroups, mInTheGroup, listener)
801 .setPositiveButton(android.R.string.ok, listener)
802 .setNegativeButton(android.R.string.cancel, null)
803 .show();
804 }
805 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700806 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700807
808 /** Handles the clicks in the groups dialog */
Evan Millar7e4accf2009-06-08 10:43:26 -0700809 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700810 private static final class GroupDialogListener implements DialogInterface.OnClickListener,
811 DialogInterface.OnMultiChoiceClickListener {
Evan Millar7e4accf2009-06-08 10:43:26 -0700812
Alex Kennberg87fc3172009-03-28 06:43:06 -0700813 private EditContactActivity mEditContactActivity;
814 private EditEntry mEntry;
815 private boolean[] mInTheGroup;
Evan Millar7e4accf2009-06-08 10:43:26 -0700816
Alex Kennberg87fc3172009-03-28 06:43:06 -0700817 public GroupDialogListener(EditContactActivity editContactActivity, EditEntry entry) {
818 mEditContactActivity = editContactActivity;
819 mEntry = entry;
820 mInTheGroup = editContactActivity.mInTheGroup.clone();
821 }
822
Evan Millar7e4accf2009-06-08 10:43:26 -0700823 // Called when the dialog's ok button is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700824 public void onClick(DialogInterface dialog, int which) {
825 mEditContactActivity.mInTheGroup = mInTheGroup;
826 mEntry.data = mEditContactActivity.generateGroupList();
827 mEditContactActivity.updateDataView(mEntry, mEntry.data);
828 }
829
Evan Millar7e4accf2009-06-08 10:43:26 -0700830 // Called when each group is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700831 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
832 mInTheGroup[which] = isChecked;
833 }
834 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700835 */
836
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800837 private void updateDataView(EditEntry entry, String text) {
838 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
839 dataView.setText(text);
840 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700841
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800842 @Override
843 protected Dialog onCreateDialog(int id) {
844 switch (id) {
845 case DELETE_CONFIRMATION_DIALOG:
846 return new AlertDialog.Builder(EditContactActivity.this)
847 .setTitle(R.string.deleteConfirmation_title)
848 .setIcon(android.R.drawable.ic_dialog_alert)
849 .setMessage(R.string.deleteConfirmation)
850 .setNegativeButton(android.R.string.cancel, null)
851 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
852 .setCancelable(false)
853 .create();
854 }
855 return super.onCreateDialog(id);
856 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700857
858 static String[] getLabelsForMimetype(Context context, String mimetype) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800859 final Resources resources = context.getResources();
Evan Millar7e4accf2009-06-08 10:43:26 -0700860 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
861 return resources.getStringArray(android.R.array.phoneTypes);
862 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
863 return resources.getStringArray(android.R.array.emailAddressTypes);
864 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
865 return resources.getStringArray(android.R.array.postalAddressTypes);
866 } else if (mimetype.equals(Im.CONTENT_ITEM_TYPE)) {
867 return resources.getStringArray(android.R.array.imProtocols);
868 } else if (mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
869 return resources.getStringArray(android.R.array.organizationTypes);
870 } else {
871 return resources.getStringArray(R.array.otherLabels);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800872 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800873 }
874
875 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
876 // In the UI Custom... comes last, but it is uses the constant 0
877 // so it is in the same location across the various kinds. Fix up the
878 // position to a valid type here.
879 if (labelPosition == labels.length - 1) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700880 return BaseTypes.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800881 } else {
882 return labelPosition + 1;
883 }
884 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700885
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800886 private EditEntry getOtherEntry(String column) {
887 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
888 EditEntry entry = mOtherEntries.get(i);
889 if (isOtherEntry(entry, column)) {
890 return entry;
891 }
892 }
893 return null;
894 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700895
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800896 private static boolean isOtherEntry(EditEntry entry, String column) {
897 return entry != null && entry.column != null && entry.column.equals(column);
898 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700899
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800900 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
901 final EditText label = new EditText(this);
902 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
903 label.requestFocus();
904 new AlertDialog.Builder(this)
905 .setView(label)
906 .setTitle(R.string.customLabelPickerTitle)
907 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
908 public void onClick(DialogInterface dialog, int which) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700909 entry.setLabel(EditContactActivity.this, BaseTypes.TYPE_CUSTOM,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800910 label.getText().toString());
911 mContactChanged = true;
912
913 if (addTo != null) {
914 addTo.add(entry);
915 buildViews();
916 entry.view.requestFocus(View.FOCUS_DOWN);
917 }
918 }
919 })
920 .setNegativeButton(android.R.string.cancel, null)
921 .show();
922 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700923
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800924 /**
925 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
926 */
927 private void doSaveAction() {
928 // Save or create the contact if needed
929 switch (mState) {
930 case STATE_EDIT:
931 save();
932 break;
933
Evan Millar7e4accf2009-06-08 10:43:26 -0700934 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800935 case STATE_INSERT:
936 create();
937 break;
Evan Millar7e4accf2009-06-08 10:43:26 -0700938 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800939
940 default:
941 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
942 break;
943 }
944 finish();
945 }
Alex Kennberg87fc3172009-03-28 06:43:06 -0700946
947 /**
948 * Gets the group id based on group name.
Evan Millar7e4accf2009-06-08 10:43:26 -0700949 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700950 * @param resolver the resolver to use
951 * @param groupName the name of the group to add the contact to
952 * @return the id of the group
953 * @throws IllegalStateException if the group can't be found
954 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700955 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700956 private long getGroupId(ContentResolver resolver, String groupName) {
957 long groupId = 0;
958 Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUP_ID_PROJECTION,
959 Groups.NAME + "=?", new String[] { groupName }, null);
960 if (groupsCursor != null) {
961 try {
962 if (groupsCursor.moveToFirst()) {
963 groupId = groupsCursor.getLong(0);
964 }
965 } finally {
966 groupsCursor.close();
967 }
968 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700969
Alex Kennberg87fc3172009-03-28 06:43:06 -0700970 if (groupId == 0) {
971 throw new IllegalStateException("Failed to find the " + groupName + "group");
972 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700973
Alex Kennberg87fc3172009-03-28 06:43:06 -0700974 return groupId;
975 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700976 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700977
978 /**
979 * Deletes group membership based on person and group ids.
Evan Millar7e4accf2009-06-08 10:43:26 -0700980 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700981 * @param personId the person id
982 * @param groupId the group id
983 * @return the id of the group membership
984 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700985 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700986 private void deleteGroupMembership(long personId, long groupId) {
987 long groupMembershipId = 0;
988 Cursor groupsCursor = mResolver.query(GroupMembership.CONTENT_URI, GROUPMEMBERSHIP_ID_PROJECTION,
989 GroupMembership.PERSON_ID + "=? AND " + GroupMembership.GROUP_ID + "=?",
990 new String[] {String.valueOf(personId), String.valueOf(groupId)}, null);
991 if (groupsCursor != null) {
992 try {
993 if (groupsCursor.moveToFirst()) {
994 groupMembershipId = groupsCursor.getLong(0);
995 }
996 } finally {
997 groupsCursor.close();
998 }
999 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001000
Alex Kennberg87fc3172009-03-28 06:43:06 -07001001 if (groupMembershipId != 0) {
1002 final Uri groupsUri = ContentUris.withAppendedId(
1003 GroupMembership.CONTENT_URI,groupMembershipId);
1004 mResolver.delete(groupsUri, null, null);
1005 }
1006 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001007 */
1008
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001009 /**
1010 * Save the various fields to the existing contact.
1011 */
1012 private void save() {
1013 ContentValues values = new ContentValues();
1014 String data;
1015 int numValues = 0;
1016
1017 // Handle the name and send to voicemail specially
1018 final String name = mNameView.getText().toString();
1019 if (name != null && TextUtils.isGraphic(name)) {
1020 numValues++;
1021 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001022
Evan Millar7e4accf2009-06-08 10:43:26 -07001023 values.put(StructuredName.DISPLAY_NAME, name);
1024 /*
1025 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1026 */
1027 mResolver.update(mStructuredNameUri, values, null, null);
1028
1029 // This will go down in for loop somewhere
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001030 if (mPhotoChanged) {
1031 // Only write the photo if it's changed, since we don't initially load mPhoto
Evan Millar7e4accf2009-06-08 10:43:26 -07001032 values.clear();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001033 if (mPhoto != null) {
1034 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1035 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
Evan Millar7e4accf2009-06-08 10:43:26 -07001036 values.put(Photo.PHOTO, stream.toByteArray());
1037 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001038 } else {
Evan Millar7e4accf2009-06-08 10:43:26 -07001039 values.putNull(Photo.PHOTO);
1040 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001041 }
1042 }
1043
1044 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1045 for (int i = 0; i < entryCount; i++) {
1046 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001047 data = entry.getData();
1048 boolean empty = data == null || !TextUtils.isGraphic(data);
Evan Millar7e4accf2009-06-08 10:43:26 -07001049 /*
1050 if (kind == EditEntry.KIND_GROUP) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001051 if (entry.id != 0) {
1052 for (int g = 0; g < mGroups.length; g++) {
1053 long groupId = getGroupId(mResolver, mGroups[g].toString());
1054 if (mInTheGroup[g]) {
1055 Contacts.People.addToGroup(mResolver, entry.id, groupId);
1056 numValues++;
1057 } else {
1058 deleteGroupMembership(entry.id, groupId);
1059 }
1060 }
1061 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001062 }
1063 */
1064 if (!empty) {
1065 values.clear();
1066 entry.toValues(values);
1067 if (entry.id != 0) {
1068 mResolver.update(entry.uri, values, null, null);
1069 } else {
1070 /* mResolver.insert(entry.uri, values); */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001071 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001072 } else if (entry.id != 0) {
1073 mResolver.delete(entry.uri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001074 }
1075 }
1076
Evan Millar7e4accf2009-06-08 10:43:26 -07001077 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001078 if (numValues == 0) {
1079 // The contact is completely empty, delete it
1080 mResolver.delete(mUri, null, null);
1081 mUri = null;
1082 setResult(RESULT_CANCELED);
1083 } else {
1084 // Add the entry to the my contacts group if it isn't there already
1085 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1086 setResult(RESULT_OK, new Intent().setData(mUri));
1087
1088 // Only notify user if we actually changed contact
1089 if (mContactChanged || mPhotoChanged) {
1090 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1091 }
1092 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001093 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001094 }
1095
1096 /**
1097 * Takes the entered data and saves it to a new contact.
1098 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001099 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001100 private void create() {
1101 ContentValues values = new ContentValues();
1102 String data;
1103 int numValues = 0;
1104
1105 // Create the contact itself
1106 final String name = mNameView.getText().toString();
1107 if (name != null && TextUtils.isGraphic(name)) {
1108 numValues++;
1109 }
1110 values.put(People.NAME, name);
1111 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1112
1113 // Add the contact to the My Contacts group
1114 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1115
1116 // Add the contact to the group that is being displayed in the contact list
1117 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1118 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1119 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1120 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1121 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
1122 null);
1123 if (!TextUtils.isEmpty(displayGroup)) {
1124 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1125 }
1126 } else {
1127 // Check to see if we're not syncing everything and if so if My Contacts is synced.
1128 // If it isn't then the created contact can end up not in any groups that are
1129 // currently synced and end up getting removed from the phone, which is really bad.
1130 boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
1131 Contacts.Settings.SYNC_EVERYTHING));
1132 if (!syncingEverything) {
1133 boolean syncingMyContacts = false;
1134 Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
1135 Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
1136 if (c != null) {
1137 try {
1138 if (c.moveToFirst()) {
1139 syncingMyContacts = !"0".equals(c.getString(0));
1140 }
1141 } finally {
1142 c.close();
1143 }
1144 }
1145
1146 if (!syncingMyContacts) {
1147 // Not syncing My Contacts, so find a group that is being synced and stick
1148 // the contact in there. We sort the list so at least all contacts
1149 // will appear in the same group.
1150 c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
1151 Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
1152 if (c != null) {
1153 try {
1154 if (c.moveToFirst()) {
1155 People.addToGroup(mResolver, ContentUris.parseId(contactUri),
1156 c.getLong(0));
1157 }
1158 } finally {
1159 c.close();
1160 }
1161 }
1162 }
1163 }
1164 }
1165
1166 // Handle the photo
1167 if (mPhoto != null) {
1168 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1169 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1170 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1171 }
1172
1173 // Create the contact methods
1174 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1175 for (int i = 0; i < entryCount; i++) {
1176 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
Alex Kennbergfb0386a2009-04-02 09:59:10 -07001177 if (entry.kind == EditEntry.KIND_GROUP) {
1178 long contactId = ContentUris.parseId(contactUri);
1179 for (int g = 0; g < mGroups.length; g++) {
1180 if (mInTheGroup[g]) {
1181 long groupId = getGroupId(mResolver, mGroups[g].toString());
1182 People.addToGroup(mResolver, contactId, groupId);
1183 numValues++;
1184 }
1185 }
1186 } else if (entry.kind != EditEntry.KIND_CONTACT) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001187 values.clear();
1188 if (entry.toValues(values)) {
1189 // Only create the entry if there is data
1190 entry.uri = mResolver.insert(
1191 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1192 entry.id = ContentUris.parseId(entry.uri);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001193 }
1194 } else {
1195 // Update the contact with any straggling data, like notes
1196 data = entry.getData();
1197 values.clear();
1198 if (data != null && TextUtils.isGraphic(data)) {
1199 values.put(entry.column, data);
1200 mResolver.update(contactUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001201 }
1202 }
1203 }
1204
1205 if (numValues == 0) {
1206 mResolver.delete(contactUri, null, null);
1207 setResult(RESULT_CANCELED);
1208 } else {
1209 mUri = contactUri;
1210 Intent resultIntent = new Intent()
1211 .setData(mUri)
1212 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1213 setResult(RESULT_OK, resultIntent);
1214 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1215 }
1216 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001217 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001218
1219 /**
1220 * Build up the entries to display on the screen.
1221 *
1222 * @param extras the extras used to start this activity, may be null
1223 */
1224 private void buildEntriesForEdit(Bundle extras) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001225 Cursor aggCursor = mResolver.query(mAggDataUri, AGGREGATE_PROJECTION, null, null, null);
1226 if (aggCursor == null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001227 Log.e(TAG, "invalid contact uri: " + mUri);
1228 finish();
1229 return;
Evan Millar7e4accf2009-06-08 10:43:26 -07001230 } else if (!aggCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001231 Log.e(TAG, "invalid contact uri: " + mUri);
1232 finish();
Evan Millar7e4accf2009-06-08 10:43:26 -07001233 aggCursor.close();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001234 return;
1235 }
1236
1237 // Clear out the old entries
1238 int numSections = mSections.size();
1239 for (int i = 0; i < numSections; i++) {
1240 mSections.get(i).clear();
1241 }
1242
1243 EditEntry entry;
1244
Evan Millar7e4accf2009-06-08 10:43:26 -07001245 while (aggCursor.moveToNext()) {
1246 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
1247 boolean isSuperPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001248
Evan Millar7e4accf2009-06-08 10:43:26 -07001249 final long id = aggCursor.getLong(DATA_ID_COLUMN);
1250 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
1251
1252 if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
1253 mNameView.setText(aggCursor.getString(DATA_9_COLUMN));
1254 mNameView.addTextChangedListener(this);
1255 mStructuredNameUri = uri;
1256 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
1257 mPhoto = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
1258 if (mPhoto == null) {
1259 setPhotoPresent(false);
1260 } else {
1261 setPhotoPresent(true);
1262 mPhotoImageView.setImageBitmap(mPhoto);
1263 }
1264 mPhotoDataUri = uri;
1265 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
1266 int type = aggCursor.getInt(DATA_1_COLUMN);
1267 String label = aggCursor.getString(DATA_2_COLUMN);
1268 String company = aggCursor.getString(DATA_3_COLUMN);
1269 String title = aggCursor.getString(DATA_4_COLUMN);
1270
1271 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1272 entry.isPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
1273 mOrgEntries.add(entry);
1274 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
1275 entry = EditEntry.newNotesEntry(this, aggCursor.getString(DATA_1_COLUMN),
1276 uri, id);
1277 mNoteEntries.add(entry);
Evan Millar7e4accf2009-06-08 10:43:26 -07001278 } else if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
1279 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
1280 || mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)
1281 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1282 int type = aggCursor.getInt(DATA_1_COLUMN);
1283 String data = aggCursor.getString(DATA_2_COLUMN);
1284 String label = aggCursor.getString(DATA_3_COLUMN);
1285
1286 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
1287 // Add a phone number entry
1288 entry = EditEntry.newPhoneEntry(this, label, type, data, uri, id);
1289 entry.isPrimary = isSuperPrimary;
1290 mPhoneEntries.add(entry);
1291
1292 // Keep track of which primary types have been added
1293 if (type == Phone.TYPE_MOBILE) {
1294 mMobilePhoneAdded = true;
1295 }
1296 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
1297 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1298 entry.isPrimary = isSuperPrimary;
1299 mEmailEntries.add(entry);
1300
1301 if (isSuperPrimary) {
1302 mPrimaryEmailAdded = true;
1303 }
1304 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
1305 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1306 entry.isPrimary = isSuperPrimary;
1307 mPostalEntries.add(entry);
1308 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1309 String protocolStr = aggCursor.getString(DATA_5_COLUMN);
1310 Object protocolObj = ContactsUtils.decodeImProtocol(protocolStr);
1311 if (protocolObj == null) {
1312 // Invalid IM protocol, log it then ignore.
1313 Log.e(TAG, "Couldn't decode IM protocol: " + protocolStr);
1314 continue;
1315 } else {
1316 if (protocolObj instanceof Number) {
1317 int protocol = ((Number) protocolObj).intValue();
1318 entry = EditEntry.newImEntry(this,
1319 getLabelsForMimetype(this, mimetype)[protocol], protocol,
1320 data, uri, id);
1321 } else {
1322 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1323 uri, id);
1324 }
1325 mImEntries.add(entry);
1326 }
1327 }
1328 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001329 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001330
1331 /*
1332 // Groups
1333 populateGroups();
1334 if (mGroups != null) {
1335 entry = EditEntry.newGroupEntry(this, generateGroupList(), mUri,
1336 personCursor.getLong(0));
1337 mOtherEntries.add(entry);
1338 }
1339
1340 // Phonetic name
1341 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
1342 mPhoneticNameView.addTextChangedListener(this);
1343
1344
1345 // Add values from the extras, if there are any
1346 if (extras != null) {
1347 addFromExtras(extras, phonesUri, methodsUri);
1348 }
1349
1350 // Add the base types if needed
1351 if (!mMobilePhoneAdded) {
1352 entry = EditEntry.newPhoneEntry(this,
1353 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1354 DEFAULT_PHONE_TYPE);
1355 mPhoneEntries.add(entry);
1356 }
1357
1358 if (!mPrimaryEmailAdded) {
1359 entry = EditEntry.newEmailEntry(this,
1360 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1361 DEFAULT_EMAIL_TYPE);
1362 entry.isPrimary = true;
1363 mEmailEntries.add(entry);
1364 }
1365 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001366
1367 mContactChanged = false;
1368 }
1369
1370 /**
1371 * Build the list of EditEntries for full mode insertions.
Evan Millar7e4accf2009-06-08 10:43:26 -07001372 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001373 * @param extras the extras used to start this activity, may be null
1374 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001375 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001376 private void buildEntriesForInsert(Bundle extras) {
1377 // Clear out the old entries
1378 int numSections = mSections.size();
1379 for (int i = 0; i < numSections; i++) {
1380 mSections.get(i).clear();
1381 }
1382
1383 EditEntry entry;
1384
1385 // Check the intent extras
1386 if (extras != null) {
1387 addFromExtras(extras, null, null);
1388 }
1389
1390 // Photo
1391 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1392
1393 // Add the base entries if they're not already present
1394 if (!mMobilePhoneAdded) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001395 entry = EditEntry.newPhoneEntry(this, null, Phone.TYPE_MOBILE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001396 entry.isPrimary = true;
1397 mPhoneEntries.add(entry);
1398 }
1399
1400 if (!mPrimaryEmailAdded) {
1401 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1402 entry.isPrimary = true;
1403 mEmailEntries.add(entry);
1404 }
1405
Alex Kennberg87fc3172009-03-28 06:43:06 -07001406 // Group
1407 populateGroups();
1408 if (mGroups != null) {
1409 entry = EditEntry.newGroupEntry(this, null, mUri, 0);
1410 mOtherEntries.add(entry);
1411 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001412 }
1413
1414 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1415 EditEntry entry;
1416
1417 // Read the name from the bundle
1418 CharSequence name = extras.getCharSequence(Insert.NAME);
1419 if (name != null && TextUtils.isGraphic(name)) {
1420 mNameView.setText(name);
1421 }
1422
1423 // Read the phonetic name from the bundle
1424 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1425 if (!TextUtils.isEmpty(phoneticName)) {
1426 mPhoneticNameView.setText(phoneticName);
1427 }
1428
1429 // Postal entries from extras
1430 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1431 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1432 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1433 postalType = DEFAULT_POSTAL_TYPE;
1434 }
1435
1436 if (postalType != INVALID_TYPE) {
1437 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1438 methodsUri, 0);
1439 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1440 mPostalEntries.add(entry);
1441 }
1442
1443 // Email entries from extras
1444 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1445 Insert.EMAIL_ISPRIMARY);
1446 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1447 null);
1448 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1449 null);
Evan Millar7e4accf2009-06-08 10:43:26 -07001450
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001451 // Phone entries from extras
1452 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1453 Insert.PHONE_ISPRIMARY);
1454 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1455 null);
1456 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1457 null);
1458
1459 // IM entries from extras
1460 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1461 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
Evan Millar7e4accf2009-06-08 10:43:26 -07001462
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001463 if (imHandle != null && imProtocol != null) {
1464 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1465 if (protocolObj instanceof Number) {
1466 int protocol = ((Number) protocolObj).intValue();
1467 entry = EditEntry.newImEntry(this,
Evan Millar7e4accf2009-06-08 10:43:26 -07001468 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001469 imHandle.toString(), methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001470 } else {
1471 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001472 methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001473 }
1474 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1475 mImEntries.add(entry);
1476 }
1477 }
1478
1479 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1480 String typeField, String primaryField) {
1481 CharSequence email = extras.getCharSequence(emailField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001482
1483 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001484 int emailType = INVALID_TYPE;
1485 String customLabel = null;
1486 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001487 emailType = ContactsContract.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001488 customLabel = extras.getString(typeField);
1489 } else {
1490 emailType = extras.getInt(typeField, INVALID_TYPE);
1491 }
1492
1493 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1494 emailType = DEFAULT_EMAIL_TYPE;
1495 mPrimaryEmailAdded = true;
1496 }
1497
1498 if (emailType != INVALID_TYPE) {
1499 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
1500 methodsUri, 0);
1501 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1502 mEmailEntries.add(entry);
1503
1504 // Keep track of which primary types have been added
1505 if (entry.isPrimary) {
1506 mPrimaryEmailAdded = true;
1507 }
1508 }
1509 }
1510
1511 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1512 String typeField, String primaryField) {
1513 CharSequence phoneNumber = extras.getCharSequence(phoneField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001514
1515 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001516 int phoneType = INVALID_TYPE;
1517 String customLabel = null;
1518 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001519 phoneType = Phone.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001520 customLabel = extras.getString(typeField);
1521 } else {
1522 phoneType = extras.getInt(typeField, INVALID_TYPE);
1523 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001524
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001525 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1526 phoneType = DEFAULT_PHONE_TYPE;
1527 }
1528
1529 if (phoneType != INVALID_TYPE) {
1530 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
1531 phoneNumber.toString(), phonesUri, 0);
1532 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1533 mPhoneEntries.add(entry);
1534
1535 // Keep track of which primary types have been added
Evan Millar7e4accf2009-06-08 10:43:26 -07001536 if (phoneType == Phone.TYPE_MOBILE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001537 mMobilePhoneAdded = true;
1538 }
1539 }
1540 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001541 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001542
1543 /**
1544 * Removes all existing views, builds new ones for all the entries, and adds them.
1545 */
1546 private void buildViews() {
1547 // Remove existing views
1548 final LinearLayout layout = mLayout;
1549 layout.removeAllViews();
Evan Millar7e4accf2009-06-08 10:43:26 -07001550
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001551 buildViewsForSection(layout, mPhoneEntries,
1552 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1553 buildViewsForSection(layout, mEmailEntries,
1554 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1555 buildViewsForSection(layout, mImEntries,
1556 R.string.listSeparatorSendIm_edit, SECTION_IM);
1557 buildViewsForSection(layout, mPostalEntries,
1558 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1559 buildViewsForSection(layout, mOrgEntries,
1560 R.string.listSeparatorOrganizations, SECTION_ORG);
1561 buildViewsForSection(layout, mNoteEntries,
1562 R.string.label_notes, SECTION_NOTE);
Evan Millar7e4accf2009-06-08 10:43:26 -07001563
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001564 buildOtherViews(layout, mOtherEntries);
1565 }
1566
1567
1568 /**
1569 * Builds the views for a specific section.
Evan Millar7e4accf2009-06-08 10:43:26 -07001570 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001571 * @param layout the container
1572 * @param section the section to build the views for
1573 */
1574 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1575 int separatorResource, int sectionType) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001576
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001577 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1578 layout.addView(divider);
Evan Millar7e4accf2009-06-08 10:43:26 -07001579
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001580 // Count up undeleted children
1581 int activeChildren = 0;
1582 for (int i = section.size() - 1; i >= 0; i--) {
1583 EditEntry entry = section.get(i);
1584 if (!entry.isDeleted) {
1585 activeChildren++;
1586 }
1587 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001588
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001589 // Build the correct group header based on undeleted children
1590 ViewGroup header;
1591 if (activeChildren == 0) {
1592 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1593 } else {
1594 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
1595 }
1596
1597 // Because we're emulating a ListView, we need to handle focus changes
1598 // with some additional logic.
1599 header.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001600
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001601 TextView text = (TextView) header.findViewById(R.id.text);
1602 text.setText(getText(separatorResource));
Evan Millar7e4accf2009-06-08 10:43:26 -07001603
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001604 // Force TextView to always default color if we have children. This makes sure
1605 // we don't change color when parent is pressed.
1606 if (activeChildren > 0) {
1607 ColorStateList stateList = text.getTextColors();
1608 text.setTextColor(stateList.getDefaultColor());
1609 }
1610
1611 View addView = header.findViewById(R.id.separator);
1612 addView.setTag(Integer.valueOf(sectionType));
1613 addView.setOnClickListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001614
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001615 // Build views for the current section
1616 for (EditEntry entry : section) {
1617 entry.activity = this; // this could be null from when the state is restored
1618 if (!entry.isDeleted) {
1619 View view = buildViewForEntry(entry);
1620 header.addView(view);
1621 }
1622 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001623
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001624 layout.addView(header);
1625 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001626
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001627 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1628 // Build views for the current section, putting a divider between each one
1629 for (EditEntry entry : section) {
1630 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1631 layout.addView(divider);
1632
1633 entry.activity = this; // this could be null from when the state is restored
1634 View view = buildViewForEntry(entry);
1635 view.setOnClickListener(this);
1636 layout.addView(view);
1637 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001638
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001639 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1640 layout.addView(divider);
1641 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001642
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001643 /**
1644 * Builds a view to display an EditEntry.
Evan Millar7e4accf2009-06-08 10:43:26 -07001645 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001646 * @param entry the entry to display
1647 * @return a view that will display the given entry
1648 */
1649 /* package */ View buildViewForEntry(final EditEntry entry) {
1650 // Look for any existing entered text, and save it if found
1651 if (entry.view != null && entry.syncDataWithView) {
1652 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1653 .getText().toString();
1654 if (!TextUtils.isEmpty(enteredText)) {
1655 entry.data = enteredText;
1656 }
1657 }
1658
1659 // Build a new view
1660 final ViewGroup parent = mLayout;
1661 View view;
1662
1663 // Because we're emulating a ListView, we might need to handle focus changes
1664 // with some additional logic.
Evan Millar7e4accf2009-06-08 10:43:26 -07001665 if (entry.mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001666 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
Evan Millar7e4accf2009-06-08 10:43:26 -07001667 /*
1668 else if (entry.mimetype.equals(Group.CONTENT_ITEM_TYPE)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001669 view = mInflater.inflate(R.layout.edit_contact_entry_group, parent, false);
1670 view.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001671 }
1672 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001673 } else if (!entry.isStaticLabel) {
1674 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1675 } else {
1676 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1677 }
1678 entry.view = view;
Evan Millar7e4accf2009-06-08 10:43:26 -07001679
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001680 // Set the entry as the tag so we can find it again later given just the view
1681 view.setTag(entry);
1682
1683 // Bind the label
1684 entry.bindLabel(this);
1685
1686 // Bind data
1687 TextView data = (TextView) view.findViewById(R.id.data);
1688 TextView data2 = (TextView) view.findViewById(R.id.data2);
Evan Millar7e4accf2009-06-08 10:43:26 -07001689
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001690 if (data instanceof Button) {
1691 data.setOnClickListener(this);
1692 }
1693 if (data.length() == 0) {
1694 if (entry.syncDataWithView) {
1695 // If there is already data entered don't overwrite it
1696 data.setText(entry.data);
1697 } else {
1698 fillViewData(entry);
1699 }
1700 }
1701 if (data2 != null && data2.length() == 0) {
1702 // If there is already data entered don't overwrite it
1703 data2.setText(entry.data2);
1704 }
1705 data.setHint(entry.hint);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001706 if (data2 != null) {
1707 data2.setHint(entry.hint2);
1708 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001709 if (entry.lines > 1) {
1710 data.setLines(entry.lines);
1711 data.setMaxLines(entry.maxLines);
1712 if (data2 != null) {
1713 data2.setLines(entry.lines);
1714 data2.setMaxLines(entry.maxLines);
1715 }
1716 }
1717 int contentType = entry.contentType;
1718 if (contentType != EditorInfo.TYPE_NULL) {
1719 data.setInputType(contentType);
1720 if (data2 != null) {
1721 data2.setInputType(contentType);
1722 }
1723 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1724 == EditorInfo.TYPE_CLASS_PHONE) {
1725 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1726 if (data2 != null) {
1727 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1728 }
1729 }
1730 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001731
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001732 // Give focus to children as requested, possibly after a configuration change
1733 View focusChild = view.findViewById(entry.requestFocusId);
1734 if (focusChild != null) {
1735 focusChild.requestFocus();
1736 if (focusChild instanceof EditText) {
1737 ((EditText) focusChild).setSelection(entry.requestCursor);
1738 }
1739 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001740
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001741 // Reset requested focus values
1742 entry.requestFocusId = View.NO_ID;
1743 entry.requestCursor = 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001744
1745 // Connect listeners up to watch for changed values.
1746 if (data instanceof EditText) {
1747 data.addTextChangedListener(this);
1748 }
1749 if (data2 instanceof EditText) {
1750 data2.addTextChangedListener(this);
1751 }
1752
1753 // Hook up the delete button
1754 View delete = view.findViewById(R.id.delete);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001755 if (delete != null) {
1756 delete.setOnClickListener(this);
1757 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001758
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001759 return view;
1760 }
1761
1762 private void fillViewData(final EditEntry entry) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001763 /*
1764 else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001765 if (entry.data != null) {
1766 updateDataView(entry, entry.data);
1767 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001768 }
1769 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001770 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001771
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001772 /**
1773 * Handles the results from the label change picker.
1774 */
1775 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1776 EditEntry mEntry;
1777 String[] mLabels;
1778
1779 public LabelPickedListener(EditEntry entry, String[] labels) {
1780 mEntry = entry;
1781 mLabels = labels;
1782 }
1783
1784 public void onClick(DialogInterface dialog, int which) {
1785 // TODO: Use a managed dialog
Evan Millar7e4accf2009-06-08 10:43:26 -07001786 if (mEntry.mimetype != Im.CONTENT_ITEM_TYPE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001787 final int type = getTypeFromLabelPosition(mLabels, which);
Evan Millar7e4accf2009-06-08 10:43:26 -07001788 if (type == BaseTypes.TYPE_CUSTOM) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001789 createCustomPicker(mEntry, null);
1790 } else {
1791 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1792 mContactChanged = true;
1793 }
1794 } else {
1795 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1796 mContactChanged = true;
1797 }
1798 }
1799 }
1800
1801 /**
1802 * A basic structure with the data for a contact entry in the list.
1803 */
1804 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1805 // These aren't stuffed into the parcel
1806 public EditContactActivity activity;
1807 public View view;
1808
1809 // These are stuffed into the parcel
1810 public String hint;
1811 public String hint2;
1812 public String column;
1813 public String contentDirectory;
1814 public String data2;
1815 public int contentType;
1816 public int type;
1817 /**
1818 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1819 * will not be called.
1820 */
1821 public int lines = 1;
1822 public boolean isPrimary;
1823 public boolean isDeleted = false;
1824 public boolean isStaticLabel = false;
1825 public boolean syncDataWithView = true;
1826
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001827 /**
1828 * Request focus on the child of this {@link EditEntry} found using
1829 * {@link View#findViewById(int)}. This value should be reset to
1830 * {@link View#NO_ID} after each use.
1831 */
1832 public int requestFocusId = View.NO_ID;
1833
1834 /**
1835 * If the {@link #requestFocusId} is an {@link EditText}, this value
1836 * indicates the requested cursor position placement.
1837 */
1838 public int requestCursor = 0;
1839
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001840 private EditEntry() {
1841 // only used by CREATOR
1842 }
1843
1844 public EditEntry(EditContactActivity activity) {
1845 this.activity = activity;
1846 }
1847
1848 public EditEntry(EditContactActivity activity, String label,
1849 int type, String data, Uri uri, long id) {
1850 this.activity = activity;
1851 this.isPrimary = false;
1852 this.label = label;
1853 this.type = type;
1854 this.data = data;
1855 this.uri = uri;
1856 this.id = id;
1857 }
1858
1859 public int describeContents() {
1860 return 0;
1861 }
1862
1863 public void writeToParcel(Parcel parcel, int flags) {
1864 // Make sure to read data from the input field, if anything is entered
1865 data = getData();
1866
1867 // Write in our own fields.
1868 parcel.writeString(hint);
1869 parcel.writeString(hint2);
1870 parcel.writeString(column);
1871 parcel.writeString(contentDirectory);
1872 parcel.writeString(data2);
1873 parcel.writeInt(contentType);
1874 parcel.writeInt(type);
1875 parcel.writeInt(lines);
1876 parcel.writeInt(isPrimary ? 1 : 0);
1877 parcel.writeInt(isDeleted ? 1 : 0);
1878 parcel.writeInt(isStaticLabel ? 1 : 0);
1879 parcel.writeInt(syncDataWithView ? 1 : 0);
1880
1881 // Write in the fields from Entry
1882 super.writeToParcel(parcel);
1883 }
1884
1885 public static final Parcelable.Creator<EditEntry> CREATOR =
1886 new Parcelable.Creator<EditEntry>() {
1887 public EditEntry createFromParcel(Parcel in) {
1888 EditEntry entry = new EditEntry();
1889
1890 // Read out our own fields
1891 entry.hint = in.readString();
1892 entry.hint2 = in.readString();
1893 entry.column = in.readString();
1894 entry.contentDirectory = in.readString();
1895 entry.data2 = in.readString();
1896 entry.contentType = in.readInt();
1897 entry.type = in.readInt();
1898 entry.lines = in.readInt();
1899 entry.isPrimary = in.readInt() == 1;
1900 entry.isDeleted = in.readInt() == 1;
1901 entry.isStaticLabel = in.readInt() == 1;
1902 entry.syncDataWithView = in.readInt() == 1;
Evan Millar7e4accf2009-06-08 10:43:26 -07001903
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001904 // Read out the fields from Entry
1905 entry.readFromParcel(in);
1906
1907 return entry;
1908 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001909
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001910 public EditEntry[] newArray(int size) {
1911 return new EditEntry[size];
1912 }
1913 };
1914
1915 public void setLabel(Context context, int typeIn, String labelIn) {
1916 type = typeIn;
1917 label = labelIn;
1918 if (view != null) {
1919 bindLabel(context);
1920 }
1921 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001922
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001923 public void bindLabel(Context context) {
1924 TextView v = (TextView) view.findViewById(R.id.label);
1925 if (isStaticLabel) {
1926 v.setText(label);
1927 return;
1928 }
1929
Evan Millar7e4accf2009-06-08 10:43:26 -07001930 v.setText(ContactsUtils.getDisplayLabel(context, mimetype, type, label));
1931 if (mimetype.equals(Im.CONTENT_ITEM_TYPE) && type >= 0) {
1932 v.setText(getLabelsForMimetype(activity, mimetype)[type]);
1933 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
1934 v.setMaxLines(3);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001935 }
1936 v.setOnClickListener(activity);
1937 }
1938
1939 /**
1940 * Returns the data for the entry
1941 * @return the data for the entry
1942 */
1943 public String getData() {
1944 if (view != null && syncDataWithView) {
1945 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1946 if (text != null) {
1947 return text.toString();
1948 }
1949 }
1950
1951 if (data != null) {
1952 return data.toString();
1953 }
1954
1955 return null;
1956 }
1957
1958 /**
1959 * Dumps the entry into a HashMap suitable for passing to the database.
Evan Millar7e4accf2009-06-08 10:43:26 -07001960 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001961 * @param values the HashMap to fill in.
1962 * @return true if the value should be saved, false otherwise
1963 */
1964 public boolean toValues(ContentValues values) {
1965 boolean success = false;
1966 String labelString = null;
1967 // Save the type and label
1968 if (view != null) {
1969 // Read the possibly updated label from the text field
1970 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1971 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001972 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
1973 if (type != Phone.TYPE_CUSTOM) {
1974 labelString = null;
1975 }
1976 values.put(Phone.LABEL, labelString);
1977 values.put(Phone.TYPE, type);
1978 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
1979 if (type != Email.TYPE_CUSTOM) {
1980 labelString = null;
1981 }
1982 values.put(Email.LABEL, labelString);
1983 values.put(Email.TYPE, type);
1984 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1985 values.put(CommonDataKinds.Im.TYPE, type);
1986 values.putNull(CommonDataKinds.Im.LABEL);
1987 if (type != -1) {
1988 values.put(CommonDataKinds.Im.PROTOCOL,
1989 ContactsUtils.encodePredefinedImProtocol(type));
1990 } else {
1991 values.put(CommonDataKinds.Im.PROTOCOL,
1992 ContactsUtils.encodeCustomImProtocol(label.toString()));
1993 }
1994 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
1995 if (type != Postal.TYPE_CUSTOM) {
1996 labelString = null;
1997 }
1998 values.put(Postal.LABEL, labelString);
1999 values.put(Postal.TYPE, type);
2000 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
2001 if (type != Organization.TYPE_CUSTOM) {
2002 labelString = null;
2003 }
2004 values.put(Organization.LABEL, labelString);
2005 values.put(Organization.TYPE, type);
2006 // Save the title
2007 if (view != null) {
2008 // Read the possibly updated data from the text field
2009 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
2010 }
2011 if (!TextUtils.isGraphic(data2)) {
2012 values.putNull(Organization.TITLE);
2013 } else {
2014 values.put(Organization.TITLE, data2.toString());
2015 success = true;
2016 }
2017 } else {
2018 Log.i(TAG, "unknown entry mimetype: " + (mimetype == null ? "" : mimetype));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002019 }
2020
2021 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
2022 // ContentProvider will try finding a new primary when setting to false, meaning
2023 // it's possible to lose primary altogether as we walk down the list. If this editor
2024 // implements editing of primaries in the future, this will need to be revisited.
2025 if (isPrimary) {
Evan Millar7e4accf2009-06-08 10:43:26 -07002026 values.put(Data.IS_PRIMARY, 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002027 }
2028
2029 // Save the data
2030 if (view != null && syncDataWithView) {
2031 // Read the possibly updated data from the text field
2032 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
2033 }
2034 if (!TextUtils.isGraphic(data)) {
2035 values.putNull(column);
2036 return success;
2037 } else {
2038 values.put(column, data.toString());
2039 return true;
2040 }
2041 }
2042
2043 /**
2044 * Create a new empty organization entry
2045 */
2046 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2047 Uri uri, int type) {
2048 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
2049 }
2050
2051 /**
2052 * Create a new company entry with the given data.
2053 */
2054 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2055 String label, int type, String company, String title, Uri uri, long id) {
2056 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
2057 entry.hint = activity.getString(R.string.ghostData_company);
2058 entry.hint2 = activity.getString(R.string.ghostData_title);
2059 entry.data2 = title;
Evan Millar7e4accf2009-06-08 10:43:26 -07002060 entry.column = Organization.COMPANY;
2061 entry.mimetype = Organization.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002062 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2063 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
2064 return entry;
2065 }
2066
2067 /**
Evan Millar7e4accf2009-06-08 10:43:26 -07002068 * Create a new empty notes entry
2069 */
2070 public static final EditEntry newNotesEntry(EditContactActivity activity,
2071 Uri uri) {
2072 return newNotesEntry(activity, null, uri, 0);
2073 }
2074
2075 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002076 * Create a new notes entry with the given data.
2077 */
2078 public static final EditEntry newNotesEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002079 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002080 EditEntry entry = new EditEntry(activity);
2081 entry.label = activity.getString(R.string.label_notes);
2082 entry.hint = activity.getString(R.string.ghostData_notes);
2083 entry.data = data;
2084 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002085 entry.column = Note.NOTE;
2086 entry.mimetype = Note.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002087 entry.maxLines = 10;
2088 entry.lines = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -07002089 entry.id = id;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002090 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2091 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
2092 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2093 entry.isStaticLabel = true;
2094 return entry;
2095 }
2096
2097 /**
Alex Kennberg87fc3172009-03-28 06:43:06 -07002098 * Create a new group entry with the given data.
2099 */
Evan Millar7e4accf2009-06-08 10:43:26 -07002100 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07002101 public static final EditEntry newGroupEntry(EditContactActivity activity,
2102 String data, Uri uri, long personId) {
2103 EditEntry entry = new EditEntry(activity);
2104 entry.label = activity.getString(R.string.label_groups);
2105 entry.data = data;
2106 entry.uri = uri;
2107 entry.id = personId;
2108 entry.column = GroupMembership.GROUP_ID;
2109 entry.kind = KIND_GROUP;
2110 entry.isStaticLabel = true;
2111 entry.syncDataWithView = false;
2112 entry.lines = -1;
2113 return entry;
2114 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002115 */
Alex Kennberg87fc3172009-03-28 06:43:06 -07002116
2117 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002118 * Create a new empty email entry
2119 */
2120 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2121 Uri uri, int type) {
2122 return newPhoneEntry(activity, null, type, null, uri, 0);
2123 }
2124
2125 /**
2126 * Create a new phone entry with the given data.
2127 */
2128 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2129 String label, int type, String data, Uri uri,
2130 long id) {
2131 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2132 entry.hint = activity.getString(R.string.ghostData_phone);
Evan Millar7e4accf2009-06-08 10:43:26 -07002133 entry.column = Phone.NUMBER;
2134 entry.mimetype = Phone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002135 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
2136 return entry;
2137 }
2138
2139 /**
2140 * Create a new empty email entry
2141 */
2142 public static final EditEntry newEmailEntry(EditContactActivity activity,
2143 Uri uri, int type) {
2144 return newEmailEntry(activity, null, type, null, uri, 0);
2145 }
2146
2147 /**
2148 * Create a new email entry with the given data.
2149 */
2150 public static final EditEntry newEmailEntry(EditContactActivity activity,
2151 String label, int type, String data, Uri uri,
2152 long id) {
2153 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2154 entry.hint = activity.getString(R.string.ghostData_email);
Evan Millar7e4accf2009-06-08 10:43:26 -07002155 entry.column = Email.DATA;
2156 entry.mimetype = Email.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002157 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2158 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
2159 return entry;
2160 }
2161
2162 /**
2163 * Create a new empty postal address entry
2164 */
2165 public static final EditEntry newPostalEntry(EditContactActivity activity,
2166 Uri uri, int type) {
2167 return newPostalEntry(activity, null, type, null, uri, 0);
2168 }
2169
2170 /**
2171 * Create a new postal address entry with the given data.
2172 *
2173 * @param label label for the item, from the db not the display label
2174 * @param type the type of postal address
2175 * @param data the starting data for the entry, may be null
2176 * @param uri the uri for the entry if it already exists, may be null
2177 * @param id the id for the entry if it already exists, 0 it it doesn't
2178 * @return the new EditEntry
2179 */
2180 public static final EditEntry newPostalEntry(EditContactActivity activity,
2181 String label, int type, String data, Uri uri, long id) {
2182 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2183 entry.hint = activity.getString(R.string.ghostData_postal);
Evan Millar7e4accf2009-06-08 10:43:26 -07002184 entry.column = Postal.DATA;
2185 entry.mimetype = Postal.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002186 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2187 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2188 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2189 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2190 entry.maxLines = 4;
2191 entry.lines = 2;
2192 return entry;
2193 }
2194
2195 /**
2196 * Create a new IM address entry
2197 */
2198 public static final EditEntry newImEntry(EditContactActivity activity,
2199 Uri uri, int type) {
2200 return newImEntry(activity, null, type, null, uri, 0);
2201 }
2202
2203 /**
2204 * Create a new IM address entry with the given data.
2205 *
2206 * @param label label for the item, from the db not the display label
2207 * @param protocol the type used
2208 * @param data the starting data for the entry, may be null
2209 * @param uri the uri for the entry if it already exists, may be null
2210 * @param id the id for the entry if it already exists, 0 it it doesn't
2211 * @return the new EditEntry
2212 */
2213 public static final EditEntry newImEntry(EditContactActivity activity,
2214 String label, int protocol, String data, Uri uri, long id) {
2215 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2216 entry.hint = activity.getString(R.string.ghostData_im);
Evan Millar7e4accf2009-06-08 10:43:26 -07002217 entry.column = Im.DATA;
2218 entry.mimetype = Im.CONTENT_ITEM_TYPE;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08002219 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2220 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002221 return entry;
2222 }
2223 }
2224
2225 public void afterTextChanged(Editable s) {
2226 // Someone edited a text field, so assume this contact is changed
2227 mContactChanged = true;
2228 }
2229
2230 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2231 // Do nothing; editing handled by afterTextChanged()
2232 }
2233
2234 public void onTextChanged(CharSequence s, int start, int before, int count) {
2235 // Do nothing; editing handled by afterTextChanged()
2236 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002237
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002238 public void onFocusChange(View v, boolean hasFocus) {
2239 // Because we're emulating a ListView, we need to setSelected() for
2240 // views as they are focused.
2241 v.setSelected(hasFocus);
2242 }
2243}