blob: edcb178500825620922a06054e777264e5ca8e93 [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts;
18
Evan Millar66388be2009-05-28 15:41:07 -070019import static com.android.contacts.ContactEntryAdapter.AGGREGATE_DISPLAY_NAME_COLUMN;
20import static com.android.contacts.ContactEntryAdapter.AGGREGATE_PROJECTION;
Evan Millar7e4accf2009-06-08 10:43:26 -070021import static com.android.contacts.ContactEntryAdapter.AGGREGATE_STARRED_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.DATA_PACKAGE_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.DATA_IS_PRIMARY_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
27import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
32import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
Alex Kennberg87fc3172009-03-28 06:43:06 -070033
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080034import android.app.Activity;
35import android.app.AlertDialog;
36import android.app.Dialog;
37import android.content.ActivityNotFoundException;
38import android.content.ContentResolver;
39import android.content.ContentUris;
40import android.content.ContentValues;
41import android.content.Context;
42import android.content.DialogInterface;
43import android.content.Intent;
44import android.content.SharedPreferences;
45import android.content.res.ColorStateList;
46import android.content.res.Resources;
47import android.database.Cursor;
48import android.graphics.Bitmap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080049import android.net.Uri;
50import android.os.Bundle;
51import android.os.Parcel;
52import android.os.Parcelable;
53import android.preference.PreferenceManager;
Evan Millar7e4accf2009-06-08 10:43:26 -070054import android.provider.ContactsContract;
55import android.provider.ContactsContract.Aggregates;
56import android.provider.ContactsContract.CommonDataKinds;
57import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
Evan Millar7e4accf2009-06-08 10:43:26 -070058import android.provider.ContactsContract.CommonDataKinds.Email;
59import android.provider.ContactsContract.CommonDataKinds.Im;
60import android.provider.ContactsContract.CommonDataKinds.Note;
61import android.provider.ContactsContract.CommonDataKinds.Organization;
62import android.provider.ContactsContract.CommonDataKinds.Phone;
63import android.provider.ContactsContract.CommonDataKinds.Photo;
64import android.provider.ContactsContract.CommonDataKinds.Postal;
65import android.provider.ContactsContract.CommonDataKinds.StructuredName;
66import android.provider.ContactsContract.Data;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080067import android.telephony.PhoneNumberFormattingTextWatcher;
68import android.text.Editable;
69import android.text.TextUtils;
70import android.text.TextWatcher;
71import android.text.method.TextKeyListener;
72import android.text.method.TextKeyListener.Capitalize;
73import android.util.Log;
74import android.util.SparseBooleanArray;
75import android.view.KeyEvent;
76import android.view.LayoutInflater;
77import android.view.Menu;
78import android.view.MenuItem;
79import android.view.View;
80import android.view.ViewGroup;
81import android.view.ViewParent;
82import android.view.inputmethod.EditorInfo;
83import android.widget.Button;
84import android.widget.CheckBox;
85import android.widget.EditText;
86import android.widget.ImageView;
87import android.widget.LinearLayout;
88import android.widget.TextView;
89import android.widget.Toast;
90
91import java.io.ByteArrayOutputStream;
92import java.util.ArrayList;
93
Evan Millar7e4accf2009-06-08 10:43:26 -070094// TODO: Much of this class has been commented out as a starting place for transition to new data
95// model. It will be added back as we progress.
96
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080097/**
98 * Activity for editing or inserting a contact. Note that if the contact data changes in the
99 * background while this activity is running, the updates will be overwritten.
100 */
101public final class EditContactActivity extends Activity implements View.OnClickListener,
102 TextWatcher, View.OnFocusChangeListener {
103 private static final String TAG = "EditContactActivity";
104
105 private static final int STATE_UNKNOWN = 0;
106 /** Editing an existing contact */
107 private static final int STATE_EDIT = 1;
108 /** The full insert mode */
109 private static final int STATE_INSERT = 2;
110
111 /** The launch code when picking a photo and the raw data is returned */
112 private static final int PHOTO_PICKED_WITH_DATA = 3021;
113
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800114 // These correspond to the string array in resources for picker "other" items
115 final static int OTHER_ORGANIZATION = 0;
116 final static int OTHER_NOTE = 1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700117
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800118 // Dialog IDs
119 final static int DELETE_CONFIRMATION_DIALOG = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -0700120
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800121 // Section IDs
122 final static int SECTION_PHONES = 3;
123 final static int SECTION_EMAIL = 4;
124 final static int SECTION_IM = 5;
125 final static int SECTION_POSTAL = 6;
126 final static int SECTION_ORG = 7;
127 final static int SECTION_NOTE = 8;
128
129 // Menu item IDs
130 public static final int MENU_ITEM_SAVE = 1;
131 public static final int MENU_ITEM_DONT_SAVE = 2;
132 public static final int MENU_ITEM_DELETE = 3;
133 public static final int MENU_ITEM_PHOTO = 6;
Evan Millar7e4accf2009-06-08 10:43:26 -0700134
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800135 /** Used to represent an invalid type for a contact entry */
136 private static final int INVALID_TYPE = -1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700137
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800138 /** The default type for a phone that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700139 private static final int DEFAULT_PHONE_TYPE = Phone.TYPE_MOBILE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800140
141 /** The default type for an email that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700142 private static final int DEFAULT_EMAIL_TYPE = Email.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800143
144 /** The default type for a postal address that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700145 private static final int DEFAULT_POSTAL_TYPE = Postal.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800146
147 private int mState; // saved across instances
148 private boolean mInsert; // saved across instances
149 private Uri mUri; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700150 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800151 /** In insert mode this is the photo */
152 private Bitmap mPhoto; // saved across instances
153 private boolean mPhotoChanged = false; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700154
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800155 private EditText mNameView;
Evan Millar7e4accf2009-06-08 10:43:26 -0700156 private Uri mStructuredNameUri;
157 private Uri mPhotoDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800158 private ImageView mPhotoImageView;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800159 private ViewGroup mContentView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800160 private LinearLayout mLayout;
161 private LayoutInflater mInflater;
162 private MenuItem mPhotoMenuItem;
163 private boolean mPhotoPresent = false;
164 private EditText mPhoneticNameView; // invisible in some locales, but always present
165
166 /** Flag marking this contact as changed, meaning we should write changes back. */
167 private boolean mContactChanged = false;
Evan Millar7e4accf2009-06-08 10:43:26 -0700168
Alex Kennberg87fc3172009-03-28 06:43:06 -0700169 /** List of all the group names */
170 private CharSequence[] mGroups;
Evan Millar7e4accf2009-06-08 10:43:26 -0700171
Alex Kennberg87fc3172009-03-28 06:43:06 -0700172 /** Is this contact part of the group */
173 private boolean[] mInTheGroup;
174
Evan Millar7e4accf2009-06-08 10:43:26 -0700175 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700176 private static final String[] GROUP_ID_PROJECTION = new String[] {
177 Groups._ID,
178 };
179
180 private static final String[] GROUPMEMBERSHIP_ID_PROJECTION = new String[] {
181 GroupMembership._ID,
182 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700183 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800184
185 // These are accessed by inner classes. They're package scoped to make access more efficient.
186 /* package */ ContentResolver mResolver;
187 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
188 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
189 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
190 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
191 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
192 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
193 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
194 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
Evan Millar7e4accf2009-06-08 10:43:26 -0700195
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800196 /* package */ static final int MSG_DELETE = 1;
197 /* package */ static final int MSG_CHANGE_LABEL = 2;
198 /* package */ static final int MSG_ADD_PHONE = 3;
199 /* package */ static final int MSG_ADD_EMAIL = 4;
200 /* package */ static final int MSG_ADD_POSTAL = 5;
Evan Millar7e4accf2009-06-08 10:43:26 -0700201
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800202 private static final int[] TYPE_PRECEDENCE_PHONES = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700203 Phone.TYPE_MOBILE, Phone.TYPE_HOME, Phone.TYPE_WORK, Phone.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800204 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700205 private static final int[] TYPE_PRECEDENCE_EMAIL = new int[] {
206 Email.TYPE_HOME, Email.TYPE_WORK, Email.TYPE_OTHER
207 };
208 private static final int[] TYPE_PRECEDENCE_POSTAL = new int[] {
209 Postal.TYPE_HOME, Postal.TYPE_WORK, Postal.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800210 };
211 private static final int[] TYPE_PRECEDENCE_IM = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700212 Im.PROTOCOL_GOOGLE_TALK, Im.PROTOCOL_AIM, Im.PROTOCOL_MSN, Im.PROTOCOL_YAHOO,
213 Im.PROTOCOL_JABBER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800214 };
215 private static final int[] TYPE_PRECEDENCE_ORG = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700216 Organization.TYPE_WORK, Organization.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800217 };
218
219 public void onClick(View v) {
220 switch (v.getId()) {
221 case R.id.photoImage: {
222 doPickPhotoAction();
223 break;
224 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700225
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800226 case R.id.checkable: {
227 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
228 checkBox.toggle();
Evan Millar7e4accf2009-06-08 10:43:26 -0700229
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800230 EditEntry entry = findEntryForView(v);
231 entry.data = checkBox.isChecked() ? "1" : "0";
Evan Millar7e4accf2009-06-08 10:43:26 -0700232
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800233 mContactChanged = true;
234 break;
235 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700236
237 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700238 case R.id.entry_group: {
239 EditEntry entry = findEntryForView(v);
240 doPickGroup(entry);
241 break;
242 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700243 */
244
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800245 case R.id.separator: {
246 // Someone clicked on a section header, so handle add action
Evan Millar7e4accf2009-06-08 10:43:26 -0700247 // TODO: Data addition is still being hashed out.
248 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800249 int sectionType = (Integer) v.getTag();
250 doAddAction(sectionType);
Evan Millar7e4accf2009-06-08 10:43:26 -0700251 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800252 break;
253 }
254
255 case R.id.saveButton:
256 doSaveAction();
257 break;
258
259 case R.id.discardButton:
260 doRevertAction();
261 break;
262
263 case R.id.delete: {
264 EditEntry entry = findEntryForView(v);
265 if (entry != null) {
266 // Clear the text and hide the view so it gets saved properly
267 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
268 entry.view.setVisibility(View.GONE);
269 entry.isDeleted = true;
270 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700271
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800272 // Force rebuild of views because section headers might need to change
273 buildViews();
274 break;
275 }
276
277 case R.id.label: {
278 EditEntry entry = findEntryForView(v);
279 if (entry != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700280 String[] labels = getLabelsForMimetype(this, entry.mimetype);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800281 LabelPickedListener listener = new LabelPickedListener(entry, labels);
282 new AlertDialog.Builder(EditContactActivity.this)
283 .setItems(labels, listener)
284 .setTitle(R.string.selectLabel)
285 .show();
286 }
287 break;
288 }
289 }
290 }
291
292 private void setPhotoPresent(boolean present) {
293 mPhotoPresent = present;
Evan Millar7e4accf2009-06-08 10:43:26 -0700294
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800295 // Correctly scale the contact photo if present, otherwise just center
296 // the photo placeholder icon.
297 if (mPhotoPresent) {
298 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
299 } else {
300 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
301 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
302 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700303
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800304 if (mPhotoMenuItem != null) {
305 if (present) {
306 mPhotoMenuItem.setTitle(R.string.removePicture);
307 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
308 } else {
309 mPhotoMenuItem.setTitle(R.string.addPicture);
310 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
311 }
312 }
313 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700314
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800315 private EditEntry findEntryForView(View v) {
316 // Try to find the entry for this view
317 EditEntry entry = null;
318 do {
319 Object tag = v.getTag();
320 if (tag != null && tag instanceof EditEntry) {
321 entry = (EditEntry) tag;
322 break;
323 } else {
324 ViewParent parent = v.getParent();
325 if (parent != null && parent instanceof View) {
326 v = (View) parent;
327 } else {
328 v = null;
329 }
330 }
331 } while (v != null);
332 return entry;
333 }
334
335 private DialogInterface.OnClickListener mDeleteContactDialogListener =
336 new DialogInterface.OnClickListener() {
337 public void onClick(DialogInterface dialog, int button) {
338 mResolver.delete(mUri, null, null);
339 finish();
340 }
341 };
342
343 private boolean mMobilePhoneAdded = false;
344 private boolean mPrimaryEmailAdded = false;
345
346 @Override
347 protected void onCreate(Bundle icicle) {
348 super.onCreate(icicle);
349
350 mResolver = getContentResolver();
351
352 // Build the list of sections
353 setupSections();
354
355 // Load the UI
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800356 mInflater = getLayoutInflater();
357 mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
358 setContentView(mContentView);
Evan Millar7e4accf2009-06-08 10:43:26 -0700359
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800360 mLayout = (LinearLayout) findViewById(R.id.list);
361 mNameView = (EditText) findViewById(R.id.name);
362 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
363 mPhotoImageView.setOnClickListener(this);
364 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
Evan Millar7e4accf2009-06-08 10:43:26 -0700365
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800366 // Setup the bottom buttons
367 View view = findViewById(R.id.saveButton);
368 view.setOnClickListener(this);
369 view = findViewById(R.id.discardButton);
370 view.setOnClickListener(this);
371
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800372 // Resolve the intent
373 mState = STATE_UNKNOWN;
374 Intent intent = getIntent();
375 String action = intent.getAction();
376 mUri = intent.getData();
Evan Millar7e4accf2009-06-08 10:43:26 -0700377 mAggDataUri = Uri.withAppendedPath(mUri, "data");
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800378 if (mUri != null) {
379 if (action.equals(Intent.ACTION_EDIT)) {
380 if (icicle == null) {
381 // Build the entries & views
382 buildEntriesForEdit(getIntent().getExtras());
383 buildViews();
384 }
385 setTitle(R.string.editContact_title_edit);
386 mState = STATE_EDIT;
387 } else if (action.equals(Intent.ACTION_INSERT)) {
388 if (icicle == null) {
389 // Build the entries & views
Evan Millar7e4accf2009-06-08 10:43:26 -0700390 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800391 buildEntriesForInsert(getIntent().getExtras());
392 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700393 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800394 }
395 setTitle(R.string.editContact_title_insert);
396 mState = STATE_INSERT;
397 mInsert = true;
398 }
399 }
400
401 if (mState == STATE_UNKNOWN) {
402 Log.e(TAG, "Cannot resolve intent: " + intent);
403 finish();
404 return;
405 }
406
407 if (mState == STATE_EDIT) {
408 setTitle(getResources().getText(R.string.editContact_title_edit));
409 } else {
410 setTitle(getResources().getText(R.string.editContact_title_insert));
411 }
412 }
413
414 private void setupSections() {
415 mSections.add(mPhoneEntries);
416 mSections.add(mEmailEntries);
417 mSections.add(mImEntries);
418 mSections.add(mPostalEntries);
419 mSections.add(mOrgEntries);
420 mSections.add(mNoteEntries);
421 mSections.add(mOtherEntries);
422 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700423
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800424 @Override
425 protected void onSaveInstanceState(Bundle outState) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700426
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800427 // To store current focus between config changes, follow focus down the
428 // view tree, keeping track of any parents with EditEntry tags
429 View focusedChild = mContentView.getFocusedChild();
430 EditEntry focusedEntry = null;
431 while (focusedChild != null) {
432 Object tag = focusedChild.getTag();
433 if (tag instanceof EditEntry) {
434 focusedEntry = (EditEntry) tag;
435 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700436
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800437 // Keep going deeper until child isn't a group
438 if (focusedChild instanceof ViewGroup) {
439 View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
440 if (deeperFocus != null) {
441 focusedChild = deeperFocus;
442 } else {
443 break;
444 }
445 } else {
446 break;
447 }
448 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700449
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800450 if (focusedChild != null) {
451 int requestFocusId = focusedChild.getId();
452 int requestCursor = 0;
453 if (focusedChild instanceof EditText) {
454 requestCursor = ((EditText) focusedChild).getSelectionStart();
455 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700456
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800457 // Store focus values in EditEntry if found, otherwise store as
458 // generic values
459 if (focusedEntry != null) {
460 focusedEntry.requestFocusId = requestFocusId;
461 focusedEntry.requestCursor = requestCursor;
462 } else {
463 outState.putInt("requestFocusId", requestFocusId);
464 outState.putInt("requestCursor", requestCursor);
465 }
466 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700467
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800468 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
469 outState.putParcelableArrayList("emailEntries", mEmailEntries);
470 outState.putParcelableArrayList("imEntries", mImEntries);
471 outState.putParcelableArrayList("postalEntries", mPostalEntries);
472 outState.putParcelableArrayList("orgEntries", mOrgEntries);
473 outState.putParcelableArrayList("noteEntries", mNoteEntries);
474 outState.putParcelableArrayList("otherEntries", mOtherEntries);
475 outState.putInt("state", mState);
476 outState.putBoolean("insert", mInsert);
477 outState.putParcelable("uri", mUri);
478 outState.putString("name", mNameView.getText().toString());
479 outState.putParcelable("photo", mPhoto);
480 outState.putBoolean("photoChanged", mPhotoChanged);
481 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
482 outState.putBoolean("contactChanged", mContactChanged);
483 }
484
485 @Override
486 protected void onRestoreInstanceState(Bundle inState) {
487 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
488 mEmailEntries = inState.getParcelableArrayList("emailEntries");
489 mImEntries = inState.getParcelableArrayList("imEntries");
490 mPostalEntries = inState.getParcelableArrayList("postalEntries");
491 mOrgEntries = inState.getParcelableArrayList("orgEntries");
492 mNoteEntries = inState.getParcelableArrayList("noteEntries");
493 mOtherEntries = inState.getParcelableArrayList("otherEntries");
494 setupSections();
495
496 mState = inState.getInt("state");
497 mInsert = inState.getBoolean("insert");
498 mUri = inState.getParcelable("uri");
499 mNameView.setText(inState.getString("name"));
500 mPhoto = inState.getParcelable("photo");
501 if (mPhoto != null) {
502 mPhotoImageView.setImageBitmap(mPhoto);
503 setPhotoPresent(true);
504 } else {
505 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
506 setPhotoPresent(false);
507 }
508 mPhotoChanged = inState.getBoolean("photoChanged");
509 mPhoneticNameView.setText(inState.getString("phoneticName"));
510 mContactChanged = inState.getBoolean("contactChanged");
511
512 // Now that everything is restored, build the view
513 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700514
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800515 // Try restoring any generally requested focus
516 int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
517 View focusedChild = mContentView.findViewById(requestFocusId);
518 if (focusedChild != null) {
519 focusedChild.requestFocus();
520 if (focusedChild instanceof EditText) {
521 int requestCursor = inState.getInt("requestCursor", 0);
522 ((EditText) focusedChild).setSelection(requestCursor);
523 }
524 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800525 }
526
527 @Override
528 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
529 if (resultCode != RESULT_OK) {
530 return;
531 }
532
533 switch (requestCode) {
534 case PHOTO_PICKED_WITH_DATA: {
535 final Bundle extras = data.getExtras();
536 if (extras != null) {
537 Bitmap photo = extras.getParcelable("data");
538 mPhoto = photo;
539 mPhotoChanged = true;
540 mPhotoImageView.setImageBitmap(photo);
541 setPhotoPresent(true);
542 }
543 break;
544 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800545 }
546 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700547
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800548 @Override
549 public boolean onKeyDown(int keyCode, KeyEvent event) {
550 switch (keyCode) {
551 case KeyEvent.KEYCODE_BACK: {
552 doSaveAction();
553 return true;
554 }
555 }
556 return super.onKeyDown(keyCode, event);
557 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700558
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800559 @Override
560 public boolean onCreateOptionsMenu(Menu menu) {
561 super.onCreateOptionsMenu(menu);
562 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
563 .setIcon(android.R.drawable.ic_menu_save)
564 .setAlphabeticShortcut('\n');
565 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
566 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
567 .setAlphabeticShortcut('q');
568 if (!mInsert) {
569 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
570 .setIcon(android.R.drawable.ic_menu_delete);
571 }
572
573 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
574 // Updates the state of the menu item
575 setPhotoPresent(mPhotoPresent);
576
577 return true;
578 }
579
580 @Override
581 public boolean onOptionsItemSelected(MenuItem item) {
582 switch (item.getItemId()) {
583 case MENU_ITEM_SAVE:
584 doSaveAction();
585 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700586
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800587 case MENU_ITEM_DONT_SAVE:
588 doRevertAction();
589 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700590
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800591 case MENU_ITEM_DELETE:
592 // Get confirmation
593 showDialog(DELETE_CONFIRMATION_DIALOG);
594 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700595
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800596 case MENU_ITEM_PHOTO:
597 if (!mPhotoPresent) {
598 doPickPhotoAction();
599 } else {
600 doRemovePhotoAction();
601 }
602 return true;
603 }
604
605 return false;
606 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700607
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800608 /**
609 * Try guessing the next-best type of {@link EditEntry} to insert into the
610 * given list. We walk down the precedence list until we find a type that
611 * doesn't exist yet, or default to the lowest ranking type.
612 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700613 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
615 // Keep track of the types we've seen already
616 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
617 for (int i = entries.size() - 1; i >= 0; i--) {
618 EditEntry entry = entries.get(i);
619 if (!entry.isDeleted) {
620 existAlready.put(entry.type, true);
621 }
622 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700623
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800624 // Pick the first item we haven't seen
625 for (int type : precedenceList) {
626 if (!existAlready.get(type, false)) {
627 return type;
628 }
629 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700630
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800631 // Otherwise default to last item
632 return precedenceList[precedenceList.length - 1];
633 }
634
635 private void doAddAction(int sectionType) {
636 EditEntry entry = null;
637 switch (sectionType) {
638 case SECTION_PHONES: {
639 // Try figuring out which type to insert next
640 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
Evan Millar7e4accf2009-06-08 10:43:26 -0700641 entry = EditEntry.newPhoneEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800642 nextType);
643 mPhoneEntries.add(entry);
644 break;
645 }
646 case SECTION_EMAIL: {
647 // Try figuring out which type to insert next
Evan Millar7e4accf2009-06-08 10:43:26 -0700648 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_EMAIL);
649 entry = EditEntry.newEmailEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800650 nextType);
651 mEmailEntries.add(entry);
652 break;
653 }
654 case SECTION_IM: {
655 // Try figuring out which type to insert next
656 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
Evan Millar7e4accf2009-06-08 10:43:26 -0700657 entry = EditEntry.newImEntry(EditContactActivity.this, Data.CONTENT_URI, nextType);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800658 mImEntries.add(entry);
659 break;
660 }
661 case SECTION_POSTAL: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700662 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_POSTAL);
663 entry = EditEntry.newPostalEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800664 nextType);
665 mPostalEntries.add(entry);
666 break;
667 }
668 case SECTION_ORG: {
669 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
Evan Millar7e4accf2009-06-08 10:43:26 -0700670 entry = EditEntry.newOrganizationEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800671 nextType);
672 mOrgEntries.add(entry);
673 break;
674 }
675 case SECTION_NOTE: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700676 entry = EditEntry.newNotesEntry(EditContactActivity.this, Data.CONTENT_URI);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800677 mNoteEntries.add(entry);
678 break;
679 }
680 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700681
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800682 // Rebuild the views if needed
683 if (entry != null) {
684 buildViews();
685 mContactChanged = true;
686
687 View dataView = entry.view.findViewById(R.id.data);
688 if (dataView == null) {
689 entry.view.requestFocus();
690 } else {
691 dataView.requestFocus();
692 }
693 }
694 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700695 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800696
697 private void doRevertAction() {
698 finish();
699 }
700
701 private void doPickPhotoAction() {
702 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
703 // TODO: get these values from constants somewhere
704 intent.setType("image/*");
705 intent.putExtra("crop", "true");
706 intent.putExtra("aspectX", 1);
707 intent.putExtra("aspectY", 1);
708 intent.putExtra("outputX", 96);
709 intent.putExtra("outputY", 96);
710 try {
711 intent.putExtra("return-data", true);
712 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
713 } catch (ActivityNotFoundException e) {
714 new AlertDialog.Builder(EditContactActivity.this)
715 .setTitle(R.string.errorDialogTitle)
716 .setMessage(R.string.photoPickerNotFoundText)
717 .setPositiveButton(android.R.string.ok, null)
718 .show();
719 }
720 }
721
722 private void doRemovePhotoAction() {
723 mPhoto = null;
724 mPhotoChanged = true;
725 setPhotoPresent(false);
726 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700727
728 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700729 private void populateGroups() {
730 // Create a list of all the groups
731 Cursor cursor = mResolver.query(Groups.CONTENT_URI, ContactsListActivity.GROUPS_PROJECTION,
732 null, null, Groups.DEFAULT_SORT_ORDER);
733 try {
734 ArrayList<Long> ids = new ArrayList<Long>();
735 ArrayList<String> items = new ArrayList<String>();
736
737 while (cursor.moveToNext()) {
738 String systemId = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
739 String name = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700740
Alex Kennberg87fc3172009-03-28 06:43:06 -0700741 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
742 continue;
743 }
744
745 if (!TextUtils.isEmpty(name)) {
746 ids.add(new Long(cursor.getLong(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID)));
747 items.add(name);
748 }
749 }
750
751 mGroups = items.toArray(new CharSequence[items.size()]);
752 mInTheGroup = new boolean[items.size()];
753 } finally {
754 cursor.close();
755 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700756
Alex Kennberg87fc3172009-03-28 06:43:06 -0700757 if (mGroups != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700758
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700759 // Go through the groups for this member and update the list
Alex Kennberg87fc3172009-03-28 06:43:06 -0700760 final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700761 Cursor groupCursor = null;
762 try {
763 groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
764 null, null, Groups.DEFAULT_SORT_ORDER);
765 } catch (IllegalArgumentException e) {
766 // Contact is new, so we don't need to do any work.
767 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700768
Alex Kennberg87fc3172009-03-28 06:43:06 -0700769 if (groupCursor != null) {
770 try {
771 while (groupCursor.moveToNext()) {
772 String systemId = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
773 String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700774
Alex Kennberg87fc3172009-03-28 06:43:06 -0700775 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
776 continue;
777 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700778
Alex Kennberg87fc3172009-03-28 06:43:06 -0700779 if (!TextUtils.isEmpty(name)) {
780 for (int i = 0; i < mGroups.length; i++) {
781 if (name.equals(mGroups[i])) {
782 mInTheGroup[i] = true;
783 break;
784 }
785 }
786 }
787 }
788 } finally {
789 groupCursor.close();
790 }
791 }
792 }
793 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700794
Alex Kennberg87fc3172009-03-28 06:43:06 -0700795 private String generateGroupList() {
796 StringBuilder groupList = new StringBuilder();
797 for (int i = 0; mGroups != null && i < mGroups.length; i++) {
798 if (mInTheGroup[i]) {
799 if (groupList.length() == 0) {
800 groupList.append(mGroups[i]);
801 } else {
802 groupList.append(getString(R.string.group_list, mGroups[i]));
803 }
804 }
805 }
806 return groupList.length() > 0 ? groupList.toString() : null;
807 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700808
Alex Kennberg87fc3172009-03-28 06:43:06 -0700809 private void doPickGroup(EditEntry entry) {
810 if (mGroups != null) {
811 GroupDialogListener listener = new GroupDialogListener(this, entry);
Evan Millar7e4accf2009-06-08 10:43:26 -0700812
Alex Kennberg87fc3172009-03-28 06:43:06 -0700813 new AlertDialog.Builder(EditContactActivity.this)
814 .setTitle(R.string.label_groups)
815 .setMultiChoiceItems(mGroups, mInTheGroup, listener)
816 .setPositiveButton(android.R.string.ok, listener)
817 .setNegativeButton(android.R.string.cancel, null)
818 .show();
819 }
820 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700821 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700822
823 /** Handles the clicks in the groups dialog */
Evan Millar7e4accf2009-06-08 10:43:26 -0700824 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700825 private static final class GroupDialogListener implements DialogInterface.OnClickListener,
826 DialogInterface.OnMultiChoiceClickListener {
Evan Millar7e4accf2009-06-08 10:43:26 -0700827
Alex Kennberg87fc3172009-03-28 06:43:06 -0700828 private EditContactActivity mEditContactActivity;
829 private EditEntry mEntry;
830 private boolean[] mInTheGroup;
Evan Millar7e4accf2009-06-08 10:43:26 -0700831
Alex Kennberg87fc3172009-03-28 06:43:06 -0700832 public GroupDialogListener(EditContactActivity editContactActivity, EditEntry entry) {
833 mEditContactActivity = editContactActivity;
834 mEntry = entry;
835 mInTheGroup = editContactActivity.mInTheGroup.clone();
836 }
837
Evan Millar7e4accf2009-06-08 10:43:26 -0700838 // Called when the dialog's ok button is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700839 public void onClick(DialogInterface dialog, int which) {
840 mEditContactActivity.mInTheGroup = mInTheGroup;
841 mEntry.data = mEditContactActivity.generateGroupList();
842 mEditContactActivity.updateDataView(mEntry, mEntry.data);
843 }
844
Evan Millar7e4accf2009-06-08 10:43:26 -0700845 // Called when each group is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700846 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
847 mInTheGroup[which] = isChecked;
848 }
849 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700850 */
851
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800852 private void updateDataView(EditEntry entry, String text) {
853 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
854 dataView.setText(text);
855 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700856
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800857 @Override
858 protected Dialog onCreateDialog(int id) {
859 switch (id) {
860 case DELETE_CONFIRMATION_DIALOG:
861 return new AlertDialog.Builder(EditContactActivity.this)
862 .setTitle(R.string.deleteConfirmation_title)
863 .setIcon(android.R.drawable.ic_dialog_alert)
864 .setMessage(R.string.deleteConfirmation)
865 .setNegativeButton(android.R.string.cancel, null)
866 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
867 .setCancelable(false)
868 .create();
869 }
870 return super.onCreateDialog(id);
871 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700872
873 static String[] getLabelsForMimetype(Context context, String mimetype) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800874 final Resources resources = context.getResources();
Evan Millar7e4accf2009-06-08 10:43:26 -0700875 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
876 return resources.getStringArray(android.R.array.phoneTypes);
877 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
878 return resources.getStringArray(android.R.array.emailAddressTypes);
879 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
880 return resources.getStringArray(android.R.array.postalAddressTypes);
881 } else if (mimetype.equals(Im.CONTENT_ITEM_TYPE)) {
882 return resources.getStringArray(android.R.array.imProtocols);
883 } else if (mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
884 return resources.getStringArray(android.R.array.organizationTypes);
885 } else {
886 return resources.getStringArray(R.array.otherLabels);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800887 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800888 }
889
890 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
891 // In the UI Custom... comes last, but it is uses the constant 0
892 // so it is in the same location across the various kinds. Fix up the
893 // position to a valid type here.
894 if (labelPosition == labels.length - 1) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700895 return BaseTypes.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800896 } else {
897 return labelPosition + 1;
898 }
899 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700900
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800901 private EditEntry getOtherEntry(String column) {
902 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
903 EditEntry entry = mOtherEntries.get(i);
904 if (isOtherEntry(entry, column)) {
905 return entry;
906 }
907 }
908 return null;
909 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700910
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800911 private static boolean isOtherEntry(EditEntry entry, String column) {
912 return entry != null && entry.column != null && entry.column.equals(column);
913 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700914
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800915 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
916 final EditText label = new EditText(this);
917 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
918 label.requestFocus();
919 new AlertDialog.Builder(this)
920 .setView(label)
921 .setTitle(R.string.customLabelPickerTitle)
922 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
923 public void onClick(DialogInterface dialog, int which) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700924 entry.setLabel(EditContactActivity.this, BaseTypes.TYPE_CUSTOM,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800925 label.getText().toString());
926 mContactChanged = true;
927
928 if (addTo != null) {
929 addTo.add(entry);
930 buildViews();
931 entry.view.requestFocus(View.FOCUS_DOWN);
932 }
933 }
934 })
935 .setNegativeButton(android.R.string.cancel, null)
936 .show();
937 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700938
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800939 /**
940 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
941 */
942 private void doSaveAction() {
943 // Save or create the contact if needed
944 switch (mState) {
945 case STATE_EDIT:
946 save();
947 break;
948
Evan Millar7e4accf2009-06-08 10:43:26 -0700949 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800950 case STATE_INSERT:
951 create();
952 break;
Evan Millar7e4accf2009-06-08 10:43:26 -0700953 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800954
955 default:
956 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
957 break;
958 }
959 finish();
960 }
Alex Kennberg87fc3172009-03-28 06:43:06 -0700961
962 /**
963 * Gets the group id based on group name.
Evan Millar7e4accf2009-06-08 10:43:26 -0700964 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700965 * @param resolver the resolver to use
966 * @param groupName the name of the group to add the contact to
967 * @return the id of the group
968 * @throws IllegalStateException if the group can't be found
969 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700970 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700971 private long getGroupId(ContentResolver resolver, String groupName) {
972 long groupId = 0;
973 Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUP_ID_PROJECTION,
974 Groups.NAME + "=?", new String[] { groupName }, null);
975 if (groupsCursor != null) {
976 try {
977 if (groupsCursor.moveToFirst()) {
978 groupId = groupsCursor.getLong(0);
979 }
980 } finally {
981 groupsCursor.close();
982 }
983 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700984
Alex Kennberg87fc3172009-03-28 06:43:06 -0700985 if (groupId == 0) {
986 throw new IllegalStateException("Failed to find the " + groupName + "group");
987 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700988
Alex Kennberg87fc3172009-03-28 06:43:06 -0700989 return groupId;
990 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700991 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700992
993 /**
994 * Deletes group membership based on person and group ids.
Evan Millar7e4accf2009-06-08 10:43:26 -0700995 *
Alex Kennberg87fc3172009-03-28 06:43:06 -0700996 * @param personId the person id
997 * @param groupId the group id
998 * @return the id of the group membership
999 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001000 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07001001 private void deleteGroupMembership(long personId, long groupId) {
1002 long groupMembershipId = 0;
1003 Cursor groupsCursor = mResolver.query(GroupMembership.CONTENT_URI, GROUPMEMBERSHIP_ID_PROJECTION,
1004 GroupMembership.PERSON_ID + "=? AND " + GroupMembership.GROUP_ID + "=?",
1005 new String[] {String.valueOf(personId), String.valueOf(groupId)}, null);
1006 if (groupsCursor != null) {
1007 try {
1008 if (groupsCursor.moveToFirst()) {
1009 groupMembershipId = groupsCursor.getLong(0);
1010 }
1011 } finally {
1012 groupsCursor.close();
1013 }
1014 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001015
Alex Kennberg87fc3172009-03-28 06:43:06 -07001016 if (groupMembershipId != 0) {
1017 final Uri groupsUri = ContentUris.withAppendedId(
1018 GroupMembership.CONTENT_URI,groupMembershipId);
1019 mResolver.delete(groupsUri, null, null);
1020 }
1021 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001022 */
1023
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001024 /**
1025 * Save the various fields to the existing contact.
1026 */
1027 private void save() {
1028 ContentValues values = new ContentValues();
1029 String data;
1030 int numValues = 0;
1031
1032 // Handle the name and send to voicemail specially
1033 final String name = mNameView.getText().toString();
1034 if (name != null && TextUtils.isGraphic(name)) {
1035 numValues++;
1036 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001037
Evan Millar7e4accf2009-06-08 10:43:26 -07001038 values.put(StructuredName.DISPLAY_NAME, name);
1039 /*
1040 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1041 */
1042 mResolver.update(mStructuredNameUri, values, null, null);
1043
1044 // This will go down in for loop somewhere
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001045 if (mPhotoChanged) {
1046 // Only write the photo if it's changed, since we don't initially load mPhoto
Evan Millar7e4accf2009-06-08 10:43:26 -07001047 values.clear();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001048 if (mPhoto != null) {
1049 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1050 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
Evan Millar7e4accf2009-06-08 10:43:26 -07001051 values.put(Photo.PHOTO, stream.toByteArray());
1052 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001053 } else {
Evan Millar7e4accf2009-06-08 10:43:26 -07001054 values.putNull(Photo.PHOTO);
1055 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001056 }
1057 }
1058
1059 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1060 for (int i = 0; i < entryCount; i++) {
1061 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001062 data = entry.getData();
1063 boolean empty = data == null || !TextUtils.isGraphic(data);
Evan Millar7e4accf2009-06-08 10:43:26 -07001064 /*
1065 if (kind == EditEntry.KIND_GROUP) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001066 if (entry.id != 0) {
1067 for (int g = 0; g < mGroups.length; g++) {
1068 long groupId = getGroupId(mResolver, mGroups[g].toString());
1069 if (mInTheGroup[g]) {
1070 Contacts.People.addToGroup(mResolver, entry.id, groupId);
1071 numValues++;
1072 } else {
1073 deleteGroupMembership(entry.id, groupId);
1074 }
1075 }
1076 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001077 }
1078 */
1079 if (!empty) {
1080 values.clear();
1081 entry.toValues(values);
1082 if (entry.id != 0) {
1083 mResolver.update(entry.uri, values, null, null);
1084 } else {
1085 /* mResolver.insert(entry.uri, values); */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001086 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001087 } else if (entry.id != 0) {
1088 mResolver.delete(entry.uri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001089 }
1090 }
1091
Evan Millar7e4accf2009-06-08 10:43:26 -07001092 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001093 if (numValues == 0) {
1094 // The contact is completely empty, delete it
1095 mResolver.delete(mUri, null, null);
1096 mUri = null;
1097 setResult(RESULT_CANCELED);
1098 } else {
1099 // Add the entry to the my contacts group if it isn't there already
1100 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1101 setResult(RESULT_OK, new Intent().setData(mUri));
1102
1103 // Only notify user if we actually changed contact
1104 if (mContactChanged || mPhotoChanged) {
1105 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1106 }
1107 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001108 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001109 }
1110
1111 /**
1112 * Takes the entered data and saves it to a new contact.
1113 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001114 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001115 private void create() {
1116 ContentValues values = new ContentValues();
1117 String data;
1118 int numValues = 0;
1119
1120 // Create the contact itself
1121 final String name = mNameView.getText().toString();
1122 if (name != null && TextUtils.isGraphic(name)) {
1123 numValues++;
1124 }
1125 values.put(People.NAME, name);
1126 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1127
1128 // Add the contact to the My Contacts group
1129 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1130
1131 // Add the contact to the group that is being displayed in the contact list
1132 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1133 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1134 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1135 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1136 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
1137 null);
1138 if (!TextUtils.isEmpty(displayGroup)) {
1139 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1140 }
1141 } else {
1142 // Check to see if we're not syncing everything and if so if My Contacts is synced.
1143 // If it isn't then the created contact can end up not in any groups that are
1144 // currently synced and end up getting removed from the phone, which is really bad.
1145 boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
1146 Contacts.Settings.SYNC_EVERYTHING));
1147 if (!syncingEverything) {
1148 boolean syncingMyContacts = false;
1149 Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
1150 Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
1151 if (c != null) {
1152 try {
1153 if (c.moveToFirst()) {
1154 syncingMyContacts = !"0".equals(c.getString(0));
1155 }
1156 } finally {
1157 c.close();
1158 }
1159 }
1160
1161 if (!syncingMyContacts) {
1162 // Not syncing My Contacts, so find a group that is being synced and stick
1163 // the contact in there. We sort the list so at least all contacts
1164 // will appear in the same group.
1165 c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
1166 Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
1167 if (c != null) {
1168 try {
1169 if (c.moveToFirst()) {
1170 People.addToGroup(mResolver, ContentUris.parseId(contactUri),
1171 c.getLong(0));
1172 }
1173 } finally {
1174 c.close();
1175 }
1176 }
1177 }
1178 }
1179 }
1180
1181 // Handle the photo
1182 if (mPhoto != null) {
1183 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1184 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1185 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1186 }
1187
1188 // Create the contact methods
1189 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1190 for (int i = 0; i < entryCount; i++) {
1191 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
Alex Kennbergfb0386a2009-04-02 09:59:10 -07001192 if (entry.kind == EditEntry.KIND_GROUP) {
1193 long contactId = ContentUris.parseId(contactUri);
1194 for (int g = 0; g < mGroups.length; g++) {
1195 if (mInTheGroup[g]) {
1196 long groupId = getGroupId(mResolver, mGroups[g].toString());
1197 People.addToGroup(mResolver, contactId, groupId);
1198 numValues++;
1199 }
1200 }
1201 } else if (entry.kind != EditEntry.KIND_CONTACT) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001202 values.clear();
1203 if (entry.toValues(values)) {
1204 // Only create the entry if there is data
1205 entry.uri = mResolver.insert(
1206 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1207 entry.id = ContentUris.parseId(entry.uri);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001208 }
1209 } else {
1210 // Update the contact with any straggling data, like notes
1211 data = entry.getData();
1212 values.clear();
1213 if (data != null && TextUtils.isGraphic(data)) {
1214 values.put(entry.column, data);
1215 mResolver.update(contactUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001216 }
1217 }
1218 }
1219
1220 if (numValues == 0) {
1221 mResolver.delete(contactUri, null, null);
1222 setResult(RESULT_CANCELED);
1223 } else {
1224 mUri = contactUri;
1225 Intent resultIntent = new Intent()
1226 .setData(mUri)
1227 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1228 setResult(RESULT_OK, resultIntent);
1229 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1230 }
1231 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001232 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001233
1234 /**
1235 * Build up the entries to display on the screen.
1236 *
1237 * @param extras the extras used to start this activity, may be null
1238 */
1239 private void buildEntriesForEdit(Bundle extras) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001240 Cursor aggCursor = mResolver.query(mAggDataUri, AGGREGATE_PROJECTION, null, null, null);
1241 if (aggCursor == null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001242 Log.e(TAG, "invalid contact uri: " + mUri);
1243 finish();
1244 return;
Evan Millar7e4accf2009-06-08 10:43:26 -07001245 } else if (!aggCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001246 Log.e(TAG, "invalid contact uri: " + mUri);
1247 finish();
Evan Millar7e4accf2009-06-08 10:43:26 -07001248 aggCursor.close();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001249 return;
1250 }
1251
1252 // Clear out the old entries
1253 int numSections = mSections.size();
1254 for (int i = 0; i < numSections; i++) {
1255 mSections.get(i).clear();
1256 }
1257
1258 EditEntry entry;
1259
Evan Millar7e4accf2009-06-08 10:43:26 -07001260 while (aggCursor.moveToNext()) {
1261 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
1262 boolean isSuperPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001263
Evan Millar7e4accf2009-06-08 10:43:26 -07001264 final long id = aggCursor.getLong(DATA_ID_COLUMN);
1265 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
1266
1267 if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
1268 mNameView.setText(aggCursor.getString(DATA_9_COLUMN));
1269 mNameView.addTextChangedListener(this);
1270 mStructuredNameUri = uri;
1271 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
1272 mPhoto = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
1273 if (mPhoto == null) {
1274 setPhotoPresent(false);
1275 } else {
1276 setPhotoPresent(true);
1277 mPhotoImageView.setImageBitmap(mPhoto);
1278 }
1279 mPhotoDataUri = uri;
1280 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
1281 int type = aggCursor.getInt(DATA_1_COLUMN);
1282 String label = aggCursor.getString(DATA_2_COLUMN);
1283 String company = aggCursor.getString(DATA_3_COLUMN);
1284 String title = aggCursor.getString(DATA_4_COLUMN);
1285
1286 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1287 entry.isPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
1288 mOrgEntries.add(entry);
1289 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
1290 entry = EditEntry.newNotesEntry(this, aggCursor.getString(DATA_1_COLUMN),
1291 uri, id);
1292 mNoteEntries.add(entry);
Evan Millar7e4accf2009-06-08 10:43:26 -07001293 } else if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
1294 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
1295 || mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)
1296 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1297 int type = aggCursor.getInt(DATA_1_COLUMN);
1298 String data = aggCursor.getString(DATA_2_COLUMN);
1299 String label = aggCursor.getString(DATA_3_COLUMN);
1300
1301 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
1302 // Add a phone number entry
1303 entry = EditEntry.newPhoneEntry(this, label, type, data, uri, id);
1304 entry.isPrimary = isSuperPrimary;
1305 mPhoneEntries.add(entry);
1306
1307 // Keep track of which primary types have been added
1308 if (type == Phone.TYPE_MOBILE) {
1309 mMobilePhoneAdded = true;
1310 }
1311 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
1312 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1313 entry.isPrimary = isSuperPrimary;
1314 mEmailEntries.add(entry);
1315
1316 if (isSuperPrimary) {
1317 mPrimaryEmailAdded = true;
1318 }
1319 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
1320 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1321 entry.isPrimary = isSuperPrimary;
1322 mPostalEntries.add(entry);
1323 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1324 String protocolStr = aggCursor.getString(DATA_5_COLUMN);
1325 Object protocolObj = ContactsUtils.decodeImProtocol(protocolStr);
1326 if (protocolObj == null) {
1327 // Invalid IM protocol, log it then ignore.
1328 Log.e(TAG, "Couldn't decode IM protocol: " + protocolStr);
1329 continue;
1330 } else {
1331 if (protocolObj instanceof Number) {
1332 int protocol = ((Number) protocolObj).intValue();
1333 entry = EditEntry.newImEntry(this,
1334 getLabelsForMimetype(this, mimetype)[protocol], protocol,
1335 data, uri, id);
1336 } else {
1337 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1338 uri, id);
1339 }
1340 mImEntries.add(entry);
1341 }
1342 }
1343 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001344 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001345
1346 /*
1347 // Groups
1348 populateGroups();
1349 if (mGroups != null) {
1350 entry = EditEntry.newGroupEntry(this, generateGroupList(), mUri,
1351 personCursor.getLong(0));
1352 mOtherEntries.add(entry);
1353 }
1354
1355 // Phonetic name
1356 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
1357 mPhoneticNameView.addTextChangedListener(this);
1358
1359
1360 // Add values from the extras, if there are any
1361 if (extras != null) {
1362 addFromExtras(extras, phonesUri, methodsUri);
1363 }
1364
1365 // Add the base types if needed
1366 if (!mMobilePhoneAdded) {
1367 entry = EditEntry.newPhoneEntry(this,
1368 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1369 DEFAULT_PHONE_TYPE);
1370 mPhoneEntries.add(entry);
1371 }
1372
1373 if (!mPrimaryEmailAdded) {
1374 entry = EditEntry.newEmailEntry(this,
1375 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1376 DEFAULT_EMAIL_TYPE);
1377 entry.isPrimary = true;
1378 mEmailEntries.add(entry);
1379 }
1380 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001381
1382 mContactChanged = false;
1383 }
1384
1385 /**
1386 * Build the list of EditEntries for full mode insertions.
Evan Millar7e4accf2009-06-08 10:43:26 -07001387 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001388 * @param extras the extras used to start this activity, may be null
1389 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001390 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001391 private void buildEntriesForInsert(Bundle extras) {
1392 // Clear out the old entries
1393 int numSections = mSections.size();
1394 for (int i = 0; i < numSections; i++) {
1395 mSections.get(i).clear();
1396 }
1397
1398 EditEntry entry;
1399
1400 // Check the intent extras
1401 if (extras != null) {
1402 addFromExtras(extras, null, null);
1403 }
1404
1405 // Photo
1406 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1407
1408 // Add the base entries if they're not already present
1409 if (!mMobilePhoneAdded) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001410 entry = EditEntry.newPhoneEntry(this, null, Phone.TYPE_MOBILE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001411 entry.isPrimary = true;
1412 mPhoneEntries.add(entry);
1413 }
1414
1415 if (!mPrimaryEmailAdded) {
1416 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1417 entry.isPrimary = true;
1418 mEmailEntries.add(entry);
1419 }
1420
Alex Kennberg87fc3172009-03-28 06:43:06 -07001421 // Group
1422 populateGroups();
1423 if (mGroups != null) {
1424 entry = EditEntry.newGroupEntry(this, null, mUri, 0);
1425 mOtherEntries.add(entry);
1426 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001427 }
1428
1429 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1430 EditEntry entry;
1431
1432 // Read the name from the bundle
1433 CharSequence name = extras.getCharSequence(Insert.NAME);
1434 if (name != null && TextUtils.isGraphic(name)) {
1435 mNameView.setText(name);
1436 }
1437
1438 // Read the phonetic name from the bundle
1439 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1440 if (!TextUtils.isEmpty(phoneticName)) {
1441 mPhoneticNameView.setText(phoneticName);
1442 }
1443
1444 // Postal entries from extras
1445 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1446 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1447 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1448 postalType = DEFAULT_POSTAL_TYPE;
1449 }
1450
1451 if (postalType != INVALID_TYPE) {
1452 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1453 methodsUri, 0);
1454 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1455 mPostalEntries.add(entry);
1456 }
1457
1458 // Email entries from extras
1459 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1460 Insert.EMAIL_ISPRIMARY);
1461 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1462 null);
1463 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1464 null);
Evan Millar7e4accf2009-06-08 10:43:26 -07001465
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001466 // Phone entries from extras
1467 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1468 Insert.PHONE_ISPRIMARY);
1469 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1470 null);
1471 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1472 null);
1473
1474 // IM entries from extras
1475 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1476 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
Evan Millar7e4accf2009-06-08 10:43:26 -07001477
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001478 if (imHandle != null && imProtocol != null) {
1479 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1480 if (protocolObj instanceof Number) {
1481 int protocol = ((Number) protocolObj).intValue();
1482 entry = EditEntry.newImEntry(this,
Evan Millar7e4accf2009-06-08 10:43:26 -07001483 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001484 imHandle.toString(), methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001485 } else {
1486 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001487 methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001488 }
1489 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1490 mImEntries.add(entry);
1491 }
1492 }
1493
1494 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1495 String typeField, String primaryField) {
1496 CharSequence email = extras.getCharSequence(emailField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001497
1498 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001499 int emailType = INVALID_TYPE;
1500 String customLabel = null;
1501 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001502 emailType = ContactsContract.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001503 customLabel = extras.getString(typeField);
1504 } else {
1505 emailType = extras.getInt(typeField, INVALID_TYPE);
1506 }
1507
1508 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1509 emailType = DEFAULT_EMAIL_TYPE;
1510 mPrimaryEmailAdded = true;
1511 }
1512
1513 if (emailType != INVALID_TYPE) {
1514 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
1515 methodsUri, 0);
1516 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1517 mEmailEntries.add(entry);
1518
1519 // Keep track of which primary types have been added
1520 if (entry.isPrimary) {
1521 mPrimaryEmailAdded = true;
1522 }
1523 }
1524 }
1525
1526 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1527 String typeField, String primaryField) {
1528 CharSequence phoneNumber = extras.getCharSequence(phoneField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001529
1530 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001531 int phoneType = INVALID_TYPE;
1532 String customLabel = null;
1533 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001534 phoneType = Phone.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001535 customLabel = extras.getString(typeField);
1536 } else {
1537 phoneType = extras.getInt(typeField, INVALID_TYPE);
1538 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001539
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001540 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1541 phoneType = DEFAULT_PHONE_TYPE;
1542 }
1543
1544 if (phoneType != INVALID_TYPE) {
1545 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
1546 phoneNumber.toString(), phonesUri, 0);
1547 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1548 mPhoneEntries.add(entry);
1549
1550 // Keep track of which primary types have been added
Evan Millar7e4accf2009-06-08 10:43:26 -07001551 if (phoneType == Phone.TYPE_MOBILE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001552 mMobilePhoneAdded = true;
1553 }
1554 }
1555 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001556 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001557
1558 /**
1559 * Removes all existing views, builds new ones for all the entries, and adds them.
1560 */
1561 private void buildViews() {
1562 // Remove existing views
1563 final LinearLayout layout = mLayout;
1564 layout.removeAllViews();
Evan Millar7e4accf2009-06-08 10:43:26 -07001565
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001566 buildViewsForSection(layout, mPhoneEntries,
1567 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1568 buildViewsForSection(layout, mEmailEntries,
1569 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1570 buildViewsForSection(layout, mImEntries,
1571 R.string.listSeparatorSendIm_edit, SECTION_IM);
1572 buildViewsForSection(layout, mPostalEntries,
1573 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1574 buildViewsForSection(layout, mOrgEntries,
1575 R.string.listSeparatorOrganizations, SECTION_ORG);
1576 buildViewsForSection(layout, mNoteEntries,
1577 R.string.label_notes, SECTION_NOTE);
Evan Millar7e4accf2009-06-08 10:43:26 -07001578
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001579 buildOtherViews(layout, mOtherEntries);
1580 }
1581
1582
1583 /**
1584 * Builds the views for a specific section.
Evan Millar7e4accf2009-06-08 10:43:26 -07001585 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001586 * @param layout the container
1587 * @param section the section to build the views for
1588 */
1589 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1590 int separatorResource, int sectionType) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001591
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001592 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1593 layout.addView(divider);
Evan Millar7e4accf2009-06-08 10:43:26 -07001594
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001595 // Count up undeleted children
1596 int activeChildren = 0;
1597 for (int i = section.size() - 1; i >= 0; i--) {
1598 EditEntry entry = section.get(i);
1599 if (!entry.isDeleted) {
1600 activeChildren++;
1601 }
1602 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001603
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001604 // Build the correct group header based on undeleted children
1605 ViewGroup header;
1606 if (activeChildren == 0) {
1607 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1608 } else {
1609 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
1610 }
1611
1612 // Because we're emulating a ListView, we need to handle focus changes
1613 // with some additional logic.
1614 header.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001615
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001616 TextView text = (TextView) header.findViewById(R.id.text);
1617 text.setText(getText(separatorResource));
Evan Millar7e4accf2009-06-08 10:43:26 -07001618
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001619 // Force TextView to always default color if we have children. This makes sure
1620 // we don't change color when parent is pressed.
1621 if (activeChildren > 0) {
1622 ColorStateList stateList = text.getTextColors();
1623 text.setTextColor(stateList.getDefaultColor());
1624 }
1625
1626 View addView = header.findViewById(R.id.separator);
1627 addView.setTag(Integer.valueOf(sectionType));
1628 addView.setOnClickListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001629
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001630 // Build views for the current section
1631 for (EditEntry entry : section) {
1632 entry.activity = this; // this could be null from when the state is restored
1633 if (!entry.isDeleted) {
1634 View view = buildViewForEntry(entry);
1635 header.addView(view);
1636 }
1637 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001638
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001639 layout.addView(header);
1640 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001641
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001642 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1643 // Build views for the current section, putting a divider between each one
1644 for (EditEntry entry : section) {
1645 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1646 layout.addView(divider);
1647
1648 entry.activity = this; // this could be null from when the state is restored
1649 View view = buildViewForEntry(entry);
1650 view.setOnClickListener(this);
1651 layout.addView(view);
1652 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001653
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001654 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1655 layout.addView(divider);
1656 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001657
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001658 /**
1659 * Builds a view to display an EditEntry.
Evan Millar7e4accf2009-06-08 10:43:26 -07001660 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001661 * @param entry the entry to display
1662 * @return a view that will display the given entry
1663 */
1664 /* package */ View buildViewForEntry(final EditEntry entry) {
1665 // Look for any existing entered text, and save it if found
1666 if (entry.view != null && entry.syncDataWithView) {
1667 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1668 .getText().toString();
1669 if (!TextUtils.isEmpty(enteredText)) {
1670 entry.data = enteredText;
1671 }
1672 }
1673
1674 // Build a new view
1675 final ViewGroup parent = mLayout;
1676 View view;
1677
1678 // Because we're emulating a ListView, we might need to handle focus changes
1679 // with some additional logic.
Evan Millar7e4accf2009-06-08 10:43:26 -07001680 if (entry.mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001681 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
Evan Millar7e4accf2009-06-08 10:43:26 -07001682 /*
1683 else if (entry.mimetype.equals(Group.CONTENT_ITEM_TYPE)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001684 view = mInflater.inflate(R.layout.edit_contact_entry_group, parent, false);
1685 view.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001686 }
1687 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001688 } else if (!entry.isStaticLabel) {
1689 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1690 } else {
1691 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1692 }
1693 entry.view = view;
Evan Millar7e4accf2009-06-08 10:43:26 -07001694
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001695 // Set the entry as the tag so we can find it again later given just the view
1696 view.setTag(entry);
1697
1698 // Bind the label
1699 entry.bindLabel(this);
1700
1701 // Bind data
1702 TextView data = (TextView) view.findViewById(R.id.data);
1703 TextView data2 = (TextView) view.findViewById(R.id.data2);
Evan Millar7e4accf2009-06-08 10:43:26 -07001704
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001705 if (data instanceof Button) {
1706 data.setOnClickListener(this);
1707 }
1708 if (data.length() == 0) {
1709 if (entry.syncDataWithView) {
1710 // If there is already data entered don't overwrite it
1711 data.setText(entry.data);
1712 } else {
1713 fillViewData(entry);
1714 }
1715 }
1716 if (data2 != null && data2.length() == 0) {
1717 // If there is already data entered don't overwrite it
1718 data2.setText(entry.data2);
1719 }
1720 data.setHint(entry.hint);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001721 if (data2 != null) {
1722 data2.setHint(entry.hint2);
1723 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001724 if (entry.lines > 1) {
1725 data.setLines(entry.lines);
1726 data.setMaxLines(entry.maxLines);
1727 if (data2 != null) {
1728 data2.setLines(entry.lines);
1729 data2.setMaxLines(entry.maxLines);
1730 }
1731 }
1732 int contentType = entry.contentType;
1733 if (contentType != EditorInfo.TYPE_NULL) {
1734 data.setInputType(contentType);
1735 if (data2 != null) {
1736 data2.setInputType(contentType);
1737 }
1738 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1739 == EditorInfo.TYPE_CLASS_PHONE) {
1740 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1741 if (data2 != null) {
1742 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1743 }
1744 }
1745 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001746
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001747 // Give focus to children as requested, possibly after a configuration change
1748 View focusChild = view.findViewById(entry.requestFocusId);
1749 if (focusChild != null) {
1750 focusChild.requestFocus();
1751 if (focusChild instanceof EditText) {
1752 ((EditText) focusChild).setSelection(entry.requestCursor);
1753 }
1754 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001755
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001756 // Reset requested focus values
1757 entry.requestFocusId = View.NO_ID;
1758 entry.requestCursor = 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001759
1760 // Connect listeners up to watch for changed values.
1761 if (data instanceof EditText) {
1762 data.addTextChangedListener(this);
1763 }
1764 if (data2 instanceof EditText) {
1765 data2.addTextChangedListener(this);
1766 }
1767
1768 // Hook up the delete button
1769 View delete = view.findViewById(R.id.delete);
Dmitri Plotnikovef038722009-06-24 18:51:47 -07001770 if (delete != null) {
1771 delete.setOnClickListener(this);
1772 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001773
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001774 return view;
1775 }
1776
1777 private void fillViewData(final EditEntry entry) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001778 /*
1779 else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001780 if (entry.data != null) {
1781 updateDataView(entry, entry.data);
1782 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001783 }
1784 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001785 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001786
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001787 /**
1788 * Handles the results from the label change picker.
1789 */
1790 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1791 EditEntry mEntry;
1792 String[] mLabels;
1793
1794 public LabelPickedListener(EditEntry entry, String[] labels) {
1795 mEntry = entry;
1796 mLabels = labels;
1797 }
1798
1799 public void onClick(DialogInterface dialog, int which) {
1800 // TODO: Use a managed dialog
Evan Millar7e4accf2009-06-08 10:43:26 -07001801 if (mEntry.mimetype != Im.CONTENT_ITEM_TYPE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001802 final int type = getTypeFromLabelPosition(mLabels, which);
Evan Millar7e4accf2009-06-08 10:43:26 -07001803 if (type == BaseTypes.TYPE_CUSTOM) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001804 createCustomPicker(mEntry, null);
1805 } else {
1806 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1807 mContactChanged = true;
1808 }
1809 } else {
1810 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1811 mContactChanged = true;
1812 }
1813 }
1814 }
1815
1816 /**
1817 * A basic structure with the data for a contact entry in the list.
1818 */
1819 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1820 // These aren't stuffed into the parcel
1821 public EditContactActivity activity;
1822 public View view;
1823
1824 // These are stuffed into the parcel
1825 public String hint;
1826 public String hint2;
1827 public String column;
1828 public String contentDirectory;
1829 public String data2;
1830 public int contentType;
1831 public int type;
1832 /**
1833 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1834 * will not be called.
1835 */
1836 public int lines = 1;
1837 public boolean isPrimary;
1838 public boolean isDeleted = false;
1839 public boolean isStaticLabel = false;
1840 public boolean syncDataWithView = true;
1841
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001842 /**
1843 * Request focus on the child of this {@link EditEntry} found using
1844 * {@link View#findViewById(int)}. This value should be reset to
1845 * {@link View#NO_ID} after each use.
1846 */
1847 public int requestFocusId = View.NO_ID;
1848
1849 /**
1850 * If the {@link #requestFocusId} is an {@link EditText}, this value
1851 * indicates the requested cursor position placement.
1852 */
1853 public int requestCursor = 0;
1854
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001855 private EditEntry() {
1856 // only used by CREATOR
1857 }
1858
1859 public EditEntry(EditContactActivity activity) {
1860 this.activity = activity;
1861 }
1862
1863 public EditEntry(EditContactActivity activity, String label,
1864 int type, String data, Uri uri, long id) {
1865 this.activity = activity;
1866 this.isPrimary = false;
1867 this.label = label;
1868 this.type = type;
1869 this.data = data;
1870 this.uri = uri;
1871 this.id = id;
1872 }
1873
1874 public int describeContents() {
1875 return 0;
1876 }
1877
1878 public void writeToParcel(Parcel parcel, int flags) {
1879 // Make sure to read data from the input field, if anything is entered
1880 data = getData();
1881
1882 // Write in our own fields.
1883 parcel.writeString(hint);
1884 parcel.writeString(hint2);
1885 parcel.writeString(column);
1886 parcel.writeString(contentDirectory);
1887 parcel.writeString(data2);
1888 parcel.writeInt(contentType);
1889 parcel.writeInt(type);
1890 parcel.writeInt(lines);
1891 parcel.writeInt(isPrimary ? 1 : 0);
1892 parcel.writeInt(isDeleted ? 1 : 0);
1893 parcel.writeInt(isStaticLabel ? 1 : 0);
1894 parcel.writeInt(syncDataWithView ? 1 : 0);
1895
1896 // Write in the fields from Entry
1897 super.writeToParcel(parcel);
1898 }
1899
1900 public static final Parcelable.Creator<EditEntry> CREATOR =
1901 new Parcelable.Creator<EditEntry>() {
1902 public EditEntry createFromParcel(Parcel in) {
1903 EditEntry entry = new EditEntry();
1904
1905 // Read out our own fields
1906 entry.hint = in.readString();
1907 entry.hint2 = in.readString();
1908 entry.column = in.readString();
1909 entry.contentDirectory = in.readString();
1910 entry.data2 = in.readString();
1911 entry.contentType = in.readInt();
1912 entry.type = in.readInt();
1913 entry.lines = in.readInt();
1914 entry.isPrimary = in.readInt() == 1;
1915 entry.isDeleted = in.readInt() == 1;
1916 entry.isStaticLabel = in.readInt() == 1;
1917 entry.syncDataWithView = in.readInt() == 1;
Evan Millar7e4accf2009-06-08 10:43:26 -07001918
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001919 // Read out the fields from Entry
1920 entry.readFromParcel(in);
1921
1922 return entry;
1923 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001924
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001925 public EditEntry[] newArray(int size) {
1926 return new EditEntry[size];
1927 }
1928 };
1929
1930 public void setLabel(Context context, int typeIn, String labelIn) {
1931 type = typeIn;
1932 label = labelIn;
1933 if (view != null) {
1934 bindLabel(context);
1935 }
1936 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001937
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001938 public void bindLabel(Context context) {
1939 TextView v = (TextView) view.findViewById(R.id.label);
1940 if (isStaticLabel) {
1941 v.setText(label);
1942 return;
1943 }
1944
Evan Millar7e4accf2009-06-08 10:43:26 -07001945 v.setText(ContactsUtils.getDisplayLabel(context, mimetype, type, label));
1946 if (mimetype.equals(Im.CONTENT_ITEM_TYPE) && type >= 0) {
1947 v.setText(getLabelsForMimetype(activity, mimetype)[type]);
1948 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
1949 v.setMaxLines(3);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001950 }
1951 v.setOnClickListener(activity);
1952 }
1953
1954 /**
1955 * Returns the data for the entry
1956 * @return the data for the entry
1957 */
1958 public String getData() {
1959 if (view != null && syncDataWithView) {
1960 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
1961 if (text != null) {
1962 return text.toString();
1963 }
1964 }
1965
1966 if (data != null) {
1967 return data.toString();
1968 }
1969
1970 return null;
1971 }
1972
1973 /**
1974 * Dumps the entry into a HashMap suitable for passing to the database.
Evan Millar7e4accf2009-06-08 10:43:26 -07001975 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001976 * @param values the HashMap to fill in.
1977 * @return true if the value should be saved, false otherwise
1978 */
1979 public boolean toValues(ContentValues values) {
1980 boolean success = false;
1981 String labelString = null;
1982 // Save the type and label
1983 if (view != null) {
1984 // Read the possibly updated label from the text field
1985 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
1986 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001987 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
1988 if (type != Phone.TYPE_CUSTOM) {
1989 labelString = null;
1990 }
1991 values.put(Phone.LABEL, labelString);
1992 values.put(Phone.TYPE, type);
1993 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
1994 if (type != Email.TYPE_CUSTOM) {
1995 labelString = null;
1996 }
1997 values.put(Email.LABEL, labelString);
1998 values.put(Email.TYPE, type);
1999 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
2000 values.put(CommonDataKinds.Im.TYPE, type);
2001 values.putNull(CommonDataKinds.Im.LABEL);
2002 if (type != -1) {
2003 values.put(CommonDataKinds.Im.PROTOCOL,
2004 ContactsUtils.encodePredefinedImProtocol(type));
2005 } else {
2006 values.put(CommonDataKinds.Im.PROTOCOL,
2007 ContactsUtils.encodeCustomImProtocol(label.toString()));
2008 }
2009 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
2010 if (type != Postal.TYPE_CUSTOM) {
2011 labelString = null;
2012 }
2013 values.put(Postal.LABEL, labelString);
2014 values.put(Postal.TYPE, type);
2015 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
2016 if (type != Organization.TYPE_CUSTOM) {
2017 labelString = null;
2018 }
2019 values.put(Organization.LABEL, labelString);
2020 values.put(Organization.TYPE, type);
2021 // Save the title
2022 if (view != null) {
2023 // Read the possibly updated data from the text field
2024 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
2025 }
2026 if (!TextUtils.isGraphic(data2)) {
2027 values.putNull(Organization.TITLE);
2028 } else {
2029 values.put(Organization.TITLE, data2.toString());
2030 success = true;
2031 }
2032 } else {
2033 Log.i(TAG, "unknown entry mimetype: " + (mimetype == null ? "" : mimetype));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002034 }
2035
2036 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
2037 // ContentProvider will try finding a new primary when setting to false, meaning
2038 // it's possible to lose primary altogether as we walk down the list. If this editor
2039 // implements editing of primaries in the future, this will need to be revisited.
2040 if (isPrimary) {
Evan Millar7e4accf2009-06-08 10:43:26 -07002041 values.put(Data.IS_PRIMARY, 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002042 }
2043
2044 // Save the data
2045 if (view != null && syncDataWithView) {
2046 // Read the possibly updated data from the text field
2047 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
2048 }
2049 if (!TextUtils.isGraphic(data)) {
2050 values.putNull(column);
2051 return success;
2052 } else {
2053 values.put(column, data.toString());
2054 return true;
2055 }
2056 }
2057
2058 /**
2059 * Create a new empty organization entry
2060 */
2061 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2062 Uri uri, int type) {
2063 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
2064 }
2065
2066 /**
2067 * Create a new company entry with the given data.
2068 */
2069 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2070 String label, int type, String company, String title, Uri uri, long id) {
2071 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
2072 entry.hint = activity.getString(R.string.ghostData_company);
2073 entry.hint2 = activity.getString(R.string.ghostData_title);
2074 entry.data2 = title;
Evan Millar7e4accf2009-06-08 10:43:26 -07002075 entry.column = Organization.COMPANY;
2076 entry.mimetype = Organization.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002077 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2078 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
2079 return entry;
2080 }
2081
2082 /**
Evan Millar7e4accf2009-06-08 10:43:26 -07002083 * Create a new empty notes entry
2084 */
2085 public static final EditEntry newNotesEntry(EditContactActivity activity,
2086 Uri uri) {
2087 return newNotesEntry(activity, null, uri, 0);
2088 }
2089
2090 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002091 * Create a new notes entry with the given data.
2092 */
2093 public static final EditEntry newNotesEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002094 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002095 EditEntry entry = new EditEntry(activity);
2096 entry.label = activity.getString(R.string.label_notes);
2097 entry.hint = activity.getString(R.string.ghostData_notes);
2098 entry.data = data;
2099 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002100 entry.column = Note.NOTE;
2101 entry.mimetype = Note.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002102 entry.maxLines = 10;
2103 entry.lines = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -07002104 entry.id = id;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002105 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2106 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
2107 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2108 entry.isStaticLabel = true;
2109 return entry;
2110 }
2111
2112 /**
Alex Kennberg87fc3172009-03-28 06:43:06 -07002113 * Create a new group entry with the given data.
2114 */
Evan Millar7e4accf2009-06-08 10:43:26 -07002115 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07002116 public static final EditEntry newGroupEntry(EditContactActivity activity,
2117 String data, Uri uri, long personId) {
2118 EditEntry entry = new EditEntry(activity);
2119 entry.label = activity.getString(R.string.label_groups);
2120 entry.data = data;
2121 entry.uri = uri;
2122 entry.id = personId;
2123 entry.column = GroupMembership.GROUP_ID;
2124 entry.kind = KIND_GROUP;
2125 entry.isStaticLabel = true;
2126 entry.syncDataWithView = false;
2127 entry.lines = -1;
2128 return entry;
2129 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002130 */
Alex Kennberg87fc3172009-03-28 06:43:06 -07002131
2132 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002133 * Create a new empty email entry
2134 */
2135 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2136 Uri uri, int type) {
2137 return newPhoneEntry(activity, null, type, null, uri, 0);
2138 }
2139
2140 /**
2141 * Create a new phone entry with the given data.
2142 */
2143 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2144 String label, int type, String data, Uri uri,
2145 long id) {
2146 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2147 entry.hint = activity.getString(R.string.ghostData_phone);
Evan Millar7e4accf2009-06-08 10:43:26 -07002148 entry.column = Phone.NUMBER;
2149 entry.mimetype = Phone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002150 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
2151 return entry;
2152 }
2153
2154 /**
2155 * Create a new empty email entry
2156 */
2157 public static final EditEntry newEmailEntry(EditContactActivity activity,
2158 Uri uri, int type) {
2159 return newEmailEntry(activity, null, type, null, uri, 0);
2160 }
2161
2162 /**
2163 * Create a new email entry with the given data.
2164 */
2165 public static final EditEntry newEmailEntry(EditContactActivity activity,
2166 String label, int type, String data, Uri uri,
2167 long id) {
2168 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2169 entry.hint = activity.getString(R.string.ghostData_email);
Evan Millar7e4accf2009-06-08 10:43:26 -07002170 entry.column = Email.DATA;
2171 entry.mimetype = Email.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002172 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2173 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
2174 return entry;
2175 }
2176
2177 /**
2178 * Create a new empty postal address entry
2179 */
2180 public static final EditEntry newPostalEntry(EditContactActivity activity,
2181 Uri uri, int type) {
2182 return newPostalEntry(activity, null, type, null, uri, 0);
2183 }
2184
2185 /**
2186 * Create a new postal address entry with the given data.
2187 *
2188 * @param label label for the item, from the db not the display label
2189 * @param type the type of postal address
2190 * @param data the starting data for the entry, may be null
2191 * @param uri the uri for the entry if it already exists, may be null
2192 * @param id the id for the entry if it already exists, 0 it it doesn't
2193 * @return the new EditEntry
2194 */
2195 public static final EditEntry newPostalEntry(EditContactActivity activity,
2196 String label, int type, String data, Uri uri, long id) {
2197 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2198 entry.hint = activity.getString(R.string.ghostData_postal);
Evan Millar7e4accf2009-06-08 10:43:26 -07002199 entry.column = Postal.DATA;
2200 entry.mimetype = Postal.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002201 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2202 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2203 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2204 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2205 entry.maxLines = 4;
2206 entry.lines = 2;
2207 return entry;
2208 }
2209
2210 /**
2211 * Create a new IM address entry
2212 */
2213 public static final EditEntry newImEntry(EditContactActivity activity,
2214 Uri uri, int type) {
2215 return newImEntry(activity, null, type, null, uri, 0);
2216 }
2217
2218 /**
2219 * Create a new IM address entry with the given data.
2220 *
2221 * @param label label for the item, from the db not the display label
2222 * @param protocol the type used
2223 * @param data the starting data for the entry, may be null
2224 * @param uri the uri for the entry if it already exists, may be null
2225 * @param id the id for the entry if it already exists, 0 it it doesn't
2226 * @return the new EditEntry
2227 */
2228 public static final EditEntry newImEntry(EditContactActivity activity,
2229 String label, int protocol, String data, Uri uri, long id) {
2230 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2231 entry.hint = activity.getString(R.string.ghostData_im);
Evan Millar7e4accf2009-06-08 10:43:26 -07002232 entry.column = Im.DATA;
2233 entry.mimetype = Im.CONTENT_ITEM_TYPE;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08002234 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2235 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002236 return entry;
2237 }
2238 }
2239
2240 public void afterTextChanged(Editable s) {
2241 // Someone edited a text field, so assume this contact is changed
2242 mContactChanged = true;
2243 }
2244
2245 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2246 // Do nothing; editing handled by afterTextChanged()
2247 }
2248
2249 public void onTextChanged(CharSequence s, int start, int before, int count) {
2250 // Do nothing; editing handled by afterTextChanged()
2251 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002252
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002253 public void onFocusChange(View v, boolean hasFocus) {
2254 // Because we're emulating a ListView, we need to setSelected() for
2255 // views as they are focused.
2256 v.setSelected(hasFocus);
2257 }
2258}