blob: 1f167157ecc1a91b87856f5a3d104196e1a0ae4a [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
323 // We also have to iterate over the Cursor in the background,
324 // as iterating over the Cursor can ANR on large result sets,
325 // especially as our ContentProvider is cross-process.
326 final ArrayList<Entity> oldEntities = mEntities;
327 (new AsyncTask<Void, Void, ArrayList<Entity>>() {
328 @Override
329 protected ArrayList<Entity> doInBackground(Void... params) {
330 ArrayList<Entity> newEntities = Lists.newArrayList();
331 EntityIterator iterator = RawContacts.newEntityIterator(cursor);
332 try {
333 while (iterator.hasNext()) {
334 Entity entity = iterator.next();
335 newEntities.add(entity);
336 }
337 } catch (RemoteException e) {
338 Log.w(TAG, "Problem reading contact data: " + e.toString());
339 return null;
340 } finally {
341 iterator.close();
342 }
343 return newEntities;
344 }
345
346 @Override
347 protected void onPostExecute(ArrayList<Entity> newEntities) {
348 if (newEntities == null) {
349 // There was an error loading.
350 return;
351 }
352 synchronized (ViewContactActivity.this) {
353 if (mEntities != oldEntities) {
354 // Multiple async tasks were in flight and we
355 // lost the race.
356 return;
357 }
358 mEntities = newEntities;
359 mHasEntities = true;
360 }
361 considerBindData();
362 }
363 }).execute();
Evan Millar11d628c2009-09-02 08:55:01 -0700364 }
365
Evan Millar6a61a1a2009-09-29 14:00:43 -0700366 private long getRefreshedContactId() {
367 Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
368 if (freshContactUri != null) {
369 return ContentUris.parseId(freshContactUri);
370 }
371 return -1;
372 }
373
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700374 /**
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700375 * Read from the given {@link Cursor} and build a set of {@link DataStatus}
376 * objects to match any valid statuses found.
377 */
378 private synchronized void readStatuses(Cursor cursor) {
379 mStatuses.clear();
380
381 // Walk found statuses, creating internal row for each
382 while (cursor.moveToNext()) {
383 final DataStatus status = new DataStatus(cursor);
384 final long dataId = cursor.getLong(StatusQuery._ID);
385 mStatuses.put(dataId, status);
386 }
387
388 mHasStatuses = true;
389 }
390
391 private synchronized void startEntityQuery() {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700392 closeCursor();
393
Evan Millar6a61a1a2009-09-29 14:00:43 -0700394 Uri uri = null;
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700395 if (mLookupUri != null) {
396 mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
397 if (mLookupUri != null) {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700398 uri = Contacts.lookupContact(getContentResolver(), mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700399 }
400 }
401
Evan Millar6a61a1a2009-09-29 14:00:43 -0700402 if (uri == null) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700403
404 // TODO either figure out a way to prevent a flash of black background or
405 // use some other UI than a toast
406 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
407 Log.e(TAG, "invalid contact uri: " + mLookupUri);
408 finish();
409 return;
410 }
411
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700412 final Uri dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
413
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700414 // This cursor has two purposes:
415 // - Fetch NAME_RAW_CONTACT_ID and DISPLAY_NAME_SOURCE
416 // - Watcher for change events
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700417 mCursor = mResolver.query(dataUri,
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700418 new String[] { Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE },
419 null, null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700420
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700421 if (mCursor.moveToFirst()) {
422 mNameRawContactId =
423 mCursor.getLong(mCursor.getColumnIndex(Contacts.NAME_RAW_CONTACT_ID));
424 mDisplayNameSource =
425 mCursor.getInt(mCursor.getColumnIndex(Contacts.DISPLAY_NAME_SOURCE));
426 } else {
427 mNameRawContactId = -1;
428 mDisplayNameSource = DisplayNameSources.UNDEFINED;
429 }
430
431 mCursor.registerContentObserver(mObserver);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700432 final long contactId = ContentUris.parseId(uri);
433
434 // Clear flags and start queries to data and status
435 mHasEntities = false;
436 mHasStatuses = false;
437
Fred Quintana8579e252009-12-07 14:50:12 -0800438 mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700439 RawContacts.CONTACT_ID + "=" + contactId, null, null);
440 mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
441 StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
442 + " IS NOT NULL", null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700443
Evan Millar9b1a1242009-09-16 10:26:10 -0700444 mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700445 }
446
447 private void closeCursor() {
448 if (mCursor != null) {
449 mCursor.unregisterContentObserver(mObserver);
450 mCursor.close();
451 mCursor = null;
452 }
Evan Millar11d628c2009-09-02 08:55:01 -0700453 }
454
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700455 /**
456 * Consider binding views after any of several background queries has
457 * completed. We check internal flags and only bind when all data has
458 * arrived.
459 */
460 private void considerBindData() {
461 if (mHasEntities && mHasStatuses) {
462 bindData();
463 }
464 }
465
Evan Millar11d628c2009-09-02 08:55:01 -0700466 private void bindData() {
467
468 // Build up the contact entries
469 buildEntries();
470
471 // Collapse similar data items in select sections.
472 Collapser.collapseList(mPhoneEntries);
473 Collapser.collapseList(mSmsEntries);
474 Collapser.collapseList(mEmailEntries);
475 Collapser.collapseList(mPostalEntries);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700476 Collapser.collapseList(mImEntries);
Evan Millar11d628c2009-09-02 08:55:01 -0700477
478 if (mAdapter == null) {
479 mAdapter = new ViewAdapter(this, mSections);
480 mListView.setAdapter(mAdapter);
481 } else {
482 mAdapter.setSections(mSections, SHOW_SEPARATORS);
483 }
Makoto Onuki7a707382009-11-19 13:35:55 +0900484 mListView.setEmptyView(mEmptyView);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800485 }
486
487 @Override
488 public boolean onCreateOptionsMenu(Menu menu) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700489 super.onCreateOptionsMenu(menu);
490
491 final MenuInflater inflater = getMenuInflater();
492 inflater.inflate(R.menu.view, menu);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800493 return true;
494 }
495
496 @Override
497 public boolean onPrepareOptionsMenu(Menu menu) {
498 super.onPrepareOptionsMenu(menu);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700499
Evan Millar51514db2009-09-22 11:42:35 -0700500 // Only allow edit when we have at least one raw_contact id
501 final boolean hasRawContact = (mRawContactIds.size() > 0);
Evan Millar2e1b8af2009-09-24 09:39:21 -0700502 menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700503
Jeff Sharkeye0408762009-11-03 12:57:16 -0800504 // Only allow share when unrestricted contacts available
505 menu.findItem(R.id.menu_share).setEnabled(!mAllRestricted);
506
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800507 return true;
508 }
509
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800510 @Override
511 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
512 AdapterView.AdapterContextMenuInfo info;
513 try {
514 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
515 } catch (ClassCastException e) {
516 Log.e(TAG, "bad menuInfo", e);
517 return;
518 }
519
520 // This can be null sometimes, don't crash...
521 if (info == null) {
522 Log.e(TAG, "bad menuInfo");
523 return;
524 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700525
Evan Millar45e0ed32009-06-01 16:44:38 -0700526 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
Neel Parekha8fb68a2009-09-25 18:05:18 -0700527 menu.setHeaderTitle(R.string.contactOptionsTitle);
Evan Millar45e0ed32009-06-01 16:44:38 -0700528 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
529 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700530 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
531 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700532 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800533 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700534 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
535 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700536 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700537 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800538 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700539 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700540 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
541 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800542 }
543
544 @Override
545 public boolean onOptionsItemSelected(MenuItem item) {
546 switch (item.getItemId()) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700547 case R.id.menu_edit: {
Evan Millardf181202009-09-30 15:15:51 -0700548 Long rawContactIdToEdit = null;
549 if (mRawContactIds.size() > 0) {
550 rawContactIdToEdit = mRawContactIds.get(0);
551 } else {
552 // There is no rawContact to edit.
553 break;
Evan Millardb5d88c2009-08-28 09:31:57 -0700554 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700555 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
556 rawContactIdToEdit);
Evan Millar9cd81242009-09-26 16:02:31 -0700557 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
558 REQUEST_EDIT_CONTACT);
Evan Millar8a79cee2009-08-19 17:20:49 -0700559 break;
560 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700561 case R.id.menu_delete: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800562 // Get confirmation
Evan Millarcdeea942009-10-07 11:47:35 -0700563 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700564 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700565 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700566 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700567 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
568 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
569 } else {
570 showDialog(DIALOG_CONFIRM_DELETE);
571 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800572 return true;
573 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700574 case R.id.menu_join: {
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700575 showJoinAggregateActivity();
576 return true;
577 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700578 case R.id.menu_options: {
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700579 showOptionsActivity();
580 return true;
581 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700582 case R.id.menu_share: {
Jeff Sharkeye0408762009-11-03 12:57:16 -0800583 if (mAllRestricted) return false;
584
Jeff Sharkey93049312009-09-27 17:28:13 -0700585 // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
Daniel Lehmanncbf75952010-03-12 13:29:07 -0800586 final String lookupKey = Uri.encode(mLookupUri.getPathSegments().get(2));
Jeff Sharkey93049312009-09-27 17:28:13 -0700587 final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
588
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700589 final Intent intent = new Intent(Intent.ACTION_SEND);
Jeff Sharkey93049312009-09-27 17:28:13 -0700590 intent.setType(Contacts.CONTENT_VCARD_TYPE);
591 intent.putExtra(Intent.EXTRA_STREAM, shareUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700592
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700593 // Launch chooser to share contact via
594 final CharSequence chooseTitle = getText(R.string.share_via);
595 final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700596
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700597 try {
598 startActivity(chooseIntent);
599 } catch (ActivityNotFoundException ex) {
600 Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800601 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700602 return true;
603 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800604 }
605 return super.onOptionsItemSelected(item);
606 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700607
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800608 @Override
609 public boolean onContextItemSelected(MenuItem item) {
610 switch (item.getItemId()) {
611 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700612 if (makeItemDefault(item)) {
613 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700615 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800616 }
617 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700618
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800619 return super.onContextItemSelected(item);
620 }
621
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700622 private boolean makeItemDefault(MenuItem item) {
623 ViewEntry entry = getViewEntryForMenuItem(item);
624 if (entry == null) {
625 return false;
626 }
627
628 // Update the primary values in the data record.
Evan Millar14fecb62009-09-09 09:23:12 -0700629 ContentValues values = new ContentValues(1);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700630 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700631 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
632 values, null, null);
Evan Millar11d628c2009-09-02 08:55:01 -0700633 startEntityQuery();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700634 return true;
635 }
636
637 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700638 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700639 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700640 public void showJoinAggregateActivity() {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700641 long freshId = getRefreshedContactId();
642 if (freshId > 0) {
643 String displayName = null;
644 if (mCursor.moveToFirst()) {
645 displayName = mCursor.getString(0);
646 }
647 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
648 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
649 if (displayName != null) {
650 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
651 }
652 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikova5cfca32009-09-29 18:13:08 -0700653 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700654 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700655
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700656 @Override
657 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar2e1b8af2009-09-24 09:39:21 -0700658 if (requestCode == REQUEST_JOIN_CONTACT) {
659 if (resultCode == RESULT_OK && intent != null) {
660 final long contactId = ContentUris.parseId(intent.getData());
661 joinAggregate(contactId);
Evan Millar8a79cee2009-08-19 17:20:49 -0700662 }
Evan Millar9cd81242009-09-26 16:02:31 -0700663 } else if (requestCode == REQUEST_EDIT_CONTACT) {
Evan Millardf181202009-09-30 15:15:51 -0700664 if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
665 finish();
Jeff Hamilton5297c6a2009-10-01 02:22:33 -0700666 } else if (resultCode == Activity.RESULT_OK) {
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -0700667 mLookupUri = intent.getData();
668 if (mLookupUri == null) {
669 finish();
670 }
Evan Millardf181202009-09-30 15:15:51 -0700671 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700672 }
673 }
674
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700675 private void joinAggregate(final long contactId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700676 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700677 RawContacts.CONTACT_ID + "=" + contactId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700678
679 try {
680 while(c.moveToNext()) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700681 long rawContactId = c.getLong(0);
682 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700683 }
684 } finally {
685 c.close();
686 }
687
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700688 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
689 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700690 }
691
692 /**
693 * Given a contact ID sets an aggregation exception to either join the contact with the
694 * current aggregate or split off.
695 */
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700696 protected void setAggregationException(long rawContactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700697 ContentValues values = new ContentValues(3);
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700698 for (long aRawContactId : mRawContactIds) {
699 if (aRawContactId != rawContactId) {
700 values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
701 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
702 values.put(AggregationExceptions.TYPE, exceptionType);
703 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
704 }
705 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700706 }
707
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700708 private void showOptionsActivity() {
709 final Intent intent = new Intent(this, ContactOptionsActivity.class);
Evan Millar6a61a1a2009-09-29 14:00:43 -0700710 intent.setData(mLookupUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700711 startActivity(intent);
712 }
713
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700714 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
715 AdapterView.AdapterContextMenuInfo info;
716 try {
717 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
718 } catch (ClassCastException e) {
719 Log.e(TAG, "bad menuInfo", e);
720 return null;
721 }
722
723 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
724 }
725
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800726 @Override
727 public boolean onKeyDown(int keyCode, KeyEvent event) {
728 switch (keyCode) {
729 case KeyEvent.KEYCODE_CALL: {
730 try {
731 ITelephony phone = ITelephony.Stub.asInterface(
732 ServiceManager.checkService("phone"));
733 if (phone != null && !phone.isIdle()) {
734 // Skip out and let the key be handled at a higher level
735 break;
736 }
737 } catch (RemoteException re) {
738 // Fall through and try to call the contact
739 }
740
Evan Millar7911ff52009-07-21 15:55:18 -0700741 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800742 if (index != -1) {
743 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Bai Tao67205ed2010-03-16 08:09:11 +0800744 if (entry != null &&
745 entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
Evan Millar66388be2009-05-28 15:41:07 -0700746 startActivity(entry.intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800747 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800748 }
Jeff Sharkey827762d2009-12-23 11:06:08 -0800749 } else if (mPrimaryPhoneUri != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800750 // There isn't anything selected, call the default number
Jeff Sharkey827762d2009-12-23 11:06:08 -0800751 final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
752 mPrimaryPhoneUri);
753 startActivity(intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800754 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800755 }
Bai Tao67205ed2010-03-16 08:09:11 +0800756 return false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800757 }
758
759 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700760 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700761 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700762 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700763 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700764 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
765 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
766 } else {
767 showDialog(DIALOG_CONFIRM_DELETE);
768 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800769 return true;
770 }
771 }
772
773 return super.onKeyDown(keyCode, event);
774 }
775
Evan Millar7911ff52009-07-21 15:55:18 -0700776 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800777 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
778 if (entry != null) {
779 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700780 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800781 try {
782 startActivity(intent);
783 } catch (ActivityNotFoundException e) {
784 Log.e(TAG, "No activity found for intent: " + intent);
785 signalError();
786 }
787 } else {
788 signalError();
789 }
790 } else {
791 signalError();
792 }
793 }
794
795 /**
796 * Signal an error to the user via a beep, or some other method.
797 */
798 private void signalError() {
799 //TODO: implement this when we have the sonification APIs
800 }
801
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800802 /**
803 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700804 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800805 * @param personCursor the URI for the contact being displayed
806 */
Evan Millar11d628c2009-09-02 08:55:01 -0700807 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800808 // Clear out the old entries
809 final int numSections = mSections.size();
810 for (int i = 0; i < numSections; i++) {
811 mSections.get(i).clear();
812 }
813
Evan Millar7911ff52009-07-21 15:55:18 -0700814 mRawContactIds.clear();
Jeff Sharkeye0408762009-11-03 12:57:16 -0800815
Megha Joshid5afd8a2009-09-22 09:38:41 -0700816 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700817 mWritableSourcesCnt = 0;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800818 mAllRestricted = true;
Jeff Sharkey827762d2009-12-23 11:06:08 -0800819 mPrimaryPhoneUri = null;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800820
Megha Joshid5afd8a2009-09-22 09:38:41 -0700821 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700822
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700823 final Context context = this;
824 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700825
Evan Millar66388be2009-05-28 15:41:07 -0700826 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700827 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700828 for (Entity entity: mEntities) {
829 final ContentValues entValues = entity.getEntityValues();
830 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700831 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700832
Jeff Sharkeye0408762009-11-03 12:57:16 -0800833 // Mark when this contact has any unrestricted components
834 final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
835 if (!isRestricted) mAllRestricted = false;
836
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700837 if (!mRawContactIds.contains(rawContactId)) {
838 mRawContactIds.add(rawContactId);
839 }
Megha Joshic508bd82009-10-01 17:31:49 -0700840 ContactsSource contactsSource = sources.getInflatedSource(accountType,
841 ContactsSource.LEVEL_SUMMARY);
842 if (contactsSource != null && contactsSource.readOnly) {
843 mReadOnlySourcesCnt += 1;
844 } else {
845 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700846 mWritableRawContactIds.add(rawContactId);
847 }
Megha Joshic508bd82009-10-01 17:31:49 -0700848
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700849
Evan Millar11d628c2009-09-02 08:55:01 -0700850 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700851 final ContentValues entryValues = subValue.values;
852 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
853
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700854 final long dataId = entryValues.getAsLong(Data._ID);
855 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
856 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700857
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700858 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700859 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700860 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800861
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700862 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
863 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800864
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700865 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700866 final boolean isSuperPrimary = entryValues.getAsInteger(
867 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700868
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700869 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700870 // Build phone entries
871 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700872
Evan Millarff04a272009-09-26 15:23:37 -0700873 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700874 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700875 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700876 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700877
Jeff Sharkey827762d2009-12-23 11:06:08 -0800878 // Remember super-primary phone
879 if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
880
Evan Millarff04a272009-09-26 15:23:37 -0700881 entry.isPrimary = isSuperPrimary;
882 mPhoneEntries.add(entry);
883
884 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
885 || mShowSmsLinksForAllPhones) {
886 // Add an SMS entry
887 if (kind.iconAltRes > 0) {
888 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700889 }
Evan Millarff04a272009-09-26 15:23:37 -0700890 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700891 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700892 // Build email entries
893 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700894 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700895 entry.isPrimary = isSuperPrimary;
896 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700897
898 // When Email rows have status, create additional Im row
899 final DataStatus status = mStatuses.get(entry.id);
900 if (status != null) {
901 final String imMime = Im.CONTENT_ITEM_TYPE;
902 final DataKind imKind = sources.getKindOrFallback(accountType,
903 imMime, this, ContactsSource.LEVEL_MIMETYPES);
904 final ViewEntry imEntry = ViewEntry.fromValues(context,
905 imMime, imKind, rawContactId, dataId, entryValues);
906 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
907 imEntry.applyStatus(status, false);
908 mImEntries.add(imEntry);
909 }
910 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700911 // Build postal entries
912 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700913 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700914 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700915 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
916 // Build IM entries
917 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700918 if (TextUtils.isEmpty(entry.label)) {
919 entry.label = getString(R.string.chat).toLowerCase();
920 }
Evan Millar11d628c2009-09-02 08:55:01 -0700921
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700922 // Apply presence and status details when available
923 final DataStatus status = mStatuses.get(entry.id);
924 if (status != null) {
925 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700926 }
Evan Millarff04a272009-09-26 15:23:37 -0700927 mImEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800928 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
929 (hasData || !TextUtils.isEmpty(entry.label))) {
930 // Build organization entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700931 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
932
933 final boolean duplicatesTitle =
934 isNameRawContact
935 && mDisplayNameSource == DisplayNameSources.ORGANIZATION
936 && !hasData;
937
938 if (!duplicatesTitle) {
939 entry.uri = null;
940 mOrganizationEntries.add(entry);
941 }
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800942 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
943 // Build nickname entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700944 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
945
946 final boolean duplicatesTitle =
947 isNameRawContact
948 && mDisplayNameSource == DisplayNameSources.NICKNAME;
949
950 if (!duplicatesTitle) {
951 entry.uri = null;
952 mNicknameEntries.add(entry);
953 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700954 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700955 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700956 entry.uri = null;
Daniel Lehmann0e322012010-02-25 16:09:46 -0800957 entry.maxLines = 100;
Evan Millar11d628c2009-09-02 08:55:01 -0700958 mOtherEntries.add(entry);
Evan Millar951fa9c2009-10-26 14:57:43 -0700959 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
960 // Build note entries
961 entry.uri = null;
962 entry.maxLines = 10;
963 try {
964 WebAddress webAddress = new WebAddress(entry.data);
Makoto Onuki7a707382009-11-19 13:35:55 +0900965 entry.intent = new Intent(Intent.ACTION_VIEW,
Evan Millar951fa9c2009-10-26 14:57:43 -0700966 Uri.parse(webAddress.toString()));
967 } catch (ParseException e) {
968 Log.e(TAG, "Couldn't parse website: " + entry.data);
969 }
970 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700971 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700972 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700973 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700974
975 // Use social summary when requested by external source
976 final DataStatus status = mStatuses.get(entry.id);
977 final boolean hasSocial = kind.actionBodySocial && status != null;
978 if (hasSocial) {
979 entry.applyStatus(status, true);
980 }
981
982 if (hasSocial || hasData) {
983 mOtherEntries.add(entry);
984 }
Evan Millar11d628c2009-09-02 08:55:01 -0700985 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700986 }
Evan Millar66388be2009-05-28 15:41:07 -0700987 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800988 }
989 }
990
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700991 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
992 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700993 if (kind.actionHeader == null) {
994 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700995 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700996 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700997 if (actionHeader == null) {
998 return null;
999 }
1000 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
1001 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -07001002
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001003 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -07001004 if (kind.actionBody == null) {
1005 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001006 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001007 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -07001008 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001009 }
Evan Millar5c22c3b2009-05-29 11:37:54 -07001010
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001011 /**
1012 * A basic structure with the data for a contact entry in the list.
1013 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001014 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
1015 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -07001016 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001017 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -07001018 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -07001019 public int secondaryActionIcon = -1;
1020 public Intent intent;
1021 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001022 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001023 public ArrayList<Long> ids = new ArrayList<Long>();
1024 public int collapseCount = 0;
1025
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001026 public int presence = -1;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001027
1028 public CharSequence footerLine = null;
1029
1030 private ViewEntry() {
1031 }
1032
1033 /**
1034 * Build new {@link ViewEntry} and populate from the given values.
1035 */
1036 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
1037 long rawContactId, long dataId, ContentValues values) {
1038 final ViewEntry entry = new ViewEntry();
1039 entry.context = context;
1040 entry.contactId = rawContactId;
1041 entry.id = dataId;
1042 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
1043 entry.mimetype = mimeType;
1044 entry.label = buildActionString(kind, values, false, context);
1045 entry.data = buildDataString(kind, values, context);
1046
1047 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
1048 entry.type = values.getAsInteger(kind.typeColumn);
1049 }
1050 if (kind.iconRes > 0) {
1051 entry.resPackageName = kind.resPackageName;
1052 entry.actionIcon = kind.iconRes;
1053 }
1054
1055 return entry;
1056 }
1057
1058 /**
1059 * Apply given {@link DataStatus} values over this {@link ViewEntry}
1060 *
1061 * @param fillData When true, the given status replaces {@link #data}
1062 * and {@link #footerLine}. Otherwise only {@link #presence}
1063 * is updated.
1064 */
1065 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
1066 presence = status.getPresence();
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001067 if (fillData && status.isValid()) {
1068 this.data = status.getStatus().toString();
1069 this.footerLine = status.getTimestampLabel(context);
1070 }
1071
1072 return this;
1073 }
1074
Evan Millar54a5c9f2009-06-23 17:41:09 -07001075 public boolean collapseWith(ViewEntry entry) {
1076 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -07001077 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001078 return false;
1079 }
1080
1081 // Choose the label associated with the highest type precedence.
1082 if (TypePrecedence.getTypePrecedence(mimetype, type)
1083 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
1084 type = entry.type;
1085 label = entry.label;
1086 }
1087
1088 // Choose the max of the maxLines and maxLabelLines values.
1089 maxLines = Math.max(maxLines, entry.maxLines);
1090 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1091
1092 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001093 if (StatusUpdates.getPresencePrecedence(presence)
1094 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1095 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001096 }
1097
1098 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001099 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001100
1101 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1102
1103 // Keep track of all the ids that have been collapsed with this one.
1104 ids.add(entry.id);
1105 collapseCount++;
1106 return true;
1107 }
1108
Evan Millaradb0f8c2009-09-28 17:20:50 -07001109 public boolean shouldCollapseWith(ViewEntry entry) {
1110 if (entry == null) {
1111 return false;
1112 }
1113
Makoto Onukic710b0e2009-10-13 15:43:24 -07001114 if (!ContactsUtils.areDataEqual(context, mimetype, data, entry.mimetype, entry.data)) {
1115 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001116 }
1117
Makoto Onukic710b0e2009-10-13 15:43:24 -07001118 if (!TextUtils.equals(mimetype, entry.mimetype)
1119 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1120 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001121 || actionIcon != entry.actionIcon) {
1122 return false;
1123 }
1124
1125 return true;
1126 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001127 }
1128
Evan Millar15e514d2009-08-04 10:14:57 -07001129 /** Cache of the children views of a row */
1130 static class ViewCache {
1131 public TextView label;
1132 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001133 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001134 public ImageView actionIcon;
1135 public ImageView presenceIcon;
1136 public ImageView primaryIcon;
1137 public ImageView secondaryActionButton;
1138 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001139
Evan Millar15e514d2009-08-04 10:14:57 -07001140 // Need to keep track of this too
1141 ViewEntry entry;
1142 }
1143
1144 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1145 implements View.OnClickListener {
1146
Evan Millar5c22c3b2009-05-29 11:37:54 -07001147
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001148 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1149 super(context, sections, SHOW_SEPARATORS);
1150 }
1151
Evan Millar15e514d2009-08-04 10:14:57 -07001152 public void onClick(View v) {
1153 Intent intent = (Intent) v.getTag();
1154 startActivity(intent);
1155 }
1156
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001157 @Override
1158 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001159 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001160 View v;
1161
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001162 ViewCache views;
1163
1164 // Check to see if we can reuse convertView
1165 if (convertView != null) {
1166 v = convertView;
1167 views = (ViewCache) v.getTag();
1168 } else {
1169 // Create a new view if needed
1170 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1171
1172 // Cache the children
1173 views = new ViewCache();
1174 views.label = (TextView) v.findViewById(android.R.id.text1);
1175 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001176 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001177 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1178 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1179 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1180 views.secondaryActionButton = (ImageView) v.findViewById(
1181 R.id.secondary_action_button);
1182 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001183 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001184 v.setTag(views);
1185 }
1186
1187 // Update the entry in the view cache
1188 views.entry = entry;
1189
1190 // Bind the data to the view
1191 bindView(v, entry);
1192 return v;
1193 }
1194
1195 @Override
1196 protected View newView(int position, ViewGroup parent) {
1197 // getView() handles this
1198 throw new UnsupportedOperationException();
1199 }
1200
1201 @Override
1202 protected void bindView(View view, ViewEntry entry) {
1203 final Resources resources = mContext.getResources();
1204 ViewCache views = (ViewCache) view.getTag();
1205
1206 // Set the label
1207 TextView label = views.label;
1208 setMaxLines(label, entry.maxLabelLines);
1209 label.setText(entry.label);
1210
1211 // Set the data
1212 TextView data = views.data;
1213 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001214 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001215 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001216 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1217 } else {
1218 data.setText(entry.data);
1219 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001220 setMaxLines(data, entry.maxLines);
1221 }
1222
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001223 // Set the footer
1224 if (!TextUtils.isEmpty(entry.footerLine)) {
1225 views.footer.setText(entry.footerLine);
1226 views.footer.setVisibility(View.VISIBLE);
1227 } else {
1228 views.footer.setVisibility(View.GONE);
1229 }
1230
Evan Millar15e514d2009-08-04 10:14:57 -07001231 // Set the primary icon
1232 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1233
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001234 // Set the action icon
1235 ImageView action = views.actionIcon;
1236 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001237 Drawable actionIcon;
1238 if (entry.resPackageName != null) {
1239 // Load external resources through PackageManager
1240 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1241 entry.actionIcon, null);
1242 } else {
1243 actionIcon = resources.getDrawable(entry.actionIcon);
1244 }
1245 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001246 action.setVisibility(View.VISIBLE);
1247 } else {
1248 // Things should still line up as if there was an icon, so make it invisible
1249 action.setVisibility(View.INVISIBLE);
1250 }
1251
1252 // Set the presence icon
Bai Tao107736c2010-03-12 08:00:42 +08001253 Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
1254 mContext, entry.presence);
Evan Millar15e514d2009-08-04 10:14:57 -07001255 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001256 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001257 presenceIconView.setImageDrawable(presenceIcon);
1258 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001259 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001260 presenceIconView.setVisibility(View.GONE);
1261 }
1262
1263 // Set the secondary action button
1264 ImageView secondaryActionView = views.secondaryActionButton;
1265 Drawable secondaryActionIcon = null;
1266 if (entry.secondaryActionIcon != -1) {
1267 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1268 }
1269 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1270 secondaryActionView.setImageDrawable(secondaryActionIcon);
1271 secondaryActionView.setTag(entry.secondaryIntent);
1272 secondaryActionView.setVisibility(View.VISIBLE);
1273 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1274 } else {
1275 secondaryActionView.setVisibility(View.GONE);
1276 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001277 }
1278 }
1279
1280 private void setMaxLines(TextView textView, int maxLines) {
1281 if (maxLines == 1) {
1282 textView.setSingleLine(true);
1283 textView.setEllipsize(TextUtils.TruncateAt.END);
1284 } else {
1285 textView.setSingleLine(false);
1286 textView.setMaxLines(maxLines);
1287 textView.setEllipsize(null);
1288 }
1289 }
1290 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001291
1292 private interface StatusQuery {
1293 final String[] PROJECTION = new String[] {
1294 Data._ID,
1295 Data.STATUS,
1296 Data.STATUS_RES_PACKAGE,
1297 Data.STATUS_ICON,
1298 Data.STATUS_LABEL,
1299 Data.STATUS_TIMESTAMP,
1300 Data.PRESENCE,
1301 };
1302
1303 final int _ID = 0;
1304 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001305
1306 @Override
1307 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1308 boolean globalSearch) {
1309 if (globalSearch) {
1310 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1311 } else {
1312 ContactsSearchManager.startSearch(this, initialQuery);
1313 }
1314 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001315}