blob: ec365dc26017e37ee0795baa801bdf2615f05e2d [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 Millar54a5c9f2009-06-23 17:41:09 -070019import com.android.contacts.Collapser.Collapsible;
Evan Millar11d628c2009-09-02 08:55:01 -070020import com.android.contacts.model.ContactsSource;
21import com.android.contacts.model.Sources;
22import com.android.contacts.model.ContactsSource.DataKind;
Evan Millardf181202009-09-30 15:15:51 -070023import com.android.contacts.ui.EditContactActivity;
Jeff Sharkey49d17b32009-09-07 02:14:21 -070024import com.android.contacts.util.Constants;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070025import com.android.contacts.util.DataStatus;
Evan Millar11d628c2009-09-02 08:55:01 -070026import com.android.contacts.util.NotifyingAsyncQueryHandler;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080027import com.android.internal.telephony.ITelephony;
Evan Millar11d628c2009-09-02 08:55:01 -070028import com.android.internal.widget.ContactHeaderWidget;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070029import com.google.android.collect.Lists;
30import com.google.android.collect.Maps;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080031
Evan Millar11d628c2009-09-02 08:55:01 -070032import android.app.Activity;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080033import android.app.AlertDialog;
34import android.app.Dialog;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080035import android.content.ActivityNotFoundException;
36import android.content.ContentResolver;
37import android.content.ContentUris;
38import android.content.ContentValues;
39import android.content.Context;
40import android.content.DialogInterface;
Evan Millar5f4af702009-08-11 11:12:00 -070041import android.content.Entity;
Evan Millar11d628c2009-09-02 08:55:01 -070042import android.content.EntityIterator;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080043import android.content.Intent;
Evan Millar11d628c2009-09-02 08:55:01 -070044import android.content.Entity.NamedContentValues;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080045import android.content.res.Resources;
46import android.database.ContentObserver;
47import android.database.Cursor;
48import android.graphics.drawable.Drawable;
Evan Millar951fa9c2009-10-26 14:57:43 -070049import android.net.ParseException;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080050import android.net.Uri;
Evan Millar951fa9c2009-10-26 14:57:43 -070051import android.net.WebAddress;
Brad Fitzpatrick23bc9b72010-03-23 23:27:34 -070052import android.os.AsyncTask;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080053import android.os.Bundle;
54import android.os.Handler;
55import android.os.RemoteException;
56import android.os.ServiceManager;
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -070057import android.provider.ContactsContract;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070058import android.provider.ContactsContract.AggregationExceptions;
59import android.provider.ContactsContract.CommonDataKinds;
Evan Millar11d628c2009-09-02 08:55:01 -070060import android.provider.ContactsContract.Contacts;
Evan Millar66388be2009-05-28 15:41:07 -070061import android.provider.ContactsContract.Data;
Daniel Lehmann350ba6f2010-03-23 18:10:57 -070062import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov39466592009-07-27 11:23:51 -070063import android.provider.ContactsContract.RawContacts;
Fred Quintana8579e252009-12-07 14:50:12 -080064import android.provider.ContactsContract.RawContactsEntity;
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -070065import android.provider.ContactsContract.StatusUpdates;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070066import android.provider.ContactsContract.CommonDataKinds.Email;
67import android.provider.ContactsContract.CommonDataKinds.Im;
68import android.provider.ContactsContract.CommonDataKinds.Nickname;
69import android.provider.ContactsContract.CommonDataKinds.Note;
70import android.provider.ContactsContract.CommonDataKinds.Organization;
Evan Millar54a5c9f2009-06-23 17:41:09 -070071import android.provider.ContactsContract.CommonDataKinds.Phone;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070072import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
Evan Millar951fa9c2009-10-26 14:57:43 -070073import android.provider.ContactsContract.CommonDataKinds.Website;
Evan Millar54a5c9f2009-06-23 17:41:09 -070074import android.telephony.PhoneNumberUtils;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080075import android.text.TextUtils;
76import android.util.Log;
77import android.view.ContextMenu;
78import android.view.KeyEvent;
Evan Millar11d628c2009-09-02 08:55:01 -070079import android.view.LayoutInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080080import android.view.Menu;
Jeff Sharkey49d17b32009-09-07 02:14:21 -070081import android.view.MenuInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080082import android.view.MenuItem;
83import android.view.View;
84import android.view.ViewGroup;
Evan Millar11d628c2009-09-02 08:55:01 -070085import android.view.Window;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080086import android.view.ContextMenu.ContextMenuInfo;
87import android.widget.AdapterView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080088import android.widget.ImageView;
89import android.widget.ListView;
90import android.widget.TextView;
91import android.widget.Toast;
92
93import java.util.ArrayList;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070094import java.util.HashMap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080095
96/**
97 * Displays the details of a specific contact.
98 */
Evan Millar11d628c2009-09-02 08:55:01 -070099public class ViewContactActivity extends Activity
Evan Millar7911ff52009-07-21 15:55:18 -0700100 implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
Evan Millardf181202009-09-30 15:15:51 -0700101 AdapterView.OnItemClickListener, NotifyingAsyncQueryHandler.AsyncQueryListener {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800102 private static final String TAG = "ViewContact";
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800103
104 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800105
106 private static final int DIALOG_CONFIRM_DELETE = 1;
Megha Joshid5afd8a2009-09-22 09:38:41 -0700107 private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
108 private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
Megha Joshic508bd82009-10-01 17:31:49 -0700109 private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800110
Evan Millar8a79cee2009-08-19 17:20:49 -0700111 private static final int REQUEST_JOIN_CONTACT = 1;
Evan Millar9cd81242009-09-26 16:02:31 -0700112 private static final int REQUEST_EDIT_CONTACT = 2;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700113
Evan Millar8a79cee2009-08-19 17:20:49 -0700114 public static final int MENU_ITEM_MAKE_DEFAULT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800115
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700116 protected Uri mLookupUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800117 private ContentResolver mResolver;
118 private ViewAdapter mAdapter;
119 private int mNumPhoneNumbers = 0;
120
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700121 /**
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700122 * A list of distinct contact IDs included in the current contact.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700123 */
Evan Millar7911ff52009-07-21 15:55:18 -0700124 private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700125
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800126 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
127 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
128 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
129 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
130 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800131 /* package */ ArrayList<ViewEntry> mNicknameEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800132 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700133 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800134 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
135 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
136
137 private Cursor mCursor;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700138
Evan Millar11d628c2009-09-02 08:55:01 -0700139 protected ContactHeaderWidget mContactHeaderWidget;
140 private NotifyingAsyncQueryHandler mHandler;
141
142 protected LayoutInflater mInflater;
143
Megha Joshid5afd8a2009-09-22 09:38:41 -0700144 protected int mReadOnlySourcesCnt;
145 protected int mWritableSourcesCnt;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800146 protected boolean mAllRestricted;
147
Jeff Sharkey827762d2009-12-23 11:06:08 -0800148 protected Uri mPrimaryPhoneUri = null;
149
Megha Joshid5afd8a2009-09-22 09:38:41 -0700150 protected ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
Evan Millar11d628c2009-09-02 08:55:01 -0700151
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700152 private static final int TOKEN_ENTITIES = 0;
153 private static final int TOKEN_STATUSES = 1;
154
155 private boolean mHasEntities = false;
156 private boolean mHasStatuses = false;
157
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700158 private long mNameRawContactId = -1;
159 private int mDisplayNameSource = DisplayNameSources.UNDEFINED;
160
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700161 private ArrayList<Entity> mEntities = Lists.newArrayList();
162 private HashMap<Long, DataStatus> mStatuses = Maps.newHashMap();
Evan Millar11d628c2009-09-02 08:55:01 -0700163
Makoto Onuki7a707382009-11-19 13:35:55 +0900164 /**
165 * The view shown if the detail list is empty.
166 * We set this to the list view when first bind the adapter, so that it won't be shown while
167 * we're loading data.
168 */
169 private View mEmptyView;
170
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800171 private ContentObserver mObserver = new ContentObserver(new Handler()) {
172 @Override
173 public boolean deliverSelfNotifications() {
174 return true;
175 }
176
177 @Override
178 public void onChange(boolean selfChange) {
Evan Millar11d628c2009-09-02 08:55:01 -0700179 if (mCursor != null && !mCursor.isClosed()) {
180 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800181 }
182 }
183 };
184
185 public void onClick(DialogInterface dialog, int which) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700186 closeCursor();
Megha Joshic508bd82009-10-01 17:31:49 -0700187 getContentResolver().delete(mLookupUri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800188 finish();
189 }
190
Evan Millar7911ff52009-07-21 15:55:18 -0700191 private ListView mListView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800192 private boolean mShowSmsLinksForAllPhones;
193
194 @Override
195 protected void onCreate(Bundle icicle) {
196 super.onCreate(icicle);
197
Evan Millar11d628c2009-09-02 08:55:01 -0700198 final Intent intent = getIntent();
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700199 Uri data = intent.getData();
200 String authority = data.getAuthority();
201 if (ContactsContract.AUTHORITY.equals(authority)) {
202 mLookupUri = data;
203 } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
204 final long rawContactId = ContentUris.parseId(data);
205 mLookupUri = RawContacts.getContactLookupUri(getContentResolver(),
206 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700207
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700208 }
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700209 mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Evan Millar11d628c2009-09-02 08:55:01 -0700210
211 requestWindowFeature(Window.FEATURE_NO_TITLE);
212 setContentView(R.layout.contact_card_layout);
213
214 mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
215 mContactHeaderWidget.showStar(true);
Evan Millar11d628c2009-09-02 08:55:01 -0700216 mContactHeaderWidget.setExcludeMimes(new String[] {
217 Contacts.CONTENT_ITEM_TYPE
218 });
219
Evan Millar11d628c2009-09-02 08:55:01 -0700220 mHandler = new NotifyingAsyncQueryHandler(this, this);
221
Evan Millarcdeea942009-10-07 11:47:35 -0700222 mListView = (ListView) findViewById(R.id.contact_data);
Evan Millar7911ff52009-07-21 15:55:18 -0700223 mListView.setOnCreateContextMenuListener(this);
224 mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
225 mListView.setOnItemClickListener(this);
Makoto Onuki7a707382009-11-19 13:35:55 +0900226 // Don't set it to mListView yet. We do so later when we bind the adapter.
227 mEmptyView = findViewById(android.R.id.empty);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800228
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800229 mResolver = getContentResolver();
230
231 // Build the list of sections. The order they're added to mSections dictates the
232 // order they are displayed in the list.
233 mSections.add(mPhoneEntries);
234 mSections.add(mSmsEntries);
235 mSections.add(mEmailEntries);
236 mSections.add(mImEntries);
237 mSections.add(mPostalEntries);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800238 mSections.add(mNicknameEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800239 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700240 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800241 mSections.add(mOtherEntries);
242
243 //TODO Read this value from a preference
244 mShowSmsLinksForAllPhones = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700245 }
246
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800247 @Override
248 protected void onResume() {
249 super.onResume();
Evan Millar11d628c2009-09-02 08:55:01 -0700250 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800251 }
252
253 @Override
254 protected void onPause() {
255 super.onPause();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700256 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800257 }
258
259 @Override
260 protected void onDestroy() {
261 super.onDestroy();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700262 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800263 }
264
265 @Override
266 protected Dialog onCreateDialog(int id) {
267 switch (id) {
268 case DIALOG_CONFIRM_DELETE:
269 return new AlertDialog.Builder(this)
270 .setTitle(R.string.deleteConfirmation_title)
271 .setIcon(android.R.drawable.ic_dialog_alert)
272 .setMessage(R.string.deleteConfirmation)
273 .setNegativeButton(android.R.string.cancel, null)
274 .setPositiveButton(android.R.string.ok, this)
275 .setCancelable(false)
276 .create();
Megha Joshid5afd8a2009-09-22 09:38:41 -0700277 case DIALOG_CONFIRM_READONLY_DELETE:
278 return new AlertDialog.Builder(this)
279 .setTitle(R.string.deleteConfirmation_title)
280 .setIcon(android.R.drawable.ic_dialog_alert)
281 .setMessage(R.string.readOnlyContactDeleteConfirmation)
282 .setNegativeButton(android.R.string.cancel, null)
283 .setPositiveButton(android.R.string.ok, this)
284 .setCancelable(false)
285 .create();
286 case DIALOG_CONFIRM_MULTIPLE_DELETE:
287 return new AlertDialog.Builder(this)
288 .setTitle(R.string.deleteConfirmation_title)
289 .setIcon(android.R.drawable.ic_dialog_alert)
290 .setMessage(R.string.multipleContactDeleteConfirmation)
291 .setNegativeButton(android.R.string.cancel, null)
292 .setPositiveButton(android.R.string.ok, this)
293 .setCancelable(false)
294 .create();
Evan Millarcdeea942009-10-07 11:47:35 -0700295 case DIALOG_CONFIRM_READONLY_HIDE: {
Megha Joshic508bd82009-10-01 17:31:49 -0700296 return new AlertDialog.Builder(this)
297 .setTitle(R.string.deleteConfirmation_title)
298 .setIcon(android.R.drawable.ic_dialog_alert)
299 .setMessage(R.string.readOnlyContactWarning)
300 .setPositiveButton(android.R.string.ok, this)
Evan Millarcdeea942009-10-07 11:47:35 -0700301 .create();
Megha Joshic508bd82009-10-01 17:31:49 -0700302 }
303
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800304 }
305 return null;
306 }
307
Evan Millar11d628c2009-09-02 08:55:01 -0700308 /** {@inheritDoc} */
Brad Fitzpatrick23bc9b72010-03-23 23:27:34 -0700309 public void onQueryComplete(int token, Object cookie, final Cursor cursor) {
Fred Quintana8579e252009-12-07 14:50:12 -0800310 if (token == TOKEN_STATUSES) {
311 try {
312 // Read available social rows and consider binding
313 readStatuses(cursor);
314 } finally {
315 if (cursor != null) {
316 cursor.close();
317 }
318 }
Brad Fitzpatrick23bc9b72010-03-23 23:27:34 -0700319 considerBindData();
320 return;
Evan Millar11d628c2009-09-02 08:55:01 -0700321 }
Brad Fitzpatrick23bc9b72010-03-23 23:27:34 -0700322
Brad Fitzpatrick1b28a0a2010-03-24 10:53:51 -0700323 // One would think we could just iterate over the Cursor
324 // directly here, as the result set should be small, and we've
325 // already run the query in an AsyncTask, but a lot of ANRs
326 // were being reported in this code nonetheless. See bug
327 // 2539603 for details. The real bug which makes this result
328 // set huge and CPU-heavy may be elsewhere.
329 // TODO: if we keep this async, perhaps the entity iteration
330 // should also be original AsyncTask, rather than ping-ponging
331 // between threads like this.
Brad Fitzpatrick23bc9b72010-03-23 23:27:34 -0700332 final ArrayList<Entity> oldEntities = mEntities;
333 (new AsyncTask<Void, Void, ArrayList<Entity>>() {
334 @Override
335 protected ArrayList<Entity> doInBackground(Void... params) {
336 ArrayList<Entity> newEntities = Lists.newArrayList();
337 EntityIterator iterator = RawContacts.newEntityIterator(cursor);
338 try {
339 while (iterator.hasNext()) {
340 Entity entity = iterator.next();
341 newEntities.add(entity);
342 }
343 } catch (RemoteException e) {
344 Log.w(TAG, "Problem reading contact data: " + e.toString());
345 return null;
346 } finally {
347 iterator.close();
348 }
349 return newEntities;
350 }
351
352 @Override
353 protected void onPostExecute(ArrayList<Entity> newEntities) {
354 if (newEntities == null) {
355 // There was an error loading.
356 return;
357 }
358 synchronized (ViewContactActivity.this) {
359 if (mEntities != oldEntities) {
360 // Multiple async tasks were in flight and we
361 // lost the race.
362 return;
363 }
364 mEntities = newEntities;
365 mHasEntities = true;
366 }
367 considerBindData();
368 }
369 }).execute();
Evan Millar11d628c2009-09-02 08:55:01 -0700370 }
371
Evan Millar6a61a1a2009-09-29 14:00:43 -0700372 private long getRefreshedContactId() {
373 Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
374 if (freshContactUri != null) {
375 return ContentUris.parseId(freshContactUri);
376 }
377 return -1;
378 }
379
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700380 /**
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700381 * Read from the given {@link Cursor} and build a set of {@link DataStatus}
382 * objects to match any valid statuses found.
383 */
384 private synchronized void readStatuses(Cursor cursor) {
385 mStatuses.clear();
386
387 // Walk found statuses, creating internal row for each
388 while (cursor.moveToNext()) {
389 final DataStatus status = new DataStatus(cursor);
390 final long dataId = cursor.getLong(StatusQuery._ID);
391 mStatuses.put(dataId, status);
392 }
393
394 mHasStatuses = true;
395 }
396
397 private synchronized void startEntityQuery() {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700398 closeCursor();
399
Evan Millar6a61a1a2009-09-29 14:00:43 -0700400 Uri uri = null;
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700401 if (mLookupUri != null) {
402 mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
403 if (mLookupUri != null) {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700404 uri = Contacts.lookupContact(getContentResolver(), mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700405 }
406 }
407
Evan Millar6a61a1a2009-09-29 14:00:43 -0700408 if (uri == null) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700409
410 // TODO either figure out a way to prevent a flash of black background or
411 // use some other UI than a toast
412 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
413 Log.e(TAG, "invalid contact uri: " + mLookupUri);
414 finish();
415 return;
416 }
417
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700418 final Uri dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
419
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700420 // This cursor has two purposes:
421 // - Fetch NAME_RAW_CONTACT_ID and DISPLAY_NAME_SOURCE
422 // - Watcher for change events
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700423 mCursor = mResolver.query(dataUri,
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700424 new String[] { Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE },
425 null, null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700426
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700427 if (mCursor.moveToFirst()) {
428 mNameRawContactId =
429 mCursor.getLong(mCursor.getColumnIndex(Contacts.NAME_RAW_CONTACT_ID));
430 mDisplayNameSource =
431 mCursor.getInt(mCursor.getColumnIndex(Contacts.DISPLAY_NAME_SOURCE));
432 } else {
433 mNameRawContactId = -1;
434 mDisplayNameSource = DisplayNameSources.UNDEFINED;
435 }
436
437 mCursor.registerContentObserver(mObserver);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700438 final long contactId = ContentUris.parseId(uri);
439
440 // Clear flags and start queries to data and status
441 mHasEntities = false;
442 mHasStatuses = false;
443
Fred Quintana8579e252009-12-07 14:50:12 -0800444 mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700445 RawContacts.CONTACT_ID + "=" + contactId, null, null);
446 mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
447 StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
448 + " IS NOT NULL", null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700449
Evan Millar9b1a1242009-09-16 10:26:10 -0700450 mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700451 }
452
453 private void closeCursor() {
454 if (mCursor != null) {
455 mCursor.unregisterContentObserver(mObserver);
456 mCursor.close();
457 mCursor = null;
458 }
Evan Millar11d628c2009-09-02 08:55:01 -0700459 }
460
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700461 /**
462 * Consider binding views after any of several background queries has
463 * completed. We check internal flags and only bind when all data has
464 * arrived.
465 */
466 private void considerBindData() {
467 if (mHasEntities && mHasStatuses) {
468 bindData();
469 }
470 }
471
Evan Millar11d628c2009-09-02 08:55:01 -0700472 private void bindData() {
473
474 // Build up the contact entries
475 buildEntries();
476
477 // Collapse similar data items in select sections.
478 Collapser.collapseList(mPhoneEntries);
479 Collapser.collapseList(mSmsEntries);
480 Collapser.collapseList(mEmailEntries);
481 Collapser.collapseList(mPostalEntries);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700482 Collapser.collapseList(mImEntries);
Evan Millar11d628c2009-09-02 08:55:01 -0700483
484 if (mAdapter == null) {
485 mAdapter = new ViewAdapter(this, mSections);
486 mListView.setAdapter(mAdapter);
487 } else {
488 mAdapter.setSections(mSections, SHOW_SEPARATORS);
489 }
Makoto Onuki7a707382009-11-19 13:35:55 +0900490 mListView.setEmptyView(mEmptyView);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800491 }
492
493 @Override
494 public boolean onCreateOptionsMenu(Menu menu) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700495 super.onCreateOptionsMenu(menu);
496
497 final MenuInflater inflater = getMenuInflater();
498 inflater.inflate(R.menu.view, menu);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800499 return true;
500 }
501
502 @Override
503 public boolean onPrepareOptionsMenu(Menu menu) {
504 super.onPrepareOptionsMenu(menu);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700505
Evan Millar51514db2009-09-22 11:42:35 -0700506 // Only allow edit when we have at least one raw_contact id
507 final boolean hasRawContact = (mRawContactIds.size() > 0);
Evan Millar2e1b8af2009-09-24 09:39:21 -0700508 menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700509
Jeff Sharkeye0408762009-11-03 12:57:16 -0800510 // Only allow share when unrestricted contacts available
511 menu.findItem(R.id.menu_share).setEnabled(!mAllRestricted);
512
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800513 return true;
514 }
515
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800516 @Override
517 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
518 AdapterView.AdapterContextMenuInfo info;
519 try {
520 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
521 } catch (ClassCastException e) {
522 Log.e(TAG, "bad menuInfo", e);
523 return;
524 }
525
526 // This can be null sometimes, don't crash...
527 if (info == null) {
528 Log.e(TAG, "bad menuInfo");
529 return;
530 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700531
Evan Millar45e0ed32009-06-01 16:44:38 -0700532 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
Neel Parekha8fb68a2009-09-25 18:05:18 -0700533 menu.setHeaderTitle(R.string.contactOptionsTitle);
Evan Millar45e0ed32009-06-01 16:44:38 -0700534 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
535 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700536 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
537 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700538 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800539 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700540 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
541 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700542 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700543 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800544 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700545 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700546 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
547 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800548 }
549
550 @Override
551 public boolean onOptionsItemSelected(MenuItem item) {
552 switch (item.getItemId()) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700553 case R.id.menu_edit: {
Evan Millardf181202009-09-30 15:15:51 -0700554 Long rawContactIdToEdit = null;
555 if (mRawContactIds.size() > 0) {
556 rawContactIdToEdit = mRawContactIds.get(0);
557 } else {
558 // There is no rawContact to edit.
559 break;
Evan Millardb5d88c2009-08-28 09:31:57 -0700560 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700561 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
562 rawContactIdToEdit);
Evan Millar9cd81242009-09-26 16:02:31 -0700563 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
564 REQUEST_EDIT_CONTACT);
Evan Millar8a79cee2009-08-19 17:20:49 -0700565 break;
566 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700567 case R.id.menu_delete: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800568 // Get confirmation
Evan Millarcdeea942009-10-07 11:47:35 -0700569 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700570 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700571 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700572 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700573 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
574 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
575 } else {
576 showDialog(DIALOG_CONFIRM_DELETE);
577 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800578 return true;
579 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700580 case R.id.menu_join: {
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700581 showJoinAggregateActivity();
582 return true;
583 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700584 case R.id.menu_options: {
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700585 showOptionsActivity();
586 return true;
587 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700588 case R.id.menu_share: {
Jeff Sharkeye0408762009-11-03 12:57:16 -0800589 if (mAllRestricted) return false;
590
Jeff Sharkey93049312009-09-27 17:28:13 -0700591 // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
Daniel Lehmanncbf75952010-03-12 13:29:07 -0800592 final String lookupKey = Uri.encode(mLookupUri.getPathSegments().get(2));
Jeff Sharkey93049312009-09-27 17:28:13 -0700593 final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
594
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700595 final Intent intent = new Intent(Intent.ACTION_SEND);
Jeff Sharkey93049312009-09-27 17:28:13 -0700596 intent.setType(Contacts.CONTENT_VCARD_TYPE);
597 intent.putExtra(Intent.EXTRA_STREAM, shareUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700598
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700599 // Launch chooser to share contact via
600 final CharSequence chooseTitle = getText(R.string.share_via);
601 final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700602
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700603 try {
604 startActivity(chooseIntent);
605 } catch (ActivityNotFoundException ex) {
606 Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800607 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700608 return true;
609 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800610 }
611 return super.onOptionsItemSelected(item);
612 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700613
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 @Override
615 public boolean onContextItemSelected(MenuItem item) {
616 switch (item.getItemId()) {
617 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700618 if (makeItemDefault(item)) {
619 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800620 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700621 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800622 }
623 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700624
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800625 return super.onContextItemSelected(item);
626 }
627
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700628 private boolean makeItemDefault(MenuItem item) {
629 ViewEntry entry = getViewEntryForMenuItem(item);
630 if (entry == null) {
631 return false;
632 }
633
634 // Update the primary values in the data record.
Evan Millar14fecb62009-09-09 09:23:12 -0700635 ContentValues values = new ContentValues(1);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700636 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700637 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
638 values, null, null);
Evan Millar11d628c2009-09-02 08:55:01 -0700639 startEntityQuery();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700640 return true;
641 }
642
643 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700644 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700645 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700646 public void showJoinAggregateActivity() {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700647 long freshId = getRefreshedContactId();
648 if (freshId > 0) {
649 String displayName = null;
650 if (mCursor.moveToFirst()) {
651 displayName = mCursor.getString(0);
652 }
653 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
654 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
655 if (displayName != null) {
656 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
657 }
658 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikova5cfca32009-09-29 18:13:08 -0700659 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700660 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700661
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700662 @Override
663 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar2e1b8af2009-09-24 09:39:21 -0700664 if (requestCode == REQUEST_JOIN_CONTACT) {
665 if (resultCode == RESULT_OK && intent != null) {
666 final long contactId = ContentUris.parseId(intent.getData());
667 joinAggregate(contactId);
Evan Millar8a79cee2009-08-19 17:20:49 -0700668 }
Evan Millar9cd81242009-09-26 16:02:31 -0700669 } else if (requestCode == REQUEST_EDIT_CONTACT) {
Evan Millardf181202009-09-30 15:15:51 -0700670 if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
671 finish();
Jeff Hamilton5297c6a2009-10-01 02:22:33 -0700672 } else if (resultCode == Activity.RESULT_OK) {
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -0700673 mLookupUri = intent.getData();
674 if (mLookupUri == null) {
675 finish();
676 }
Evan Millardf181202009-09-30 15:15:51 -0700677 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700678 }
679 }
680
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700681 private void joinAggregate(final long contactId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700682 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700683 RawContacts.CONTACT_ID + "=" + contactId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700684
685 try {
686 while(c.moveToNext()) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700687 long rawContactId = c.getLong(0);
688 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700689 }
690 } finally {
691 c.close();
692 }
693
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700694 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
695 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700696 }
697
698 /**
699 * Given a contact ID sets an aggregation exception to either join the contact with the
700 * current aggregate or split off.
701 */
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700702 protected void setAggregationException(long rawContactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700703 ContentValues values = new ContentValues(3);
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700704 for (long aRawContactId : mRawContactIds) {
705 if (aRawContactId != rawContactId) {
706 values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
707 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
708 values.put(AggregationExceptions.TYPE, exceptionType);
709 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
710 }
711 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700712 }
713
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700714 private void showOptionsActivity() {
715 final Intent intent = new Intent(this, ContactOptionsActivity.class);
Evan Millar6a61a1a2009-09-29 14:00:43 -0700716 intent.setData(mLookupUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700717 startActivity(intent);
718 }
719
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700720 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
721 AdapterView.AdapterContextMenuInfo info;
722 try {
723 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
724 } catch (ClassCastException e) {
725 Log.e(TAG, "bad menuInfo", e);
726 return null;
727 }
728
729 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
730 }
731
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800732 @Override
733 public boolean onKeyDown(int keyCode, KeyEvent event) {
734 switch (keyCode) {
735 case KeyEvent.KEYCODE_CALL: {
736 try {
737 ITelephony phone = ITelephony.Stub.asInterface(
738 ServiceManager.checkService("phone"));
739 if (phone != null && !phone.isIdle()) {
740 // Skip out and let the key be handled at a higher level
741 break;
742 }
743 } catch (RemoteException re) {
744 // Fall through and try to call the contact
745 }
746
Evan Millar7911ff52009-07-21 15:55:18 -0700747 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800748 if (index != -1) {
749 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Bai Tao67205ed2010-03-16 08:09:11 +0800750 if (entry != null &&
751 entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
Evan Millar66388be2009-05-28 15:41:07 -0700752 startActivity(entry.intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800753 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800754 }
Jeff Sharkey827762d2009-12-23 11:06:08 -0800755 } else if (mPrimaryPhoneUri != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800756 // There isn't anything selected, call the default number
Jeff Sharkey827762d2009-12-23 11:06:08 -0800757 final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
758 mPrimaryPhoneUri);
759 startActivity(intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800760 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800761 }
Bai Tao67205ed2010-03-16 08:09:11 +0800762 return false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800763 }
764
765 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700766 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700767 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700768 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700769 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700770 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
771 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
772 } else {
773 showDialog(DIALOG_CONFIRM_DELETE);
774 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800775 return true;
776 }
777 }
778
779 return super.onKeyDown(keyCode, event);
780 }
781
Evan Millar7911ff52009-07-21 15:55:18 -0700782 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800783 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
784 if (entry != null) {
785 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700786 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800787 try {
788 startActivity(intent);
789 } catch (ActivityNotFoundException e) {
790 Log.e(TAG, "No activity found for intent: " + intent);
791 signalError();
792 }
793 } else {
794 signalError();
795 }
796 } else {
797 signalError();
798 }
799 }
800
801 /**
802 * Signal an error to the user via a beep, or some other method.
803 */
804 private void signalError() {
805 //TODO: implement this when we have the sonification APIs
806 }
807
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800808 /**
809 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700810 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800811 * @param personCursor the URI for the contact being displayed
812 */
Evan Millar11d628c2009-09-02 08:55:01 -0700813 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800814 // Clear out the old entries
815 final int numSections = mSections.size();
816 for (int i = 0; i < numSections; i++) {
817 mSections.get(i).clear();
818 }
819
Evan Millar7911ff52009-07-21 15:55:18 -0700820 mRawContactIds.clear();
Jeff Sharkeye0408762009-11-03 12:57:16 -0800821
Megha Joshid5afd8a2009-09-22 09:38:41 -0700822 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700823 mWritableSourcesCnt = 0;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800824 mAllRestricted = true;
Jeff Sharkey827762d2009-12-23 11:06:08 -0800825 mPrimaryPhoneUri = null;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800826
Megha Joshid5afd8a2009-09-22 09:38:41 -0700827 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700828
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700829 final Context context = this;
830 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700831
Evan Millar66388be2009-05-28 15:41:07 -0700832 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700833 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700834 for (Entity entity: mEntities) {
835 final ContentValues entValues = entity.getEntityValues();
836 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700837 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700838
Jeff Sharkeye0408762009-11-03 12:57:16 -0800839 // Mark when this contact has any unrestricted components
840 final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
841 if (!isRestricted) mAllRestricted = false;
842
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700843 if (!mRawContactIds.contains(rawContactId)) {
844 mRawContactIds.add(rawContactId);
845 }
Megha Joshic508bd82009-10-01 17:31:49 -0700846 ContactsSource contactsSource = sources.getInflatedSource(accountType,
847 ContactsSource.LEVEL_SUMMARY);
848 if (contactsSource != null && contactsSource.readOnly) {
849 mReadOnlySourcesCnt += 1;
850 } else {
851 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700852 mWritableRawContactIds.add(rawContactId);
853 }
Megha Joshic508bd82009-10-01 17:31:49 -0700854
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700855
Evan Millar11d628c2009-09-02 08:55:01 -0700856 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700857 final ContentValues entryValues = subValue.values;
858 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
859
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700860 final long dataId = entryValues.getAsLong(Data._ID);
861 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
862 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700863
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700864 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700865 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700866 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800867
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700868 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
869 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800870
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700871 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700872 final boolean isSuperPrimary = entryValues.getAsInteger(
873 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700874
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700875 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700876 // Build phone entries
877 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700878
Evan Millarff04a272009-09-26 15:23:37 -0700879 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700880 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700881 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700882 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700883
Jeff Sharkey827762d2009-12-23 11:06:08 -0800884 // Remember super-primary phone
885 if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
886
Evan Millarff04a272009-09-26 15:23:37 -0700887 entry.isPrimary = isSuperPrimary;
888 mPhoneEntries.add(entry);
889
890 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
891 || mShowSmsLinksForAllPhones) {
892 // Add an SMS entry
893 if (kind.iconAltRes > 0) {
894 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700895 }
Evan Millarff04a272009-09-26 15:23:37 -0700896 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700897 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700898 // Build email entries
899 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700900 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700901 entry.isPrimary = isSuperPrimary;
902 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700903
904 // When Email rows have status, create additional Im row
905 final DataStatus status = mStatuses.get(entry.id);
906 if (status != null) {
907 final String imMime = Im.CONTENT_ITEM_TYPE;
908 final DataKind imKind = sources.getKindOrFallback(accountType,
909 imMime, this, ContactsSource.LEVEL_MIMETYPES);
910 final ViewEntry imEntry = ViewEntry.fromValues(context,
911 imMime, imKind, rawContactId, dataId, entryValues);
912 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
913 imEntry.applyStatus(status, false);
914 mImEntries.add(imEntry);
915 }
916 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700917 // Build postal entries
918 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700919 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700920 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700921 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
922 // Build IM entries
923 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700924 if (TextUtils.isEmpty(entry.label)) {
925 entry.label = getString(R.string.chat).toLowerCase();
926 }
Evan Millar11d628c2009-09-02 08:55:01 -0700927
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700928 // Apply presence and status details when available
929 final DataStatus status = mStatuses.get(entry.id);
930 if (status != null) {
931 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700932 }
Evan Millarff04a272009-09-26 15:23:37 -0700933 mImEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800934 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
935 (hasData || !TextUtils.isEmpty(entry.label))) {
936 // Build organization entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700937 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
938
939 final boolean duplicatesTitle =
940 isNameRawContact
941 && mDisplayNameSource == DisplayNameSources.ORGANIZATION
Daniel Lehmann15a194a2010-03-24 13:45:05 -0700942 && (!hasData || TextUtils.isEmpty(entry.label));
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700943
944 if (!duplicatesTitle) {
945 entry.uri = null;
Daniel Lehmann15a194a2010-03-24 13:45:05 -0700946
947 if (TextUtils.isEmpty(entry.label)) {
948 entry.label = entry.data;
949 entry.data = "";
950 }
951
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700952 mOrganizationEntries.add(entry);
953 }
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800954 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
955 // Build nickname entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700956 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
957
958 final boolean duplicatesTitle =
959 isNameRawContact
960 && mDisplayNameSource == DisplayNameSources.NICKNAME;
961
962 if (!duplicatesTitle) {
963 entry.uri = null;
964 mNicknameEntries.add(entry);
965 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700966 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700967 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700968 entry.uri = null;
Daniel Lehmann0e322012010-02-25 16:09:46 -0800969 entry.maxLines = 100;
Evan Millar11d628c2009-09-02 08:55:01 -0700970 mOtherEntries.add(entry);
Evan Millar951fa9c2009-10-26 14:57:43 -0700971 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
972 // Build note entries
973 entry.uri = null;
974 entry.maxLines = 10;
975 try {
976 WebAddress webAddress = new WebAddress(entry.data);
Makoto Onuki7a707382009-11-19 13:35:55 +0900977 entry.intent = new Intent(Intent.ACTION_VIEW,
Evan Millar951fa9c2009-10-26 14:57:43 -0700978 Uri.parse(webAddress.toString()));
979 } catch (ParseException e) {
980 Log.e(TAG, "Couldn't parse website: " + entry.data);
981 }
982 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700983 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700984 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700985 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700986
987 // Use social summary when requested by external source
988 final DataStatus status = mStatuses.get(entry.id);
989 final boolean hasSocial = kind.actionBodySocial && status != null;
990 if (hasSocial) {
991 entry.applyStatus(status, true);
992 }
993
994 if (hasSocial || hasData) {
995 mOtherEntries.add(entry);
996 }
Evan Millar11d628c2009-09-02 08:55:01 -0700997 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700998 }
Evan Millar66388be2009-05-28 15:41:07 -0700999 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001000 }
1001 }
1002
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001003 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
1004 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -07001005 if (kind.actionHeader == null) {
1006 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -07001007 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001008 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -07001009 if (actionHeader == null) {
1010 return null;
1011 }
1012 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
1013 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -07001014
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001015 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -07001016 if (kind.actionBody == null) {
1017 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001018 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001019 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -07001020 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001021 }
Evan Millar5c22c3b2009-05-29 11:37:54 -07001022
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001023 /**
1024 * A basic structure with the data for a contact entry in the list.
1025 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001026 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
1027 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -07001028 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001029 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -07001030 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -07001031 public int secondaryActionIcon = -1;
1032 public Intent intent;
1033 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001034 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001035 public ArrayList<Long> ids = new ArrayList<Long>();
1036 public int collapseCount = 0;
1037
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001038 public int presence = -1;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001039
1040 public CharSequence footerLine = null;
1041
1042 private ViewEntry() {
1043 }
1044
1045 /**
1046 * Build new {@link ViewEntry} and populate from the given values.
1047 */
1048 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
1049 long rawContactId, long dataId, ContentValues values) {
1050 final ViewEntry entry = new ViewEntry();
1051 entry.context = context;
1052 entry.contactId = rawContactId;
1053 entry.id = dataId;
1054 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
1055 entry.mimetype = mimeType;
1056 entry.label = buildActionString(kind, values, false, context);
1057 entry.data = buildDataString(kind, values, context);
1058
1059 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
1060 entry.type = values.getAsInteger(kind.typeColumn);
1061 }
1062 if (kind.iconRes > 0) {
1063 entry.resPackageName = kind.resPackageName;
1064 entry.actionIcon = kind.iconRes;
1065 }
1066
1067 return entry;
1068 }
1069
1070 /**
1071 * Apply given {@link DataStatus} values over this {@link ViewEntry}
1072 *
1073 * @param fillData When true, the given status replaces {@link #data}
1074 * and {@link #footerLine}. Otherwise only {@link #presence}
1075 * is updated.
1076 */
1077 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
1078 presence = status.getPresence();
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001079 if (fillData && status.isValid()) {
1080 this.data = status.getStatus().toString();
1081 this.footerLine = status.getTimestampLabel(context);
1082 }
1083
1084 return this;
1085 }
1086
Evan Millar54a5c9f2009-06-23 17:41:09 -07001087 public boolean collapseWith(ViewEntry entry) {
1088 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -07001089 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001090 return false;
1091 }
1092
1093 // Choose the label associated with the highest type precedence.
1094 if (TypePrecedence.getTypePrecedence(mimetype, type)
1095 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
1096 type = entry.type;
1097 label = entry.label;
1098 }
1099
1100 // Choose the max of the maxLines and maxLabelLines values.
1101 maxLines = Math.max(maxLines, entry.maxLines);
1102 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1103
1104 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001105 if (StatusUpdates.getPresencePrecedence(presence)
1106 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1107 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001108 }
1109
1110 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001111 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001112
1113 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1114
1115 // Keep track of all the ids that have been collapsed with this one.
1116 ids.add(entry.id);
1117 collapseCount++;
1118 return true;
1119 }
1120
Evan Millaradb0f8c2009-09-28 17:20:50 -07001121 public boolean shouldCollapseWith(ViewEntry entry) {
1122 if (entry == null) {
1123 return false;
1124 }
1125
Daniel Lehmannd8b0a052010-03-25 17:41:00 -07001126 if (!ContactsUtils.shouldCollapse(context, mimetype, data, entry.mimetype,
1127 entry.data)) {
Makoto Onukic710b0e2009-10-13 15:43:24 -07001128 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001129 }
1130
Makoto Onukic710b0e2009-10-13 15:43:24 -07001131 if (!TextUtils.equals(mimetype, entry.mimetype)
1132 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1133 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001134 || actionIcon != entry.actionIcon) {
1135 return false;
1136 }
1137
1138 return true;
1139 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001140 }
1141
Evan Millar15e514d2009-08-04 10:14:57 -07001142 /** Cache of the children views of a row */
1143 static class ViewCache {
1144 public TextView label;
1145 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001146 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001147 public ImageView actionIcon;
1148 public ImageView presenceIcon;
1149 public ImageView primaryIcon;
1150 public ImageView secondaryActionButton;
1151 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001152
Evan Millar15e514d2009-08-04 10:14:57 -07001153 // Need to keep track of this too
1154 ViewEntry entry;
1155 }
1156
1157 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1158 implements View.OnClickListener {
1159
Evan Millar5c22c3b2009-05-29 11:37:54 -07001160
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001161 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1162 super(context, sections, SHOW_SEPARATORS);
1163 }
1164
Evan Millar15e514d2009-08-04 10:14:57 -07001165 public void onClick(View v) {
1166 Intent intent = (Intent) v.getTag();
1167 startActivity(intent);
1168 }
1169
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001170 @Override
1171 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001172 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001173 View v;
1174
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001175 ViewCache views;
1176
1177 // Check to see if we can reuse convertView
1178 if (convertView != null) {
1179 v = convertView;
1180 views = (ViewCache) v.getTag();
1181 } else {
1182 // Create a new view if needed
1183 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1184
1185 // Cache the children
1186 views = new ViewCache();
1187 views.label = (TextView) v.findViewById(android.R.id.text1);
1188 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001189 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001190 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1191 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1192 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1193 views.secondaryActionButton = (ImageView) v.findViewById(
1194 R.id.secondary_action_button);
1195 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001196 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001197 v.setTag(views);
1198 }
1199
1200 // Update the entry in the view cache
1201 views.entry = entry;
1202
1203 // Bind the data to the view
1204 bindView(v, entry);
1205 return v;
1206 }
1207
1208 @Override
1209 protected View newView(int position, ViewGroup parent) {
1210 // getView() handles this
1211 throw new UnsupportedOperationException();
1212 }
1213
1214 @Override
1215 protected void bindView(View view, ViewEntry entry) {
1216 final Resources resources = mContext.getResources();
1217 ViewCache views = (ViewCache) view.getTag();
1218
1219 // Set the label
1220 TextView label = views.label;
1221 setMaxLines(label, entry.maxLabelLines);
1222 label.setText(entry.label);
1223
1224 // Set the data
1225 TextView data = views.data;
1226 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001227 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001228 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001229 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1230 } else {
1231 data.setText(entry.data);
1232 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001233 setMaxLines(data, entry.maxLines);
1234 }
1235
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001236 // Set the footer
1237 if (!TextUtils.isEmpty(entry.footerLine)) {
1238 views.footer.setText(entry.footerLine);
1239 views.footer.setVisibility(View.VISIBLE);
1240 } else {
1241 views.footer.setVisibility(View.GONE);
1242 }
1243
Evan Millar15e514d2009-08-04 10:14:57 -07001244 // Set the primary icon
1245 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1246
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001247 // Set the action icon
1248 ImageView action = views.actionIcon;
1249 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001250 Drawable actionIcon;
1251 if (entry.resPackageName != null) {
1252 // Load external resources through PackageManager
1253 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1254 entry.actionIcon, null);
1255 } else {
1256 actionIcon = resources.getDrawable(entry.actionIcon);
1257 }
1258 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001259 action.setVisibility(View.VISIBLE);
1260 } else {
1261 // Things should still line up as if there was an icon, so make it invisible
1262 action.setVisibility(View.INVISIBLE);
1263 }
1264
1265 // Set the presence icon
Bai Tao107736c2010-03-12 08:00:42 +08001266 Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
1267 mContext, entry.presence);
Evan Millar15e514d2009-08-04 10:14:57 -07001268 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001269 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001270 presenceIconView.setImageDrawable(presenceIcon);
1271 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001272 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001273 presenceIconView.setVisibility(View.GONE);
1274 }
1275
1276 // Set the secondary action button
1277 ImageView secondaryActionView = views.secondaryActionButton;
1278 Drawable secondaryActionIcon = null;
1279 if (entry.secondaryActionIcon != -1) {
1280 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1281 }
1282 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1283 secondaryActionView.setImageDrawable(secondaryActionIcon);
1284 secondaryActionView.setTag(entry.secondaryIntent);
1285 secondaryActionView.setVisibility(View.VISIBLE);
1286 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1287 } else {
1288 secondaryActionView.setVisibility(View.GONE);
1289 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001290 }
1291 }
1292
1293 private void setMaxLines(TextView textView, int maxLines) {
1294 if (maxLines == 1) {
1295 textView.setSingleLine(true);
1296 textView.setEllipsize(TextUtils.TruncateAt.END);
1297 } else {
1298 textView.setSingleLine(false);
1299 textView.setMaxLines(maxLines);
1300 textView.setEllipsize(null);
1301 }
1302 }
1303 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001304
1305 private interface StatusQuery {
1306 final String[] PROJECTION = new String[] {
1307 Data._ID,
1308 Data.STATUS,
1309 Data.STATUS_RES_PACKAGE,
1310 Data.STATUS_ICON,
1311 Data.STATUS_LABEL,
1312 Data.STATUS_TIMESTAMP,
1313 Data.PRESENCE,
1314 };
1315
1316 final int _ID = 0;
1317 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001318
1319 @Override
1320 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1321 boolean globalSearch) {
1322 if (globalSearch) {
1323 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1324 } else {
1325 ContactsSearchManager.startSearch(this, initialQuery);
1326 }
1327 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001328}