blob: f10ff938b038f8d9384fca7393ab4e27eda163a5 [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
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -070019import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
Evan Millar7e4accf2009-06-08 10:43:26 -070020import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
21import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -070026import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
Alex Kennberg87fc3172009-03-28 06:43:06 -070029
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080030import android.app.Activity;
31import android.app.AlertDialog;
32import android.app.Dialog;
33import android.content.ActivityNotFoundException;
34import android.content.ContentResolver;
35import android.content.ContentUris;
36import android.content.ContentValues;
37import android.content.Context;
38import android.content.DialogInterface;
39import android.content.Intent;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080040import android.content.res.ColorStateList;
41import android.content.res.Resources;
42import android.database.Cursor;
43import android.graphics.Bitmap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080044import android.net.Uri;
45import android.os.Bundle;
46import android.os.Parcel;
47import android.os.Parcelable;
Evan Millar7e4accf2009-06-08 10:43:26 -070048import android.provider.ContactsContract.CommonDataKinds;
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -070049import android.provider.ContactsContract.Data;
Evan Millar7e4accf2009-06-08 10:43:26 -070050import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
Evan Millar7e4accf2009-06-08 10:43:26 -070051import android.provider.ContactsContract.CommonDataKinds.Email;
52import android.provider.ContactsContract.CommonDataKinds.Im;
53import android.provider.ContactsContract.CommonDataKinds.Note;
54import android.provider.ContactsContract.CommonDataKinds.Organization;
55import android.provider.ContactsContract.CommonDataKinds.Phone;
56import android.provider.ContactsContract.CommonDataKinds.Photo;
Evan Millar7e4accf2009-06-08 10:43:26 -070057import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -070058import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080059import android.telephony.PhoneNumberFormattingTextWatcher;
60import android.text.Editable;
61import android.text.TextUtils;
62import android.text.TextWatcher;
63import android.text.method.TextKeyListener;
64import android.text.method.TextKeyListener.Capitalize;
65import android.util.Log;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080066import android.view.KeyEvent;
67import android.view.LayoutInflater;
68import android.view.Menu;
69import android.view.MenuItem;
70import android.view.View;
71import android.view.ViewGroup;
72import android.view.ViewParent;
73import android.view.inputmethod.EditorInfo;
74import android.widget.Button;
75import android.widget.CheckBox;
76import android.widget.EditText;
77import android.widget.ImageView;
78import android.widget.LinearLayout;
79import android.widget.TextView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080080
81import java.io.ByteArrayOutputStream;
82import java.util.ArrayList;
83
Evan Millar7e4accf2009-06-08 10:43:26 -070084// TODO: Much of this class has been commented out as a starting place for transition to new data
85// model. It will be added back as we progress.
86
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080087/**
88 * Activity for editing or inserting a contact. Note that if the contact data changes in the
89 * background while this activity is running, the updates will be overwritten.
90 */
91public final class EditContactActivity extends Activity implements View.OnClickListener,
92 TextWatcher, View.OnFocusChangeListener {
93 private static final String TAG = "EditContactActivity";
94
95 private static final int STATE_UNKNOWN = 0;
96 /** Editing an existing contact */
97 private static final int STATE_EDIT = 1;
98 /** The full insert mode */
99 private static final int STATE_INSERT = 2;
100
101 /** The launch code when picking a photo and the raw data is returned */
102 private static final int PHOTO_PICKED_WITH_DATA = 3021;
103
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800104 // These correspond to the string array in resources for picker "other" items
105 final static int OTHER_ORGANIZATION = 0;
106 final static int OTHER_NOTE = 1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700107
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800108 // Dialog IDs
109 final static int DELETE_CONFIRMATION_DIALOG = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -0700110
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800111 // Section IDs
112 final static int SECTION_PHONES = 3;
113 final static int SECTION_EMAIL = 4;
114 final static int SECTION_IM = 5;
115 final static int SECTION_POSTAL = 6;
116 final static int SECTION_ORG = 7;
117 final static int SECTION_NOTE = 8;
118
119 // Menu item IDs
120 public static final int MENU_ITEM_SAVE = 1;
121 public static final int MENU_ITEM_DONT_SAVE = 2;
122 public static final int MENU_ITEM_DELETE = 3;
123 public static final int MENU_ITEM_PHOTO = 6;
Evan Millar7e4accf2009-06-08 10:43:26 -0700124
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800125 /** Used to represent an invalid type for a contact entry */
126 private static final int INVALID_TYPE = -1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700127
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800128 /** The default type for a phone that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700129 private static final int DEFAULT_PHONE_TYPE = Phone.TYPE_MOBILE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800130
131 /** The default type for an email that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700132 private static final int DEFAULT_EMAIL_TYPE = Email.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800133
134 /** The default type for a postal address that is added via an intent */
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700135 private static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800136
137 private int mState; // saved across instances
138 private boolean mInsert; // saved across instances
139 private Uri mUri; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700140 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800141 /** In insert mode this is the photo */
142 private Bitmap mPhoto; // saved across instances
143 private boolean mPhotoChanged = false; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700144
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800145 private EditText mNameView;
Evan Millar7e4accf2009-06-08 10:43:26 -0700146 private Uri mStructuredNameUri;
147 private Uri mPhotoDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800148 private ImageView mPhotoImageView;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800149 private ViewGroup mContentView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800150 private LinearLayout mLayout;
151 private LayoutInflater mInflater;
152 private MenuItem mPhotoMenuItem;
153 private boolean mPhotoPresent = false;
154 private EditText mPhoneticNameView; // invisible in some locales, but always present
155
156 /** Flag marking this contact as changed, meaning we should write changes back. */
157 private boolean mContactChanged = false;
Evan Millar7e4accf2009-06-08 10:43:26 -0700158
Alex Kennberg87fc3172009-03-28 06:43:06 -0700159 /** List of all the group names */
160 private CharSequence[] mGroups;
Evan Millar7e4accf2009-06-08 10:43:26 -0700161
Alex Kennberg87fc3172009-03-28 06:43:06 -0700162 /** Is this contact part of the group */
163 private boolean[] mInTheGroup;
164
Evan Millar7e4accf2009-06-08 10:43:26 -0700165 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700166 private static final String[] GROUP_ID_PROJECTION = new String[] {
167 Groups._ID,
168 };
169
170 private static final String[] GROUPMEMBERSHIP_ID_PROJECTION = new String[] {
171 GroupMembership._ID,
172 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700173 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800174
175 // These are accessed by inner classes. They're package scoped to make access more efficient.
176 /* package */ ContentResolver mResolver;
177 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
178 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
179 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
180 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
181 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
182 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
183 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
184 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
Evan Millar7e4accf2009-06-08 10:43:26 -0700185
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800186 /* package */ static final int MSG_DELETE = 1;
187 /* package */ static final int MSG_CHANGE_LABEL = 2;
188 /* package */ static final int MSG_ADD_PHONE = 3;
189 /* package */ static final int MSG_ADD_EMAIL = 4;
190 /* package */ static final int MSG_ADD_POSTAL = 5;
Evan Millar7e4accf2009-06-08 10:43:26 -0700191
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800192 public void onClick(View v) {
193 switch (v.getId()) {
194 case R.id.photoImage: {
195 doPickPhotoAction();
196 break;
197 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700198
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800199 case R.id.checkable: {
200 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
201 checkBox.toggle();
Evan Millar7e4accf2009-06-08 10:43:26 -0700202
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800203 EditEntry entry = findEntryForView(v);
204 entry.data = checkBox.isChecked() ? "1" : "0";
Evan Millar7e4accf2009-06-08 10:43:26 -0700205
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800206 mContactChanged = true;
207 break;
208 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700209
210 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700211 case R.id.entry_group: {
212 EditEntry entry = findEntryForView(v);
213 doPickGroup(entry);
214 break;
215 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700216 */
217
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800218 case R.id.separator: {
219 // Someone clicked on a section header, so handle add action
Evan Millar7e4accf2009-06-08 10:43:26 -0700220 // TODO: Data addition is still being hashed out.
221 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800222 int sectionType = (Integer) v.getTag();
223 doAddAction(sectionType);
Evan Millar7e4accf2009-06-08 10:43:26 -0700224 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800225 break;
226 }
227
228 case R.id.saveButton:
229 doSaveAction();
230 break;
231
232 case R.id.discardButton:
233 doRevertAction();
234 break;
235
236 case R.id.delete: {
237 EditEntry entry = findEntryForView(v);
238 if (entry != null) {
239 // Clear the text and hide the view so it gets saved properly
240 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
241 entry.view.setVisibility(View.GONE);
242 entry.isDeleted = true;
243 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700244
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800245 // Force rebuild of views because section headers might need to change
246 buildViews();
247 break;
248 }
249
250 case R.id.label: {
251 EditEntry entry = findEntryForView(v);
252 if (entry != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700253 String[] labels = getLabelsForMimetype(this, entry.mimetype);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800254 LabelPickedListener listener = new LabelPickedListener(entry, labels);
255 new AlertDialog.Builder(EditContactActivity.this)
256 .setItems(labels, listener)
257 .setTitle(R.string.selectLabel)
258 .show();
259 }
260 break;
261 }
262 }
263 }
264
265 private void setPhotoPresent(boolean present) {
266 mPhotoPresent = present;
Evan Millar7e4accf2009-06-08 10:43:26 -0700267
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800268 // Correctly scale the contact photo if present, otherwise just center
269 // the photo placeholder icon.
270 if (mPhotoPresent) {
271 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
272 } else {
273 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
274 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
275 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700276
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800277 if (mPhotoMenuItem != null) {
278 if (present) {
279 mPhotoMenuItem.setTitle(R.string.removePicture);
280 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
281 } else {
282 mPhotoMenuItem.setTitle(R.string.addPicture);
283 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
284 }
285 }
286 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700287
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800288 private EditEntry findEntryForView(View v) {
289 // Try to find the entry for this view
290 EditEntry entry = null;
291 do {
292 Object tag = v.getTag();
293 if (tag != null && tag instanceof EditEntry) {
294 entry = (EditEntry) tag;
295 break;
296 } else {
297 ViewParent parent = v.getParent();
298 if (parent != null && parent instanceof View) {
299 v = (View) parent;
300 } else {
301 v = null;
302 }
303 }
304 } while (v != null);
305 return entry;
306 }
307
308 private DialogInterface.OnClickListener mDeleteContactDialogListener =
309 new DialogInterface.OnClickListener() {
310 public void onClick(DialogInterface dialog, int button) {
311 mResolver.delete(mUri, null, null);
312 finish();
313 }
314 };
315
316 private boolean mMobilePhoneAdded = false;
317 private boolean mPrimaryEmailAdded = false;
318
319 @Override
320 protected void onCreate(Bundle icicle) {
321 super.onCreate(icicle);
322
323 mResolver = getContentResolver();
324
325 // Build the list of sections
326 setupSections();
327
328 // Load the UI
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800329 mInflater = getLayoutInflater();
330 mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
331 setContentView(mContentView);
Evan Millar7e4accf2009-06-08 10:43:26 -0700332
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800333 mLayout = (LinearLayout) findViewById(R.id.list);
334 mNameView = (EditText) findViewById(R.id.name);
335 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
336 mPhotoImageView.setOnClickListener(this);
337 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
Evan Millar7e4accf2009-06-08 10:43:26 -0700338
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800339 // Setup the bottom buttons
340 View view = findViewById(R.id.saveButton);
341 view.setOnClickListener(this);
342 view = findViewById(R.id.discardButton);
343 view.setOnClickListener(this);
344
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800345 // Resolve the intent
346 mState = STATE_UNKNOWN;
347 Intent intent = getIntent();
348 String action = intent.getAction();
349 mUri = intent.getData();
Evan Millar7e4accf2009-06-08 10:43:26 -0700350 mAggDataUri = Uri.withAppendedPath(mUri, "data");
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800351 if (mUri != null) {
352 if (action.equals(Intent.ACTION_EDIT)) {
353 if (icicle == null) {
354 // Build the entries & views
355 buildEntriesForEdit(getIntent().getExtras());
356 buildViews();
357 }
358 setTitle(R.string.editContact_title_edit);
359 mState = STATE_EDIT;
360 } else if (action.equals(Intent.ACTION_INSERT)) {
361 if (icicle == null) {
362 // Build the entries & views
Evan Millar7e4accf2009-06-08 10:43:26 -0700363 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800364 buildEntriesForInsert(getIntent().getExtras());
365 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700366 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800367 }
368 setTitle(R.string.editContact_title_insert);
369 mState = STATE_INSERT;
370 mInsert = true;
371 }
372 }
373
374 if (mState == STATE_UNKNOWN) {
375 Log.e(TAG, "Cannot resolve intent: " + intent);
376 finish();
377 return;
378 }
379
380 if (mState == STATE_EDIT) {
381 setTitle(getResources().getText(R.string.editContact_title_edit));
382 } else {
383 setTitle(getResources().getText(R.string.editContact_title_insert));
384 }
385 }
386
387 private void setupSections() {
388 mSections.add(mPhoneEntries);
389 mSections.add(mEmailEntries);
390 mSections.add(mImEntries);
391 mSections.add(mPostalEntries);
392 mSections.add(mOrgEntries);
393 mSections.add(mNoteEntries);
394 mSections.add(mOtherEntries);
395 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700396
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800397 @Override
398 protected void onSaveInstanceState(Bundle outState) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700399
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800400 // To store current focus between config changes, follow focus down the
401 // view tree, keeping track of any parents with EditEntry tags
402 View focusedChild = mContentView.getFocusedChild();
403 EditEntry focusedEntry = null;
404 while (focusedChild != null) {
405 Object tag = focusedChild.getTag();
406 if (tag instanceof EditEntry) {
407 focusedEntry = (EditEntry) tag;
408 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700409
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800410 // Keep going deeper until child isn't a group
411 if (focusedChild instanceof ViewGroup) {
412 View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
413 if (deeperFocus != null) {
414 focusedChild = deeperFocus;
415 } else {
416 break;
417 }
418 } else {
419 break;
420 }
421 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700422
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800423 if (focusedChild != null) {
424 int requestFocusId = focusedChild.getId();
425 int requestCursor = 0;
426 if (focusedChild instanceof EditText) {
427 requestCursor = ((EditText) focusedChild).getSelectionStart();
428 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700429
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800430 // Store focus values in EditEntry if found, otherwise store as
431 // generic values
432 if (focusedEntry != null) {
433 focusedEntry.requestFocusId = requestFocusId;
434 focusedEntry.requestCursor = requestCursor;
435 } else {
436 outState.putInt("requestFocusId", requestFocusId);
437 outState.putInt("requestCursor", requestCursor);
438 }
439 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700440
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800441 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
442 outState.putParcelableArrayList("emailEntries", mEmailEntries);
443 outState.putParcelableArrayList("imEntries", mImEntries);
444 outState.putParcelableArrayList("postalEntries", mPostalEntries);
445 outState.putParcelableArrayList("orgEntries", mOrgEntries);
446 outState.putParcelableArrayList("noteEntries", mNoteEntries);
447 outState.putParcelableArrayList("otherEntries", mOtherEntries);
448 outState.putInt("state", mState);
449 outState.putBoolean("insert", mInsert);
450 outState.putParcelable("uri", mUri);
451 outState.putString("name", mNameView.getText().toString());
452 outState.putParcelable("photo", mPhoto);
453 outState.putBoolean("photoChanged", mPhotoChanged);
454 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
455 outState.putBoolean("contactChanged", mContactChanged);
456 }
457
458 @Override
459 protected void onRestoreInstanceState(Bundle inState) {
460 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
461 mEmailEntries = inState.getParcelableArrayList("emailEntries");
462 mImEntries = inState.getParcelableArrayList("imEntries");
463 mPostalEntries = inState.getParcelableArrayList("postalEntries");
464 mOrgEntries = inState.getParcelableArrayList("orgEntries");
465 mNoteEntries = inState.getParcelableArrayList("noteEntries");
466 mOtherEntries = inState.getParcelableArrayList("otherEntries");
467 setupSections();
468
469 mState = inState.getInt("state");
470 mInsert = inState.getBoolean("insert");
471 mUri = inState.getParcelable("uri");
472 mNameView.setText(inState.getString("name"));
473 mPhoto = inState.getParcelable("photo");
474 if (mPhoto != null) {
475 mPhotoImageView.setImageBitmap(mPhoto);
476 setPhotoPresent(true);
477 } else {
478 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
479 setPhotoPresent(false);
480 }
481 mPhotoChanged = inState.getBoolean("photoChanged");
482 mPhoneticNameView.setText(inState.getString("phoneticName"));
483 mContactChanged = inState.getBoolean("contactChanged");
484
485 // Now that everything is restored, build the view
486 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700487
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800488 // Try restoring any generally requested focus
489 int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
490 View focusedChild = mContentView.findViewById(requestFocusId);
491 if (focusedChild != null) {
492 focusedChild.requestFocus();
493 if (focusedChild instanceof EditText) {
494 int requestCursor = inState.getInt("requestCursor", 0);
495 ((EditText) focusedChild).setSelection(requestCursor);
496 }
497 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800498 }
499
500 @Override
501 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
502 if (resultCode != RESULT_OK) {
503 return;
504 }
505
506 switch (requestCode) {
507 case PHOTO_PICKED_WITH_DATA: {
508 final Bundle extras = data.getExtras();
509 if (extras != null) {
510 Bitmap photo = extras.getParcelable("data");
511 mPhoto = photo;
512 mPhotoChanged = true;
513 mPhotoImageView.setImageBitmap(photo);
514 setPhotoPresent(true);
515 }
516 break;
517 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800518 }
519 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700520
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800521 @Override
522 public boolean onKeyDown(int keyCode, KeyEvent event) {
523 switch (keyCode) {
524 case KeyEvent.KEYCODE_BACK: {
525 doSaveAction();
526 return true;
527 }
528 }
529 return super.onKeyDown(keyCode, event);
530 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700531
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800532 @Override
533 public boolean onCreateOptionsMenu(Menu menu) {
534 super.onCreateOptionsMenu(menu);
535 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
536 .setIcon(android.R.drawable.ic_menu_save)
537 .setAlphabeticShortcut('\n');
538 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
539 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
540 .setAlphabeticShortcut('q');
541 if (!mInsert) {
542 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
543 .setIcon(android.R.drawable.ic_menu_delete);
544 }
545
546 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
547 // Updates the state of the menu item
548 setPhotoPresent(mPhotoPresent);
549
550 return true;
551 }
552
553 @Override
554 public boolean onOptionsItemSelected(MenuItem item) {
555 switch (item.getItemId()) {
556 case MENU_ITEM_SAVE:
557 doSaveAction();
558 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700559
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800560 case MENU_ITEM_DONT_SAVE:
561 doRevertAction();
562 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700563
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800564 case MENU_ITEM_DELETE:
565 // Get confirmation
566 showDialog(DELETE_CONFIRMATION_DIALOG);
567 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700568
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800569 case MENU_ITEM_PHOTO:
570 if (!mPhotoPresent) {
571 doPickPhotoAction();
572 } else {
573 doRemovePhotoAction();
574 }
575 return true;
576 }
577
578 return false;
579 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700580
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800581 /**
582 * Try guessing the next-best type of {@link EditEntry} to insert into the
583 * given list. We walk down the precedence list until we find a type that
584 * doesn't exist yet, or default to the lowest ranking type.
585 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700586 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800587 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
588 // Keep track of the types we've seen already
589 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
590 for (int i = entries.size() - 1; i >= 0; i--) {
591 EditEntry entry = entries.get(i);
592 if (!entry.isDeleted) {
593 existAlready.put(entry.type, true);
594 }
595 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700596
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800597 // Pick the first item we haven't seen
598 for (int type : precedenceList) {
599 if (!existAlready.get(type, false)) {
600 return type;
601 }
602 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700603
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800604 // Otherwise default to last item
605 return precedenceList[precedenceList.length - 1];
606 }
607
Evan Millar54a5c9f2009-06-23 17:41:09 -0700608 // TODO When this gets brought back we'll need to use the new TypePrecedence class instead of
609 // the older local TYPE_PRECEDENCE* contstants.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800610 private void doAddAction(int sectionType) {
611 EditEntry entry = null;
612 switch (sectionType) {
613 case SECTION_PHONES: {
614 // Try figuring out which type to insert next
615 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
Evan Millar7e4accf2009-06-08 10:43:26 -0700616 entry = EditEntry.newPhoneEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800617 nextType);
618 mPhoneEntries.add(entry);
619 break;
620 }
621 case SECTION_EMAIL: {
622 // Try figuring out which type to insert next
Evan Millar7e4accf2009-06-08 10:43:26 -0700623 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_EMAIL);
624 entry = EditEntry.newEmailEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800625 nextType);
626 mEmailEntries.add(entry);
627 break;
628 }
629 case SECTION_IM: {
630 // Try figuring out which type to insert next
631 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
Evan Millar7e4accf2009-06-08 10:43:26 -0700632 entry = EditEntry.newImEntry(EditContactActivity.this, Data.CONTENT_URI, nextType);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800633 mImEntries.add(entry);
634 break;
635 }
636 case SECTION_POSTAL: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700637 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_POSTAL);
638 entry = EditEntry.newPostalEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800639 nextType);
640 mPostalEntries.add(entry);
641 break;
642 }
643 case SECTION_ORG: {
644 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
Evan Millar7e4accf2009-06-08 10:43:26 -0700645 entry = EditEntry.newOrganizationEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800646 nextType);
647 mOrgEntries.add(entry);
648 break;
649 }
650 case SECTION_NOTE: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700651 entry = EditEntry.newNotesEntry(EditContactActivity.this, Data.CONTENT_URI);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800652 mNoteEntries.add(entry);
653 break;
654 }
655 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700656
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800657 // Rebuild the views if needed
658 if (entry != null) {
659 buildViews();
660 mContactChanged = true;
661
662 View dataView = entry.view.findViewById(R.id.data);
663 if (dataView == null) {
664 entry.view.requestFocus();
665 } else {
666 dataView.requestFocus();
667 }
668 }
669 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700670 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800671
672 private void doRevertAction() {
673 finish();
674 }
675
676 private void doPickPhotoAction() {
677 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
678 // TODO: get these values from constants somewhere
679 intent.setType("image/*");
680 intent.putExtra("crop", "true");
681 intent.putExtra("aspectX", 1);
682 intent.putExtra("aspectY", 1);
683 intent.putExtra("outputX", 96);
684 intent.putExtra("outputY", 96);
685 try {
686 intent.putExtra("return-data", true);
687 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
688 } catch (ActivityNotFoundException e) {
689 new AlertDialog.Builder(EditContactActivity.this)
690 .setTitle(R.string.errorDialogTitle)
691 .setMessage(R.string.photoPickerNotFoundText)
692 .setPositiveButton(android.R.string.ok, null)
693 .show();
694 }
695 }
696
697 private void doRemovePhotoAction() {
698 mPhoto = null;
699 mPhotoChanged = true;
700 setPhotoPresent(false);
701 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700702
703 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700704 private void populateGroups() {
705 // Create a list of all the groups
706 Cursor cursor = mResolver.query(Groups.CONTENT_URI, ContactsListActivity.GROUPS_PROJECTION,
707 null, null, Groups.DEFAULT_SORT_ORDER);
708 try {
709 ArrayList<Long> ids = new ArrayList<Long>();
710 ArrayList<String> items = new ArrayList<String>();
711
712 while (cursor.moveToNext()) {
713 String systemId = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
714 String name = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700715
Alex Kennberg87fc3172009-03-28 06:43:06 -0700716 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
717 continue;
718 }
719
720 if (!TextUtils.isEmpty(name)) {
721 ids.add(new Long(cursor.getLong(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID)));
722 items.add(name);
723 }
724 }
725
726 mGroups = items.toArray(new CharSequence[items.size()]);
727 mInTheGroup = new boolean[items.size()];
728 } finally {
729 cursor.close();
730 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700731
Alex Kennberg87fc3172009-03-28 06:43:06 -0700732 if (mGroups != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700733
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700734 // Go through the groups for this member and update the list
Alex Kennberg87fc3172009-03-28 06:43:06 -0700735 final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700736 Cursor groupCursor = null;
737 try {
738 groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
739 null, null, Groups.DEFAULT_SORT_ORDER);
740 } catch (IllegalArgumentException e) {
741 // Contact is new, so we don't need to do any work.
742 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700743
Alex Kennberg87fc3172009-03-28 06:43:06 -0700744 if (groupCursor != null) {
745 try {
746 while (groupCursor.moveToNext()) {
747 String systemId = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
748 String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700749
Alex Kennberg87fc3172009-03-28 06:43:06 -0700750 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
751 continue;
752 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700753
Alex Kennberg87fc3172009-03-28 06:43:06 -0700754 if (!TextUtils.isEmpty(name)) {
755 for (int i = 0; i < mGroups.length; i++) {
756 if (name.equals(mGroups[i])) {
757 mInTheGroup[i] = true;
758 break;
759 }
760 }
761 }
762 }
763 } finally {
764 groupCursor.close();
765 }
766 }
767 }
768 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700769
Alex Kennberg87fc3172009-03-28 06:43:06 -0700770 private String generateGroupList() {
771 StringBuilder groupList = new StringBuilder();
772 for (int i = 0; mGroups != null && i < mGroups.length; i++) {
773 if (mInTheGroup[i]) {
774 if (groupList.length() == 0) {
775 groupList.append(mGroups[i]);
776 } else {
777 groupList.append(getString(R.string.group_list, mGroups[i]));
778 }
779 }
780 }
781 return groupList.length() > 0 ? groupList.toString() : null;
782 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700783
Alex Kennberg87fc3172009-03-28 06:43:06 -0700784 private void doPickGroup(EditEntry entry) {
785 if (mGroups != null) {
786 GroupDialogListener listener = new GroupDialogListener(this, entry);
Evan Millar7e4accf2009-06-08 10:43:26 -0700787
Alex Kennberg87fc3172009-03-28 06:43:06 -0700788 new AlertDialog.Builder(EditContactActivity.this)
789 .setTitle(R.string.label_groups)
790 .setMultiChoiceItems(mGroups, mInTheGroup, listener)
791 .setPositiveButton(android.R.string.ok, listener)
792 .setNegativeButton(android.R.string.cancel, null)
793 .show();
794 }
795 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700796 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700797
798 /** Handles the clicks in the groups dialog */
Evan Millar7e4accf2009-06-08 10:43:26 -0700799 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700800 private static final class GroupDialogListener implements DialogInterface.OnClickListener,
801 DialogInterface.OnMultiChoiceClickListener {
Evan Millar7e4accf2009-06-08 10:43:26 -0700802
Alex Kennberg87fc3172009-03-28 06:43:06 -0700803 private EditContactActivity mEditContactActivity;
804 private EditEntry mEntry;
805 private boolean[] mInTheGroup;
Evan Millar7e4accf2009-06-08 10:43:26 -0700806
Alex Kennberg87fc3172009-03-28 06:43:06 -0700807 public GroupDialogListener(EditContactActivity editContactActivity, EditEntry entry) {
808 mEditContactActivity = editContactActivity;
809 mEntry = entry;
810 mInTheGroup = editContactActivity.mInTheGroup.clone();
811 }
812
Evan Millar7e4accf2009-06-08 10:43:26 -0700813 // Called when the dialog's ok button is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700814 public void onClick(DialogInterface dialog, int which) {
815 mEditContactActivity.mInTheGroup = mInTheGroup;
816 mEntry.data = mEditContactActivity.generateGroupList();
817 mEditContactActivity.updateDataView(mEntry, mEntry.data);
818 }
819
Evan Millar7e4accf2009-06-08 10:43:26 -0700820 // Called when each group is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700821 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
822 mInTheGroup[which] = isChecked;
823 }
824 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700825 */
826
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800827 private void updateDataView(EditEntry entry, String text) {
828 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
829 dataView.setText(text);
830 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700831
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800832 @Override
833 protected Dialog onCreateDialog(int id) {
834 switch (id) {
835 case DELETE_CONFIRMATION_DIALOG:
836 return new AlertDialog.Builder(EditContactActivity.this)
837 .setTitle(R.string.deleteConfirmation_title)
838 .setIcon(android.R.drawable.ic_dialog_alert)
839 .setMessage(R.string.deleteConfirmation)
840 .setNegativeButton(android.R.string.cancel, null)
841 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
842 .setCancelable(false)
843 .create();
844 }
845 return super.onCreateDialog(id);
846 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700847
848 static String[] getLabelsForMimetype(Context context, String mimetype) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800849 final Resources resources = context.getResources();
Evan Millar7e4accf2009-06-08 10:43:26 -0700850 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
851 return resources.getStringArray(android.R.array.phoneTypes);
852 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
853 return resources.getStringArray(android.R.array.emailAddressTypes);
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700854 } else if (mimetype.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700855 return resources.getStringArray(android.R.array.postalAddressTypes);
856 } else if (mimetype.equals(Im.CONTENT_ITEM_TYPE)) {
857 return resources.getStringArray(android.R.array.imProtocols);
858 } else if (mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
859 return resources.getStringArray(android.R.array.organizationTypes);
860 } else {
861 return resources.getStringArray(R.array.otherLabels);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800862 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800863 }
864
865 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
866 // In the UI Custom... comes last, but it is uses the constant 0
867 // so it is in the same location across the various kinds. Fix up the
868 // position to a valid type here.
869 if (labelPosition == labels.length - 1) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700870 return BaseTypes.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800871 } else {
872 return labelPosition + 1;
873 }
874 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700875
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800876 private EditEntry getOtherEntry(String column) {
877 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
878 EditEntry entry = mOtherEntries.get(i);
879 if (isOtherEntry(entry, column)) {
880 return entry;
881 }
882 }
883 return null;
884 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700885
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800886 private static boolean isOtherEntry(EditEntry entry, String column) {
887 return entry != null && entry.column != null && entry.column.equals(column);
888 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700889
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800890 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
891 final EditText label = new EditText(this);
892 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
893 label.requestFocus();
894 new AlertDialog.Builder(this)
895 .setView(label)
896 .setTitle(R.string.customLabelPickerTitle)
897 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
898 public void onClick(DialogInterface dialog, int which) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700899 entry.setLabel(EditContactActivity.this, BaseTypes.TYPE_CUSTOM,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800900 label.getText().toString());
901 mContactChanged = true;
902
903 if (addTo != null) {
904 addTo.add(entry);
905 buildViews();
906 entry.view.requestFocus(View.FOCUS_DOWN);
907 }
908 }
909 })
910 .setNegativeButton(android.R.string.cancel, null)
911 .show();
912 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700913
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800914 /**
915 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
916 */
917 private void doSaveAction() {
918 // Save or create the contact if needed
919 switch (mState) {
920 case STATE_EDIT:
921 save();
922 break;
923
Evan Millar7e4accf2009-06-08 10:43:26 -0700924 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800925 case STATE_INSERT:
926 create();
927 break;
Evan Millar7e4accf2009-06-08 10:43:26 -0700928 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800929
930 default:
931 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
932 break;
933 }
934 finish();
935 }
Alex Kennberg87fc3172009-03-28 06:43:06 -0700936
937 /**
938 * Gets the group id based on group name.
Evan Millar7e4accf2009-06-08 10:43:26 -0700939 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700940 * @param resolver the resolver to use
941 * @param groupName the name of the group to add the contact to
942 * @return the id of the group
943 * @throws IllegalStateException if the group can't be found
944 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700945 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700946 private long getGroupId(ContentResolver resolver, String groupName) {
947 long groupId = 0;
948 Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUP_ID_PROJECTION,
949 Groups.NAME + "=?", new String[] { groupName }, null);
950 if (groupsCursor != null) {
951 try {
952 if (groupsCursor.moveToFirst()) {
953 groupId = groupsCursor.getLong(0);
954 }
955 } finally {
956 groupsCursor.close();
957 }
958 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700959
Alex Kennberg87fc3172009-03-28 06:43:06 -0700960 if (groupId == 0) {
961 throw new IllegalStateException("Failed to find the " + groupName + "group");
962 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700963
Alex Kennberg87fc3172009-03-28 06:43:06 -0700964 return groupId;
965 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700966 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700967
968 /**
969 * Deletes group membership based on person and group ids.
Evan Millar7e4accf2009-06-08 10:43:26 -0700970 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700971 * @param personId the person id
972 * @param groupId the group id
973 * @return the id of the group membership
974 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700975 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700976 private void deleteGroupMembership(long personId, long groupId) {
977 long groupMembershipId = 0;
978 Cursor groupsCursor = mResolver.query(GroupMembership.CONTENT_URI, GROUPMEMBERSHIP_ID_PROJECTION,
979 GroupMembership.PERSON_ID + "=? AND " + GroupMembership.GROUP_ID + "=?",
980 new String[] {String.valueOf(personId), String.valueOf(groupId)}, null);
981 if (groupsCursor != null) {
982 try {
983 if (groupsCursor.moveToFirst()) {
984 groupMembershipId = groupsCursor.getLong(0);
985 }
986 } finally {
987 groupsCursor.close();
988 }
989 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700990
Alex Kennberg87fc3172009-03-28 06:43:06 -0700991 if (groupMembershipId != 0) {
992 final Uri groupsUri = ContentUris.withAppendedId(
993 GroupMembership.CONTENT_URI,groupMembershipId);
994 mResolver.delete(groupsUri, null, null);
995 }
996 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700997 */
998
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800999 /**
1000 * Save the various fields to the existing contact.
1001 */
1002 private void save() {
1003 ContentValues values = new ContentValues();
1004 String data;
1005 int numValues = 0;
1006
1007 // Handle the name and send to voicemail specially
1008 final String name = mNameView.getText().toString();
1009 if (name != null && TextUtils.isGraphic(name)) {
1010 numValues++;
1011 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001012
Evan Millar7e4accf2009-06-08 10:43:26 -07001013 values.put(StructuredName.DISPLAY_NAME, name);
1014 /*
1015 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1016 */
1017 mResolver.update(mStructuredNameUri, values, null, null);
1018
1019 // This will go down in for loop somewhere
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001020 if (mPhotoChanged) {
1021 // Only write the photo if it's changed, since we don't initially load mPhoto
Evan Millar7e4accf2009-06-08 10:43:26 -07001022 values.clear();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001023 if (mPhoto != null) {
1024 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1025 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
Evan Millar7e4accf2009-06-08 10:43:26 -07001026 values.put(Photo.PHOTO, stream.toByteArray());
1027 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001028 } else {
Evan Millar7e4accf2009-06-08 10:43:26 -07001029 values.putNull(Photo.PHOTO);
1030 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001031 }
1032 }
1033
1034 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1035 for (int i = 0; i < entryCount; i++) {
1036 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001037 data = entry.getData();
1038 boolean empty = data == null || !TextUtils.isGraphic(data);
Evan Millar7e4accf2009-06-08 10:43:26 -07001039 /*
1040 if (kind == EditEntry.KIND_GROUP) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001041 if (entry.id != 0) {
1042 for (int g = 0; g < mGroups.length; g++) {
1043 long groupId = getGroupId(mResolver, mGroups[g].toString());
1044 if (mInTheGroup[g]) {
1045 Contacts.People.addToGroup(mResolver, entry.id, groupId);
1046 numValues++;
1047 } else {
1048 deleteGroupMembership(entry.id, groupId);
1049 }
1050 }
1051 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001052 }
1053 */
1054 if (!empty) {
1055 values.clear();
1056 entry.toValues(values);
1057 if (entry.id != 0) {
1058 mResolver.update(entry.uri, values, null, null);
1059 } else {
1060 /* mResolver.insert(entry.uri, values); */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001061 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001062 } else if (entry.id != 0) {
1063 mResolver.delete(entry.uri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001064 }
1065 }
1066
Evan Millar7e4accf2009-06-08 10:43:26 -07001067 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001068 if (numValues == 0) {
1069 // The contact is completely empty, delete it
1070 mResolver.delete(mUri, null, null);
1071 mUri = null;
1072 setResult(RESULT_CANCELED);
1073 } else {
1074 // Add the entry to the my contacts group if it isn't there already
1075 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1076 setResult(RESULT_OK, new Intent().setData(mUri));
1077
1078 // Only notify user if we actually changed contact
1079 if (mContactChanged || mPhotoChanged) {
1080 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1081 }
1082 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001083 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001084 }
1085
1086 /**
1087 * Takes the entered data and saves it to a new contact.
1088 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001089 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001090 private void create() {
1091 ContentValues values = new ContentValues();
1092 String data;
1093 int numValues = 0;
1094
1095 // Create the contact itself
1096 final String name = mNameView.getText().toString();
1097 if (name != null && TextUtils.isGraphic(name)) {
1098 numValues++;
1099 }
1100 values.put(People.NAME, name);
1101 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1102
1103 // Add the contact to the My Contacts group
1104 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1105
1106 // Add the contact to the group that is being displayed in the contact list
1107 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1108 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1109 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1110 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1111 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
1112 null);
1113 if (!TextUtils.isEmpty(displayGroup)) {
1114 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1115 }
1116 } else {
1117 // Check to see if we're not syncing everything and if so if My Contacts is synced.
1118 // If it isn't then the created contact can end up not in any groups that are
1119 // currently synced and end up getting removed from the phone, which is really bad.
1120 boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
1121 Contacts.Settings.SYNC_EVERYTHING));
1122 if (!syncingEverything) {
1123 boolean syncingMyContacts = false;
1124 Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
1125 Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
1126 if (c != null) {
1127 try {
1128 if (c.moveToFirst()) {
1129 syncingMyContacts = !"0".equals(c.getString(0));
1130 }
1131 } finally {
1132 c.close();
1133 }
1134 }
1135
1136 if (!syncingMyContacts) {
1137 // Not syncing My Contacts, so find a group that is being synced and stick
1138 // the contact in there. We sort the list so at least all contacts
1139 // will appear in the same group.
1140 c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
1141 Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
1142 if (c != null) {
1143 try {
1144 if (c.moveToFirst()) {
1145 People.addToGroup(mResolver, ContentUris.parseId(contactUri),
1146 c.getLong(0));
1147 }
1148 } finally {
1149 c.close();
1150 }
1151 }
1152 }
1153 }
1154 }
1155
1156 // Handle the photo
1157 if (mPhoto != null) {
1158 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1159 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1160 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1161 }
1162
1163 // Create the contact methods
1164 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1165 for (int i = 0; i < entryCount; i++) {
1166 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
Alex Kennbergfb0386a2009-04-02 09:59:10 -07001167 if (entry.kind == EditEntry.KIND_GROUP) {
1168 long contactId = ContentUris.parseId(contactUri);
1169 for (int g = 0; g < mGroups.length; g++) {
1170 if (mInTheGroup[g]) {
1171 long groupId = getGroupId(mResolver, mGroups[g].toString());
1172 People.addToGroup(mResolver, contactId, groupId);
1173 numValues++;
1174 }
1175 }
1176 } else if (entry.kind != EditEntry.KIND_CONTACT) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001177 values.clear();
1178 if (entry.toValues(values)) {
1179 // Only create the entry if there is data
1180 entry.uri = mResolver.insert(
1181 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1182 entry.id = ContentUris.parseId(entry.uri);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001183 }
1184 } else {
1185 // Update the contact with any straggling data, like notes
1186 data = entry.getData();
1187 values.clear();
1188 if (data != null && TextUtils.isGraphic(data)) {
1189 values.put(entry.column, data);
1190 mResolver.update(contactUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001191 }
1192 }
1193 }
1194
1195 if (numValues == 0) {
1196 mResolver.delete(contactUri, null, null);
1197 setResult(RESULT_CANCELED);
1198 } else {
1199 mUri = contactUri;
1200 Intent resultIntent = new Intent()
1201 .setData(mUri)
1202 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1203 setResult(RESULT_OK, resultIntent);
1204 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1205 }
1206 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001207 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001208
1209 /**
1210 * Build up the entries to display on the screen.
1211 *
1212 * @param extras the extras used to start this activity, may be null
1213 */
1214 private void buildEntriesForEdit(Bundle extras) {
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -07001215 Cursor aggCursor = mResolver.query(mAggDataUri, CONTACT_PROJECTION, null, null, null);
Evan Millar7e4accf2009-06-08 10:43:26 -07001216 if (aggCursor == null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001217 Log.e(TAG, "invalid contact uri: " + mUri);
1218 finish();
1219 return;
Evan Millar7e4accf2009-06-08 10:43:26 -07001220 } else if (!aggCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001221 Log.e(TAG, "invalid contact uri: " + mUri);
1222 finish();
Evan Millar7e4accf2009-06-08 10:43:26 -07001223 aggCursor.close();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001224 return;
1225 }
1226
1227 // Clear out the old entries
1228 int numSections = mSections.size();
1229 for (int i = 0; i < numSections; i++) {
1230 mSections.get(i).clear();
1231 }
1232
1233 EditEntry entry;
1234
Evan Millar7e4accf2009-06-08 10:43:26 -07001235 while (aggCursor.moveToNext()) {
1236 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
1237 boolean isSuperPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001238
Evan Millar7e4accf2009-06-08 10:43:26 -07001239 final long id = aggCursor.getLong(DATA_ID_COLUMN);
1240 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
1241
1242 if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
1243 mNameView.setText(aggCursor.getString(DATA_9_COLUMN));
1244 mNameView.addTextChangedListener(this);
1245 mStructuredNameUri = uri;
1246 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
1247 mPhoto = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
1248 if (mPhoto == null) {
1249 setPhotoPresent(false);
1250 } else {
1251 setPhotoPresent(true);
1252 mPhotoImageView.setImageBitmap(mPhoto);
1253 }
1254 mPhotoDataUri = uri;
1255 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
1256 int type = aggCursor.getInt(DATA_1_COLUMN);
1257 String label = aggCursor.getString(DATA_2_COLUMN);
1258 String company = aggCursor.getString(DATA_3_COLUMN);
1259 String title = aggCursor.getString(DATA_4_COLUMN);
1260
1261 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1262 entry.isPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
1263 mOrgEntries.add(entry);
1264 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
1265 entry = EditEntry.newNotesEntry(this, aggCursor.getString(DATA_1_COLUMN),
1266 uri, id);
1267 mNoteEntries.add(entry);
Evan Millar7e4accf2009-06-08 10:43:26 -07001268 } else if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
1269 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07001270 || mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
Evan Millar7e4accf2009-06-08 10:43:26 -07001271 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1272 int type = aggCursor.getInt(DATA_1_COLUMN);
1273 String data = aggCursor.getString(DATA_2_COLUMN);
1274 String label = aggCursor.getString(DATA_3_COLUMN);
1275
1276 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
1277 // Add a phone number entry
1278 entry = EditEntry.newPhoneEntry(this, label, type, data, uri, id);
1279 entry.isPrimary = isSuperPrimary;
1280 mPhoneEntries.add(entry);
1281
1282 // Keep track of which primary types have been added
1283 if (type == Phone.TYPE_MOBILE) {
1284 mMobilePhoneAdded = true;
1285 }
1286 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
1287 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1288 entry.isPrimary = isSuperPrimary;
1289 mEmailEntries.add(entry);
1290
1291 if (isSuperPrimary) {
1292 mPrimaryEmailAdded = true;
1293 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07001294 } else if (mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001295 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1296 entry.isPrimary = isSuperPrimary;
1297 mPostalEntries.add(entry);
1298 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1299 String protocolStr = aggCursor.getString(DATA_5_COLUMN);
1300 Object protocolObj = ContactsUtils.decodeImProtocol(protocolStr);
1301 if (protocolObj == null) {
1302 // Invalid IM protocol, log it then ignore.
1303 Log.e(TAG, "Couldn't decode IM protocol: " + protocolStr);
1304 continue;
1305 } else {
1306 if (protocolObj instanceof Number) {
1307 int protocol = ((Number) protocolObj).intValue();
1308 entry = EditEntry.newImEntry(this,
1309 getLabelsForMimetype(this, mimetype)[protocol], protocol,
1310 data, uri, id);
1311 } else {
1312 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1313 uri, id);
1314 }
1315 mImEntries.add(entry);
1316 }
1317 }
1318 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001319 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001320
1321 /*
1322 // Groups
1323 populateGroups();
1324 if (mGroups != null) {
1325 entry = EditEntry.newGroupEntry(this, generateGroupList(), mUri,
1326 personCursor.getLong(0));
1327 mOtherEntries.add(entry);
1328 }
1329
1330 // Phonetic name
1331 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
1332 mPhoneticNameView.addTextChangedListener(this);
1333
1334
1335 // Add values from the extras, if there are any
1336 if (extras != null) {
1337 addFromExtras(extras, phonesUri, methodsUri);
1338 }
1339
1340 // Add the base types if needed
1341 if (!mMobilePhoneAdded) {
1342 entry = EditEntry.newPhoneEntry(this,
1343 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1344 DEFAULT_PHONE_TYPE);
1345 mPhoneEntries.add(entry);
1346 }
1347
1348 if (!mPrimaryEmailAdded) {
1349 entry = EditEntry.newEmailEntry(this,
1350 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1351 DEFAULT_EMAIL_TYPE);
1352 entry.isPrimary = true;
1353 mEmailEntries.add(entry);
1354 }
1355 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001356
1357 mContactChanged = false;
1358 }
1359
1360 /**
1361 * Build the list of EditEntries for full mode insertions.
Evan Millar7e4accf2009-06-08 10:43:26 -07001362 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001363 * @param extras the extras used to start this activity, may be null
1364 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001365 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001366 private void buildEntriesForInsert(Bundle extras) {
1367 // Clear out the old entries
1368 int numSections = mSections.size();
1369 for (int i = 0; i < numSections; i++) {
1370 mSections.get(i).clear();
1371 }
1372
1373 EditEntry entry;
1374
1375 // Check the intent extras
1376 if (extras != null) {
1377 addFromExtras(extras, null, null);
1378 }
1379
1380 // Photo
1381 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1382
1383 // Add the base entries if they're not already present
1384 if (!mMobilePhoneAdded) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001385 entry = EditEntry.newPhoneEntry(this, null, Phone.TYPE_MOBILE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001386 entry.isPrimary = true;
1387 mPhoneEntries.add(entry);
1388 }
1389
1390 if (!mPrimaryEmailAdded) {
1391 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1392 entry.isPrimary = true;
1393 mEmailEntries.add(entry);
1394 }
1395
Alex Kennberg87fc3172009-03-28 06:43:06 -07001396 // Group
1397 populateGroups();
1398 if (mGroups != null) {
1399 entry = EditEntry.newGroupEntry(this, null, mUri, 0);
1400 mOtherEntries.add(entry);
1401 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001402 }
1403
1404 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1405 EditEntry entry;
1406
1407 // Read the name from the bundle
1408 CharSequence name = extras.getCharSequence(Insert.NAME);
1409 if (name != null && TextUtils.isGraphic(name)) {
1410 mNameView.setText(name);
1411 }
1412
1413 // Read the phonetic name from the bundle
1414 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1415 if (!TextUtils.isEmpty(phoneticName)) {
1416 mPhoneticNameView.setText(phoneticName);
1417 }
1418
1419 // Postal entries from extras
1420 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1421 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1422 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1423 postalType = DEFAULT_POSTAL_TYPE;
1424 }
1425
1426 if (postalType != INVALID_TYPE) {
1427 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1428 methodsUri, 0);
1429 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1430 mPostalEntries.add(entry);
1431 }
1432
1433 // Email entries from extras
1434 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1435 Insert.EMAIL_ISPRIMARY);
1436 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1437 null);
1438 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1439 null);
Evan Millar7e4accf2009-06-08 10:43:26 -07001440
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001441 // Phone entries from extras
1442 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1443 Insert.PHONE_ISPRIMARY);
1444 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1445 null);
1446 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1447 null);
1448
1449 // IM entries from extras
1450 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1451 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
Evan Millar7e4accf2009-06-08 10:43:26 -07001452
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001453 if (imHandle != null && imProtocol != null) {
1454 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1455 if (protocolObj instanceof Number) {
1456 int protocol = ((Number) protocolObj).intValue();
1457 entry = EditEntry.newImEntry(this,
Evan Millar7e4accf2009-06-08 10:43:26 -07001458 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001459 imHandle.toString(), methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001460 } else {
1461 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001462 methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001463 }
1464 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1465 mImEntries.add(entry);
1466 }
1467 }
1468
1469 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1470 String typeField, String primaryField) {
1471 CharSequence email = extras.getCharSequence(emailField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001472
1473 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001474 int emailType = INVALID_TYPE;
1475 String customLabel = null;
1476 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001477 emailType = ContactsContract.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001478 customLabel = extras.getString(typeField);
1479 } else {
1480 emailType = extras.getInt(typeField, INVALID_TYPE);
1481 }
1482
1483 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1484 emailType = DEFAULT_EMAIL_TYPE;
1485 mPrimaryEmailAdded = true;
1486 }
1487
1488 if (emailType != INVALID_TYPE) {
1489 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
1490 methodsUri, 0);
1491 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1492 mEmailEntries.add(entry);
1493
1494 // Keep track of which primary types have been added
1495 if (entry.isPrimary) {
1496 mPrimaryEmailAdded = true;
1497 }
1498 }
1499 }
1500
1501 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1502 String typeField, String primaryField) {
1503 CharSequence phoneNumber = extras.getCharSequence(phoneField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001504
1505 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001506 int phoneType = INVALID_TYPE;
1507 String customLabel = null;
1508 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001509 phoneType = Phone.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001510 customLabel = extras.getString(typeField);
1511 } else {
1512 phoneType = extras.getInt(typeField, INVALID_TYPE);
1513 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001514
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001515 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1516 phoneType = DEFAULT_PHONE_TYPE;
1517 }
1518
1519 if (phoneType != INVALID_TYPE) {
1520 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
1521 phoneNumber.toString(), phonesUri, 0);
1522 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1523 mPhoneEntries.add(entry);
1524
1525 // Keep track of which primary types have been added
Evan Millar7e4accf2009-06-08 10:43:26 -07001526 if (phoneType == Phone.TYPE_MOBILE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001527 mMobilePhoneAdded = true;
1528 }
1529 }
1530 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001531 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001532
1533 /**
1534 * Removes all existing views, builds new ones for all the entries, and adds them.
1535 */
1536 private void buildViews() {
1537 // Remove existing views
1538 final LinearLayout layout = mLayout;
1539 layout.removeAllViews();
Evan Millar7e4accf2009-06-08 10:43:26 -07001540
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001541 buildViewsForSection(layout, mPhoneEntries,
1542 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1543 buildViewsForSection(layout, mEmailEntries,
1544 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1545 buildViewsForSection(layout, mImEntries,
1546 R.string.listSeparatorSendIm_edit, SECTION_IM);
1547 buildViewsForSection(layout, mPostalEntries,
1548 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1549 buildViewsForSection(layout, mOrgEntries,
1550 R.string.listSeparatorOrganizations, SECTION_ORG);
1551 buildViewsForSection(layout, mNoteEntries,
1552 R.string.label_notes, SECTION_NOTE);
Evan Millar7e4accf2009-06-08 10:43:26 -07001553
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001554 buildOtherViews(layout, mOtherEntries);
1555 }
1556
1557
1558 /**
1559 * Builds the views for a specific section.
Evan Millar7e4accf2009-06-08 10:43:26 -07001560 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001561 * @param layout the container
1562 * @param section the section to build the views for
1563 */
1564 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1565 int separatorResource, int sectionType) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001566
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001567 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1568 layout.addView(divider);
Evan Millar7e4accf2009-06-08 10:43:26 -07001569
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001570 // Count up undeleted children
1571 int activeChildren = 0;
1572 for (int i = section.size() - 1; i >= 0; i--) {
1573 EditEntry entry = section.get(i);
1574 if (!entry.isDeleted) {
1575 activeChildren++;
1576 }
1577 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001578
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001579 // Build the correct group header based on undeleted children
1580 ViewGroup header;
1581 if (activeChildren == 0) {
1582 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1583 } else {
1584 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
1585 }
1586
1587 // Because we're emulating a ListView, we need to handle focus changes
1588 // with some additional logic.
1589 header.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001590
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001591 TextView text = (TextView) header.findViewById(R.id.text);
1592 text.setText(getText(separatorResource));
Evan Millar7e4accf2009-06-08 10:43:26 -07001593
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001594 // Force TextView to always default color if we have children. This makes sure
1595 // we don't change color when parent is pressed.
1596 if (activeChildren > 0) {
1597 ColorStateList stateList = text.getTextColors();
1598 text.setTextColor(stateList.getDefaultColor());
1599 }
1600
1601 View addView = header.findViewById(R.id.separator);
1602 addView.setTag(Integer.valueOf(sectionType));
1603 addView.setOnClickListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001604
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001605 // Build views for the current section
1606 for (EditEntry entry : section) {
1607 entry.activity = this; // this could be null from when the state is restored
1608 if (!entry.isDeleted) {
1609 View view = buildViewForEntry(entry);
1610 header.addView(view);
1611 }
1612 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001613
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001614 layout.addView(header);
1615 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001616
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001617 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1618 // Build views for the current section, putting a divider between each one
1619 for (EditEntry entry : section) {
1620 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1621 layout.addView(divider);
1622
1623 entry.activity = this; // this could be null from when the state is restored
1624 View view = buildViewForEntry(entry);
1625 view.setOnClickListener(this);
1626 layout.addView(view);
1627 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001628
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001629 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1630 layout.addView(divider);
1631 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001632
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001633 /**
1634 * Builds a view to display an EditEntry.
Evan Millar7e4accf2009-06-08 10:43:26 -07001635 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001636 * @param entry the entry to display
1637 * @return a view that will display the given entry
1638 */
1639 /* package */ View buildViewForEntry(final EditEntry entry) {
1640 // Look for any existing entered text, and save it if found
1641 if (entry.view != null && entry.syncDataWithView) {
1642 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1643 .getText().toString();
1644 if (!TextUtils.isEmpty(enteredText)) {
1645 entry.data = enteredText;
1646 }
1647 }
1648
1649 // Build a new view
1650 final ViewGroup parent = mLayout;
1651 View view;
1652
1653 // Because we're emulating a ListView, we might need to handle focus changes
1654 // with some additional logic.
Evan Millar7e4accf2009-06-08 10:43:26 -07001655 if (entry.mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001656 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
Evan Millar7e4accf2009-06-08 10:43:26 -07001657 /*
1658 else if (entry.mimetype.equals(Group.CONTENT_ITEM_TYPE)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001659 view = mInflater.inflate(R.layout.edit_contact_entry_group, parent, false);
1660 view.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001661 }
1662 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001663 } else if (!entry.isStaticLabel) {
1664 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1665 } else {
1666 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1667 }
1668 entry.view = view;
Evan Millar7e4accf2009-06-08 10:43:26 -07001669
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001670 // Set the entry as the tag so we can find it again later given just the view
1671 view.setTag(entry);
1672
1673 // Bind the label
1674 entry.bindLabel(this);
1675
1676 // Bind data
1677 TextView data = (TextView) view.findViewById(R.id.data);
1678 TextView data2 = (TextView) view.findViewById(R.id.data2);
Evan Millar7e4accf2009-06-08 10:43:26 -07001679
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001680 if (data instanceof Button) {
1681 data.setOnClickListener(this);
1682 }
1683 if (data.length() == 0) {
1684 if (entry.syncDataWithView) {
1685 // If there is already data entered don't overwrite it
1686 data.setText(entry.data);
1687 } else {
1688 fillViewData(entry);
1689 }
1690 }
1691 if (data2 != null && data2.length() == 0) {
1692 // If there is already data entered don't overwrite it
1693 data2.setText(entry.data2);
1694 }
1695 data.setHint(entry.hint);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001696 if (data2 != null) {
1697 data2.setHint(entry.hint2);
1698 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001699 if (entry.lines > 1) {
1700 data.setLines(entry.lines);
1701 data.setMaxLines(entry.maxLines);
1702 if (data2 != null) {
1703 data2.setLines(entry.lines);
1704 data2.setMaxLines(entry.maxLines);
1705 }
1706 }
1707 int contentType = entry.contentType;
1708 if (contentType != EditorInfo.TYPE_NULL) {
1709 data.setInputType(contentType);
1710 if (data2 != null) {
1711 data2.setInputType(contentType);
1712 }
1713 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1714 == EditorInfo.TYPE_CLASS_PHONE) {
1715 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1716 if (data2 != null) {
1717 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1718 }
1719 }
1720 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001721
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001722 // Give focus to children as requested, possibly after a configuration change
1723 View focusChild = view.findViewById(entry.requestFocusId);
1724 if (focusChild != null) {
1725 focusChild.requestFocus();
1726 if (focusChild instanceof EditText) {
1727 ((EditText) focusChild).setSelection(entry.requestCursor);
1728 }
1729 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001730
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001731 // Reset requested focus values
1732 entry.requestFocusId = View.NO_ID;
1733 entry.requestCursor = 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001734
1735 // Connect listeners up to watch for changed values.
1736 if (data instanceof EditText) {
1737 data.addTextChangedListener(this);
1738 }
1739 if (data2 instanceof EditText) {
1740 data2.addTextChangedListener(this);
1741 }
1742
1743 // Hook up the delete button
1744 View delete = view.findViewById(R.id.delete);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001745 if (delete != null) {
1746 delete.setOnClickListener(this);
1747 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001748
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001749 return view;
1750 }
1751
1752 private void fillViewData(final EditEntry entry) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001753 /*
1754 else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001755 if (entry.data != null) {
1756 updateDataView(entry, entry.data);
1757 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001758 }
1759 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001760 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001761
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001762 /**
1763 * Handles the results from the label change picker.
1764 */
1765 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1766 EditEntry mEntry;
1767 String[] mLabels;
1768
1769 public LabelPickedListener(EditEntry entry, String[] labels) {
1770 mEntry = entry;
1771 mLabels = labels;
1772 }
1773
1774 public void onClick(DialogInterface dialog, int which) {
1775 // TODO: Use a managed dialog
Evan Millar7e4accf2009-06-08 10:43:26 -07001776 if (mEntry.mimetype != Im.CONTENT_ITEM_TYPE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001777 final int type = getTypeFromLabelPosition(mLabels, which);
Evan Millar7e4accf2009-06-08 10:43:26 -07001778 if (type == BaseTypes.TYPE_CUSTOM) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001779 createCustomPicker(mEntry, null);
1780 } else {
1781 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1782 mContactChanged = true;
1783 }
1784 } else {
1785 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1786 mContactChanged = true;
1787 }
1788 }
1789 }
1790
1791 /**
1792 * A basic structure with the data for a contact entry in the list.
1793 */
1794 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1795 // These aren't stuffed into the parcel
1796 public EditContactActivity activity;
1797 public View view;
1798
1799 // These are stuffed into the parcel
1800 public String hint;
1801 public String hint2;
1802 public String column;
1803 public String contentDirectory;
1804 public String data2;
1805 public int contentType;
1806 public int type;
1807 /**
1808 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1809 * will not be called.
1810 */
1811 public int lines = 1;
1812 public boolean isPrimary;
1813 public boolean isDeleted = false;
1814 public boolean isStaticLabel = false;
1815 public boolean syncDataWithView = true;
1816
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001817 /**
1818 * Request focus on the child of this {@link EditEntry} found using
1819 * {@link View#findViewById(int)}. This value should be reset to
1820 * {@link View#NO_ID} after each use.
1821 */
1822 public int requestFocusId = View.NO_ID;
1823
1824 /**
1825 * If the {@link #requestFocusId} is an {@link EditText}, this value
1826 * indicates the requested cursor position placement.
1827 */
1828 public int requestCursor = 0;
1829
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001830 private EditEntry() {
1831 // only used by CREATOR
1832 }
1833
1834 public EditEntry(EditContactActivity activity) {
1835 this.activity = activity;
1836 }
1837
1838 public EditEntry(EditContactActivity activity, String label,
1839 int type, String data, Uri uri, long id) {
1840 this.activity = activity;
1841 this.isPrimary = false;
1842 this.label = label;
1843 this.type = type;
1844 this.data = data;
1845 this.uri = uri;
1846 this.id = id;
1847 }
1848
1849 public int describeContents() {
1850 return 0;
1851 }
1852
1853 public void writeToParcel(Parcel parcel, int flags) {
1854 // Make sure to read data from the input field, if anything is entered
1855 data = getData();
1856
1857 // Write in our own fields.
1858 parcel.writeString(hint);
1859 parcel.writeString(hint2);
1860 parcel.writeString(column);
1861 parcel.writeString(contentDirectory);
1862 parcel.writeString(data2);
1863 parcel.writeInt(contentType);
1864 parcel.writeInt(type);
1865 parcel.writeInt(lines);
1866 parcel.writeInt(isPrimary ? 1 : 0);
1867 parcel.writeInt(isDeleted ? 1 : 0);
1868 parcel.writeInt(isStaticLabel ? 1 : 0);
1869 parcel.writeInt(syncDataWithView ? 1 : 0);
1870
1871 // Write in the fields from Entry
1872 super.writeToParcel(parcel);
1873 }
1874
1875 public static final Parcelable.Creator<EditEntry> CREATOR =
1876 new Parcelable.Creator<EditEntry>() {
1877 public EditEntry createFromParcel(Parcel in) {
1878 EditEntry entry = new EditEntry();
1879
1880 // Read out our own fields
1881 entry.hint = in.readString();
1882 entry.hint2 = in.readString();
1883 entry.column = in.readString();
1884 entry.contentDirectory = in.readString();
1885 entry.data2 = in.readString();
1886 entry.contentType = in.readInt();
1887 entry.type = in.readInt();
1888 entry.lines = in.readInt();
1889 entry.isPrimary = in.readInt() == 1;
1890 entry.isDeleted = in.readInt() == 1;
1891 entry.isStaticLabel = in.readInt() == 1;
1892 entry.syncDataWithView = in.readInt() == 1;
Evan Millar7e4accf2009-06-08 10:43:26 -07001893
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001894 // Read out the fields from Entry
1895 entry.readFromParcel(in);
1896
1897 return entry;
1898 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001899
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001900 public EditEntry[] newArray(int size) {
1901 return new EditEntry[size];
1902 }
1903 };
1904
1905 public void setLabel(Context context, int typeIn, String labelIn) {
1906 type = typeIn;
1907 label = labelIn;
1908 if (view != null) {
1909 bindLabel(context);
1910 }
1911 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001912
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001913 public void bindLabel(Context context) {
1914 TextView v = (TextView) view.findViewById(R.id.label);
1915 if (isStaticLabel) {
1916 v.setText(label);
1917 return;
1918 }
1919
Evan Millar7e4accf2009-06-08 10:43:26 -07001920 v.setText(ContactsUtils.getDisplayLabel(context, mimetype, type, label));
1921 if (mimetype.equals(Im.CONTENT_ITEM_TYPE) && type >= 0) {
1922 v.setText(getLabelsForMimetype(activity, mimetype)[type]);
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07001923 } else if (mimetype.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001924 v.setMaxLines(3);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001925 }
1926 v.setOnClickListener(activity);
1927 }
1928
1929 /**
1930 * Returns the data for the entry
1931 * @return the data for the entry
1932 */
1933 public String getData() {
1934 if (view != null && syncDataWithView) {
1935 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1936 if (text != null) {
1937 return text.toString();
1938 }
1939 }
1940
1941 if (data != null) {
1942 return data.toString();
1943 }
1944
1945 return null;
1946 }
1947
1948 /**
1949 * Dumps the entry into a HashMap suitable for passing to the database.
Evan Millar7e4accf2009-06-08 10:43:26 -07001950 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001951 * @param values the HashMap to fill in.
1952 * @return true if the value should be saved, false otherwise
1953 */
1954 public boolean toValues(ContentValues values) {
1955 boolean success = false;
1956 String labelString = null;
1957 // Save the type and label
1958 if (view != null) {
1959 // Read the possibly updated label from the text field
1960 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1961 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001962 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
1963 if (type != Phone.TYPE_CUSTOM) {
1964 labelString = null;
1965 }
1966 values.put(Phone.LABEL, labelString);
1967 values.put(Phone.TYPE, type);
1968 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
1969 if (type != Email.TYPE_CUSTOM) {
1970 labelString = null;
1971 }
1972 values.put(Email.LABEL, labelString);
1973 values.put(Email.TYPE, type);
1974 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1975 values.put(CommonDataKinds.Im.TYPE, type);
1976 values.putNull(CommonDataKinds.Im.LABEL);
1977 if (type != -1) {
1978 values.put(CommonDataKinds.Im.PROTOCOL,
1979 ContactsUtils.encodePredefinedImProtocol(type));
1980 } else {
1981 values.put(CommonDataKinds.Im.PROTOCOL,
1982 ContactsUtils.encodeCustomImProtocol(label.toString()));
1983 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07001984 } else if (mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
1985 if (type != StructuredPostal.TYPE_CUSTOM) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001986 labelString = null;
1987 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07001988 values.put(StructuredPostal.LABEL, labelString);
1989 values.put(StructuredPostal.TYPE, type);
Evan Millar7e4accf2009-06-08 10:43:26 -07001990 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
1991 if (type != Organization.TYPE_CUSTOM) {
1992 labelString = null;
1993 }
1994 values.put(Organization.LABEL, labelString);
1995 values.put(Organization.TYPE, type);
1996 // Save the title
1997 if (view != null) {
1998 // Read the possibly updated data from the text field
1999 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
2000 }
2001 if (!TextUtils.isGraphic(data2)) {
2002 values.putNull(Organization.TITLE);
2003 } else {
2004 values.put(Organization.TITLE, data2.toString());
2005 success = true;
2006 }
2007 } else {
2008 Log.i(TAG, "unknown entry mimetype: " + (mimetype == null ? "" : mimetype));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002009 }
2010
2011 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
2012 // ContentProvider will try finding a new primary when setting to false, meaning
2013 // it's possible to lose primary altogether as we walk down the list. If this editor
2014 // implements editing of primaries in the future, this will need to be revisited.
2015 if (isPrimary) {
Evan Millar7e4accf2009-06-08 10:43:26 -07002016 values.put(Data.IS_PRIMARY, 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002017 }
2018
2019 // Save the data
2020 if (view != null && syncDataWithView) {
2021 // Read the possibly updated data from the text field
2022 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
2023 }
2024 if (!TextUtils.isGraphic(data)) {
2025 values.putNull(column);
2026 return success;
2027 } else {
2028 values.put(column, data.toString());
2029 return true;
2030 }
2031 }
2032
2033 /**
2034 * Create a new empty organization entry
2035 */
2036 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2037 Uri uri, int type) {
2038 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
2039 }
2040
2041 /**
2042 * Create a new company entry with the given data.
2043 */
2044 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2045 String label, int type, String company, String title, Uri uri, long id) {
2046 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
2047 entry.hint = activity.getString(R.string.ghostData_company);
2048 entry.hint2 = activity.getString(R.string.ghostData_title);
2049 entry.data2 = title;
Evan Millar7e4accf2009-06-08 10:43:26 -07002050 entry.column = Organization.COMPANY;
2051 entry.mimetype = Organization.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002052 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2053 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
2054 return entry;
2055 }
2056
2057 /**
Evan Millar7e4accf2009-06-08 10:43:26 -07002058 * Create a new empty notes entry
2059 */
2060 public static final EditEntry newNotesEntry(EditContactActivity activity,
2061 Uri uri) {
2062 return newNotesEntry(activity, null, uri, 0);
2063 }
2064
2065 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002066 * Create a new notes entry with the given data.
2067 */
2068 public static final EditEntry newNotesEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002069 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002070 EditEntry entry = new EditEntry(activity);
2071 entry.label = activity.getString(R.string.label_notes);
2072 entry.hint = activity.getString(R.string.ghostData_notes);
2073 entry.data = data;
2074 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002075 entry.column = Note.NOTE;
2076 entry.mimetype = Note.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002077 entry.maxLines = 10;
2078 entry.lines = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -07002079 entry.id = id;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002080 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2081 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
2082 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2083 entry.isStaticLabel = true;
2084 return entry;
2085 }
2086
2087 /**
Alex Kennberg87fc3172009-03-28 06:43:06 -07002088 * Create a new group entry with the given data.
2089 */
Evan Millar7e4accf2009-06-08 10:43:26 -07002090 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07002091 public static final EditEntry newGroupEntry(EditContactActivity activity,
2092 String data, Uri uri, long personId) {
2093 EditEntry entry = new EditEntry(activity);
2094 entry.label = activity.getString(R.string.label_groups);
2095 entry.data = data;
2096 entry.uri = uri;
2097 entry.id = personId;
2098 entry.column = GroupMembership.GROUP_ID;
2099 entry.kind = KIND_GROUP;
2100 entry.isStaticLabel = true;
2101 entry.syncDataWithView = false;
2102 entry.lines = -1;
2103 return entry;
2104 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002105 */
Alex Kennberg87fc3172009-03-28 06:43:06 -07002106
2107 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002108 * Create a new empty email entry
2109 */
2110 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2111 Uri uri, int type) {
2112 return newPhoneEntry(activity, null, type, null, uri, 0);
2113 }
2114
2115 /**
2116 * Create a new phone entry with the given data.
2117 */
2118 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2119 String label, int type, String data, Uri uri,
2120 long id) {
2121 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2122 entry.hint = activity.getString(R.string.ghostData_phone);
Evan Millar7e4accf2009-06-08 10:43:26 -07002123 entry.column = Phone.NUMBER;
2124 entry.mimetype = Phone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002125 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
2126 return entry;
2127 }
2128
2129 /**
2130 * Create a new empty email entry
2131 */
2132 public static final EditEntry newEmailEntry(EditContactActivity activity,
2133 Uri uri, int type) {
2134 return newEmailEntry(activity, null, type, null, uri, 0);
2135 }
2136
2137 /**
2138 * Create a new email entry with the given data.
2139 */
2140 public static final EditEntry newEmailEntry(EditContactActivity activity,
2141 String label, int type, String data, Uri uri,
2142 long id) {
2143 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2144 entry.hint = activity.getString(R.string.ghostData_email);
Evan Millar7e4accf2009-06-08 10:43:26 -07002145 entry.column = Email.DATA;
2146 entry.mimetype = Email.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002147 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2148 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
2149 return entry;
2150 }
2151
2152 /**
2153 * Create a new empty postal address entry
2154 */
2155 public static final EditEntry newPostalEntry(EditContactActivity activity,
2156 Uri uri, int type) {
2157 return newPostalEntry(activity, null, type, null, uri, 0);
2158 }
2159
2160 /**
2161 * Create a new postal address entry with the given data.
2162 *
2163 * @param label label for the item, from the db not the display label
2164 * @param type the type of postal address
2165 * @param data the starting data for the entry, may be null
2166 * @param uri the uri for the entry if it already exists, may be null
2167 * @param id the id for the entry if it already exists, 0 it it doesn't
2168 * @return the new EditEntry
2169 */
2170 public static final EditEntry newPostalEntry(EditContactActivity activity,
2171 String label, int type, String data, Uri uri, long id) {
2172 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2173 entry.hint = activity.getString(R.string.ghostData_postal);
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -07002174 entry.column = StructuredPostal.DATA;
2175 entry.mimetype = StructuredPostal.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002176 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2177 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2178 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2179 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2180 entry.maxLines = 4;
2181 entry.lines = 2;
2182 return entry;
2183 }
2184
2185 /**
2186 * Create a new IM address entry
2187 */
2188 public static final EditEntry newImEntry(EditContactActivity activity,
2189 Uri uri, int type) {
2190 return newImEntry(activity, null, type, null, uri, 0);
2191 }
2192
2193 /**
2194 * Create a new IM address entry with the given data.
2195 *
2196 * @param label label for the item, from the db not the display label
2197 * @param protocol the type used
2198 * @param data the starting data for the entry, may be null
2199 * @param uri the uri for the entry if it already exists, may be null
2200 * @param id the id for the entry if it already exists, 0 it it doesn't
2201 * @return the new EditEntry
2202 */
2203 public static final EditEntry newImEntry(EditContactActivity activity,
2204 String label, int protocol, String data, Uri uri, long id) {
2205 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2206 entry.hint = activity.getString(R.string.ghostData_im);
Evan Millar7e4accf2009-06-08 10:43:26 -07002207 entry.column = Im.DATA;
2208 entry.mimetype = Im.CONTENT_ITEM_TYPE;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08002209 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2210 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002211 return entry;
2212 }
2213 }
2214
2215 public void afterTextChanged(Editable s) {
2216 // Someone edited a text field, so assume this contact is changed
2217 mContactChanged = true;
2218 }
2219
2220 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2221 // Do nothing; editing handled by afterTextChanged()
2222 }
2223
2224 public void onTextChanged(CharSequence s, int start, int before, int count) {
2225 // Do nothing; editing handled by afterTextChanged()
2226 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002227
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002228 public void onFocusChange(View v, boolean hasFocus) {
2229 // Because we're emulating a ListView, we need to setSelected() for
2230 // views as they are focused.
2231 v.setSelected(hasFocus);
2232 }
2233}