blob: 633c18051ad2ec8b4a7e31d42eaa75dce856e9af [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;
49import android.media.Ringtone;
50import android.media.RingtoneManager;
51import android.net.Uri;
52import android.os.Bundle;
53import android.os.Parcel;
54import android.os.Parcelable;
55import android.preference.PreferenceManager;
Evan Millar7e4accf2009-06-08 10:43:26 -070056import android.provider.ContactsContract;
57import android.provider.ContactsContract.Aggregates;
58import android.provider.ContactsContract.CommonDataKinds;
59import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
60import android.provider.ContactsContract.CommonDataKinds.CustomRingtone;
61import android.provider.ContactsContract.CommonDataKinds.Email;
62import android.provider.ContactsContract.CommonDataKinds.Im;
63import android.provider.ContactsContract.CommonDataKinds.Note;
64import android.provider.ContactsContract.CommonDataKinds.Organization;
65import android.provider.ContactsContract.CommonDataKinds.Phone;
66import android.provider.ContactsContract.CommonDataKinds.Photo;
67import android.provider.ContactsContract.CommonDataKinds.Postal;
68import android.provider.ContactsContract.CommonDataKinds.StructuredName;
69import android.provider.ContactsContract.Data;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080070import android.telephony.PhoneNumberFormattingTextWatcher;
71import android.text.Editable;
72import android.text.TextUtils;
73import android.text.TextWatcher;
74import android.text.method.TextKeyListener;
75import android.text.method.TextKeyListener.Capitalize;
76import android.util.Log;
77import android.util.SparseBooleanArray;
78import android.view.KeyEvent;
79import android.view.LayoutInflater;
80import android.view.Menu;
81import android.view.MenuItem;
82import android.view.View;
83import android.view.ViewGroup;
84import android.view.ViewParent;
85import android.view.inputmethod.EditorInfo;
86import android.widget.Button;
87import android.widget.CheckBox;
88import android.widget.EditText;
89import android.widget.ImageView;
90import android.widget.LinearLayout;
91import android.widget.TextView;
92import android.widget.Toast;
93
94import java.io.ByteArrayOutputStream;
95import java.util.ArrayList;
96
Evan Millar7e4accf2009-06-08 10:43:26 -070097// TODO: Much of this class has been commented out as a starting place for transition to new data
98// model. It will be added back as we progress.
99
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800100/**
101 * Activity for editing or inserting a contact. Note that if the contact data changes in the
102 * background while this activity is running, the updates will be overwritten.
103 */
104public final class EditContactActivity extends Activity implements View.OnClickListener,
105 TextWatcher, View.OnFocusChangeListener {
106 private static final String TAG = "EditContactActivity";
107
108 private static final int STATE_UNKNOWN = 0;
109 /** Editing an existing contact */
110 private static final int STATE_EDIT = 1;
111 /** The full insert mode */
112 private static final int STATE_INSERT = 2;
113
114 /** The launch code when picking a photo and the raw data is returned */
115 private static final int PHOTO_PICKED_WITH_DATA = 3021;
116
117 /** The launch code when picking a ringtone */
118 private static final int RINGTONE_PICKED = 3023;
Evan Millar7e4accf2009-06-08 10:43:26 -0700119
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800120 // These correspond to the string array in resources for picker "other" items
121 final static int OTHER_ORGANIZATION = 0;
122 final static int OTHER_NOTE = 1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700123
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800124 // Dialog IDs
125 final static int DELETE_CONFIRMATION_DIALOG = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -0700126
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800127 // Section IDs
128 final static int SECTION_PHONES = 3;
129 final static int SECTION_EMAIL = 4;
130 final static int SECTION_IM = 5;
131 final static int SECTION_POSTAL = 6;
132 final static int SECTION_ORG = 7;
133 final static int SECTION_NOTE = 8;
134
135 // Menu item IDs
136 public static final int MENU_ITEM_SAVE = 1;
137 public static final int MENU_ITEM_DONT_SAVE = 2;
138 public static final int MENU_ITEM_DELETE = 3;
139 public static final int MENU_ITEM_PHOTO = 6;
Evan Millar7e4accf2009-06-08 10:43:26 -0700140
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800141 /** Used to represent an invalid type for a contact entry */
142 private static final int INVALID_TYPE = -1;
Evan Millar7e4accf2009-06-08 10:43:26 -0700143
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800144 /** The default type for a phone that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700145 private static final int DEFAULT_PHONE_TYPE = Phone.TYPE_MOBILE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800146
147 /** The default type for an email that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700148 private static final int DEFAULT_EMAIL_TYPE = Email.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800149
150 /** The default type for a postal address that is added via an intent */
Evan Millar7e4accf2009-06-08 10:43:26 -0700151 private static final int DEFAULT_POSTAL_TYPE = Postal.TYPE_HOME;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800152
153 private int mState; // saved across instances
154 private boolean mInsert; // saved across instances
155 private Uri mUri; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700156 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800157 /** In insert mode this is the photo */
158 private Bitmap mPhoto; // saved across instances
159 private boolean mPhotoChanged = false; // saved across instances
Evan Millar7e4accf2009-06-08 10:43:26 -0700160
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800161 private EditText mNameView;
Evan Millar7e4accf2009-06-08 10:43:26 -0700162 private Uri mStructuredNameUri;
163 private Uri mPhotoDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800164 private ImageView mPhotoImageView;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800165 private ViewGroup mContentView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800166 private LinearLayout mLayout;
167 private LayoutInflater mInflater;
168 private MenuItem mPhotoMenuItem;
169 private boolean mPhotoPresent = false;
170 private EditText mPhoneticNameView; // invisible in some locales, but always present
171
172 /** Flag marking this contact as changed, meaning we should write changes back. */
173 private boolean mContactChanged = false;
Evan Millar7e4accf2009-06-08 10:43:26 -0700174
Alex Kennberg87fc3172009-03-28 06:43:06 -0700175 /** List of all the group names */
176 private CharSequence[] mGroups;
Evan Millar7e4accf2009-06-08 10:43:26 -0700177
Alex Kennberg87fc3172009-03-28 06:43:06 -0700178 /** Is this contact part of the group */
179 private boolean[] mInTheGroup;
180
Evan Millar7e4accf2009-06-08 10:43:26 -0700181 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700182 private static final String[] GROUP_ID_PROJECTION = new String[] {
183 Groups._ID,
184 };
185
186 private static final String[] GROUPMEMBERSHIP_ID_PROJECTION = new String[] {
187 GroupMembership._ID,
188 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700189 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800190
191 // These are accessed by inner classes. They're package scoped to make access more efficient.
192 /* package */ ContentResolver mResolver;
193 /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
194 /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
195 /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
196 /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
197 /* package */ ArrayList<EditEntry> mOrgEntries = new ArrayList<EditEntry>();
198 /* package */ ArrayList<EditEntry> mNoteEntries = new ArrayList<EditEntry>();
199 /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
200 /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
Evan Millar7e4accf2009-06-08 10:43:26 -0700201
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800202 /* package */ static final int MSG_DELETE = 1;
203 /* package */ static final int MSG_CHANGE_LABEL = 2;
204 /* package */ static final int MSG_ADD_PHONE = 3;
205 /* package */ static final int MSG_ADD_EMAIL = 4;
206 /* package */ static final int MSG_ADD_POSTAL = 5;
Evan Millar7e4accf2009-06-08 10:43:26 -0700207
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800208 private static final int[] TYPE_PRECEDENCE_PHONES = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700209 Phone.TYPE_MOBILE, Phone.TYPE_HOME, Phone.TYPE_WORK, Phone.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800210 };
Evan Millar7e4accf2009-06-08 10:43:26 -0700211 private static final int[] TYPE_PRECEDENCE_EMAIL = new int[] {
212 Email.TYPE_HOME, Email.TYPE_WORK, Email.TYPE_OTHER
213 };
214 private static final int[] TYPE_PRECEDENCE_POSTAL = new int[] {
215 Postal.TYPE_HOME, Postal.TYPE_WORK, Postal.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800216 };
217 private static final int[] TYPE_PRECEDENCE_IM = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700218 Im.PROTOCOL_GOOGLE_TALK, Im.PROTOCOL_AIM, Im.PROTOCOL_MSN, Im.PROTOCOL_YAHOO,
219 Im.PROTOCOL_JABBER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800220 };
221 private static final int[] TYPE_PRECEDENCE_ORG = new int[] {
Evan Millar7e4accf2009-06-08 10:43:26 -0700222 Organization.TYPE_WORK, Organization.TYPE_OTHER
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800223 };
224
225 public void onClick(View v) {
226 switch (v.getId()) {
227 case R.id.photoImage: {
228 doPickPhotoAction();
229 break;
230 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700231
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800232 case R.id.checkable: {
233 CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
234 checkBox.toggle();
Evan Millar7e4accf2009-06-08 10:43:26 -0700235
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800236 EditEntry entry = findEntryForView(v);
237 entry.data = checkBox.isChecked() ? "1" : "0";
Evan Millar7e4accf2009-06-08 10:43:26 -0700238
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800239 mContactChanged = true;
240 break;
241 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700242
243 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700244 case R.id.entry_group: {
245 EditEntry entry = findEntryForView(v);
246 doPickGroup(entry);
247 break;
248 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700249 */
250
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800251 case R.id.entry_ringtone: {
252 EditEntry entry = findEntryForView(v);
253 doPickRingtone(entry);
254 break;
255 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700256
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800257 case R.id.separator: {
258 // Someone clicked on a section header, so handle add action
Evan Millar7e4accf2009-06-08 10:43:26 -0700259 // TODO: Data addition is still being hashed out.
260 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800261 int sectionType = (Integer) v.getTag();
262 doAddAction(sectionType);
Evan Millar7e4accf2009-06-08 10:43:26 -0700263 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800264 break;
265 }
266
267 case R.id.saveButton:
268 doSaveAction();
269 break;
270
271 case R.id.discardButton:
272 doRevertAction();
273 break;
274
275 case R.id.delete: {
276 EditEntry entry = findEntryForView(v);
277 if (entry != null) {
278 // Clear the text and hide the view so it gets saved properly
279 ((TextView) entry.view.findViewById(R.id.data)).setText(null);
280 entry.view.setVisibility(View.GONE);
281 entry.isDeleted = true;
282 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700283
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800284 // Force rebuild of views because section headers might need to change
285 buildViews();
286 break;
287 }
288
289 case R.id.label: {
290 EditEntry entry = findEntryForView(v);
291 if (entry != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700292 String[] labels = getLabelsForMimetype(this, entry.mimetype);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800293 LabelPickedListener listener = new LabelPickedListener(entry, labels);
294 new AlertDialog.Builder(EditContactActivity.this)
295 .setItems(labels, listener)
296 .setTitle(R.string.selectLabel)
297 .show();
298 }
299 break;
300 }
301 }
302 }
303
304 private void setPhotoPresent(boolean present) {
305 mPhotoPresent = present;
Evan Millar7e4accf2009-06-08 10:43:26 -0700306
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800307 // Correctly scale the contact photo if present, otherwise just center
308 // the photo placeholder icon.
309 if (mPhotoPresent) {
310 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
311 } else {
312 mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
313 mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
314 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700315
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800316 if (mPhotoMenuItem != null) {
317 if (present) {
318 mPhotoMenuItem.setTitle(R.string.removePicture);
319 mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
320 } else {
321 mPhotoMenuItem.setTitle(R.string.addPicture);
322 mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
323 }
324 }
325 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700326
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800327 private EditEntry findEntryForView(View v) {
328 // Try to find the entry for this view
329 EditEntry entry = null;
330 do {
331 Object tag = v.getTag();
332 if (tag != null && tag instanceof EditEntry) {
333 entry = (EditEntry) tag;
334 break;
335 } else {
336 ViewParent parent = v.getParent();
337 if (parent != null && parent instanceof View) {
338 v = (View) parent;
339 } else {
340 v = null;
341 }
342 }
343 } while (v != null);
344 return entry;
345 }
346
347 private DialogInterface.OnClickListener mDeleteContactDialogListener =
348 new DialogInterface.OnClickListener() {
349 public void onClick(DialogInterface dialog, int button) {
350 mResolver.delete(mUri, null, null);
351 finish();
352 }
353 };
354
355 private boolean mMobilePhoneAdded = false;
356 private boolean mPrimaryEmailAdded = false;
357
358 @Override
359 protected void onCreate(Bundle icicle) {
360 super.onCreate(icicle);
361
362 mResolver = getContentResolver();
363
364 // Build the list of sections
365 setupSections();
366
367 // Load the UI
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800368 mInflater = getLayoutInflater();
369 mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
370 setContentView(mContentView);
Evan Millar7e4accf2009-06-08 10:43:26 -0700371
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800372 mLayout = (LinearLayout) findViewById(R.id.list);
373 mNameView = (EditText) findViewById(R.id.name);
374 mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
375 mPhotoImageView.setOnClickListener(this);
376 mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
Evan Millar7e4accf2009-06-08 10:43:26 -0700377
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800378 // Setup the bottom buttons
379 View view = findViewById(R.id.saveButton);
380 view.setOnClickListener(this);
381 view = findViewById(R.id.discardButton);
382 view.setOnClickListener(this);
383
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800384 // Resolve the intent
385 mState = STATE_UNKNOWN;
386 Intent intent = getIntent();
387 String action = intent.getAction();
388 mUri = intent.getData();
Evan Millar7e4accf2009-06-08 10:43:26 -0700389 mAggDataUri = Uri.withAppendedPath(mUri, "data");
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800390 if (mUri != null) {
391 if (action.equals(Intent.ACTION_EDIT)) {
392 if (icicle == null) {
393 // Build the entries & views
394 buildEntriesForEdit(getIntent().getExtras());
395 buildViews();
396 }
397 setTitle(R.string.editContact_title_edit);
398 mState = STATE_EDIT;
399 } else if (action.equals(Intent.ACTION_INSERT)) {
400 if (icicle == null) {
401 // Build the entries & views
Evan Millar7e4accf2009-06-08 10:43:26 -0700402 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800403 buildEntriesForInsert(getIntent().getExtras());
404 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700405 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800406 }
407 setTitle(R.string.editContact_title_insert);
408 mState = STATE_INSERT;
409 mInsert = true;
410 }
411 }
412
413 if (mState == STATE_UNKNOWN) {
414 Log.e(TAG, "Cannot resolve intent: " + intent);
415 finish();
416 return;
417 }
418
419 if (mState == STATE_EDIT) {
420 setTitle(getResources().getText(R.string.editContact_title_edit));
421 } else {
422 setTitle(getResources().getText(R.string.editContact_title_insert));
423 }
424 }
425
426 private void setupSections() {
427 mSections.add(mPhoneEntries);
428 mSections.add(mEmailEntries);
429 mSections.add(mImEntries);
430 mSections.add(mPostalEntries);
431 mSections.add(mOrgEntries);
432 mSections.add(mNoteEntries);
433 mSections.add(mOtherEntries);
434 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700435
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800436 @Override
437 protected void onSaveInstanceState(Bundle outState) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700438
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800439 // To store current focus between config changes, follow focus down the
440 // view tree, keeping track of any parents with EditEntry tags
441 View focusedChild = mContentView.getFocusedChild();
442 EditEntry focusedEntry = null;
443 while (focusedChild != null) {
444 Object tag = focusedChild.getTag();
445 if (tag instanceof EditEntry) {
446 focusedEntry = (EditEntry) tag;
447 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700448
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800449 // Keep going deeper until child isn't a group
450 if (focusedChild instanceof ViewGroup) {
451 View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
452 if (deeperFocus != null) {
453 focusedChild = deeperFocus;
454 } else {
455 break;
456 }
457 } else {
458 break;
459 }
460 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700461
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800462 if (focusedChild != null) {
463 int requestFocusId = focusedChild.getId();
464 int requestCursor = 0;
465 if (focusedChild instanceof EditText) {
466 requestCursor = ((EditText) focusedChild).getSelectionStart();
467 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700468
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800469 // Store focus values in EditEntry if found, otherwise store as
470 // generic values
471 if (focusedEntry != null) {
472 focusedEntry.requestFocusId = requestFocusId;
473 focusedEntry.requestCursor = requestCursor;
474 } else {
475 outState.putInt("requestFocusId", requestFocusId);
476 outState.putInt("requestCursor", requestCursor);
477 }
478 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700479
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800480 outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
481 outState.putParcelableArrayList("emailEntries", mEmailEntries);
482 outState.putParcelableArrayList("imEntries", mImEntries);
483 outState.putParcelableArrayList("postalEntries", mPostalEntries);
484 outState.putParcelableArrayList("orgEntries", mOrgEntries);
485 outState.putParcelableArrayList("noteEntries", mNoteEntries);
486 outState.putParcelableArrayList("otherEntries", mOtherEntries);
487 outState.putInt("state", mState);
488 outState.putBoolean("insert", mInsert);
489 outState.putParcelable("uri", mUri);
490 outState.putString("name", mNameView.getText().toString());
491 outState.putParcelable("photo", mPhoto);
492 outState.putBoolean("photoChanged", mPhotoChanged);
493 outState.putString("phoneticName", mPhoneticNameView.getText().toString());
494 outState.putBoolean("contactChanged", mContactChanged);
495 }
496
497 @Override
498 protected void onRestoreInstanceState(Bundle inState) {
499 mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
500 mEmailEntries = inState.getParcelableArrayList("emailEntries");
501 mImEntries = inState.getParcelableArrayList("imEntries");
502 mPostalEntries = inState.getParcelableArrayList("postalEntries");
503 mOrgEntries = inState.getParcelableArrayList("orgEntries");
504 mNoteEntries = inState.getParcelableArrayList("noteEntries");
505 mOtherEntries = inState.getParcelableArrayList("otherEntries");
506 setupSections();
507
508 mState = inState.getInt("state");
509 mInsert = inState.getBoolean("insert");
510 mUri = inState.getParcelable("uri");
511 mNameView.setText(inState.getString("name"));
512 mPhoto = inState.getParcelable("photo");
513 if (mPhoto != null) {
514 mPhotoImageView.setImageBitmap(mPhoto);
515 setPhotoPresent(true);
516 } else {
517 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
518 setPhotoPresent(false);
519 }
520 mPhotoChanged = inState.getBoolean("photoChanged");
521 mPhoneticNameView.setText(inState.getString("phoneticName"));
522 mContactChanged = inState.getBoolean("contactChanged");
523
524 // Now that everything is restored, build the view
525 buildViews();
Evan Millar7e4accf2009-06-08 10:43:26 -0700526
The Android Open Source Project928ccbd2009-03-05 14:34:37 -0800527 // Try restoring any generally requested focus
528 int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
529 View focusedChild = mContentView.findViewById(requestFocusId);
530 if (focusedChild != null) {
531 focusedChild.requestFocus();
532 if (focusedChild instanceof EditText) {
533 int requestCursor = inState.getInt("requestCursor", 0);
534 ((EditText) focusedChild).setSelection(requestCursor);
535 }
536 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800537 }
538
539 @Override
540 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
541 if (resultCode != RESULT_OK) {
542 return;
543 }
544
545 switch (requestCode) {
546 case PHOTO_PICKED_WITH_DATA: {
547 final Bundle extras = data.getExtras();
548 if (extras != null) {
549 Bitmap photo = extras.getParcelable("data");
550 mPhoto = photo;
551 mPhotoChanged = true;
552 mPhotoImageView.setImageBitmap(photo);
553 setPhotoPresent(true);
554 }
555 break;
556 }
557
558 case RINGTONE_PICKED: {
559 Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
560 handleRingtonePicked(pickedUri);
561 mContactChanged = true;
562 break;
563 }
564 }
565 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700566
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800567 @Override
568 public boolean onKeyDown(int keyCode, KeyEvent event) {
569 switch (keyCode) {
570 case KeyEvent.KEYCODE_BACK: {
571 doSaveAction();
572 return true;
573 }
574 }
575 return super.onKeyDown(keyCode, event);
576 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700577
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800578 @Override
579 public boolean onCreateOptionsMenu(Menu menu) {
580 super.onCreateOptionsMenu(menu);
581 menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
582 .setIcon(android.R.drawable.ic_menu_save)
583 .setAlphabeticShortcut('\n');
584 menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
585 .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
586 .setAlphabeticShortcut('q');
587 if (!mInsert) {
588 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
589 .setIcon(android.R.drawable.ic_menu_delete);
590 }
591
592 mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
593 // Updates the state of the menu item
594 setPhotoPresent(mPhotoPresent);
595
596 return true;
597 }
598
599 @Override
600 public boolean onOptionsItemSelected(MenuItem item) {
601 switch (item.getItemId()) {
602 case MENU_ITEM_SAVE:
603 doSaveAction();
604 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700605
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800606 case MENU_ITEM_DONT_SAVE:
607 doRevertAction();
608 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700609
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800610 case MENU_ITEM_DELETE:
611 // Get confirmation
612 showDialog(DELETE_CONFIRMATION_DIALOG);
613 return true;
Evan Millar7e4accf2009-06-08 10:43:26 -0700614
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800615 case MENU_ITEM_PHOTO:
616 if (!mPhotoPresent) {
617 doPickPhotoAction();
618 } else {
619 doRemovePhotoAction();
620 }
621 return true;
622 }
623
624 return false;
625 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700626
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800627 /**
628 * Try guessing the next-best type of {@link EditEntry} to insert into the
629 * given list. We walk down the precedence list until we find a type that
630 * doesn't exist yet, or default to the lowest ranking type.
631 */
Evan Millar7e4accf2009-06-08 10:43:26 -0700632 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800633 private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
634 // Keep track of the types we've seen already
635 SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
636 for (int i = entries.size() - 1; i >= 0; i--) {
637 EditEntry entry = entries.get(i);
638 if (!entry.isDeleted) {
639 existAlready.put(entry.type, true);
640 }
641 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700642
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800643 // Pick the first item we haven't seen
644 for (int type : precedenceList) {
645 if (!existAlready.get(type, false)) {
646 return type;
647 }
648 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700649
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800650 // Otherwise default to last item
651 return precedenceList[precedenceList.length - 1];
652 }
653
654 private void doAddAction(int sectionType) {
655 EditEntry entry = null;
656 switch (sectionType) {
657 case SECTION_PHONES: {
658 // Try figuring out which type to insert next
659 int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
Evan Millar7e4accf2009-06-08 10:43:26 -0700660 entry = EditEntry.newPhoneEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800661 nextType);
662 mPhoneEntries.add(entry);
663 break;
664 }
665 case SECTION_EMAIL: {
666 // Try figuring out which type to insert next
Evan Millar7e4accf2009-06-08 10:43:26 -0700667 int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_EMAIL);
668 entry = EditEntry.newEmailEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800669 nextType);
670 mEmailEntries.add(entry);
671 break;
672 }
673 case SECTION_IM: {
674 // Try figuring out which type to insert next
675 int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
Evan Millar7e4accf2009-06-08 10:43:26 -0700676 entry = EditEntry.newImEntry(EditContactActivity.this, Data.CONTENT_URI, nextType);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800677 mImEntries.add(entry);
678 break;
679 }
680 case SECTION_POSTAL: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700681 int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_POSTAL);
682 entry = EditEntry.newPostalEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800683 nextType);
684 mPostalEntries.add(entry);
685 break;
686 }
687 case SECTION_ORG: {
688 int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
Evan Millar7e4accf2009-06-08 10:43:26 -0700689 entry = EditEntry.newOrganizationEntry(EditContactActivity.this, Data.CONTENT_URI,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800690 nextType);
691 mOrgEntries.add(entry);
692 break;
693 }
694 case SECTION_NOTE: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700695 entry = EditEntry.newNotesEntry(EditContactActivity.this, Data.CONTENT_URI);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800696 mNoteEntries.add(entry);
697 break;
698 }
699 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700700
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800701 // Rebuild the views if needed
702 if (entry != null) {
703 buildViews();
704 mContactChanged = true;
705
706 View dataView = entry.view.findViewById(R.id.data);
707 if (dataView == null) {
708 entry.view.requestFocus();
709 } else {
710 dataView.requestFocus();
711 }
712 }
713 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700714 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800715
716 private void doRevertAction() {
717 finish();
718 }
719
720 private void doPickPhotoAction() {
721 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
722 // TODO: get these values from constants somewhere
723 intent.setType("image/*");
724 intent.putExtra("crop", "true");
725 intent.putExtra("aspectX", 1);
726 intent.putExtra("aspectY", 1);
727 intent.putExtra("outputX", 96);
728 intent.putExtra("outputY", 96);
729 try {
730 intent.putExtra("return-data", true);
731 startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
732 } catch (ActivityNotFoundException e) {
733 new AlertDialog.Builder(EditContactActivity.this)
734 .setTitle(R.string.errorDialogTitle)
735 .setMessage(R.string.photoPickerNotFoundText)
736 .setPositiveButton(android.R.string.ok, null)
737 .show();
738 }
739 }
740
741 private void doRemovePhotoAction() {
742 mPhoto = null;
743 mPhotoChanged = true;
744 setPhotoPresent(false);
745 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700746
747 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700748 private void populateGroups() {
749 // Create a list of all the groups
750 Cursor cursor = mResolver.query(Groups.CONTENT_URI, ContactsListActivity.GROUPS_PROJECTION,
751 null, null, Groups.DEFAULT_SORT_ORDER);
752 try {
753 ArrayList<Long> ids = new ArrayList<Long>();
754 ArrayList<String> items = new ArrayList<String>();
755
756 while (cursor.moveToNext()) {
757 String systemId = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
758 String name = cursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700759
Alex Kennberg87fc3172009-03-28 06:43:06 -0700760 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
761 continue;
762 }
763
764 if (!TextUtils.isEmpty(name)) {
765 ids.add(new Long(cursor.getLong(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID)));
766 items.add(name);
767 }
768 }
769
770 mGroups = items.toArray(new CharSequence[items.size()]);
771 mInTheGroup = new boolean[items.size()];
772 } finally {
773 cursor.close();
774 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700775
Alex Kennberg87fc3172009-03-28 06:43:06 -0700776 if (mGroups != null) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700777
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700778 // Go through the groups for this member and update the list
Alex Kennberg87fc3172009-03-28 06:43:06 -0700779 final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
Alex Kennbergfb0386a2009-04-02 09:59:10 -0700780 Cursor groupCursor = null;
781 try {
782 groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
783 null, null, Groups.DEFAULT_SORT_ORDER);
784 } catch (IllegalArgumentException e) {
785 // Contact is new, so we don't need to do any work.
786 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700787
Alex Kennberg87fc3172009-03-28 06:43:06 -0700788 if (groupCursor != null) {
789 try {
790 while (groupCursor.moveToNext()) {
791 String systemId = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
792 String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
Evan Millar7e4accf2009-06-08 10:43:26 -0700793
Alex Kennberg87fc3172009-03-28 06:43:06 -0700794 if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
795 continue;
796 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700797
Alex Kennberg87fc3172009-03-28 06:43:06 -0700798 if (!TextUtils.isEmpty(name)) {
799 for (int i = 0; i < mGroups.length; i++) {
800 if (name.equals(mGroups[i])) {
801 mInTheGroup[i] = true;
802 break;
803 }
804 }
805 }
806 }
807 } finally {
808 groupCursor.close();
809 }
810 }
811 }
812 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700813
Alex Kennberg87fc3172009-03-28 06:43:06 -0700814 private String generateGroupList() {
815 StringBuilder groupList = new StringBuilder();
816 for (int i = 0; mGroups != null && i < mGroups.length; i++) {
817 if (mInTheGroup[i]) {
818 if (groupList.length() == 0) {
819 groupList.append(mGroups[i]);
820 } else {
821 groupList.append(getString(R.string.group_list, mGroups[i]));
822 }
823 }
824 }
825 return groupList.length() > 0 ? groupList.toString() : null;
826 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700827
Alex Kennberg87fc3172009-03-28 06:43:06 -0700828 private void doPickGroup(EditEntry entry) {
829 if (mGroups != null) {
830 GroupDialogListener listener = new GroupDialogListener(this, entry);
Evan Millar7e4accf2009-06-08 10:43:26 -0700831
Alex Kennberg87fc3172009-03-28 06:43:06 -0700832 new AlertDialog.Builder(EditContactActivity.this)
833 .setTitle(R.string.label_groups)
834 .setMultiChoiceItems(mGroups, mInTheGroup, listener)
835 .setPositiveButton(android.R.string.ok, listener)
836 .setNegativeButton(android.R.string.cancel, null)
837 .show();
838 }
839 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700840 */
Alex Kennberg87fc3172009-03-28 06:43:06 -0700841
842 /** Handles the clicks in the groups dialog */
Evan Millar7e4accf2009-06-08 10:43:26 -0700843 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -0700844 private static final class GroupDialogListener implements DialogInterface.OnClickListener,
845 DialogInterface.OnMultiChoiceClickListener {
Evan Millar7e4accf2009-06-08 10:43:26 -0700846
Alex Kennberg87fc3172009-03-28 06:43:06 -0700847 private EditContactActivity mEditContactActivity;
848 private EditEntry mEntry;
849 private boolean[] mInTheGroup;
Evan Millar7e4accf2009-06-08 10:43:26 -0700850
Alex Kennberg87fc3172009-03-28 06:43:06 -0700851 public GroupDialogListener(EditContactActivity editContactActivity, EditEntry entry) {
852 mEditContactActivity = editContactActivity;
853 mEntry = entry;
854 mInTheGroup = editContactActivity.mInTheGroup.clone();
855 }
856
Evan Millar7e4accf2009-06-08 10:43:26 -0700857 // Called when the dialog's ok button is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700858 public void onClick(DialogInterface dialog, int which) {
859 mEditContactActivity.mInTheGroup = mInTheGroup;
860 mEntry.data = mEditContactActivity.generateGroupList();
861 mEditContactActivity.updateDataView(mEntry, mEntry.data);
862 }
863
Evan Millar7e4accf2009-06-08 10:43:26 -0700864 // Called when each group is clicked
Alex Kennberg87fc3172009-03-28 06:43:06 -0700865 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
866 mInTheGroup[which] = isChecked;
867 }
868 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700869 */
870
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800871 private void doPickRingtone(EditEntry entry) {
872 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
873 // Allow user to pick 'Default'
874 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
875 // Show only ringtones
876 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
877 // Don't show 'Silent'
878 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
Evan Millar7e4accf2009-06-08 10:43:26 -0700879
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800880 Uri ringtoneUri;
881 if (entry.data != null) {
882 ringtoneUri = Uri.parse(entry.data);
883 } else {
884 // Otherwise pick default ringtone Uri so that something is selected.
885 ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
886 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700887
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800888 // Put checkmark next to the current ringtone for this contact
889 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
890 // Launch!
891 startActivityForResult(intent, RINGTONE_PICKED);
892 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700893
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800894 private void handleRingtonePicked(Uri pickedUri) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700895 EditEntry entry = getOtherEntry(CustomRingtone.RINGTONE_URI);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800896 if (entry == null) {
897 Log.w(TAG, "Ringtone picked but could not find ringtone entry");
898 return;
899 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700900
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800901 if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
902 entry.data = null;
903 } else {
904 entry.data = pickedUri.toString();
905 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700906
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800907 updateRingtoneView(entry);
908 }
909
910 private void updateRingtoneView(EditEntry entry) {
911 String ringtoneName;
912 if (entry.data == null) {
913 ringtoneName = getString(R.string.default_ringtone);
914 } else {
915 Uri ringtoneUri = Uri.parse(entry.data);
916 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
917 if (ringtone == null) {
918 Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
919 return;
920 }
921 ringtoneName = ringtone.getTitle(this);
922 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700923
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800924 updateDataView(entry, ringtoneName);
925 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700926
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800927 private void updateDataView(EditEntry entry, String text) {
928 TextView dataView = (TextView) entry.view.findViewById(R.id.data);
929 dataView.setText(text);
930 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700931
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800932 @Override
933 protected Dialog onCreateDialog(int id) {
934 switch (id) {
935 case DELETE_CONFIRMATION_DIALOG:
936 return new AlertDialog.Builder(EditContactActivity.this)
937 .setTitle(R.string.deleteConfirmation_title)
938 .setIcon(android.R.drawable.ic_dialog_alert)
939 .setMessage(R.string.deleteConfirmation)
940 .setNegativeButton(android.R.string.cancel, null)
941 .setPositiveButton(android.R.string.ok, mDeleteContactDialogListener)
942 .setCancelable(false)
943 .create();
944 }
945 return super.onCreateDialog(id);
946 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700947
948 static String[] getLabelsForMimetype(Context context, String mimetype) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800949 final Resources resources = context.getResources();
Evan Millar7e4accf2009-06-08 10:43:26 -0700950 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
951 return resources.getStringArray(android.R.array.phoneTypes);
952 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
953 return resources.getStringArray(android.R.array.emailAddressTypes);
954 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
955 return resources.getStringArray(android.R.array.postalAddressTypes);
956 } else if (mimetype.equals(Im.CONTENT_ITEM_TYPE)) {
957 return resources.getStringArray(android.R.array.imProtocols);
958 } else if (mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
959 return resources.getStringArray(android.R.array.organizationTypes);
960 } else {
961 return resources.getStringArray(R.array.otherLabels);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800962 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800963 }
964
965 int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
966 // In the UI Custom... comes last, but it is uses the constant 0
967 // so it is in the same location across the various kinds. Fix up the
968 // position to a valid type here.
969 if (labelPosition == labels.length - 1) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700970 return BaseTypes.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800971 } else {
972 return labelPosition + 1;
973 }
974 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700975
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800976 private EditEntry getOtherEntry(String column) {
977 for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
978 EditEntry entry = mOtherEntries.get(i);
979 if (isOtherEntry(entry, column)) {
980 return entry;
981 }
982 }
983 return null;
984 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700985
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800986 private static boolean isOtherEntry(EditEntry entry, String column) {
987 return entry != null && entry.column != null && entry.column.equals(column);
988 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700989
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800990 private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
991 final EditText label = new EditText(this);
992 label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
993 label.requestFocus();
994 new AlertDialog.Builder(this)
995 .setView(label)
996 .setTitle(R.string.customLabelPickerTitle)
997 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
998 public void onClick(DialogInterface dialog, int which) {
Evan Millar7e4accf2009-06-08 10:43:26 -0700999 entry.setLabel(EditContactActivity.this, BaseTypes.TYPE_CUSTOM,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001000 label.getText().toString());
1001 mContactChanged = true;
1002
1003 if (addTo != null) {
1004 addTo.add(entry);
1005 buildViews();
1006 entry.view.requestFocus(View.FOCUS_DOWN);
1007 }
1008 }
1009 })
1010 .setNegativeButton(android.R.string.cancel, null)
1011 .show();
1012 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001013
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001014 /**
1015 * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
1016 */
1017 private void doSaveAction() {
1018 // Save or create the contact if needed
1019 switch (mState) {
1020 case STATE_EDIT:
1021 save();
1022 break;
1023
Evan Millar7e4accf2009-06-08 10:43:26 -07001024 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001025 case STATE_INSERT:
1026 create();
1027 break;
Evan Millar7e4accf2009-06-08 10:43:26 -07001028 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001029
1030 default:
1031 Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
1032 break;
1033 }
1034 finish();
1035 }
Alex Kennberg87fc3172009-03-28 06:43:06 -07001036
1037 /**
1038 * Gets the group id based on group name.
Evan Millar7e4accf2009-06-08 10:43:26 -07001039 *
Alex Kennberg87fc3172009-03-28 06:43:06 -07001040 * @param resolver the resolver to use
1041 * @param groupName the name of the group to add the contact to
1042 * @return the id of the group
1043 * @throws IllegalStateException if the group can't be found
1044 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001045 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07001046 private long getGroupId(ContentResolver resolver, String groupName) {
1047 long groupId = 0;
1048 Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUP_ID_PROJECTION,
1049 Groups.NAME + "=?", new String[] { groupName }, null);
1050 if (groupsCursor != null) {
1051 try {
1052 if (groupsCursor.moveToFirst()) {
1053 groupId = groupsCursor.getLong(0);
1054 }
1055 } finally {
1056 groupsCursor.close();
1057 }
1058 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001059
Alex Kennberg87fc3172009-03-28 06:43:06 -07001060 if (groupId == 0) {
1061 throw new IllegalStateException("Failed to find the " + groupName + "group");
1062 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001063
Alex Kennberg87fc3172009-03-28 06:43:06 -07001064 return groupId;
1065 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001066 */
Alex Kennberg87fc3172009-03-28 06:43:06 -07001067
1068 /**
1069 * Deletes group membership based on person and group ids.
Evan Millar7e4accf2009-06-08 10:43:26 -07001070 *
Alex Kennberg87fc3172009-03-28 06:43:06 -07001071 * @param personId the person id
1072 * @param groupId the group id
1073 * @return the id of the group membership
1074 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001075 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07001076 private void deleteGroupMembership(long personId, long groupId) {
1077 long groupMembershipId = 0;
1078 Cursor groupsCursor = mResolver.query(GroupMembership.CONTENT_URI, GROUPMEMBERSHIP_ID_PROJECTION,
1079 GroupMembership.PERSON_ID + "=? AND " + GroupMembership.GROUP_ID + "=?",
1080 new String[] {String.valueOf(personId), String.valueOf(groupId)}, null);
1081 if (groupsCursor != null) {
1082 try {
1083 if (groupsCursor.moveToFirst()) {
1084 groupMembershipId = groupsCursor.getLong(0);
1085 }
1086 } finally {
1087 groupsCursor.close();
1088 }
1089 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001090
Alex Kennberg87fc3172009-03-28 06:43:06 -07001091 if (groupMembershipId != 0) {
1092 final Uri groupsUri = ContentUris.withAppendedId(
1093 GroupMembership.CONTENT_URI,groupMembershipId);
1094 mResolver.delete(groupsUri, null, null);
1095 }
1096 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001097 */
1098
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001099 /**
1100 * Save the various fields to the existing contact.
1101 */
1102 private void save() {
1103 ContentValues values = new ContentValues();
1104 String data;
1105 int numValues = 0;
1106
1107 // Handle the name and send to voicemail specially
1108 final String name = mNameView.getText().toString();
1109 if (name != null && TextUtils.isGraphic(name)) {
1110 numValues++;
1111 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001112
Evan Millar7e4accf2009-06-08 10:43:26 -07001113 values.put(StructuredName.DISPLAY_NAME, name);
1114 /*
1115 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1116 */
1117 mResolver.update(mStructuredNameUri, values, null, null);
1118
1119 // This will go down in for loop somewhere
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001120 if (mPhotoChanged) {
1121 // Only write the photo if it's changed, since we don't initially load mPhoto
Evan Millar7e4accf2009-06-08 10:43:26 -07001122 values.clear();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001123 if (mPhoto != null) {
1124 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1125 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
Evan Millar7e4accf2009-06-08 10:43:26 -07001126 values.put(Photo.PHOTO, stream.toByteArray());
1127 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001128 } else {
Evan Millar7e4accf2009-06-08 10:43:26 -07001129 values.putNull(Photo.PHOTO);
1130 mResolver.update(mPhotoDataUri, values, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001131 }
1132 }
1133
1134 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1135 for (int i = 0; i < entryCount; i++) {
1136 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001137 data = entry.getData();
1138 boolean empty = data == null || !TextUtils.isGraphic(data);
Evan Millar7e4accf2009-06-08 10:43:26 -07001139 /*
1140 if (kind == EditEntry.KIND_GROUP) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001141 if (entry.id != 0) {
1142 for (int g = 0; g < mGroups.length; g++) {
1143 long groupId = getGroupId(mResolver, mGroups[g].toString());
1144 if (mInTheGroup[g]) {
1145 Contacts.People.addToGroup(mResolver, entry.id, groupId);
1146 numValues++;
1147 } else {
1148 deleteGroupMembership(entry.id, groupId);
1149 }
1150 }
1151 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001152 }
1153 */
1154 if (!empty) {
1155 values.clear();
1156 entry.toValues(values);
1157 if (entry.id != 0) {
1158 mResolver.update(entry.uri, values, null, null);
1159 } else {
1160 /* mResolver.insert(entry.uri, values); */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001161 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001162 if (!CustomRingtone.RINGTONE_URI.equals(entry.column) &&
1163 !CustomRingtone.SEND_TO_VOICEMAIL.equals(entry.column)) {
1164 numValues++;
1165 }
1166 } else if (entry.id != 0) {
1167 mResolver.delete(entry.uri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001168 }
1169 }
1170
Evan Millar7e4accf2009-06-08 10:43:26 -07001171 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001172 if (numValues == 0) {
1173 // The contact is completely empty, delete it
1174 mResolver.delete(mUri, null, null);
1175 mUri = null;
1176 setResult(RESULT_CANCELED);
1177 } else {
1178 // Add the entry to the my contacts group if it isn't there already
1179 People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
1180 setResult(RESULT_OK, new Intent().setData(mUri));
1181
1182 // Only notify user if we actually changed contact
1183 if (mContactChanged || mPhotoChanged) {
1184 Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1185 }
1186 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001187 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001188 }
1189
1190 /**
1191 * Takes the entered data and saves it to a new contact.
1192 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001193 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001194 private void create() {
1195 ContentValues values = new ContentValues();
1196 String data;
1197 int numValues = 0;
1198
1199 // Create the contact itself
1200 final String name = mNameView.getText().toString();
1201 if (name != null && TextUtils.isGraphic(name)) {
1202 numValues++;
1203 }
1204 values.put(People.NAME, name);
1205 values.put(People.PHONETIC_NAME, mPhoneticNameView.getText().toString());
1206
1207 // Add the contact to the My Contacts group
1208 Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
1209
1210 // Add the contact to the group that is being displayed in the contact list
1211 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1212 int displayType = prefs.getInt(ContactsListActivity.PREF_DISPLAY_TYPE,
1213 ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
1214 if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
1215 String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
1216 null);
1217 if (!TextUtils.isEmpty(displayGroup)) {
1218 People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
1219 }
1220 } else {
1221 // Check to see if we're not syncing everything and if so if My Contacts is synced.
1222 // If it isn't then the created contact can end up not in any groups that are
1223 // currently synced and end up getting removed from the phone, which is really bad.
1224 boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
1225 Contacts.Settings.SYNC_EVERYTHING));
1226 if (!syncingEverything) {
1227 boolean syncingMyContacts = false;
1228 Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
1229 Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
1230 if (c != null) {
1231 try {
1232 if (c.moveToFirst()) {
1233 syncingMyContacts = !"0".equals(c.getString(0));
1234 }
1235 } finally {
1236 c.close();
1237 }
1238 }
1239
1240 if (!syncingMyContacts) {
1241 // Not syncing My Contacts, so find a group that is being synced and stick
1242 // the contact in there. We sort the list so at least all contacts
1243 // will appear in the same group.
1244 c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
1245 Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
1246 if (c != null) {
1247 try {
1248 if (c.moveToFirst()) {
1249 People.addToGroup(mResolver, ContentUris.parseId(contactUri),
1250 c.getLong(0));
1251 }
1252 } finally {
1253 c.close();
1254 }
1255 }
1256 }
1257 }
1258 }
1259
1260 // Handle the photo
1261 if (mPhoto != null) {
1262 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1263 mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
1264 Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
1265 }
1266
1267 // Create the contact methods
1268 int entryCount = ContactEntryAdapter.countEntries(mSections, false);
1269 for (int i = 0; i < entryCount; i++) {
1270 EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
Alex Kennbergfb0386a2009-04-02 09:59:10 -07001271 if (entry.kind == EditEntry.KIND_GROUP) {
1272 long contactId = ContentUris.parseId(contactUri);
1273 for (int g = 0; g < mGroups.length; g++) {
1274 if (mInTheGroup[g]) {
1275 long groupId = getGroupId(mResolver, mGroups[g].toString());
1276 People.addToGroup(mResolver, contactId, groupId);
1277 numValues++;
1278 }
1279 }
1280 } else if (entry.kind != EditEntry.KIND_CONTACT) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001281 values.clear();
1282 if (entry.toValues(values)) {
1283 // Only create the entry if there is data
1284 entry.uri = mResolver.insert(
1285 Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
1286 entry.id = ContentUris.parseId(entry.uri);
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001287 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1288 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1289 numValues++;
1290 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001291 }
1292 } else {
1293 // Update the contact with any straggling data, like notes
1294 data = entry.getData();
1295 values.clear();
1296 if (data != null && TextUtils.isGraphic(data)) {
1297 values.put(entry.column, data);
1298 mResolver.update(contactUri, values, null, null);
The Android Open Source Projecte740e2e2009-03-11 12:11:58 -07001299 if (!People.CUSTOM_RINGTONE.equals(entry.column) &&
1300 !People.SEND_TO_VOICEMAIL.equals(entry.column)) {
1301 numValues++;
1302 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001303 }
1304 }
1305 }
1306
1307 if (numValues == 0) {
1308 mResolver.delete(contactUri, null, null);
1309 setResult(RESULT_CANCELED);
1310 } else {
1311 mUri = contactUri;
1312 Intent resultIntent = new Intent()
1313 .setData(mUri)
1314 .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
1315 setResult(RESULT_OK, resultIntent);
1316 Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
1317 }
1318 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001319 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001320
1321 /**
1322 * Build up the entries to display on the screen.
1323 *
1324 * @param extras the extras used to start this activity, may be null
1325 */
1326 private void buildEntriesForEdit(Bundle extras) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001327 Cursor aggCursor = mResolver.query(mAggDataUri, AGGREGATE_PROJECTION, null, null, null);
1328 if (aggCursor == null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001329 Log.e(TAG, "invalid contact uri: " + mUri);
1330 finish();
1331 return;
Evan Millar7e4accf2009-06-08 10:43:26 -07001332 } else if (!aggCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001333 Log.e(TAG, "invalid contact uri: " + mUri);
1334 finish();
Evan Millar7e4accf2009-06-08 10:43:26 -07001335 aggCursor.close();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001336 return;
1337 }
1338
1339 // Clear out the old entries
1340 int numSections = mSections.size();
1341 for (int i = 0; i < numSections; i++) {
1342 mSections.get(i).clear();
1343 }
1344
1345 EditEntry entry;
1346
Evan Millar7e4accf2009-06-08 10:43:26 -07001347 while (aggCursor.moveToNext()) {
1348 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
1349 boolean isSuperPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001350
Evan Millar7e4accf2009-06-08 10:43:26 -07001351 final long id = aggCursor.getLong(DATA_ID_COLUMN);
1352 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
1353
1354 if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
1355 mNameView.setText(aggCursor.getString(DATA_9_COLUMN));
1356 mNameView.addTextChangedListener(this);
1357 mStructuredNameUri = uri;
1358 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
1359 mPhoto = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
1360 if (mPhoto == null) {
1361 setPhotoPresent(false);
1362 } else {
1363 setPhotoPresent(true);
1364 mPhotoImageView.setImageBitmap(mPhoto);
1365 }
1366 mPhotoDataUri = uri;
1367 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
1368 int type = aggCursor.getInt(DATA_1_COLUMN);
1369 String label = aggCursor.getString(DATA_2_COLUMN);
1370 String company = aggCursor.getString(DATA_3_COLUMN);
1371 String title = aggCursor.getString(DATA_4_COLUMN);
1372
1373 entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
1374 entry.isPrimary = aggCursor.getLong(DATA_IS_SUPER_PRIMARY_COLUMN) != 0;
1375 mOrgEntries.add(entry);
1376 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
1377 entry = EditEntry.newNotesEntry(this, aggCursor.getString(DATA_1_COLUMN),
1378 uri, id);
1379 mNoteEntries.add(entry);
1380 } else if (mimetype.equals(CommonDataKinds.CustomRingtone.CONTENT_ITEM_TYPE)) {
1381 entry = EditEntry.newRingtoneEntry(this,
1382 aggCursor.getString(DATA_2_COLUMN), uri, id);
1383 mOtherEntries.add(entry);
1384
1385 entry = EditEntry.newSendToVoicemailEntry(this,
1386 aggCursor.getString(DATA_1_COLUMN), uri, id);
1387 mOtherEntries.add(entry);
1388 } else if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
1389 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
1390 || mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)
1391 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1392 int type = aggCursor.getInt(DATA_1_COLUMN);
1393 String data = aggCursor.getString(DATA_2_COLUMN);
1394 String label = aggCursor.getString(DATA_3_COLUMN);
1395
1396 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
1397 // Add a phone number entry
1398 entry = EditEntry.newPhoneEntry(this, label, type, data, uri, id);
1399 entry.isPrimary = isSuperPrimary;
1400 mPhoneEntries.add(entry);
1401
1402 // Keep track of which primary types have been added
1403 if (type == Phone.TYPE_MOBILE) {
1404 mMobilePhoneAdded = true;
1405 }
1406 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
1407 entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
1408 entry.isPrimary = isSuperPrimary;
1409 mEmailEntries.add(entry);
1410
1411 if (isSuperPrimary) {
1412 mPrimaryEmailAdded = true;
1413 }
1414 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
1415 entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
1416 entry.isPrimary = isSuperPrimary;
1417 mPostalEntries.add(entry);
1418 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
1419 String protocolStr = aggCursor.getString(DATA_5_COLUMN);
1420 Object protocolObj = ContactsUtils.decodeImProtocol(protocolStr);
1421 if (protocolObj == null) {
1422 // Invalid IM protocol, log it then ignore.
1423 Log.e(TAG, "Couldn't decode IM protocol: " + protocolStr);
1424 continue;
1425 } else {
1426 if (protocolObj instanceof Number) {
1427 int protocol = ((Number) protocolObj).intValue();
1428 entry = EditEntry.newImEntry(this,
1429 getLabelsForMimetype(this, mimetype)[protocol], protocol,
1430 data, uri, id);
1431 } else {
1432 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
1433 uri, id);
1434 }
1435 mImEntries.add(entry);
1436 }
1437 }
1438 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001439 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001440
1441 /*
1442 // Groups
1443 populateGroups();
1444 if (mGroups != null) {
1445 entry = EditEntry.newGroupEntry(this, generateGroupList(), mUri,
1446 personCursor.getLong(0));
1447 mOtherEntries.add(entry);
1448 }
1449
1450 // Phonetic name
1451 mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
1452 mPhoneticNameView.addTextChangedListener(this);
1453
1454
1455 // Add values from the extras, if there are any
1456 if (extras != null) {
1457 addFromExtras(extras, phonesUri, methodsUri);
1458 }
1459
1460 // Add the base types if needed
1461 if (!mMobilePhoneAdded) {
1462 entry = EditEntry.newPhoneEntry(this,
1463 Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
1464 DEFAULT_PHONE_TYPE);
1465 mPhoneEntries.add(entry);
1466 }
1467
1468 if (!mPrimaryEmailAdded) {
1469 entry = EditEntry.newEmailEntry(this,
1470 Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
1471 DEFAULT_EMAIL_TYPE);
1472 entry.isPrimary = true;
1473 mEmailEntries.add(entry);
1474 }
1475 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001476
1477 mContactChanged = false;
1478 }
1479
1480 /**
1481 * Build the list of EditEntries for full mode insertions.
Evan Millar7e4accf2009-06-08 10:43:26 -07001482 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001483 * @param extras the extras used to start this activity, may be null
1484 */
Evan Millar7e4accf2009-06-08 10:43:26 -07001485 /*
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001486 private void buildEntriesForInsert(Bundle extras) {
1487 // Clear out the old entries
1488 int numSections = mSections.size();
1489 for (int i = 0; i < numSections; i++) {
1490 mSections.get(i).clear();
1491 }
1492
1493 EditEntry entry;
1494
1495 // Check the intent extras
1496 if (extras != null) {
1497 addFromExtras(extras, null, null);
1498 }
1499
1500 // Photo
1501 mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
1502
1503 // Add the base entries if they're not already present
1504 if (!mMobilePhoneAdded) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001505 entry = EditEntry.newPhoneEntry(this, null, Phone.TYPE_MOBILE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001506 entry.isPrimary = true;
1507 mPhoneEntries.add(entry);
1508 }
1509
1510 if (!mPrimaryEmailAdded) {
1511 entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
1512 entry.isPrimary = true;
1513 mEmailEntries.add(entry);
1514 }
1515
Alex Kennberg87fc3172009-03-28 06:43:06 -07001516 // Group
1517 populateGroups();
1518 if (mGroups != null) {
1519 entry = EditEntry.newGroupEntry(this, null, mUri, 0);
1520 mOtherEntries.add(entry);
1521 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001522
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001523 // Ringtone
Evan Millar7e4accf2009-06-08 10:43:26 -07001524 entry = EditEntry.newRingtoneEntry(this, null, mUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001525 mOtherEntries.add(entry);
Evan Millar7e4accf2009-06-08 10:43:26 -07001526
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001527 // Send to voicemail
Evan Millar7e4accf2009-06-08 10:43:26 -07001528 entry = EditEntry.newSendToVoicemailEntry(this, "0", mUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001529 mOtherEntries.add(entry);
1530 }
1531
1532 private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
1533 EditEntry entry;
1534
1535 // Read the name from the bundle
1536 CharSequence name = extras.getCharSequence(Insert.NAME);
1537 if (name != null && TextUtils.isGraphic(name)) {
1538 mNameView.setText(name);
1539 }
1540
1541 // Read the phonetic name from the bundle
1542 CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
1543 if (!TextUtils.isEmpty(phoneticName)) {
1544 mPhoneticNameView.setText(phoneticName);
1545 }
1546
1547 // Postal entries from extras
1548 CharSequence postal = extras.getCharSequence(Insert.POSTAL);
1549 int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
1550 if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
1551 postalType = DEFAULT_POSTAL_TYPE;
1552 }
1553
1554 if (postalType != INVALID_TYPE) {
1555 entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
1556 methodsUri, 0);
1557 entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
1558 mPostalEntries.add(entry);
1559 }
1560
1561 // Email entries from extras
1562 addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
1563 Insert.EMAIL_ISPRIMARY);
1564 addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
1565 null);
1566 addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
1567 null);
Evan Millar7e4accf2009-06-08 10:43:26 -07001568
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001569 // Phone entries from extras
1570 addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
1571 Insert.PHONE_ISPRIMARY);
1572 addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
1573 null);
1574 addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
1575 null);
1576
1577 // IM entries from extras
1578 CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
1579 CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
Evan Millar7e4accf2009-06-08 10:43:26 -07001580
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001581 if (imHandle != null && imProtocol != null) {
1582 Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
1583 if (protocolObj instanceof Number) {
1584 int protocol = ((Number) protocolObj).intValue();
1585 entry = EditEntry.newImEntry(this,
Evan Millar7e4accf2009-06-08 10:43:26 -07001586 getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol,
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001587 imHandle.toString(), methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001588 } else {
1589 entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
The Android Open Source Projecta10b15c2009-03-05 15:45:11 -08001590 methodsUri, 0);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001591 }
1592 entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
1593 mImEntries.add(entry);
1594 }
1595 }
1596
1597 private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
1598 String typeField, String primaryField) {
1599 CharSequence email = extras.getCharSequence(emailField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001600
1601 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001602 int emailType = INVALID_TYPE;
1603 String customLabel = null;
1604 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001605 emailType = ContactsContract.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001606 customLabel = extras.getString(typeField);
1607 } else {
1608 emailType = extras.getInt(typeField, INVALID_TYPE);
1609 }
1610
1611 if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
1612 emailType = DEFAULT_EMAIL_TYPE;
1613 mPrimaryEmailAdded = true;
1614 }
1615
1616 if (emailType != INVALID_TYPE) {
1617 EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
1618 methodsUri, 0);
1619 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1620 mEmailEntries.add(entry);
1621
1622 // Keep track of which primary types have been added
1623 if (entry.isPrimary) {
1624 mPrimaryEmailAdded = true;
1625 }
1626 }
1627 }
1628
1629 private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
1630 String typeField, String primaryField) {
1631 CharSequence phoneNumber = extras.getCharSequence(phoneField);
Evan Millar7e4accf2009-06-08 10:43:26 -07001632
1633 // Correctly handle String in typeField as TYPE_CUSTOM
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001634 int phoneType = INVALID_TYPE;
1635 String customLabel = null;
1636 if(extras.get(typeField) instanceof String) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001637 phoneType = Phone.TYPE_CUSTOM;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001638 customLabel = extras.getString(typeField);
1639 } else {
1640 phoneType = extras.getInt(typeField, INVALID_TYPE);
1641 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001642
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001643 if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
1644 phoneType = DEFAULT_PHONE_TYPE;
1645 }
1646
1647 if (phoneType != INVALID_TYPE) {
1648 EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
1649 phoneNumber.toString(), phonesUri, 0);
1650 entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
1651 mPhoneEntries.add(entry);
1652
1653 // Keep track of which primary types have been added
Evan Millar7e4accf2009-06-08 10:43:26 -07001654 if (phoneType == Phone.TYPE_MOBILE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001655 mMobilePhoneAdded = true;
1656 }
1657 }
1658 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001659 */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001660
1661 /**
1662 * Removes all existing views, builds new ones for all the entries, and adds them.
1663 */
1664 private void buildViews() {
1665 // Remove existing views
1666 final LinearLayout layout = mLayout;
1667 layout.removeAllViews();
Evan Millar7e4accf2009-06-08 10:43:26 -07001668
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001669 buildViewsForSection(layout, mPhoneEntries,
1670 R.string.listSeparatorCallNumber_edit, SECTION_PHONES);
1671 buildViewsForSection(layout, mEmailEntries,
1672 R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
1673 buildViewsForSection(layout, mImEntries,
1674 R.string.listSeparatorSendIm_edit, SECTION_IM);
1675 buildViewsForSection(layout, mPostalEntries,
1676 R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
1677 buildViewsForSection(layout, mOrgEntries,
1678 R.string.listSeparatorOrganizations, SECTION_ORG);
1679 buildViewsForSection(layout, mNoteEntries,
1680 R.string.label_notes, SECTION_NOTE);
Evan Millar7e4accf2009-06-08 10:43:26 -07001681
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001682 buildOtherViews(layout, mOtherEntries);
1683 }
1684
1685
1686 /**
1687 * Builds the views for a specific section.
Evan Millar7e4accf2009-06-08 10:43:26 -07001688 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001689 * @param layout the container
1690 * @param section the section to build the views for
1691 */
1692 private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
1693 int separatorResource, int sectionType) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001694
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001695 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1696 layout.addView(divider);
Evan Millar7e4accf2009-06-08 10:43:26 -07001697
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001698 // Count up undeleted children
1699 int activeChildren = 0;
1700 for (int i = section.size() - 1; i >= 0; i--) {
1701 EditEntry entry = section.get(i);
1702 if (!entry.isDeleted) {
1703 activeChildren++;
1704 }
1705 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001706
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001707 // Build the correct group header based on undeleted children
1708 ViewGroup header;
1709 if (activeChildren == 0) {
1710 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
1711 } else {
1712 header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
1713 }
1714
1715 // Because we're emulating a ListView, we need to handle focus changes
1716 // with some additional logic.
1717 header.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001718
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001719 TextView text = (TextView) header.findViewById(R.id.text);
1720 text.setText(getText(separatorResource));
Evan Millar7e4accf2009-06-08 10:43:26 -07001721
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001722 // Force TextView to always default color if we have children. This makes sure
1723 // we don't change color when parent is pressed.
1724 if (activeChildren > 0) {
1725 ColorStateList stateList = text.getTextColors();
1726 text.setTextColor(stateList.getDefaultColor());
1727 }
1728
1729 View addView = header.findViewById(R.id.separator);
1730 addView.setTag(Integer.valueOf(sectionType));
1731 addView.setOnClickListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001732
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001733 // Build views for the current section
1734 for (EditEntry entry : section) {
1735 entry.activity = this; // this could be null from when the state is restored
1736 if (!entry.isDeleted) {
1737 View view = buildViewForEntry(entry);
1738 header.addView(view);
1739 }
1740 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001741
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001742 layout.addView(header);
1743 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001744
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001745 private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
1746 // Build views for the current section, putting a divider between each one
1747 for (EditEntry entry : section) {
1748 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1749 layout.addView(divider);
1750
1751 entry.activity = this; // this could be null from when the state is restored
1752 View view = buildViewForEntry(entry);
1753 view.setOnClickListener(this);
1754 layout.addView(view);
1755 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001756
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001757 View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
1758 layout.addView(divider);
1759 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001760
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001761 /**
1762 * Builds a view to display an EditEntry.
Evan Millar7e4accf2009-06-08 10:43:26 -07001763 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001764 * @param entry the entry to display
1765 * @return a view that will display the given entry
1766 */
1767 /* package */ View buildViewForEntry(final EditEntry entry) {
1768 // Look for any existing entered text, and save it if found
1769 if (entry.view != null && entry.syncDataWithView) {
1770 String enteredText = ((TextView) entry.view.findViewById(R.id.data))
1771 .getText().toString();
1772 if (!TextUtils.isEmpty(enteredText)) {
1773 entry.data = enteredText;
1774 }
1775 }
1776
1777 // Build a new view
1778 final ViewGroup parent = mLayout;
1779 View view;
1780
1781 // Because we're emulating a ListView, we might need to handle focus changes
1782 // with some additional logic.
Evan Millar7e4accf2009-06-08 10:43:26 -07001783 if (entry.mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001784 view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
Evan Millar7e4accf2009-06-08 10:43:26 -07001785 }
1786 /*
1787 else if (entry.mimetype.equals(Group.CONTENT_ITEM_TYPE)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001788 view = mInflater.inflate(R.layout.edit_contact_entry_group, parent, false);
1789 view.setOnFocusChangeListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001790 }
1791 */
1792 else if (entry.mimetype.equals(CustomRingtone.CONTENT_ITEM_TYPE)) {
1793 if (entry.column.equals(CustomRingtone.RINGTONE_URI)) {
1794 view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
1795 view.setOnFocusChangeListener(this);
1796 } else {
1797 view = mInflater.inflate(R.layout.edit_contact_entry_voicemail, parent, false);
1798 view.setOnFocusChangeListener(this);
1799 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001800 } else if (!entry.isStaticLabel) {
1801 view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
1802 } else {
1803 view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
1804 }
1805 entry.view = view;
Evan Millar7e4accf2009-06-08 10:43:26 -07001806
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001807 // Set the entry as the tag so we can find it again later given just the view
1808 view.setTag(entry);
1809
1810 // Bind the label
1811 entry.bindLabel(this);
1812
1813 // Bind data
1814 TextView data = (TextView) view.findViewById(R.id.data);
1815 TextView data2 = (TextView) view.findViewById(R.id.data2);
Evan Millar7e4accf2009-06-08 10:43:26 -07001816
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001817 if (data instanceof Button) {
1818 data.setOnClickListener(this);
1819 }
1820 if (data.length() == 0) {
1821 if (entry.syncDataWithView) {
1822 // If there is already data entered don't overwrite it
1823 data.setText(entry.data);
1824 } else {
1825 fillViewData(entry);
1826 }
1827 }
1828 if (data2 != null && data2.length() == 0) {
1829 // If there is already data entered don't overwrite it
1830 data2.setText(entry.data2);
1831 }
1832 data.setHint(entry.hint);
1833 if (data2 != null) data2.setHint(entry.hint2);
1834 if (entry.lines > 1) {
1835 data.setLines(entry.lines);
1836 data.setMaxLines(entry.maxLines);
1837 if (data2 != null) {
1838 data2.setLines(entry.lines);
1839 data2.setMaxLines(entry.maxLines);
1840 }
1841 }
1842 int contentType = entry.contentType;
1843 if (contentType != EditorInfo.TYPE_NULL) {
1844 data.setInputType(contentType);
1845 if (data2 != null) {
1846 data2.setInputType(contentType);
1847 }
1848 if ((contentType&EditorInfo.TYPE_MASK_CLASS)
1849 == EditorInfo.TYPE_CLASS_PHONE) {
1850 data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1851 if (data2 != null) {
1852 data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
1853 }
1854 }
1855 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001856
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001857 // Give focus to children as requested, possibly after a configuration change
1858 View focusChild = view.findViewById(entry.requestFocusId);
1859 if (focusChild != null) {
1860 focusChild.requestFocus();
1861 if (focusChild instanceof EditText) {
1862 ((EditText) focusChild).setSelection(entry.requestCursor);
1863 }
1864 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001865
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001866 // Reset requested focus values
1867 entry.requestFocusId = View.NO_ID;
1868 entry.requestCursor = 0;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001869
1870 // Connect listeners up to watch for changed values.
1871 if (data instanceof EditText) {
1872 data.addTextChangedListener(this);
1873 }
1874 if (data2 instanceof EditText) {
1875 data2.addTextChangedListener(this);
1876 }
1877
1878 // Hook up the delete button
1879 View delete = view.findViewById(R.id.delete);
1880 if (delete != null) delete.setOnClickListener(this);
Evan Millar7e4accf2009-06-08 10:43:26 -07001881
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001882 return view;
1883 }
1884
1885 private void fillViewData(final EditEntry entry) {
Evan Millar7e4accf2009-06-08 10:43:26 -07001886 if (isOtherEntry(entry, CustomRingtone.RINGTONE_URI)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001887 updateRingtoneView(entry);
Evan Millar7e4accf2009-06-08 10:43:26 -07001888 }
1889 /*
1890 else if (isOtherEntry(entry, GroupMembership.GROUP_ID)) {
Alex Kennberg87fc3172009-03-28 06:43:06 -07001891 if (entry.data != null) {
1892 updateDataView(entry, entry.data);
1893 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001894 }
1895 */
1896 else if (isOtherEntry(entry, CustomRingtone.SEND_TO_VOICEMAIL)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001897 CheckBox checkBox = (CheckBox) entry.view.findViewById(R.id.checkbox);
1898 boolean sendToVoicemail = false;
1899 if (entry.data != null) {
1900 sendToVoicemail = (Integer.valueOf(entry.data) == 1);
1901 }
1902 checkBox.setChecked(sendToVoicemail);
1903 }
1904 }
Evan Millar7e4accf2009-06-08 10:43:26 -07001905
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001906 /**
1907 * Handles the results from the label change picker.
1908 */
1909 private final class LabelPickedListener implements DialogInterface.OnClickListener {
1910 EditEntry mEntry;
1911 String[] mLabels;
1912
1913 public LabelPickedListener(EditEntry entry, String[] labels) {
1914 mEntry = entry;
1915 mLabels = labels;
1916 }
1917
1918 public void onClick(DialogInterface dialog, int which) {
1919 // TODO: Use a managed dialog
Evan Millar7e4accf2009-06-08 10:43:26 -07001920 if (mEntry.mimetype != Im.CONTENT_ITEM_TYPE) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001921 final int type = getTypeFromLabelPosition(mLabels, which);
Evan Millar7e4accf2009-06-08 10:43:26 -07001922 if (type == BaseTypes.TYPE_CUSTOM) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001923 createCustomPicker(mEntry, null);
1924 } else {
1925 mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
1926 mContactChanged = true;
1927 }
1928 } else {
1929 mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
1930 mContactChanged = true;
1931 }
1932 }
1933 }
1934
1935 /**
1936 * A basic structure with the data for a contact entry in the list.
1937 */
1938 private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
1939 // These aren't stuffed into the parcel
1940 public EditContactActivity activity;
1941 public View view;
1942
1943 // These are stuffed into the parcel
1944 public String hint;
1945 public String hint2;
1946 public String column;
1947 public String contentDirectory;
1948 public String data2;
1949 public int contentType;
1950 public int type;
1951 /**
1952 * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
1953 * will not be called.
1954 */
1955 public int lines = 1;
1956 public boolean isPrimary;
1957 public boolean isDeleted = false;
1958 public boolean isStaticLabel = false;
1959 public boolean syncDataWithView = true;
1960
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08001961 /**
1962 * Request focus on the child of this {@link EditEntry} found using
1963 * {@link View#findViewById(int)}. This value should be reset to
1964 * {@link View#NO_ID} after each use.
1965 */
1966 public int requestFocusId = View.NO_ID;
1967
1968 /**
1969 * If the {@link #requestFocusId} is an {@link EditText}, this value
1970 * indicates the requested cursor position placement.
1971 */
1972 public int requestCursor = 0;
1973
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001974 private EditEntry() {
1975 // only used by CREATOR
1976 }
1977
1978 public EditEntry(EditContactActivity activity) {
1979 this.activity = activity;
1980 }
1981
1982 public EditEntry(EditContactActivity activity, String label,
1983 int type, String data, Uri uri, long id) {
1984 this.activity = activity;
1985 this.isPrimary = false;
1986 this.label = label;
1987 this.type = type;
1988 this.data = data;
1989 this.uri = uri;
1990 this.id = id;
1991 }
1992
1993 public int describeContents() {
1994 return 0;
1995 }
1996
1997 public void writeToParcel(Parcel parcel, int flags) {
1998 // Make sure to read data from the input field, if anything is entered
1999 data = getData();
2000
2001 // Write in our own fields.
2002 parcel.writeString(hint);
2003 parcel.writeString(hint2);
2004 parcel.writeString(column);
2005 parcel.writeString(contentDirectory);
2006 parcel.writeString(data2);
2007 parcel.writeInt(contentType);
2008 parcel.writeInt(type);
2009 parcel.writeInt(lines);
2010 parcel.writeInt(isPrimary ? 1 : 0);
2011 parcel.writeInt(isDeleted ? 1 : 0);
2012 parcel.writeInt(isStaticLabel ? 1 : 0);
2013 parcel.writeInt(syncDataWithView ? 1 : 0);
2014
2015 // Write in the fields from Entry
2016 super.writeToParcel(parcel);
2017 }
2018
2019 public static final Parcelable.Creator<EditEntry> CREATOR =
2020 new Parcelable.Creator<EditEntry>() {
2021 public EditEntry createFromParcel(Parcel in) {
2022 EditEntry entry = new EditEntry();
2023
2024 // Read out our own fields
2025 entry.hint = in.readString();
2026 entry.hint2 = in.readString();
2027 entry.column = in.readString();
2028 entry.contentDirectory = in.readString();
2029 entry.data2 = in.readString();
2030 entry.contentType = in.readInt();
2031 entry.type = in.readInt();
2032 entry.lines = in.readInt();
2033 entry.isPrimary = in.readInt() == 1;
2034 entry.isDeleted = in.readInt() == 1;
2035 entry.isStaticLabel = in.readInt() == 1;
2036 entry.syncDataWithView = in.readInt() == 1;
Evan Millar7e4accf2009-06-08 10:43:26 -07002037
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002038 // Read out the fields from Entry
2039 entry.readFromParcel(in);
2040
2041 return entry;
2042 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002043
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002044 public EditEntry[] newArray(int size) {
2045 return new EditEntry[size];
2046 }
2047 };
2048
2049 public void setLabel(Context context, int typeIn, String labelIn) {
2050 type = typeIn;
2051 label = labelIn;
2052 if (view != null) {
2053 bindLabel(context);
2054 }
2055 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002056
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002057 public void bindLabel(Context context) {
2058 TextView v = (TextView) view.findViewById(R.id.label);
2059 if (isStaticLabel) {
2060 v.setText(label);
2061 return;
2062 }
2063
Evan Millar7e4accf2009-06-08 10:43:26 -07002064 v.setText(ContactsUtils.getDisplayLabel(context, mimetype, type, label));
2065 if (mimetype.equals(Im.CONTENT_ITEM_TYPE) && type >= 0) {
2066 v.setText(getLabelsForMimetype(activity, mimetype)[type]);
2067 } else if (mimetype.equals(Postal.CONTENT_ITEM_TYPE)) {
2068 v.setMaxLines(3);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002069 }
2070 v.setOnClickListener(activity);
2071 }
2072
2073 /**
2074 * Returns the data for the entry
2075 * @return the data for the entry
2076 */
2077 public String getData() {
2078 if (view != null && syncDataWithView) {
2079 CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
2080 if (text != null) {
2081 return text.toString();
2082 }
2083 }
2084
2085 if (data != null) {
2086 return data.toString();
2087 }
2088
2089 return null;
2090 }
2091
2092 /**
2093 * Dumps the entry into a HashMap suitable for passing to the database.
Evan Millar7e4accf2009-06-08 10:43:26 -07002094 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002095 * @param values the HashMap to fill in.
2096 * @return true if the value should be saved, false otherwise
2097 */
2098 public boolean toValues(ContentValues values) {
2099 boolean success = false;
2100 String labelString = null;
2101 // Save the type and label
2102 if (view != null) {
2103 // Read the possibly updated label from the text field
2104 labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
2105 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002106 if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
2107 if (type != Phone.TYPE_CUSTOM) {
2108 labelString = null;
2109 }
2110 values.put(Phone.LABEL, labelString);
2111 values.put(Phone.TYPE, type);
2112 } else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
2113 if (type != Email.TYPE_CUSTOM) {
2114 labelString = null;
2115 }
2116 values.put(Email.LABEL, labelString);
2117 values.put(Email.TYPE, type);
2118 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
2119 values.put(CommonDataKinds.Im.TYPE, type);
2120 values.putNull(CommonDataKinds.Im.LABEL);
2121 if (type != -1) {
2122 values.put(CommonDataKinds.Im.PROTOCOL,
2123 ContactsUtils.encodePredefinedImProtocol(type));
2124 } else {
2125 values.put(CommonDataKinds.Im.PROTOCOL,
2126 ContactsUtils.encodeCustomImProtocol(label.toString()));
2127 }
2128 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
2129 if (type != Postal.TYPE_CUSTOM) {
2130 labelString = null;
2131 }
2132 values.put(Postal.LABEL, labelString);
2133 values.put(Postal.TYPE, type);
2134 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
2135 if (type != Organization.TYPE_CUSTOM) {
2136 labelString = null;
2137 }
2138 values.put(Organization.LABEL, labelString);
2139 values.put(Organization.TYPE, type);
2140 // Save the title
2141 if (view != null) {
2142 // Read the possibly updated data from the text field
2143 data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
2144 }
2145 if (!TextUtils.isGraphic(data2)) {
2146 values.putNull(Organization.TITLE);
2147 } else {
2148 values.put(Organization.TITLE, data2.toString());
2149 success = true;
2150 }
2151 } else {
2152 Log.i(TAG, "unknown entry mimetype: " + (mimetype == null ? "" : mimetype));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002153 }
2154
2155 // Only set the ISPRIMARY flag if part of the incoming data. This is because the
2156 // ContentProvider will try finding a new primary when setting to false, meaning
2157 // it's possible to lose primary altogether as we walk down the list. If this editor
2158 // implements editing of primaries in the future, this will need to be revisited.
2159 if (isPrimary) {
Evan Millar7e4accf2009-06-08 10:43:26 -07002160 values.put(Data.IS_PRIMARY, 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002161 }
2162
2163 // Save the data
2164 if (view != null && syncDataWithView) {
2165 // Read the possibly updated data from the text field
2166 data = ((TextView) view.findViewById(R.id.data)).getText().toString();
2167 }
2168 if (!TextUtils.isGraphic(data)) {
2169 values.putNull(column);
2170 return success;
2171 } else {
2172 values.put(column, data.toString());
2173 return true;
2174 }
2175 }
2176
2177 /**
2178 * Create a new empty organization entry
2179 */
2180 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2181 Uri uri, int type) {
2182 return newOrganizationEntry(activity, null, type, null, null, uri, 0);
2183 }
2184
2185 /**
2186 * Create a new company entry with the given data.
2187 */
2188 public static final EditEntry newOrganizationEntry(EditContactActivity activity,
2189 String label, int type, String company, String title, Uri uri, long id) {
2190 EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
2191 entry.hint = activity.getString(R.string.ghostData_company);
2192 entry.hint2 = activity.getString(R.string.ghostData_title);
2193 entry.data2 = title;
Evan Millar7e4accf2009-06-08 10:43:26 -07002194 entry.column = Organization.COMPANY;
2195 entry.mimetype = Organization.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002196 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2197 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
2198 return entry;
2199 }
2200
2201 /**
Evan Millar7e4accf2009-06-08 10:43:26 -07002202 * Create a new empty notes entry
2203 */
2204 public static final EditEntry newNotesEntry(EditContactActivity activity,
2205 Uri uri) {
2206 return newNotesEntry(activity, null, uri, 0);
2207 }
2208
2209 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002210 * Create a new notes entry with the given data.
2211 */
2212 public static final EditEntry newNotesEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002213 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002214 EditEntry entry = new EditEntry(activity);
2215 entry.label = activity.getString(R.string.label_notes);
2216 entry.hint = activity.getString(R.string.ghostData_notes);
2217 entry.data = data;
2218 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002219 entry.column = Note.NOTE;
2220 entry.mimetype = Note.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002221 entry.maxLines = 10;
2222 entry.lines = 2;
Evan Millar7e4accf2009-06-08 10:43:26 -07002223 entry.id = id;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002224 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2225 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
2226 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2227 entry.isStaticLabel = true;
2228 return entry;
2229 }
2230
2231 /**
Alex Kennberg87fc3172009-03-28 06:43:06 -07002232 * Create a new group entry with the given data.
2233 */
Evan Millar7e4accf2009-06-08 10:43:26 -07002234 /*
Alex Kennberg87fc3172009-03-28 06:43:06 -07002235 public static final EditEntry newGroupEntry(EditContactActivity activity,
2236 String data, Uri uri, long personId) {
2237 EditEntry entry = new EditEntry(activity);
2238 entry.label = activity.getString(R.string.label_groups);
2239 entry.data = data;
2240 entry.uri = uri;
2241 entry.id = personId;
2242 entry.column = GroupMembership.GROUP_ID;
2243 entry.kind = KIND_GROUP;
2244 entry.isStaticLabel = true;
2245 entry.syncDataWithView = false;
2246 entry.lines = -1;
2247 return entry;
2248 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002249 */
Alex Kennberg87fc3172009-03-28 06:43:06 -07002250
2251 /**
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002252 * Create a new ringtone entry with the given data.
2253 */
2254 public static final EditEntry newRingtoneEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002255 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002256 EditEntry entry = new EditEntry(activity);
2257 entry.label = activity.getString(R.string.label_ringtone);
2258 entry.data = data;
2259 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002260 entry.id = id;
2261 entry.column = CustomRingtone.RINGTONE_URI;
2262 entry.mimetype = CustomRingtone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002263 entry.isStaticLabel = true;
2264 entry.syncDataWithView = false;
2265 entry.lines = -1;
2266 return entry;
2267 }
2268
2269 /**
2270 * Create a new send-to-voicemail entry with the given data.
2271 */
2272 public static final EditEntry newSendToVoicemailEntry(EditContactActivity activity,
Evan Millar7e4accf2009-06-08 10:43:26 -07002273 String data, Uri uri, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002274 EditEntry entry = new EditEntry(activity);
2275 entry.label = activity.getString(R.string.actionIncomingCall);
2276 entry.data = data;
2277 entry.uri = uri;
Evan Millar7e4accf2009-06-08 10:43:26 -07002278 entry.id = id;
2279 entry.column = CustomRingtone.SEND_TO_VOICEMAIL;
2280 entry.mimetype = CustomRingtone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002281 entry.isStaticLabel = true;
2282 entry.syncDataWithView = false;
2283 entry.lines = -1;
2284 return entry;
2285 }
2286
2287 /**
2288 * Create a new empty email entry
2289 */
2290 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2291 Uri uri, int type) {
2292 return newPhoneEntry(activity, null, type, null, uri, 0);
2293 }
2294
2295 /**
2296 * Create a new phone entry with the given data.
2297 */
2298 public static final EditEntry newPhoneEntry(EditContactActivity activity,
2299 String label, int type, String data, Uri uri,
2300 long id) {
2301 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2302 entry.hint = activity.getString(R.string.ghostData_phone);
Evan Millar7e4accf2009-06-08 10:43:26 -07002303 entry.column = Phone.NUMBER;
2304 entry.mimetype = Phone.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002305 entry.contentType = EditorInfo.TYPE_CLASS_PHONE;
2306 return entry;
2307 }
2308
2309 /**
2310 * Create a new empty email entry
2311 */
2312 public static final EditEntry newEmailEntry(EditContactActivity activity,
2313 Uri uri, int type) {
2314 return newEmailEntry(activity, null, type, null, uri, 0);
2315 }
2316
2317 /**
2318 * Create a new email entry with the given data.
2319 */
2320 public static final EditEntry newEmailEntry(EditContactActivity activity,
2321 String label, int type, String data, Uri uri,
2322 long id) {
2323 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2324 entry.hint = activity.getString(R.string.ghostData_email);
Evan Millar7e4accf2009-06-08 10:43:26 -07002325 entry.column = Email.DATA;
2326 entry.mimetype = Email.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002327 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2328 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
2329 return entry;
2330 }
2331
2332 /**
2333 * Create a new empty postal address entry
2334 */
2335 public static final EditEntry newPostalEntry(EditContactActivity activity,
2336 Uri uri, int type) {
2337 return newPostalEntry(activity, null, type, null, uri, 0);
2338 }
2339
2340 /**
2341 * Create a new postal address entry with the given data.
2342 *
2343 * @param label label for the item, from the db not the display label
2344 * @param type the type of postal address
2345 * @param data the starting data for the entry, may be null
2346 * @param uri the uri for the entry if it already exists, may be null
2347 * @param id the id for the entry if it already exists, 0 it it doesn't
2348 * @return the new EditEntry
2349 */
2350 public static final EditEntry newPostalEntry(EditContactActivity activity,
2351 String label, int type, String data, Uri uri, long id) {
2352 EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
2353 entry.hint = activity.getString(R.string.ghostData_postal);
Evan Millar7e4accf2009-06-08 10:43:26 -07002354 entry.column = Postal.DATA;
2355 entry.mimetype = Postal.CONTENT_ITEM_TYPE;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002356 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2357 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
2358 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
2359 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
2360 entry.maxLines = 4;
2361 entry.lines = 2;
2362 return entry;
2363 }
2364
2365 /**
2366 * Create a new IM address entry
2367 */
2368 public static final EditEntry newImEntry(EditContactActivity activity,
2369 Uri uri, int type) {
2370 return newImEntry(activity, null, type, null, uri, 0);
2371 }
2372
2373 /**
2374 * Create a new IM address entry with the given data.
2375 *
2376 * @param label label for the item, from the db not the display label
2377 * @param protocol the type used
2378 * @param data the starting data for the entry, may be null
2379 * @param uri the uri for the entry if it already exists, may be null
2380 * @param id the id for the entry if it already exists, 0 it it doesn't
2381 * @return the new EditEntry
2382 */
2383 public static final EditEntry newImEntry(EditContactActivity activity,
2384 String label, int protocol, String data, Uri uri, long id) {
2385 EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
2386 entry.hint = activity.getString(R.string.ghostData_im);
Evan Millar7e4accf2009-06-08 10:43:26 -07002387 entry.column = Im.DATA;
2388 entry.mimetype = Im.CONTENT_ITEM_TYPE;
The Android Open Source Project928ccbd2009-03-05 14:34:37 -08002389 entry.contentType = EditorInfo.TYPE_CLASS_TEXT
2390 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002391 return entry;
2392 }
2393 }
2394
2395 public void afterTextChanged(Editable s) {
2396 // Someone edited a text field, so assume this contact is changed
2397 mContactChanged = true;
2398 }
2399
2400 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2401 // Do nothing; editing handled by afterTextChanged()
2402 }
2403
2404 public void onTextChanged(CharSequence s, int start, int before, int count) {
2405 // Do nothing; editing handled by afterTextChanged()
2406 }
Evan Millar7e4accf2009-06-08 10:43:26 -07002407
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08002408 public void onFocusChange(View v, boolean hasFocus) {
2409 // Because we're emulating a ListView, we need to setSelected() for
2410 // views as they are focused.
2411 v.setSelected(hasFocus);
2412 }
2413}